/** * buildFailureHandler.test.ts — Build Failure Handler (S40) * 14 test: action mapping, priority, targetFiles, instructions, DI, guards */ import { describe, it, expect, beforeEach } from "vitest"; import { handleBuildFailure, shouldUseCriticMode, extractTargetFiles, _setTestHandler, _resetHandler, } from "@/lib/buildFailureHandler"; beforeEach(() => _resetHandler()); // ── handleBuildFailure ──────────────────────────────────────────────────── describe("handleBuildFailure", () => { it("1. errore triviale → action=patch, priority=low", () => { const d = handleBuildFailure("Unused variable 'x' at line 5."); expect(d.action).toBe("patch"); expect(d.priority).toBe("low"); }); it("2. errore serio → action ≠ patch o almeno priority >= medium", () => { const d = handleBuildFailure("Cannot find module '@/lib/missing'"); // action può essere patch/rewrite/split ma priority NON può essere low if (d.action === "patch") { expect(["medium", "high", "critical"]).toContain(d.priority); } else { expect(["rewrite", "split", "escalate"]).toContain(d.action); } }); it("3. retryCount >= 3 → action=escalate, priority=critical", () => { const d = handleBuildFailure("Cannot find module '@/missing'", { retryCount: 3 }); expect(d.action).toBe("escalate"); expect(d.priority).toBe("critical"); }); it("4. action=split → priority=high", () => { // Forza split_task via DI _setTestHandler({ classify: () => "serious", analyze: () => ({ scope: "systemic", strategy: "split_task", confidence: 0.6, reason: "Too many retries", affectedFiles: [], suggestedAction: "split it", escalate: false, }), }); const d = handleBuildFailure("any error", { retryCount: 2 }); expect(d.priority).toBe("high"); }); it("5. instructions è stringa non vuota", () => { const d = handleBuildFailure("Build error occurred"); expect(typeof d.instructions).toBe("string"); expect(d.instructions.length).toBeGreaterThan(10); }); it("6. instructions contiene [PATCH] per azione patch", () => { _setTestHandler({ classify: () => "trivial", analyze: () => ({ scope: "local", strategy: "patch", confidence: 0.9, reason: "trivial", affectedFiles: ["src/foo.ts"], suggestedAction: "fix it", escalate: false, }), }); const d = handleBuildFailure("Unused variable"); expect(d.instructions).toContain("[PATCH]"); }); it("7. instructions contiene [ESCALATE] per action=escalate", () => { const d = handleBuildFailure("Cannot find module '@/lib/missing'", { retryCount: 4 }); expect(d.instructions).toContain("[ESCALATE]"); }); it("8. targetFiles è array (anche vuoto)", () => { const d = handleBuildFailure("Generic build error"); expect(Array.isArray(d.targetFiles)).toBe(true); }); it("9. targetFiles include i file se l'errore li contiene", () => { const d = handleBuildFailure( "Error in src/lib/taskClassifier.ts: Cannot find module", ); expect(d.targetFiles.some(f => f.includes("taskClassifier.ts"))).toBe(true); }); it("10. usesCriticMode=true per errore serio", () => { const d = handleBuildFailure("Cannot find module '@/lib/planner'"); expect(d.usesCriticMode).toBe(true); }); it("10b. usesCriticMode=false per errore triviale + retryCount=0", () => { const d = handleBuildFailure("Unused variable 'x'."); expect(d.usesCriticMode).toBe(false); }); it("11. errorSummary è stringa ≤ 200 char", () => { const longErr = "E".repeat(500); const d = handleBuildFailure(longErr); expect(d.errorSummary.length).toBeLessThanOrEqual(200); }); it("12. tutti i campi obbligatori presenti", () => { const d = handleBuildFailure("Any error"); const keys: (keyof typeof d)[] = [ "action", "priority", "targetFiles", "instructions", "usesCriticMode", "errorSummary", ]; for (const k of keys) { expect(d).toHaveProperty(k); } }); }); // ── shouldUseCriticMode ─────────────────────────────────────────────────── describe("shouldUseCriticMode", () => { it("13. cannot find module → true", () => { expect(shouldUseCriticMode("Cannot find module '@/foo'")).toBe(true); }); it("13b. unused variable → false", () => { expect(shouldUseCriticMode("Unused variable 'y'.")).toBe(false); }); it("13c. retryCount=2 → true qualunque errore", () => { expect(shouldUseCriticMode("Minor warning", 2)).toBe(true); }); }); // ── extractTargetFiles ──────────────────────────────────────────────────── describe("extractTargetFiles", () => { it("14. estrae file da errore con path", () => { const files = extractTargetFiles("Error in src/core/agentLoop.ts line 42"); expect(files.some(f => f.includes("agentLoop.ts"))).toBe(true); }); it("14b. nessun path → array (anche vuoto, non crasha)", () => { const files = extractTargetFiles("Build failed."); expect(Array.isArray(files)).toBe(true); }); });