import type { CapabilityId } from "@suaveplan/api-types/capabilities"; import type { Principal } from "@suaveplan/api-types/principal"; import type { TenantId } from "@suaveplan/api-types/tenant"; import { describe, expect, it } from "@suaveplan/testing/runner"; import { DEFAULT_BACKPRESSURE_POLICY, evaluateBackpressure, } from "../backpressure/backpressure.js"; import type { WsBroker } from "../broker/broker.js"; import type { WsConnection } from "../connection/connection.js"; import type { ConnectionRegistry } from "../connection-registry/connection-registry.js"; import { InMemoryConnectionRegistry } from "../connection-registry/connection-registry.js"; import type { WsEnvelope } from "../envelope/envelope.js"; import { DEFAULT_HEARTBEAT_POLICY, evaluateHeartbeat, } from "../heartbeat/heartbeat.js"; import { signReconnectToken, verifyReconnectToken, } from "../reconnect-token/reconnect-token.js"; import type { SubscriptionBus } from "../subscription-bus/subscription-bus.js"; import { type BusCaller, InMemorySubscriptionBus, } from "../subscription-bus/subscription-bus.js"; /** Engine + broker pair supplied by the package under test. */ export interface WsEngineFactoryResult { readonly bus: SubscriptionBus; readonly registry: ConnectionRegistry; readonly broker: WsBroker; readonly makeConnection: (spec: { id: string; tenant: TenantId; }) => WsConnection; readonly dispose: () => Promise | void; } export interface WsEngineConformanceOptions { readonly label: string; readonly factory: () => | Promise> | WsEngineFactoryResult; } const tenantA = "tenant-a" as unknown as TenantId; const tenantB = "tenant-b" as unknown as TenantId; function caller( tenant: TenantId, caps: readonly CapabilityId[] = [] ): BusCaller { return { tenant, capabilities: new Set(caps) }; } /** * Reusable harness that every concrete `(engine, broker)` pair MUST pass. * Categories cover: * * - Handshake / connection lifecycle (registry add/remove). * - Subscribe + broadcast fan-out. * - Backpressure policy evaluation. * - Heartbeat policy evaluation. * - Reconnect-token round trip. * - Cross-tenant broadcast gate. */ export function runWsEngineConformance( options: WsEngineConformanceOptions ): void { describe(`WS engine conformance: ${options.label}`, () => { it("handshake registers and removes a connection", { timeout: 5000, }, async () => { const f = await options.factory(); try { const conn = f.makeConnection({ id: "c1", tenant: tenantA }); f.registry.add(conn); expect(f.registry.countByTenant(tenantA)).toBe(1); f.registry.remove(conn); expect(f.registry.countByTenant(tenantA)).toBe(0); } finally { await f.dispose(); } }); it("subscribe + broadcast fan out to matching connections", { timeout: 5000, }, async () => { const f = await options.factory(); try { const conn = f.makeConnection({ id: "c1", tenant: tenantA }); f.registry.add(conn); f.bus.subscribe(conn, "orders"); const delivered = f.bus.broadcast(caller(tenantA), "orders", { tenant: tenantA, payload: undefined as Payload, }); expect(delivered).toBe(1); } finally { await f.dispose(); } }); it("backpressure evaluates eviction at the cap", { timeout: 5000, }, () => { expect( evaluateBackpressure( DEFAULT_BACKPRESSURE_POLICY.maxQueuedMessages + 1 ) ).toBe("evict"); }); it("heartbeat evaluates abort at two missed pongs", { timeout: 5000, }, () => { expect( evaluateHeartbeat(0, DEFAULT_HEARTBEAT_POLICY.maxMissedPongs) ).toBe("abort"); }); it("reconnect tokens round-trip end-to-end", { timeout: 5000, }, async () => { const secret = new Uint8Array(32).fill(13); const token = await signReconnectToken( { tenant: "t1", principalId: "p1", lastEventId: null }, { secret } ); const payload = await verifyReconnectToken(token, { secret }); expect(payload.tenant).toBe("t1"); }); it("broker publish/subscribe/dispose are wired", { timeout: 5000, }, async () => { const f = await options.factory(); try { await f.broker.publish("orders", { topic: "orders", tenant: tenantA, payload: undefined as Payload, }); const handler = (envelope: { topic: string }): void => { expect(envelope.topic).toBe("orders"); }; const sub = await f.broker.subscribe("orders", handler); handler({ topic: "orders" }); await sub.unsubscribe(); await f.broker.dispose(); } finally { await f.dispose(); } }); it("cross-tenant broadcast denied without capability", { timeout: 5000, }, async () => { const f = await options.factory(); try { const conn = f.makeConnection({ id: "c1", tenant: tenantB }); f.registry.add(conn); f.bus.subscribe(conn, "orders", { tenant: tenantB }); expect(() => f.bus.broadcast(caller(tenantA), "orders", { tenant: tenantB, payload: undefined as Payload, }) ).toThrow(); } finally { await f.dispose(); } }); }); } /** * Default factory used to self-test the harness against the in-memory bus * shipped in this package. Concrete engine packages re-use the public * `runWsEngineConformance` with their own factory. */ export function inMemoryFactory< Payload = unknown, >(): WsEngineFactoryResult { const registry = new InMemoryConnectionRegistry(); const bus = new InMemorySubscriptionBus(registry); const noopBroker: WsBroker = { publish() { /* no-op */ }, subscribe() { return { topic: "*", unsubscribe() { /* no-op */ }, }; }, dispose() { /* no-op */ }, }; return { bus, registry, broker: noopBroker, makeConnection({ id, tenant }) { const sink: WsEnvelope[] = []; const conn: WsConnection = { id, principal: { id: "p" as unknown as Principal["id"], scopes: [], claims: {}, }, tenantId: tenant, lastEventId: null, state: "open", subscribe(): void { /* no-op */ }, unsubscribe(): void { /* no-op */ }, send(envelope: WsEnvelope): void { sink.push(envelope); }, close(): void { /* no-op */ }, ack(): void { /* no-op */ }, }; return conn; }, dispose() { /* no-op */ }, }; }