import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { createCommandDispatch } from "./command-dispatch.js"; import { HOST_EVENT_KEYS, toHostEventName } from "./constants.js"; const settledEventName = toHostEventName( HOST_EVENT_KEYS.INSTANCE_COMMAND_SETTLED, ); /** * Avoid native `EventTarget.dispatchEvent`: other suites swap `globalThis` (e.g. SDK tests * set `window` + `CustomEvent` from happy-dom), so `new Event(...)` and `new EventTarget()` * can come from different realms and Bun rejects `dispatchEvent`. */ const createWindowRef = (): Window => { const listeners = new Map void>>(); return { addEventListener( type: string, listener: EventListenerOrEventListenerObject, _options?: boolean | AddEventListenerOptions, ): void { const handler = typeof listener === "function" ? listener : listener.handleEvent.bind(listener); let bucket = listeners.get(type); if (!bucket) { bucket = new Set(); listeners.set(type, bucket); } bucket.add(handler as (event: Event) => void); }, removeEventListener( type: string, listener: EventListenerOrEventListenerObject, _options?: boolean | EventListenerOptions, ): void { const handler = typeof listener === "function" ? listener : listener.handleEvent.bind(listener); listeners.get(type)?.delete(handler as (event: Event) => void); }, dispatchEvent(event: Event): boolean { const bucket = listeners.get(event.type); if (!bucket) { return true; } for (const handler of [...bucket]) { handler(event); } return true; }, } as unknown as Window; }; const createSettledEvent = (detail: unknown): Event => { const event = new Event(settledEventName) as Event & { detail?: unknown }; Object.defineProperty(event, "detail", { value: detail }); return event; }; let originalWindow: unknown; beforeEach(() => { originalWindow = (globalThis as { window?: unknown }).window; }); afterEach(() => { (globalThis as { window?: unknown }).window = originalWindow; }); describe("createCommandDispatch", () => { test("defaults idempotencyKey to requestId", async () => { const windowRef = createWindowRef(); (globalThis as { window?: unknown }).window = windowRef; const queued: Array> = []; const dispatch = createCommandDispatch((envelope) => { queued.push(envelope as unknown as Record); }); const pending = dispatch({ kind: "close" }); expect(queued).toHaveLength(1); const queuedEnvelope = queued[0]; if (!queuedEnvelope || typeof queuedEnvelope.requestId !== "string") { throw new Error("Expected queued envelope with requestId"); } expect(queuedEnvelope.idempotencyKey).toBe(queuedEnvelope.requestId); windowRef.dispatchEvent( createSettledEvent({ requestId: queuedEnvelope.requestId, instanceId: "instance_1", kind: "close", ok: true, }), ); await pending; }); });