/* RFC-0216: tests for maintenance-plan contracts — task-id idempotency, pre-deadline dueAt, and the shared red/amber gate verdict. Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only. RFC-0216: initial plan tests (added in self-review pass). RFC-0323: blocking review-due comparative claims are red. */ import { test, expect } from "vitest"; import { stableTaskId, computeDueAt, defaultCriticality, isRedTask, type MaintenanceTask, } from "../plan.ts"; test("stableTaskId: idempotent for the same (subject, trigger)", () => { const a = stableTaskId("business/de/offer#price.monthly", "expired"); const b = stableTaskId("business/de/offer#price.monthly", "expired"); expect(a).toBe(b); expect(a).toMatch(/^t_[0-9a-f]{16}$/); }); test("stableTaskId: distinct per trigger so two problems on one subject do not collide", () => { const expired = stableTaskId("business/de/offer#price.monthly", "expired"); const review = stableTaskId("business/de/offer#price.monthly", "review-due"); expect(expired).not.toBe(review); }); test("computeDueAt: schedules leadTime days before validUntil", () => { const due = computeDueAt({ validUntil: "2026-12-31", leadTimeDays: 30, today: "2026-06-20" }); expect(due).toBe("2026-12-01"); }); test("computeDueAt: clamps to today when the lead window has already passed", () => { const due = computeDueAt({ validUntil: "2026-06-25", leadTimeDays: 30, today: "2026-06-20" }); expect(due).toBe("2026-06-20"); }); test("computeDueAt: uses reviewDueAt when there is no validUntil", () => { const future = computeDueAt({ reviewDueAt: "2027-01-01", leadTimeDays: 30, today: "2026-06-20" }); expect(future).toBe("2027-01-01"); const past = computeDueAt({ reviewDueAt: "2026-01-01", leadTimeDays: 30, today: "2026-06-20" }); expect(past).toBe("2026-06-20"); }); function task(partial: Partial): MaintenanceTask { return { id: "t_x", subject: "business/de/offer#price.monthly", trigger: "expired", dueAt: "2026-06-20", criticality: "advisory", owner: "agent:test", diagnostics: [], status: "open", ...partial, }; } test("isRedTask: blocking + expired is red", () => { expect(isRedTask(task({ criticality: "blocking", trigger: "expired" }))).toBe(true); }); test("isRedTask: blocking + source-diverged and derived-outdated are red", () => { expect(isRedTask(task({ criticality: "blocking", trigger: "source-diverged" }))).toBe(true); expect(isRedTask(task({ criticality: "blocking", trigger: "derived-outdated" }))).toBe(true); }); test("isRedTask: blocking review-due is red, while expiring-soon stays amber", () => { expect(isRedTask(task({ criticality: "blocking", trigger: "review-due" }))).toBe(true); expect(isRedTask(task({ criticality: "blocking", trigger: "expiring-soon" }))).toBe(false); }); test("isRedTask: advisory/important expired never blocks the build", () => { expect(isRedTask(task({ criticality: "advisory", trigger: "expired" }))).toBe(false); expect(isRedTask(task({ criticality: "important", trigger: "expired" }))).toBe(false); }); test("isRedTask: a done task is never red", () => { expect(isRedTask(task({ criticality: "blocking", trigger: "expired", status: "done" }))).toBe( false, ); }); test("defaultCriticality: only expired/source-diverged default above advisory", () => { expect(defaultCriticality("expired")).toBe("important"); expect(defaultCriticality("source-diverged")).toBe("important"); expect(defaultCriticality("review-due")).toBe("advisory"); expect(defaultCriticality("derived-outdated")).toBe("advisory"); });