mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 09:39:25 +00:00
test: reuse Google Live bridge fixtures (#156161)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
parent
7f0ea8e546
commit
06bb025f03
1 changed files with 31 additions and 144 deletions
|
|
@ -280,10 +280,8 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
});
|
||||
|
||||
it("connects with Google Live setup config and tool declarations", async () => {
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const bridge = provider.createBridge({
|
||||
const bridge = createGoogleLiveBridge({
|
||||
providerConfig: {
|
||||
apiKey: "gemini-key",
|
||||
model: "gemini-live-2.5-flash-preview",
|
||||
voice: "Kore",
|
||||
temperature: 0.3,
|
||||
|
|
@ -319,8 +317,6 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
},
|
||||
},
|
||||
],
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
});
|
||||
|
||||
await bridge.connect();
|
||||
|
|
@ -688,12 +684,7 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
const pendingSession = createDeferred<MockGoogleLiveSession>();
|
||||
const connectedSession = createMockGoogleLiveSession();
|
||||
connectMock.mockReturnValueOnce(pendingSession.promise);
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
});
|
||||
const bridge = createGoogleLiveBridge();
|
||||
|
||||
const firstConnect = bridge.connect();
|
||||
const secondConnect = bridge.connect();
|
||||
|
|
@ -712,13 +703,9 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
connectMock
|
||||
.mockReturnValueOnce(pendingSession.promise)
|
||||
.mockResolvedValueOnce(replacementSession);
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onReady = vi.fn();
|
||||
const onError = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
const bridge = createGoogleLiveBridge({
|
||||
onReady,
|
||||
onError,
|
||||
});
|
||||
|
|
@ -760,16 +747,12 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
|
||||
it("preserves transcript fragments and interruption while reusing a resumption handle", async () => {
|
||||
vi.useFakeTimers();
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onTranscript = vi.fn();
|
||||
const onResponseDone = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
const bridge = createGoogleLiveBridge({
|
||||
providerConfig: {
|
||||
model: "gemini-2.5-flash-native-audio-preview-12-2025",
|
||||
apiKey: "gemini-key",
|
||||
},
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
onTranscript,
|
||||
onResponseDone,
|
||||
});
|
||||
|
|
@ -798,13 +781,9 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
|
||||
it("preserves tool ownership while reusing a resumption handle", async () => {
|
||||
vi.useFakeTimers();
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onError = vi.fn();
|
||||
const onToolCall = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
const bridge = createGoogleLiveBridge({
|
||||
onError,
|
||||
onToolCall,
|
||||
});
|
||||
|
|
@ -851,13 +830,9 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
|
||||
it("fails closed when resumable tool responses exceed the reconnect buffer", async () => {
|
||||
vi.useFakeTimers();
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onError = vi.fn();
|
||||
const onClose = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
const bridge = createGoogleLiveBridge({
|
||||
onToolCall: vi.fn(),
|
||||
onError,
|
||||
onClose,
|
||||
|
|
@ -887,12 +862,8 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
|
||||
it("drops queued reconnect responses when the resumed session cancels their call", async () => {
|
||||
vi.useFakeTimers();
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onEvent = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
const bridge = createGoogleLiveBridge({
|
||||
onToolCall: vi.fn(),
|
||||
onEvent,
|
||||
});
|
||||
|
|
@ -927,14 +898,10 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
|
||||
it("resets tool ownership and interruption before a fresh automatic reconnect", async () => {
|
||||
vi.useFakeTimers();
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onToolCall = vi.fn();
|
||||
const onEvent = vi.fn();
|
||||
const onResponseDone = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
const bridge = createGoogleLiveBridge({
|
||||
onToolCall,
|
||||
onEvent,
|
||||
onResponseDone,
|
||||
|
|
@ -978,16 +945,12 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
|
||||
it("preserves continuity when resumability recovers before reconnect", async () => {
|
||||
vi.useFakeTimers();
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onEvent = vi.fn();
|
||||
const onTranscript = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
const bridge = createGoogleLiveBridge({
|
||||
providerConfig: {
|
||||
model: "gemini-2.5-flash-native-audio-preview-12-2025",
|
||||
apiKey: "gemini-key",
|
||||
},
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
onEvent,
|
||||
onTranscript,
|
||||
});
|
||||
|
|
@ -1021,16 +984,12 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
|
||||
it("drops unfinished hypotheses when a new session has no continuity", async () => {
|
||||
vi.useFakeTimers();
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onTranscript = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
const bridge = createGoogleLiveBridge({
|
||||
providerConfig: {
|
||||
model: "gemini-2.5-flash-native-audio-preview-12-2025",
|
||||
apiKey: "gemini-key",
|
||||
sessionResumption: false,
|
||||
},
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
onTranscript,
|
||||
});
|
||||
|
||||
|
|
@ -1053,18 +1012,14 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
it.each([undefined, "invalidated-handle"])("invalidates resume handles %#", async (newHandle) => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onClose = vi.fn();
|
||||
const onError = vi.fn();
|
||||
const onEvent = vi.fn();
|
||||
const onTranscript = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
const bridge = createGoogleLiveBridge({
|
||||
providerConfig: {
|
||||
model: "gemini-2.5-flash-native-audio-preview-12-2025",
|
||||
apiKey: "gemini-key",
|
||||
},
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
onClose,
|
||||
onError,
|
||||
onEvent,
|
||||
|
|
@ -1217,13 +1172,10 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
|
||||
it("emits one continuity reset across failed fresh reconnect attempts", async () => {
|
||||
vi.useFakeTimers();
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onClose = vi.fn();
|
||||
const onEvent = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key", sessionResumption: false },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
const bridge = createGoogleLiveBridge({
|
||||
providerConfig: { sessionResumption: false },
|
||||
onClose,
|
||||
onEvent,
|
||||
});
|
||||
|
|
@ -1253,18 +1205,14 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
connectMock
|
||||
.mockReturnValueOnce(Promise.resolve(session))
|
||||
.mockReturnValueOnce(pendingSession.promise);
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onEvent = vi.fn();
|
||||
const onReady = vi.fn();
|
||||
const onTranscript = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
const bridge = createGoogleLiveBridge({
|
||||
providerConfig: {
|
||||
model: "gemini-2.5-flash-native-audio-preview-12-2025",
|
||||
apiKey: "gemini-key",
|
||||
sessionResumption: false,
|
||||
},
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
onEvent,
|
||||
onReady,
|
||||
onTranscript,
|
||||
|
|
@ -1336,12 +1284,8 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
const pendingSession = createDeferred<MockGoogleLiveSession>();
|
||||
const connectedSession = createMockGoogleLiveSession();
|
||||
connectMock.mockReturnValueOnce(pendingSession.promise);
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onReady = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
const bridge = createGoogleLiveBridge({
|
||||
onReady,
|
||||
});
|
||||
|
||||
|
|
@ -1379,12 +1323,8 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
it("copies and bounds pending audio by aggregate bytes before activation", async () => {
|
||||
const connectedSession = createMockGoogleLiveSession();
|
||||
connectMock.mockResolvedValueOnce(connectedSession);
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
const bridge = createGoogleLiveBridge({
|
||||
audioFormat: REALTIME_VOICE_AUDIO_FORMAT_PCM16_24KHZ,
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
});
|
||||
const backing = Buffer.alloc(2 * 1024 * 1024);
|
||||
const firstChunk = backing.subarray(0, 512 * 1024);
|
||||
|
|
@ -1412,12 +1352,8 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
it("bounds pending audio by chunk count before activation", async () => {
|
||||
const connectedSession = createMockGoogleLiveSession();
|
||||
connectMock.mockResolvedValueOnce(connectedSession);
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
const bridge = createGoogleLiveBridge({
|
||||
audioFormat: REALTIME_VOICE_AUDIO_FORMAT_PCM16_24KHZ,
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
});
|
||||
|
||||
for (let index = 0; index < 321; index += 1) {
|
||||
|
|
@ -1434,13 +1370,9 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
it("drops reconnect audio on terminal exhaustion until an explicit reconnect owns admission", async () => {
|
||||
vi.useFakeTimers();
|
||||
const reconnectedSession = createMockGoogleLiveSession();
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onClose = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
const bridge = createGoogleLiveBridge({
|
||||
audioFormat: REALTIME_VOICE_AUDIO_FORMAT_PCM16_24KHZ,
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
onClose,
|
||||
});
|
||||
|
||||
|
|
@ -1480,13 +1412,9 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
const pendingSession = createDeferred<MockGoogleLiveSession>();
|
||||
const lateSession = createMockGoogleLiveSession();
|
||||
connectMock.mockReturnValueOnce(pendingSession.promise);
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onReady = vi.fn();
|
||||
const onClose = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
const bridge = createGoogleLiveBridge({
|
||||
onReady,
|
||||
onClose,
|
||||
});
|
||||
|
|
@ -1514,11 +1442,7 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
const pendingSession = createDeferred<MockGoogleLiveSession>();
|
||||
const connectedSession = createMockGoogleLiveSession();
|
||||
connectMock.mockReturnValueOnce(pendingSession.promise);
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
const bridge = createGoogleLiveBridge({
|
||||
onReady: () => {
|
||||
throw new Error("ready callback failed");
|
||||
},
|
||||
|
|
@ -1641,12 +1565,9 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
});
|
||||
|
||||
it("converts Google PCM output to mu-law audio", async () => {
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onAudio = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
const bridge = createGoogleLiveBridge({
|
||||
onAudio,
|
||||
onClearAudio: vi.fn(),
|
||||
});
|
||||
const pcm24k = Buffer.alloc(480);
|
||||
pcm24k.set([0xfb, 0xff]);
|
||||
|
|
@ -1675,13 +1596,10 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
});
|
||||
|
||||
it("can keep Google PCM output as PCM16 24 kHz audio", async () => {
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onAudio = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
const bridge = createGoogleLiveBridge({
|
||||
audioFormat: REALTIME_VOICE_AUDIO_FORMAT_PCM16_24KHZ,
|
||||
onAudio,
|
||||
onClearAudio: vi.fn(),
|
||||
});
|
||||
const pcm24k = Buffer.alloc(480);
|
||||
|
||||
|
|
@ -1711,16 +1629,13 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
["non-canonical pad bits", "ZE=="],
|
||||
["mixed alphabet", "aGVsbG8+_"],
|
||||
])("terminates the session for %s in output audio", async (_scenario, data) => {
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onAudio = vi.fn();
|
||||
const onError = vi.fn();
|
||||
const onClose = vi.fn();
|
||||
const onTranscript = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
const bridge = createGoogleLiveBridge({
|
||||
audioFormat: REALTIME_VOICE_AUDIO_FORMAT_PCM16_24KHZ,
|
||||
onAudio,
|
||||
onClearAudio: vi.fn(),
|
||||
onError,
|
||||
onClose,
|
||||
onTranscript,
|
||||
|
|
@ -1755,12 +1670,8 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
});
|
||||
|
||||
it("uses official output transcription instead of model-turn text", async () => {
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onTranscript = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
const bridge = createGoogleLiveBridge({
|
||||
onTranscript,
|
||||
});
|
||||
|
||||
|
|
@ -1820,15 +1731,11 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
});
|
||||
|
||||
it("allows each role's UTF-8 transcript limit and releases it on finished", async () => {
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onTranscript = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
const bridge = createGoogleLiveBridge({
|
||||
providerConfig: {
|
||||
model: "gemini-2.5-flash-native-audio-preview-12-2025",
|
||||
apiKey: "gemini-key",
|
||||
},
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
onTranscript,
|
||||
});
|
||||
|
||||
|
|
@ -1858,17 +1765,13 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
});
|
||||
|
||||
it("terminates and clears a runaway transcript stream at the UTF-8 byte limit", async () => {
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onError = vi.fn();
|
||||
const onClose = vi.fn();
|
||||
const onTranscript = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
const bridge = createGoogleLiveBridge({
|
||||
providerConfig: {
|
||||
model: "gemini-2.5-flash-native-audio-preview-12-2025",
|
||||
apiKey: "gemini-key",
|
||||
},
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
onError,
|
||||
onClose,
|
||||
onTranscript,
|
||||
|
|
@ -2023,12 +1926,8 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
});
|
||||
|
||||
it("deduplicates replayed Google Live tool calls by call id", async () => {
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onToolCall = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
const bridge = createGoogleLiveBridge({
|
||||
onToolCall,
|
||||
});
|
||||
|
||||
|
|
@ -2065,13 +1964,9 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
});
|
||||
|
||||
it("ignores late results after Google cancels a tool call", async () => {
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onEvent = vi.fn();
|
||||
const onError = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
const bridge = createGoogleLiveBridge({
|
||||
onToolCall: vi.fn(),
|
||||
onEvent,
|
||||
onError,
|
||||
|
|
@ -2100,14 +1995,10 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
});
|
||||
|
||||
it("fails closed when Google exceeds the tool-call session limit", async () => {
|
||||
const provider = buildGoogleRealtimeVoiceProvider();
|
||||
const onToolCall = vi.fn();
|
||||
const onError = vi.fn();
|
||||
const onClose = vi.fn();
|
||||
const bridge = provider.createBridge({
|
||||
providerConfig: { apiKey: "gemini-key" },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
const bridge = createGoogleLiveBridge({
|
||||
onToolCall,
|
||||
onError,
|
||||
onClose,
|
||||
|
|
@ -2246,10 +2137,8 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
"rejects %s Google Live tool results while keeping the call retryable",
|
||||
async (_label, create) => {
|
||||
const onError = vi.fn();
|
||||
const bridge = buildGoogleRealtimeVoiceProvider().createBridge({
|
||||
const bridge = createGoogleLiveBridge({
|
||||
providerConfig: { apiKey: ["google", "test"].join("-") },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
onError,
|
||||
onToolCall: vi.fn(),
|
||||
});
|
||||
|
|
@ -2272,10 +2161,8 @@ describe("buildGoogleRealtimeVoiceProvider", () => {
|
|||
);
|
||||
|
||||
it("preserves valid Google Live tool results and nested serialization keys", async () => {
|
||||
const bridge = buildGoogleRealtimeVoiceProvider().createBridge({
|
||||
const bridge = createGoogleLiveBridge({
|
||||
providerConfig: { apiKey: ["google", "test"].join("-") },
|
||||
onAudio: vi.fn(),
|
||||
onClearAudio: vi.fn(),
|
||||
onToolCall: vi.fn(),
|
||||
});
|
||||
const objectSerialization = vi.fn((key: string) => ({ key }));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue