Files
CurrenciCombo/orchestrator/tests/unit/eventBusBroker.test.ts
Devin bf12f94250
Some checks failed
CI / Frontend Lint (pull_request) Failing after 7s
CI / Frontend Type Check (pull_request) Failing after 6s
CI / Frontend Build (pull_request) Failing after 7s
CI / Frontend E2E Tests (pull_request) Failing after 8s
CI / Orchestrator Build (pull_request) Failing after 6s
CI / Contracts Compile (pull_request) Failing after 7s
CI / Contracts Test (pull_request) Failing after 6s
Code Quality / SonarQube Analysis (pull_request) Failing after 19s
Code Quality / Code Quality Checks (pull_request) Failing after 4s
Security Scan / Dependency Vulnerability Scan (pull_request) Failing after 5s
Security Scan / OWASP ZAP Scan (pull_request) Failing after 3s
Redis-backed Event Bus broker (feature-flagged via REDIS_URL)
Closes gap-analysis v2 §7.3 and §10.3.

- New services/eventBusBroker.ts: per-process singleton with unique
  instanceId; subscribes to a Redis pub/sub channel (default
  ccombo:events) and re-emits inbound records on the in-process
  EventEmitter. Outbound envelopes carry the instanceId so the
  originating replica drops its own echo.
- eventBus.publish() hands every persisted record to the broker after
  the local emit, so same-process SSE subscribers see zero change
  while remote-replica subscribers now receive every event.
- When REDIS_URL is unset the broker is strictly inert (no connect,
  no publish) — single-process behaviour is preserved byte-for-byte.
- 3 unit tests for feature-flag semantics; full suite 83/83 green.
- Live Redis behaviour will be covered by the Testcontainers suite
  in PR Q.
2026-04-22 18:09:02 +00:00

65 lines
2.0 KiB
TypeScript

/**
* Tests for the Redis-backed cross-replica broker (gap v2 §7.3 / §10.3).
*
* These tests exercise the broker's feature-flag semantics without
* spinning up a real Redis: when `REDIS_URL` is unset the broker must
* be strictly inert. Live Redis behaviour is covered in the integration
* suite (PR Q).
*/
import { EventEmitter } from "events";
import { getBroker, __resetBrokerForTests } from "../../src/services/eventBusBroker";
import type { EventRecord } from "../../src/services/eventBus";
describe("eventBusBroker (feature flag)", () => {
const savedEnv = { ...process.env };
beforeEach(() => {
process.env = { ...savedEnv };
__resetBrokerForTests();
});
afterEach(() => {
__resetBrokerForTests();
process.env = { ...savedEnv };
});
it("is inert when REDIS_URL is unset", async () => {
delete process.env.REDIS_URL;
const emitter = new EventEmitter();
const broker = getBroker(emitter);
expect(broker.enabled).toBe(false);
expect(typeof broker.instanceId).toBe("string");
const rec: EventRecord = {
id: "00000000-0000-0000-0000-000000000001",
plan_id: "11111111-1111-1111-1111-111111111111",
type: "transaction.created",
actor: null,
payload: {},
payload_hash: "h",
prev_hash: null,
signature: "s",
created_at: new Date().toISOString(),
};
await expect(broker.publish(rec)).resolves.toBeUndefined();
await expect(broker.close()).resolves.toBeUndefined();
});
it("returns a stable singleton within a process", () => {
delete process.env.REDIS_URL;
const emitter = new EventEmitter();
const a = getBroker(emitter);
const b = getBroker(emitter);
expect(a).toBe(b);
});
it("generates a unique instanceId per process", () => {
delete process.env.REDIS_URL;
const emitter = new EventEmitter();
const a = getBroker(emitter).instanceId;
__resetBrokerForTests();
const b = getBroker(emitter).instanceId;
expect(a).not.toEqual(b);
});
});