import * as fs from "node:fs"; import * as path from "node:path"; import * as os from "node:os"; import { ErrorRecovery } from "../core/error-recovery.js"; import { DEFAULT_CI_CONFIG } from "../types/config.js"; describe("ErrorRecovery", () => { let tempDir: string; let recovery: ErrorRecovery; beforeEach(() => { tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ci-recovery-test-")); fs.mkdirSync(path.join(tempDir, ".ci"), { recursive: true }); recovery = new ErrorRecovery(DEFAULT_CI_CONFIG, tempDir); }); afterEach(() => { fs.rmSync(tempDir, { recursive: true, force: true }); }); describe("recoverFromFailure", () => { it("recommends retry for verification failures within retry limit", async () => { const result = await recovery.recoverFromFailure("Test failed", 1, "verify", 1); expect(result.recovered).toBe(true); expect(result.strategy).toBe("retry"); expect(result.attempts).toBe(1); }); it("escalates after max verification retries exceeded", async () => { const result = await recovery.recoverFromFailure("Test failed", 1, "verify", 4); expect(result.recovered).toBe(false); expect(result.strategy).toBe("escalate"); }); it("recommends plan revision for plan stage failures", async () => { const result = await recovery.recoverFromFailure("Plan issues found", 1, "plan", 1); expect(result.recovered).toBe(true); expect(result.strategy).toBe("plan_revision"); }); it("escalates after max plan revisions", async () => { await recovery.recoverFromFailure("Plan issues", 1, "plan", 1); await recovery.recoverFromFailure("Plan issues", 1, "plan", 1); await recovery.recoverFromFailure("Plan issues", 1, "plan", 1); const result = await recovery.recoverFromFailure("Plan issues", 1, "plan", 1); expect(result.recovered).toBe(false); expect(result.strategy).toBe("escalate"); }); it("escalates for non-recoverable stages", async () => { const result = await recovery.recoverFromFailure("Unknown error", 1, "specify", 1); expect(result.recovered).toBe(false); expect(result.strategy).toBe("escalate"); }); }); describe("rollback", () => { it("returns recovered result with rollback strategy", async () => { const result = await recovery.rollback(1, "User-requested rollback"); expect(result.recovered).toBe(true); expect(result.strategy).toBe("rollback"); expect(result.message).toContain("phase 1"); }); }); describe("canAutoDebug", () => { it("returns true when confidence meets threshold", () => { expect(recovery.canAutoDebug("error", 0.6)).toBe(true); expect(recovery.canAutoDebug("error", 0.8)).toBe(true); expect(recovery.canAutoDebug("error", 1.0)).toBe(true); }); it("returns false when confidence is below threshold", () => { expect(recovery.canAutoDebug("error", 0.59)).toBe(false); expect(recovery.canAutoDebug("error", 0.3)).toBe(false); }); }); describe("getMaxRetries", () => { it("returns the configured max verification retries", () => { expect(recovery.getMaxRetries()).toBe(2); }); }); describe("getMaxRevisions", () => { it("returns the configured max revision iterations", () => { expect(recovery.getMaxRevisions()).toBe(3); }); }); });