import type { WsLike, TimerLike } from '../../src/transport/seams.js' /** In-memory WsLike double: records sent frames, lets tests emit inbound events. */ export class FakeWs implements WsLike { readonly sent: Uint8Array[] = [] closed = false private readonly listeners = new Map void>>() send(d: Uint8Array): void { this.sent.push(d) } on(ev: 'message' | 'close' | 'error', cb: (...a: unknown[]) => void): void { const arr = this.listeners.get(ev) ?? [] arr.push(cb) this.listeners.set(ev, arr) } close(): void { this.closed = true this.emit('close') } emit(ev: string, ...args: unknown[]): void { for (const cb of this.listeners.get(ev) ?? []) cb(...args) } emitMessage(bytes: Uint8Array): void { this.emit('message', bytes) } } /** Deterministic controllable timer for heartbeat/rotation tests. */ export class FakeTimer implements TimerLike { private seq = 0 private readonly timeouts = new Map void; ms: number }>() private readonly intervals = new Map void; ms: number }>() setTimeout(cb: () => void, ms: number): unknown { const id = this.seq++ this.timeouts.set(id, { cb, ms }) return id } clearTimeout(handle: unknown): void { this.timeouts.delete(handle as number) } setInterval(cb: () => void, ms: number): unknown { const id = this.seq++ this.intervals.set(id, { cb, ms }) return id } clearInterval(handle: unknown): void { this.intervals.delete(handle as number) } /** Fire every armed timeout whose delay is ≤ ms (single shot) and every interval once. */ advance(ms: number): void { for (const [id, t] of [...this.timeouts]) { if (t.ms <= ms) { this.timeouts.delete(id) t.cb() } } for (const t of [...this.intervals.values()]) { if (t.ms <= ms) t.cb() } } }