From 21463e6e19e843d99de93e4fdf2be26596360b4e Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Thu, 30 Jul 2026 12:35:40 +0800 Subject: [PATCH] test(openai): verify terminal audio cleanup --- .../realtime-quicksilver-bridge.test.ts | 66 ++++++++++++++++++ .../openai/realtime-voice-provider.test.ts | 69 +++++++++++++++++++ 2 files changed, 135 insertions(+) diff --git a/extensions/openai/realtime-quicksilver-bridge.test.ts b/extensions/openai/realtime-quicksilver-bridge.test.ts index 60ace669068d..43e2bb83fcb5 100644 --- a/extensions/openai/realtime-quicksilver-bridge.test.ts +++ b/extensions/openai/realtime-quicksilver-bridge.test.ts @@ -185,6 +185,72 @@ describe("OpenAIQuicksilverVoiceBridge", () => { expect(harness.onReady).toHaveBeenCalledOnce(); }); + it("bounds queued audio by aggregate bytes before session readiness", async () => { + const harness = createHarness({ autoStart: false }); + const connecting = harness.bridge.connect(); + await vi.waitFor(() => expect(harness.socket.readyState).toBe(1)); + + harness.bridge.sendAudio(Buffer.alloc(512 * 1024, 0x01)); + harness.bridge.sendAudio(Buffer.alloc(512 * 1024, 0x02)); + harness.bridge.sendAudio(Buffer.from("overflow")); + harness.socket.serverEvent({ + type: "session.started", + session: { id: "live-1", expires_at: Math.floor(Date.now() / 1000) + 60 }, + }); + await connecting; + + const audioEvents = sentEvents(harness.socket).filter( + (event) => event.type === "input_audio.append", + ); + expect(audioEvents).toHaveLength(2); + expect( + audioEvents.map((event) => Buffer.from(String(event.audio), "base64").byteLength), + ).toEqual([512 * 1024, 512 * 1024]); + harness.bridge.close(); + }); + + it("does not carry queued audio across terminal close and explicit reconnect", async () => { + const sockets: FakeSocket[] = []; + const bridge = new OpenAIQuicksilverVoiceBridge({ + providerConfig: {}, + model: "gpt-live-1-codex", + audioFormat: { encoding: "pcm16", sampleRateHz: 24000, channels: 1 }, + resolveAuth: async () => ({ type: "api-key", token: "test-key" }), + webSocketFactory: (_url, _options) => { + const socket = new FakeSocket(false); + sockets.push(socket); + queueMicrotask(() => socket.open()); + return socket as unknown as OpenAIQuicksilverSocket; + }, + onAudio: vi.fn(), + onClearAudio: vi.fn(), + }); + + const firstConnect = bridge.connect(); + await vi.waitFor(() => expect(sockets[0]?.readyState).toBe(1)); + bridge.sendAudio(Buffer.from("queued-before-close")); + bridge.close(); + await firstConnect; + bridge.sendAudio(Buffer.from("sent-after-close")); + + const reconnecting = bridge.connect(); + await vi.waitFor(() => expect(sockets[1]?.readyState).toBe(1)); + sockets[1]?.serverEvent({ + type: "session.started", + session: { id: "live-2", expires_at: Math.floor(Date.now() / 1000) + 60 }, + }); + await reconnecting; + + const secondSocket = sockets[1]; + if (!secondSocket) { + throw new Error("expected bridge to reconnect"); + } + expect( + sentEvents(secondSocket).filter((event) => event.type === "input_audio.append"), + ).toHaveLength(0); + bridge.close(); + }); + it("rejects startup failures without emitting terminal callbacks", async () => { const harness = createHarness({ autoStart: false }); const connecting = harness.bridge.connect(); diff --git a/extensions/openai/realtime-voice-provider.test.ts b/extensions/openai/realtime-voice-provider.test.ts index 88e891313114..20963363acfb 100644 --- a/extensions/openai/realtime-voice-provider.test.ts +++ b/extensions/openai/realtime-voice-provider.test.ts @@ -1517,6 +1517,75 @@ describe("buildOpenAIRealtimeVoiceProvider", () => { expect(bridge.isConnected()).toBe(true); }); + it("bounds queued audio by aggregate bytes before session readiness", async () => { + const provider = buildOpenAIRealtimeVoiceProvider(); + const bridge = provider.createBridge({ + providerConfig: { apiKey: "sk-test" }, // pragma: allowlist secret + onAudio: vi.fn(), + onClearAudio: vi.fn(), + }); + const connecting = bridge.connect(); + const socket = FakeWebSocket.instances[0]; + if (!socket) { + throw new Error("expected bridge to create a websocket"); + } + socket.readyState = FakeWebSocket.OPEN; + socket.emit("open"); + await Promise.resolve(); + + bridge.sendAudio(Buffer.alloc(512 * 1024, 0x01)); + bridge.sendAudio(Buffer.alloc(512 * 1024, 0x02)); + bridge.sendAudio(Buffer.from("overflow")); + socket.emit("message", Buffer.from(JSON.stringify({ type: "session.updated" }))); + await connecting; + + const audioEvents = parseSent(socket).filter( + (event) => event.type === "input_audio_buffer.append", + ); + expect(audioEvents).toHaveLength(2); + expect( + audioEvents.map((event) => Buffer.from(String(event.audio), "base64").byteLength), + ).toEqual([512 * 1024, 512 * 1024]); + bridge.close(); + }); + + it("does not carry queued audio across terminal close and explicit reconnect", async () => { + const provider = buildOpenAIRealtimeVoiceProvider(); + const bridge = provider.createBridge({ + providerConfig: { apiKey: "sk-test" }, // pragma: allowlist secret + onAudio: vi.fn(), + onClearAudio: vi.fn(), + }); + const firstConnect = bridge.connect(); + const firstSocket = FakeWebSocket.instances[0]; + if (!firstSocket) { + throw new Error("expected bridge to create a websocket"); + } + firstSocket.readyState = FakeWebSocket.OPEN; + firstSocket.emit("open"); + await Promise.resolve(); + + bridge.sendAudio(Buffer.from("queued-before-close")); + bridge.close(); + await firstConnect; + bridge.sendAudio(Buffer.from("sent-after-close")); + + const reconnecting = bridge.connect(); + const secondSocket = FakeWebSocket.instances[1]; + if (!secondSocket) { + throw new Error("expected bridge to reconnect"); + } + secondSocket.readyState = FakeWebSocket.OPEN; + secondSocket.emit("open"); + secondSocket.emit("message", Buffer.from(JSON.stringify({ type: "session.updated" }))); + await reconnecting; + + expect( + parseSent(secondSocket).filter((event) => event.type === "input_audio_buffer.append"), + ).toHaveLength(0); + bridge.close(); + }); + it("shares an in-flight connection until session readiness", async () => { const provider = buildOpenAIRealtimeVoiceProvider(); const onReady = vi.fn();