// Sibling test for c32_judge.ts. The orchestrator itself (judge()) does // git clone + test execution and isn't unit-testable without a real // agent repo; the pure helpers underneath it (applyMode, explainRefactor) // are the structural surface that matters for scoring decisions. Cover // the mode-aware penalty math + the operator-facing explanations here. import { describe, test, expect } from "bun:test"; import { applyMode, explainRefactor, judge } from "./c14_judge.ts"; describe("c32_judge — applyMode (mode-aware penalty math)", () => { test("positive deltas pass through unchanged in every mode", () => { expect(applyMode(10, "strict")).toBe(10); expect(applyMode(10, "pragmatic")).toBe(10); expect(applyMode(10, "learning")).toBe(10); }); test("strict mode keeps the full negative penalty", () => { expect(applyMode(-20, "strict")).toBe(-20); expect(applyMode(-5, "strict")).toBe(-5); }); test("pragmatic mode halves negative deltas (Math.ceil — never below half)", () => { expect(applyMode(-20, "pragmatic")).toBe(-10); expect(applyMode(-10, "pragmatic")).toBe(-5); // -5 / 2 = -2.5 → Math.ceil(-2.5) = -2: the harsher half rounds up // toward zero, which is the documented "softer score" behaviour. expect(applyMode(-5, "pragmatic")).toBe(-2); }); test("learning mode zeroes out every negative delta", () => { expect(applyMode(-20, "learning")).toBe(0); expect(applyMode(-5, "learning")).toBe(0); expect(applyMode(-1, "learning")).toBe(0); }); test("zero delta is neutral in every mode", () => { expect(applyMode(0, "strict")).toBe(0); expect(applyMode(0, "pragmatic")).toBe(0); expect(applyMode(0, "learning")).toBe(0); }); }); describe("c32_judge — explainRefactor", () => { test("passed=true returns the canonical-refactor explanation", () => { const s = explainRefactor(true); expect(s).toContain("stayed green"); expect(s).toMatch(/canonical/i); }); test("passed=false returns guidance to revert or open a new red→green", () => { const s = explainRefactor(false); expect(s).toContain("broke"); expect(s).toMatch(/revert|red→green/); }); test("the two branches return different strings", () => { expect(explainRefactor(true)).not.toBe(explainRefactor(false)); }); }); describe("c32_judge — orchestrator entry point", () => { test("judge is exported as an async function (Promise-returning)", () => { expect(typeof judge).toBe("function"); // The orchestrator does git clone + test execution; covering it // end-to-end needs a real agent repo. A type-level check that the // shape didn't drift is the documented minimum for this layer. expect(judge.length).toBe(2); }); });