syntaxai/tdd.md · main · src / c14_judge.test.ts

c14_judge.test.ts 70 lines · 2750 bytes raw
// 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);
  });
});