Files
CurrenciCombo/orchestrator/tests/unit/phaseTimeouts.test.ts
Devin e3328725b4
Some checks failed
CI / Frontend Lint (pull_request) Failing after 6s
CI / Frontend Type Check (pull_request) Failing after 7s
CI / Frontend Build (pull_request) Failing after 6s
CI / Frontend E2E Tests (pull_request) Failing after 8s
CI / Orchestrator Build (pull_request) Failing after 6s
CI / Contracts Compile (pull_request) Failing after 6s
CI / Contracts Test (pull_request) Failing after 7s
Code Quality / SonarQube Analysis (pull_request) Failing after 20s
Code Quality / Code Quality Checks (pull_request) Failing after 6s
Security Scan / Dependency Vulnerability Scan (pull_request) Failing after 4s
Security Scan / OWASP ZAP Scan (pull_request) Failing after 4s
Per-state PHASE_TIMEOUTS map + env overrides
Closes gap-analysis v2 §7.6 / §10.7.

- Adds DEFAULT_PHASE_TIMEOUTS: Record<TransactionState, number | null>
  to orchestrator/src/types/transactionState.ts, covering all 12 states
  of the §8 state machine with rationale-per-state comments.
- getPhaseTimeoutMs(state) honours per-state env overrides
  (PHASE_TIMEOUT_<STATE>=<ms>; '0' disables; invalid → default).
- CLOSED is the only state with a null (no-timeout) value.
- 9 unit tests; full suite passes.
2026-04-22 18:10:51 +00:00

76 lines
2.2 KiB
TypeScript

/**
* Tests for per-state phase timeouts (arch §12.1 / gap v2 §7.6 / §10.7).
*/
import {
DEFAULT_PHASE_TIMEOUTS,
TRANSACTION_STATES,
getPhaseTimeoutMs,
} from "../../src/types/transactionState";
describe("PHASE_TIMEOUTS", () => {
const savedEnv = { ...process.env };
afterEach(() => {
process.env = { ...savedEnv };
});
it("has a mapping for every declared transaction state", () => {
for (const s of TRANSACTION_STATES) {
expect(DEFAULT_PHASE_TIMEOUTS).toHaveProperty(s);
}
});
it("CLOSED is the only state without a timeout", () => {
const nullStates = TRANSACTION_STATES.filter(
(s) => DEFAULT_PHASE_TIMEOUTS[s] === null,
);
expect(nullStates).toEqual(["CLOSED"]);
});
it("all non-terminal timeouts are strictly positive integers", () => {
for (const s of TRANSACTION_STATES) {
const v = DEFAULT_PHASE_TIMEOUTS[s];
if (v === null) continue;
expect(v).toBeGreaterThan(0);
expect(Number.isInteger(v)).toBe(true);
}
});
it("getPhaseTimeoutMs honours a valid env override", () => {
process.env.PHASE_TIMEOUT_EXECUTING = "123456";
expect(getPhaseTimeoutMs("EXECUTING")).toBe(123456);
});
it("getPhaseTimeoutMs treats override '0' as 'no timeout' (null)", () => {
process.env.PHASE_TIMEOUT_EXECUTING = "0";
expect(getPhaseTimeoutMs("EXECUTING")).toBeNull();
});
it("getPhaseTimeoutMs falls back to default when override is invalid", () => {
process.env.PHASE_TIMEOUT_EXECUTING = "not-a-number";
expect(getPhaseTimeoutMs("EXECUTING")).toBe(
DEFAULT_PHASE_TIMEOUTS.EXECUTING,
);
});
it("getPhaseTimeoutMs falls back to default when override is negative", () => {
process.env.PHASE_TIMEOUT_PREPARED = "-1";
expect(getPhaseTimeoutMs("PREPARED")).toBe(
DEFAULT_PHASE_TIMEOUTS.PREPARED,
);
});
it("returns the default when no env override exists", () => {
delete process.env.PHASE_TIMEOUT_VALIDATING;
expect(getPhaseTimeoutMs("VALIDATING")).toBe(
DEFAULT_PHASE_TIMEOUTS.VALIDATING,
);
});
it("CLOSED stays null even with no env override", () => {
delete process.env.PHASE_TIMEOUT_CLOSED;
expect(getPhaseTimeoutMs("CLOSED")).toBeNull();
});
});