import { describe, it, expect } from "@jest/globals"; import { Business, Control, Data, SettlementException, Timing, classify, route, } from "../../src/services/exceptionManager"; describe("ExceptionManager — architecture note §12", () => { describe("classification taxonomy", () => { it("builds the four §12 classes via factory functions", () => { expect(Timing.dispatch().exceptionClass).toBe("timing"); expect(Timing.dispatch().code).toBe("dispatch_timeout"); expect(Data.valueMismatch().exceptionClass).toBe("data"); expect(Data.documentHashMismatch().code).toBe("document_hash_mismatch"); expect(Control.unauthorized("nobody").exceptionClass).toBe("control"); expect(Control.duplicate("ev-1").code).toBe("duplicate_event"); expect(Business.manualStop("operator halted").exceptionClass).toBe("business"); expect(Business.policyViolation({ rule: "LTV" }).code).toBe("policy_rule_violation"); }); it("classify() tags network/timeout errors as system/network_error", () => { const ex = classify(new Error("ETIMEDOUT connect")); expect(ex.exceptionClass).toBe("system"); expect(ex.code).toBe("network_error"); }); it("classify() tags postgres errors as system/database_error", () => { const ex = classify(new Error("postgres connection refused")); expect(ex.exceptionClass).toBe("system"); expect(ex.code).toBe("database_error"); }); it("classify() is idempotent for SettlementException inputs", () => { const original = Data.valueMismatch({ field: "amount" }); expect(classify(original)).toBe(original); }); }); describe("deterministic routing", () => { const cases: Array<[SettlementException, string]> = [ [Timing.dispatch(), "retry"], [Timing.settlement(), "retry"], [Data.valueMismatch(), "abort_transaction"], [Data.documentHashMismatch(), "abort_transaction"], [Control.missingApproval(), "escalate"], [Control.unauthorized("x"), "escalate"], [Control.duplicate("ev"), "dead_letter"], [Business.manualStop("halt"), "abort_transaction"], [Business.policyViolation({ rule: "LTV" }), "escalate"], ]; it.each(cases)("routes %j to %s", (ex, expected) => { expect(route(ex)).toBe(expected); }); it("network errors retry; non-network system errors dead-letter", () => { expect(route(classify(new Error("ETIMEDOUT")))).toBe("retry"); const dbErr = classify(new Error("postgres broken")); expect(route(dbErr)).toBe("dead_letter"); }); }); });