Files
CurrenciCombo/orchestrator/tests/unit/exceptionManager.test.ts
nsatoshi 3e1fb9ef7e
Some checks failed
CI / Frontend Lint (push) Failing after 6s
CI / Frontend Type Check (push) Failing after 6s
CI / Frontend Build (push) Failing after 6s
CI / Frontend E2E Tests (push) Failing after 8s
CI / Contracts Compile (push) Has been cancelled
CI / Contracts Test (push) Has been cancelled
CI / Orchestrator Build (push) Has been cancelled
Security Scan / OWASP ZAP Scan (push) Has been cancelled
Security Scan / Dependency Vulnerability Scan (push) Has been cancelled
PR C: wire real NotaryRegistry on Chain 138 (arch step 4) (#7)
2026-04-22 17:11:50 +00:00

70 lines
2.5 KiB
TypeScript

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");
});
});
});