import { describe, it, expect, beforeEach, vi } from "vitest"; import { Test, TestingModule } from "@nestjs/testing"; import { TasksController } from "./tasks.controller"; import { TasksService } from "./tasks.service"; import { TaskStatus, TaskPriority } from "@prisma/client"; import { AuthGuard } from "../auth/guards/auth.guard"; import { WorkspaceGuard } from "../common/guards/workspace.guard"; import { PermissionGuard } from "../common/guards/permission.guard"; import { ExecutionContext } from "@nestjs/common"; describe("TasksController", () => { let controller: TasksController; let service: TasksService; const mockTasksService = { create: vi.fn(), findAll: vi.fn(), findOne: vi.fn(), update: vi.fn(), remove: vi.fn(), }; const mockAuthGuard = { canActivate: vi.fn((context: ExecutionContext) => { const request = context.switchToHttp().getRequest(); request.user = { id: "550e8400-e29b-41d4-a716-446655440002", email: "test@example.com", name: "Test User", workspaceId: "550e8400-e29b-41d4-a716-446655440001", }; return true; }), }; const mockWorkspaceGuard = { canActivate: vi.fn(() => true), }; const mockPermissionGuard = { canActivate: vi.fn(() => true), }; const mockWorkspaceId = "550e8400-e29b-41d4-a716-446655440001"; const mockUserId = "550e8400-e29b-41d4-a716-446655440002"; const mockTaskId = "550e8400-e29b-41d4-a716-446655440003"; const mockRequest = { user: { id: mockUserId, email: "test@example.com", name: "Test User", workspaceId: mockWorkspaceId, }, }; const mockTask = { id: mockTaskId, workspaceId: mockWorkspaceId, title: "Test Task", description: "Test Description", status: TaskStatus.NOT_STARTED, priority: TaskPriority.MEDIUM, dueDate: new Date("2026-02-01T12:00:00Z"), assigneeId: null, creatorId: mockUserId, projectId: null, parentId: null, sortOrder: 0, metadata: {}, createdAt: new Date(), updatedAt: new Date(), completedAt: null, }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [TasksController], providers: [ { provide: TasksService, useValue: mockTasksService, }, ], }) .overrideGuard(AuthGuard) .useValue(mockAuthGuard) .overrideGuard(WorkspaceGuard) .useValue(mockWorkspaceGuard) .overrideGuard(PermissionGuard) .useValue(mockPermissionGuard) .compile(); controller = module.get(TasksController); service = module.get(TasksService); vi.clearAllMocks(); }); it("should be defined", () => { expect(controller).toBeDefined(); }); describe("create", () => { it("should create a task", async () => { const createDto = { title: "New Task", description: "Task description", }; mockTasksService.create.mockResolvedValue(mockTask); const result = await controller.create(createDto, mockWorkspaceId, mockRequest.user); expect(result).toEqual(mockTask); expect(service.create).toHaveBeenCalledWith(mockWorkspaceId, mockUserId, createDto); }); }); describe("findAll", () => { it("should return paginated tasks", async () => { const query = { page: 1, limit: 50, }; const paginatedResult = { data: [mockTask], meta: { total: 1, page: 1, limit: 50, totalPages: 1, }, }; mockTasksService.findAll.mockResolvedValue(paginatedResult); const result = await controller.findAll(query, mockWorkspaceId, mockRequest.user); expect(result).toEqual(paginatedResult); expect(service.findAll).toHaveBeenCalledWith( { ...query, workspaceId: mockWorkspaceId, }, mockUserId ); }); it("should extract workspaceId from request.user if not in query", async () => { const query = {}; mockTasksService.findAll.mockResolvedValue({ data: [], meta: { total: 0, page: 1, limit: 50, totalPages: 0 }, }); await controller.findAll(query as any, mockWorkspaceId, mockRequest.user); expect(service.findAll).toHaveBeenCalledWith( expect.objectContaining({ workspaceId: mockWorkspaceId, }), mockUserId ); }); }); describe("findOne", () => { it("should return a task by id", async () => { mockTasksService.findOne.mockResolvedValue(mockTask); const result = await controller.findOne(mockTaskId, mockWorkspaceId, mockRequest.user); expect(result).toEqual(mockTask); expect(service.findOne).toHaveBeenCalledWith(mockTaskId, mockWorkspaceId, mockUserId); }); it("should throw error if workspaceId not found", async () => { // This test doesn't make sense anymore since workspaceId is extracted by the guard // The guard would reject the request before it reaches the controller // We can test that the controller properly uses the provided workspaceId instead mockTasksService.findOne.mockResolvedValue(mockTask); const result = await controller.findOne(mockTaskId, mockWorkspaceId, mockRequest.user); expect(result).toEqual(mockTask); expect(service.findOne).toHaveBeenCalledWith(mockTaskId, mockWorkspaceId, mockUserId); }); }); describe("update", () => { it("should update a task", async () => { const updateDto = { title: "Updated Task", status: TaskStatus.IN_PROGRESS, }; const updatedTask = { ...mockTask, ...updateDto }; mockTasksService.update.mockResolvedValue(updatedTask); const result = await controller.update( mockTaskId, updateDto, mockWorkspaceId, mockRequest.user ); expect(result).toEqual(updatedTask); expect(service.update).toHaveBeenCalledWith( mockTaskId, mockWorkspaceId, mockUserId, updateDto ); }); it("should throw error if workspaceId not found", async () => { // This test doesn't make sense anymore since workspaceId is extracted by the guard // The guard would reject the request before it reaches the controller // We can test that the controller properly uses the provided parameters instead const updateDto = { title: "Test" }; const updatedTask = { ...mockTask, title: "Test" }; mockTasksService.update.mockResolvedValue(updatedTask); const result = await controller.update( mockTaskId, updateDto, mockWorkspaceId, mockRequest.user ); expect(result).toEqual(updatedTask); expect(service.update).toHaveBeenCalledWith( mockTaskId, mockWorkspaceId, mockUserId, updateDto ); }); }); describe("remove", () => { it("should delete a task", async () => { mockTasksService.remove.mockResolvedValue(undefined); await controller.remove(mockTaskId, mockWorkspaceId, mockRequest.user); expect(service.remove).toHaveBeenCalledWith(mockTaskId, mockWorkspaceId, mockUserId); }); it("should throw error if workspaceId not found", async () => { // This test doesn't make sense anymore since workspaceId is extracted by the guard // The guard would reject the request before it reaches the controller // We can test that the controller properly uses the provided parameters instead mockTasksService.remove.mockResolvedValue(undefined); await controller.remove(mockTaskId, mockWorkspaceId, mockRequest.user); expect(service.remove).toHaveBeenCalledWith(mockTaskId, mockWorkspaceId, mockUserId); }); }); });