import { describe, it, expect, beforeEach } from "vitest"; import { CompletionVerificationService } from "./completion-verification.service"; import { VerificationContext } from "./interfaces"; describe("CompletionVerificationService", () => { let service: CompletionVerificationService; let baseContext: VerificationContext; beforeEach(() => { service = new CompletionVerificationService(); baseContext = { taskId: "task-1", workspaceId: "workspace-1", agentId: "agent-1", claimMessage: "Completed task", filesChanged: ["src/feature.ts"], outputLogs: "Implementation complete", previousAttempts: 0, }; }); describe("verify", () => { it("should verify using all registered strategies", async () => { const context: VerificationContext = { ...baseContext, filesChanged: ["src/feature.ts", "src/feature.spec.ts"], testResults: { total: 10, passed: 10, failed: 0, skipped: 0, coverage: 90, }, buildOutput: "Build successful", }; const result = await service.verify(context); expect(result.verdict).toBe("complete"); expect(result.isComplete).toBe(true); expect(result.confidence).toBeGreaterThan(80); expect(result.issues).toHaveLength(0); }); it("should aggregate issues from all strategies", async () => { const context: VerificationContext = { ...baseContext, filesChanged: [], testResults: { total: 10, passed: 7, failed: 3, skipped: 0, coverage: 70, }, buildOutput: "error TS2304: Cannot find name", }; const result = await service.verify(context); expect(result.verdict).toBe("incomplete"); expect(result.isComplete).toBe(false); expect(result.issues.length).toBeGreaterThan(0); expect(result.issues.some((i) => i.type === "missing-files")).toBe(true); expect(result.issues.some((i) => i.type === "test-failure")).toBe(true); expect(result.issues.some((i) => i.type === "build-error")).toBe(true); }); it("should detect deferred work in claim message", async () => { const context: VerificationContext = { ...baseContext, claimMessage: "Implemented basic feature, will add tests in follow-up", filesChanged: ["src/feature.ts"], }; const result = await service.verify(context); expect(result.isComplete).toBe(false); expect(result.issues.some((i) => i.type === "deferred-work")).toBe(true); expect(result.issues.some((i) => i.message.includes("deferred work"))).toBe(true); }); it("should generate appropriate suggestions", async () => { const context: VerificationContext = { ...baseContext, testResults: { total: 10, passed: 10, failed: 0, skipped: 0, coverage: 70, }, }; const result = await service.verify(context); expect(result.suggestions.length).toBeGreaterThan(0); expect(result.suggestions.some((s) => s.includes("coverage"))).toBe(true); }); it("should return needs-review verdict for marginal cases", async () => { const context: VerificationContext = { ...baseContext, filesChanged: ["src/feature.ts"], testResults: { total: 10, passed: 9, failed: 0, skipped: 1, coverage: 85, // At threshold - no error }, buildOutput: "Build successful\nwarning: unused variable x\nwarning: deprecated API\nwarning: complexity high", outputLogs: "Implementation complete", }; const result = await service.verify(context); // Has warnings but no errors -> needs-review expect(result.verdict).toBe("needs-review"); expect(result.isComplete).toBe(false); }); it("should calculate confidence from strategy results", async () => { const context: VerificationContext = { ...baseContext, filesChanged: ["src/feature.ts"], testResults: { total: 10, passed: 10, failed: 0, skipped: 0, coverage: 95, }, buildOutput: "Build successful", }; const result = await service.verify(context); expect(result.confidence).toBeGreaterThan(85); }); }); describe("detectDeferredWork", () => { it('should detect "will implement in follow-up"', () => { const message = "Added basic feature, will implement advanced features in follow-up"; const issues = service.detectDeferredWork(message); expect(issues.length).toBeGreaterThan(0); expect(issues[0].type).toBe("deferred-work"); }); it('should detect "to be added later"', () => { const message = "Core functionality done, tests to be added later"; const issues = service.detectDeferredWork(message); expect(issues.length).toBeGreaterThan(0); expect(issues[0].type).toBe("deferred-work"); }); it('should detect "incremental improvement"', () => { const message = "This is an incremental improvement, more to come"; const issues = service.detectDeferredWork(message); expect(issues.length).toBeGreaterThan(0); expect(issues[0].type).toBe("deferred-work"); }); it('should detect "future enhancement"', () => { const message = "Basic feature implemented, future enhancements planned"; const issues = service.detectDeferredWork(message); expect(issues.length).toBeGreaterThan(0); expect(issues[0].type).toBe("deferred-work"); }); it('should detect "TODO: complete"', () => { const message = "Started implementation, TODO: complete validation logic"; const issues = service.detectDeferredWork(message); expect(issues.length).toBeGreaterThan(0); expect(issues[0].type).toBe("deferred-work"); }); it('should detect "placeholder"', () => { const message = "Added placeholder implementation for now"; const issues = service.detectDeferredWork(message); expect(issues.length).toBeGreaterThan(0); expect(issues[0].type).toBe("deferred-work"); }); it('should detect "stub"', () => { const message = "Created stub for the new service"; const issues = service.detectDeferredWork(message); expect(issues.length).toBeGreaterThan(0); expect(issues[0].type).toBe("deferred-work"); }); it("should return empty array for complete messages", () => { const message = "Implemented feature with all tests passing and 95% coverage"; const issues = service.detectDeferredWork(message); expect(issues).toHaveLength(0); }); }); describe("registerStrategy", () => { it("should allow registering custom strategies", async () => { class CustomStrategy { name = "custom"; async verify() { return { strategyName: "custom", passed: true, confidence: 100, issues: [], }; } } service.registerStrategy(new CustomStrategy()); const result = await service.verify(baseContext); expect(result).toBeDefined(); }); }); describe("calculateConfidence", () => { it("should return average confidence from strategies", () => { const results = [ { strategyName: "s1", passed: true, confidence: 90, issues: [] }, { strategyName: "s2", passed: true, confidence: 80, issues: [] }, { strategyName: "s3", passed: true, confidence: 70, issues: [] }, ]; const confidence = service.calculateConfidence(results); expect(confidence).toBe(80); // Average of 90, 80, 70 }); it("should return 0 for empty results", () => { const confidence = service.calculateConfidence([]); expect(confidence).toBe(0); }); }); describe("generateSuggestions", () => { it("should suggest fixing tests for test failures", () => { const issues = [ { type: "test-failure" as const, severity: "error" as const, message: "3 tests failed", }, ]; const suggestions = service.generateSuggestions(issues); expect(suggestions.some((s) => s.includes("failing tests"))).toBe(true); }); it("should suggest fixing build errors", () => { const issues = [ { type: "build-error" as const, severity: "error" as const, message: "TypeScript errors", }, ]; const suggestions = service.generateSuggestions(issues); expect(suggestions.some((s) => s.includes("build errors"))).toBe(true); }); it("should suggest increasing coverage", () => { const issues = [ { type: "low-coverage" as const, severity: "error" as const, message: "Coverage below 85%", }, ]; const suggestions = service.generateSuggestions(issues); expect(suggestions.some((s) => s.includes("coverage"))).toBe(true); }); it("should suggest completing deferred work", () => { const issues = [ { type: "deferred-work" as const, severity: "warning" as const, message: "Work deferred", }, ]; const suggestions = service.generateSuggestions(issues); expect(suggestions.some((s) => s.includes("deferred work"))).toBe(true); }); }); });