import { describe, it, expect, beforeEach, vi } from "vitest"; import { Test, TestingModule } from "@nestjs/testing"; import { EventsService } from "./events.service"; import { PrismaService } from "../prisma/prisma.service"; import { ActivityService } from "../activity/activity.service"; import { WebSocketGateway } from "../websocket/websocket.gateway"; import { NotFoundException } from "@nestjs/common"; import { Prisma } from "@prisma/client"; describe("EventsService", () => { let service: EventsService; let prisma: PrismaService; let activityService: ActivityService; let wsGateway: WebSocketGateway; const mockPrismaService = { event: { create: vi.fn(), findMany: vi.fn(), count: vi.fn(), findUnique: vi.fn(), update: vi.fn(), delete: vi.fn(), }, }; const mockActivityService = { logEventCreated: vi.fn(), logEventUpdated: vi.fn(), logEventDeleted: vi.fn(), }; const mockWebSocketGateway = { emitEventCreated: vi.fn(), emitEventUpdated: vi.fn(), emitEventDeleted: vi.fn(), }; const mockWorkspaceId = "550e8400-e29b-41d4-a716-446655440001"; const mockUserId = "550e8400-e29b-41d4-a716-446655440002"; const mockEventId = "550e8400-e29b-41d4-a716-446655440003"; const mockEvent = { id: mockEventId, workspaceId: mockWorkspaceId, title: "Test Event", description: "Test Description", startTime: new Date("2026-02-01T10:00:00Z"), endTime: new Date("2026-02-01T11:00:00Z"), allDay: false, location: "Conference Room A", recurrence: null, creatorId: mockUserId, projectId: null, metadata: {}, createdAt: new Date(), updatedAt: new Date(), }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ EventsService, { provide: PrismaService, useValue: mockPrismaService, }, { provide: ActivityService, useValue: mockActivityService, }, { provide: WebSocketGateway, useValue: mockWebSocketGateway, }, ], }).compile(); service = module.get(EventsService); prisma = module.get(PrismaService); activityService = module.get(ActivityService); wsGateway = module.get(WebSocketGateway); vi.clearAllMocks(); }); it("should be defined", () => { expect(service).toBeDefined(); }); describe("create", () => { it("should create an event and log activity", async () => { const createDto = { title: "New Event", description: "Event description", startTime: new Date("2026-02-01T10:00:00Z"), allDay: false, }; mockPrismaService.event.create.mockResolvedValue(mockEvent); mockActivityService.logEventCreated.mockResolvedValue({}); const result = await service.create(mockWorkspaceId, mockUserId, createDto); expect(result).toEqual(mockEvent); expect(prisma.event.create).toHaveBeenCalledWith({ data: { ...createDto, workspace: { connect: { id: mockWorkspaceId } }, creator: { connect: { id: mockUserId } }, project: undefined, allDay: false, metadata: {}, }, include: { creator: { select: { id: true, name: true, email: true }, }, project: { select: { id: true, name: true, color: true }, }, }, }); expect(activityService.logEventCreated).toHaveBeenCalledWith( mockWorkspaceId, mockUserId, mockEvent.id, { title: mockEvent.title } ); }); }); describe("findAll", () => { it("should return paginated events with default pagination", async () => { const events = [mockEvent]; mockPrismaService.event.findMany.mockResolvedValue(events); mockPrismaService.event.count.mockResolvedValue(1); const result = await service.findAll({ workspaceId: mockWorkspaceId }); expect(result).toEqual({ data: events, meta: { total: 1, page: 1, limit: 50, totalPages: 1, }, }); }); it("should filter by date range", async () => { const startFrom = new Date("2026-02-01"); const startTo = new Date("2026-02-28"); mockPrismaService.event.findMany.mockResolvedValue([mockEvent]); mockPrismaService.event.count.mockResolvedValue(1); await service.findAll({ workspaceId: mockWorkspaceId, startFrom, startTo, }); expect(prisma.event.findMany).toHaveBeenCalledWith( expect.objectContaining({ where: { workspaceId: mockWorkspaceId, startTime: { gte: startFrom, lte: startTo, }, }, }) ); }); }); describe("findOne", () => { it("should return an event by id", async () => { mockPrismaService.event.findUnique.mockResolvedValue(mockEvent); const result = await service.findOne(mockEventId, mockWorkspaceId); expect(result).toEqual(mockEvent); }); it("should throw NotFoundException if event not found", async () => { mockPrismaService.event.findUnique.mockResolvedValue(null); await expect(service.findOne(mockEventId, mockWorkspaceId)).rejects.toThrow( NotFoundException ); }); it("should enforce workspace isolation when finding event", async () => { const otherWorkspaceId = "550e8400-e29b-41d4-a716-446655440099"; mockPrismaService.event.findUnique.mockResolvedValue(null); await expect(service.findOne(mockEventId, otherWorkspaceId)).rejects.toThrow( NotFoundException ); expect(prisma.event.findUnique).toHaveBeenCalledWith({ where: { id: mockEventId, workspaceId: otherWorkspaceId, }, include: expect.any(Object), }); }); }); describe("update", () => { it("should update an event and log activity", async () => { const updateDto = { title: "Updated Event", location: "New Location", }; mockPrismaService.event.findUnique.mockResolvedValue(mockEvent); mockPrismaService.event.update.mockResolvedValue({ ...mockEvent, ...updateDto, }); mockActivityService.logEventUpdated.mockResolvedValue({}); const result = await service.update( mockEventId, mockWorkspaceId, mockUserId, updateDto ); expect(result.title).toBe("Updated Event"); expect(activityService.logEventUpdated).toHaveBeenCalled(); }); it("should throw NotFoundException if event not found", async () => { mockPrismaService.event.findUnique.mockResolvedValue(null); await expect( service.update(mockEventId, mockWorkspaceId, mockUserId, { title: "Test" }) ).rejects.toThrow(NotFoundException); }); it("should enforce workspace isolation when updating event", async () => { const otherWorkspaceId = "550e8400-e29b-41d4-a716-446655440099"; mockPrismaService.event.findUnique.mockResolvedValue(null); await expect( service.update(mockEventId, otherWorkspaceId, mockUserId, { title: "Hacked" }) ).rejects.toThrow(NotFoundException); expect(prisma.event.findUnique).toHaveBeenCalledWith({ where: { id: mockEventId, workspaceId: otherWorkspaceId }, }); }); }); describe("remove", () => { it("should delete an event and log activity", async () => { mockPrismaService.event.findUnique.mockResolvedValue(mockEvent); mockPrismaService.event.delete.mockResolvedValue(mockEvent); mockActivityService.logEventDeleted.mockResolvedValue({}); await service.remove(mockEventId, mockWorkspaceId, mockUserId); expect(prisma.event.delete).toHaveBeenCalled(); expect(activityService.logEventDeleted).toHaveBeenCalled(); }); it("should throw NotFoundException if event not found", async () => { mockPrismaService.event.findUnique.mockResolvedValue(null); await expect( service.remove(mockEventId, mockWorkspaceId, mockUserId) ).rejects.toThrow(NotFoundException); }); it("should enforce workspace isolation when deleting event", async () => { const otherWorkspaceId = "550e8400-e29b-41d4-a716-446655440099"; mockPrismaService.event.findUnique.mockResolvedValue(null); await expect( service.remove(mockEventId, otherWorkspaceId, mockUserId) ).rejects.toThrow(NotFoundException); expect(prisma.event.findUnique).toHaveBeenCalledWith({ where: { id: mockEventId, workspaceId: otherWorkspaceId }, }); }); }); describe("database constraint violations", () => { it("should handle foreign key constraint violations on create", async () => { const createDto = { title: "Event with invalid project", startTime: new Date("2026-02-01T10:00:00Z"), projectId: "non-existent-project-id", }; const prismaError = new Prisma.PrismaClientKnownRequestError( "Foreign key constraint failed", { code: "P2003", clientVersion: "5.0.0", } ); mockPrismaService.event.create.mockRejectedValue(prismaError); await expect( service.create(mockWorkspaceId, mockUserId, createDto) ).rejects.toThrow(Prisma.PrismaClientKnownRequestError); }); it("should handle foreign key constraint violations on update", async () => { const updateDto = { projectId: "non-existent-project-id", }; mockPrismaService.event.findUnique.mockResolvedValue(mockEvent); const prismaError = new Prisma.PrismaClientKnownRequestError( "Foreign key constraint failed", { code: "P2003", clientVersion: "5.0.0", } ); mockPrismaService.event.update.mockRejectedValue(prismaError); await expect( service.update(mockEventId, mockWorkspaceId, mockUserId, updateDto) ).rejects.toThrow(Prisma.PrismaClientKnownRequestError); }); }); });