import { describe, it, expect, beforeEach, vi } from "vitest"; import { Test, TestingModule } from "@nestjs/testing"; import { QualityGateConfigService } from "./quality-gate-config.service"; import { PrismaService } from "../prisma/prisma.service"; import { NotFoundException, ConflictException } from "@nestjs/common"; describe("QualityGateConfigService", () => { let service: QualityGateConfigService; let prisma: PrismaService; const mockPrismaService = { qualityGate: { create: vi.fn(), findMany: vi.fn(), findUnique: vi.fn(), update: vi.fn(), delete: vi.fn(), count: vi.fn(), }, $transaction: vi.fn(), }; const mockWorkspaceId = "550e8400-e29b-41d4-a716-446655440001"; const mockGateId = "550e8400-e29b-41d4-a716-446655440002"; const mockQualityGate = { id: mockGateId, workspaceId: mockWorkspaceId, name: "Build Check", description: "Verify code compiles without errors", type: "build", command: "pnpm build", expectedOutput: null, isRegex: false, required: true, order: 1, isEnabled: true, createdAt: new Date(), updatedAt: new Date(), }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ QualityGateConfigService, { provide: PrismaService, useValue: mockPrismaService, }, ], }).compile(); service = module.get(QualityGateConfigService); prisma = module.get(PrismaService); vi.clearAllMocks(); }); it("should be defined", () => { expect(service).toBeDefined(); }); describe("create", () => { it("should create a quality gate successfully", async () => { const createDto = { name: "Build Check", description: "Verify code compiles without errors", type: "build" as const, command: "pnpm build", required: true, order: 1, }; mockPrismaService.qualityGate.create.mockResolvedValue(mockQualityGate); const result = await service.create(mockWorkspaceId, createDto); expect(result).toEqual(mockQualityGate); expect(prisma.qualityGate.create).toHaveBeenCalledWith({ data: { workspaceId: mockWorkspaceId, name: createDto.name, description: createDto.description, type: createDto.type, command: createDto.command, expectedOutput: null, isRegex: false, required: true, order: 1, isEnabled: true, }, }); }); it("should use default values when optional fields are not provided", async () => { const createDto = { name: "Test Gate", type: "test" as const, }; mockPrismaService.qualityGate.create.mockResolvedValue({ ...mockQualityGate, name: createDto.name, type: createDto.type, }); await service.create(mockWorkspaceId, createDto); expect(prisma.qualityGate.create).toHaveBeenCalledWith({ data: { workspaceId: mockWorkspaceId, name: createDto.name, description: null, type: createDto.type, command: null, expectedOutput: null, isRegex: false, required: true, order: 0, isEnabled: true, }, }); }); }); describe("findAll", () => { it("should return all quality gates for a workspace", async () => { const mockGates = [mockQualityGate]; mockPrismaService.qualityGate.findMany.mockResolvedValue(mockGates); const result = await service.findAll(mockWorkspaceId); expect(result).toEqual(mockGates); expect(prisma.qualityGate.findMany).toHaveBeenCalledWith({ where: { workspaceId: mockWorkspaceId }, orderBy: { order: "asc" }, }); }); it("should return empty array when no gates exist", async () => { mockPrismaService.qualityGate.findMany.mockResolvedValue([]); const result = await service.findAll(mockWorkspaceId); expect(result).toEqual([]); }); }); describe("findEnabled", () => { it("should return only enabled quality gates ordered by priority", async () => { const mockGates = [mockQualityGate]; mockPrismaService.qualityGate.findMany.mockResolvedValue(mockGates); const result = await service.findEnabled(mockWorkspaceId); expect(result).toEqual(mockGates); expect(prisma.qualityGate.findMany).toHaveBeenCalledWith({ where: { workspaceId: mockWorkspaceId, isEnabled: true, }, orderBy: { order: "asc" }, }); }); }); describe("findOne", () => { it("should return a quality gate by id", async () => { mockPrismaService.qualityGate.findUnique.mockResolvedValue(mockQualityGate); const result = await service.findOne(mockWorkspaceId, mockGateId); expect(result).toEqual(mockQualityGate); expect(prisma.qualityGate.findUnique).toHaveBeenCalledWith({ where: { id: mockGateId, workspaceId: mockWorkspaceId, }, }); }); it("should throw NotFoundException when gate does not exist", async () => { mockPrismaService.qualityGate.findUnique.mockResolvedValue(null); await expect(service.findOne(mockWorkspaceId, mockGateId)).rejects.toThrow(NotFoundException); }); }); describe("update", () => { it("should update a quality gate successfully", async () => { const updateDto = { name: "Updated Build Check", required: false, }; const updatedGate = { ...mockQualityGate, ...updateDto, }; mockPrismaService.qualityGate.findUnique.mockResolvedValue(mockQualityGate); mockPrismaService.qualityGate.update.mockResolvedValue(updatedGate); const result = await service.update(mockWorkspaceId, mockGateId, updateDto); expect(result).toEqual(updatedGate); expect(prisma.qualityGate.update).toHaveBeenCalledWith({ where: { id: mockGateId }, data: updateDto, }); }); it("should throw NotFoundException when gate does not exist", async () => { const updateDto = { name: "Updated" }; mockPrismaService.qualityGate.findUnique.mockResolvedValue(null); await expect(service.update(mockWorkspaceId, mockGateId, updateDto)).rejects.toThrow( NotFoundException ); }); }); describe("delete", () => { it("should delete a quality gate successfully", async () => { mockPrismaService.qualityGate.findUnique.mockResolvedValue(mockQualityGate); mockPrismaService.qualityGate.delete.mockResolvedValue(mockQualityGate); await service.delete(mockWorkspaceId, mockGateId); expect(prisma.qualityGate.delete).toHaveBeenCalledWith({ where: { id: mockGateId }, }); }); it("should throw NotFoundException when gate does not exist", async () => { mockPrismaService.qualityGate.findUnique.mockResolvedValue(null); await expect(service.delete(mockWorkspaceId, mockGateId)).rejects.toThrow(NotFoundException); }); }); describe("reorder", () => { it("should reorder gates successfully", async () => { const gateIds = ["gate1", "gate2", "gate3"]; const mockGates = gateIds.map((id, index) => ({ ...mockQualityGate, id, order: index, })); mockPrismaService.$transaction.mockImplementation((callback) => { return callback(mockPrismaService); }); mockPrismaService.qualityGate.update.mockResolvedValue(mockGates[0]); mockPrismaService.qualityGate.findMany.mockResolvedValue(mockGates); const result = await service.reorder(mockWorkspaceId, gateIds); expect(result).toEqual(mockGates); expect(prisma.$transaction).toHaveBeenCalled(); }); }); describe("seedDefaults", () => { it("should seed default quality gates for a workspace", async () => { const mockDefaultGates = [ { id: "1", workspaceId: mockWorkspaceId, name: "Build Check", description: null, type: "build", command: "pnpm build", expectedOutput: null, isRegex: false, required: true, order: 1, isEnabled: true, createdAt: new Date(), updatedAt: new Date(), }, { id: "2", workspaceId: mockWorkspaceId, name: "Lint Check", description: null, type: "lint", command: "pnpm lint", expectedOutput: null, isRegex: false, required: true, order: 2, isEnabled: true, createdAt: new Date(), updatedAt: new Date(), }, ]; mockPrismaService.$transaction.mockImplementation((callback) => { return callback(mockPrismaService); }); mockPrismaService.qualityGate.create.mockImplementation((args) => Promise.resolve({ ...mockDefaultGates[0], ...args.data, }) ); mockPrismaService.qualityGate.findMany.mockResolvedValue(mockDefaultGates); const result = await service.seedDefaults(mockWorkspaceId); expect(result.length).toBeGreaterThan(0); expect(prisma.$transaction).toHaveBeenCalled(); }); }); describe("toOrchestratorFormat", () => { it("should convert database gates to orchestrator format", () => { const gates = [mockQualityGate]; const result = service.toOrchestratorFormat(gates); expect(result).toHaveLength(1); expect(result[0]).toMatchObject({ id: mockQualityGate.id, name: mockQualityGate.name, description: mockQualityGate.description, type: mockQualityGate.type, command: mockQualityGate.command, required: mockQualityGate.required, order: mockQualityGate.order, }); }); it("should handle regex patterns correctly", () => { const gateWithRegex = { ...mockQualityGate, expectedOutput: "Coverage: (\\d+)%", isRegex: true, }; const result = service.toOrchestratorFormat([gateWithRegex]); expect(result[0]?.expectedOutput).toBeInstanceOf(RegExp); }); it("should handle string patterns correctly", () => { const gateWithString = { ...mockQualityGate, expectedOutput: "All tests passed", isRegex: false, }; const result = service.toOrchestratorFormat([gateWithString]); expect(typeof result[0]?.expectedOutput).toBe("string"); }); }); });