Fixed failing tests in job-steps.service.spec.ts and job-steps.controller.spec.ts caused by undefined Prisma enum imports in the test environment. Root cause: When importing JobStepPhase, JobStepType, and JobStepStatus from @prisma/client in the test environment with mocked Prisma, the enums were undefined, causing "Cannot read properties of undefined" errors. Solution: Used vi.mock() with importOriginal to mock the @prisma/client module and explicitly provide enum values while preserving other exports like PrismaClient. Changes: - Added vi.mock() for @prisma/client in both test files - Defined all three enums (JobStepPhase, JobStepType, JobStepStatus) with their values - Moved imports after the mock setup to ensure proper initialization Test results: All 16 job-steps tests now passing (13 service + 3 controller) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
177 lines
4.8 KiB
TypeScript
177 lines
4.8 KiB
TypeScript
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<typeof import("@prisma/client")>();
|
|
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>(JobStepsController);
|
|
service = module.get<JobStepsService>(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);
|
|
});
|
|
});
|
|
});
|