import { describe, it, expect, beforeEach, vi } from "vitest"; import { Test, TestingModule } from "@nestjs/testing"; import { ExecutionContext } from "@nestjs/common"; // Mock @prisma/client BEFORE importing other modules vi.mock("@prisma/client", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, JobStepPhase: { SETUP: "SETUP", EXECUTION: "EXECUTION", VALIDATION: "VALIDATION", CLEANUP: "CLEANUP", }, JobStepType: { COMMAND: "COMMAND", AI_ACTION: "AI_ACTION", GATE: "GATE", ARTIFACT: "ARTIFACT", }, JobStepStatus: { PENDING: "PENDING", RUNNING: "RUNNING", COMPLETED: "COMPLETED", FAILED: "FAILED", SKIPPED: "SKIPPED", }, }; }); // Import after mocking import { JobStepsController } from "./job-steps.controller"; import { JobStepsService } from "./job-steps.service"; import { JobStepPhase, JobStepType, JobStepStatus } from "@prisma/client"; import { AuthGuard } from "../auth/guards/auth.guard"; import { WorkspaceGuard } from "../common/guards/workspace.guard"; import { PermissionGuard } from "../common/guards/permission.guard"; describe("JobStepsController", () => { let controller: JobStepsController; let service: JobStepsService; const mockJobStepsService = { findAllByJob: vi.fn(), findOne: vi.fn(), create: vi.fn(), update: vi.fn(), startStep: vi.fn(), completeStep: vi.fn(), failStep: vi.fn(), }; const mockAuthGuard = { canActivate: vi.fn((context: ExecutionContext) => { const request = context.switchToHttp().getRequest(); request.user = { id: "user-123", workspaceId: "workspace-123", }; return true; }), }; const mockWorkspaceGuard = { canActivate: vi.fn(() => true), }; const mockPermissionGuard = { canActivate: vi.fn(() => true), }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [JobStepsController], providers: [ { provide: JobStepsService, useValue: mockJobStepsService, }, ], }) .overrideGuard(AuthGuard) .useValue(mockAuthGuard) .overrideGuard(WorkspaceGuard) .useValue(mockWorkspaceGuard) .overrideGuard(PermissionGuard) .useValue(mockPermissionGuard) .compile(); controller = module.get(JobStepsController); service = module.get(JobStepsService); // Clear all mocks before each test vi.clearAllMocks(); }); it("should be defined", () => { expect(controller).toBeDefined(); }); describe("findAll", () => { it("should return all steps for a job", async () => { const jobId = "job-123"; const mockSteps = [ { id: "step-1", jobId, ordinal: 1, phase: JobStepPhase.SETUP, name: "Clone repo", type: JobStepType.COMMAND, status: JobStepStatus.COMPLETED, output: "Cloned successfully", tokensInput: null, tokensOutput: null, startedAt: new Date("2024-01-01T10:00:00Z"), completedAt: new Date("2024-01-01T10:00:05Z"), durationMs: 5000, }, { id: "step-2", jobId, ordinal: 2, phase: JobStepPhase.EXECUTION, name: "Run tests", type: JobStepType.COMMAND, status: JobStepStatus.RUNNING, output: null, tokensInput: null, tokensOutput: null, startedAt: new Date("2024-01-01T10:00:05Z"), completedAt: null, durationMs: null, }, ]; mockJobStepsService.findAllByJob.mockResolvedValue(mockSteps); const result = await controller.findAll(jobId); expect(result).toEqual(mockSteps); expect(service.findAllByJob).toHaveBeenCalledWith(jobId); }); }); describe("findOne", () => { it("should return a single step by ID", async () => { const jobId = "job-123"; const stepId = "step-123"; const mockStep = { id: stepId, jobId, ordinal: 1, phase: JobStepPhase.SETUP, name: "Clone repo", type: JobStepType.COMMAND, status: JobStepStatus.COMPLETED, output: "Cloned successfully", tokensInput: null, tokensOutput: null, startedAt: new Date("2024-01-01T10:00:00Z"), completedAt: new Date("2024-01-01T10:00:05Z"), durationMs: 5000, }; mockJobStepsService.findOne.mockResolvedValue(mockStep); const result = await controller.findOne(jobId, stepId); expect(result).toEqual(mockStep); expect(service.findOne).toHaveBeenCalledWith(stepId, jobId); }); }); });