diff --git a/extensions/google/embedding-provider.test.ts b/extensions/google/embedding-provider.test.ts index 2f235744412..0519ea1e406 100644 --- a/extensions/google/embedding-provider.test.ts +++ b/extensions/google/embedding-provider.test.ts @@ -37,6 +37,14 @@ function fetchJsonBody(fetchMock: ReturnType, index: number): unkn return JSON.parse(body) as unknown; } +function requireFirstFetchInput(fetchMock: ReturnType): RequestInfo | URL { + const [call] = fetchMock.mock.calls; + if (!call) { + throw new Error("expected Gemini embedding fetch call"); + } + return call[0] as RequestInfo | URL; +} + describe("Gemini embedding request helpers", () => { it("builds requests and resolves model settings", () => { expect( @@ -153,7 +161,7 @@ describe("Gemini embedding provider", () => { [0, 0, 1], ]); - expect(fetchMock.mock.calls[0]?.[0]).toBe( + expect(requireFirstFetchInput(fetchMock)).toBe( "https://generativelanguage.googleapis.com/v1beta/models/gemini-embedding-2-preview:embedContent", ); expect(fetchJsonBody(fetchMock, 0)).toEqual({ diff --git a/extensions/google/transport-stream.test.ts b/extensions/google/transport-stream.test.ts index 2d93c7adb91..6a1bba3ad5f 100644 --- a/extensions/google/transport-stream.test.ts +++ b/extensions/google/transport-stream.test.ts @@ -398,13 +398,19 @@ describe("google transport stream", () => { expect(result.content).toEqual([{ type: "text", text: "recovered" }]); expect(guardedFetchMock).toHaveBeenCalledTimes(2); - const firstBody = JSON.parse(guardedFetchMock.mock.calls[0]?.[1]?.body as string); - const retryBody = JSON.parse(guardedFetchMock.mock.calls[1]?.[1]?.body as string); - expect(firstBody.generationConfig.thinkingConfig).toEqual({ + const firstBody = parseRequestJsonBody( + requireRequestInit(requireMockCall(guardedFetchMock, 0, "guarded fetch"), "guarded fetch"), + ); + const retryBody = parseRequestJsonBody( + requireRequestInit(requireMockCall(guardedFetchMock, 1, "guarded fetch"), "guarded fetch"), + ); + const firstGenerationConfig = requireGenerationConfig(firstBody); + const retryGenerationConfig = requireGenerationConfig(retryBody); + expect(firstGenerationConfig.thinkingConfig).toEqual({ includeThoughts: true, thinkingLevel: "HIGH", }); - expect(retryBody.generationConfig.thinkingConfig).toEqual({ + expect(retryGenerationConfig.thinkingConfig).toEqual({ thinkingLevel: "LOW", }); expect(retryBody.tools).toEqual(firstBody.tools); diff --git a/extensions/google/video-generation-provider.test.ts b/extensions/google/video-generation-provider.test.ts index a20baa933f7..2dbc6fd2fc6 100644 --- a/extensions/google/video-generation-provider.test.ts +++ b/extensions/google/video-generation-provider.test.ts @@ -37,7 +37,11 @@ type MockWithCalls = { }; function firstObjectArg(mock: MockWithCalls): Record { - const value = mock.mock.calls[0]?.[0]; + const [call] = mock.mock.calls; + if (!call) { + throw new Error("expected first mock call to receive an object argument"); + } + const value = call[0]; if (value === undefined || value === null || typeof value !== "object" || Array.isArray(value)) { throw new Error("expected first mock call to receive an object argument"); } @@ -55,6 +59,37 @@ function firstGoogleClientHttpOptions(): Record { return recordField(firstObjectArg(createGoogleGenAIMock).httpOptions, "httpOptions"); } +function requireFetchCall( + fetchMock: ReturnType, + index: number, +): [RequestInfo | URL, RequestInit | undefined] { + const call = fetchMock.mock.calls[index]; + if (!call) { + throw new Error(`expected Google video fetch call ${index}`); + } + return call as [RequestInfo | URL, RequestInit | undefined]; +} + +function parseFetchJsonBody(fetchMock: ReturnType, index: number): unknown { + const [, init] = requireFetchCall(fetchMock, index); + const body = init?.body; + if (typeof body !== "string") { + throw new Error(`expected Google video fetch body ${index}`); + } + return JSON.parse(body) as unknown; +} + +function fetchInputUrl(fetchMock: ReturnType, index: number): string { + const [input] = requireFetchCall(fetchMock, index); + if (typeof input === "string") { + return input; + } + if (input instanceof URL) { + return input.toString(); + } + return input.url; +} + let ssrfMock: { mockRestore: () => void } | undefined; describe("google video generation provider", () => { @@ -308,14 +343,14 @@ describe("google video generation provider", () => { }); expect(fetchMock).toHaveBeenCalledTimes(2); - expect(String(fetchMock.mock.calls[0]?.[0])).toBe( + expect(fetchInputUrl(fetchMock, 0)).toBe( "https://generativelanguage.googleapis.com/v1beta/models/veo-3.1-fast-generate-preview:predictLongRunning", ); - expect(JSON.parse(String(fetchMock.mock.calls[0]?.[1]?.body))).toEqual({ + expect(parseFetchJsonBody(fetchMock, 0)).toEqual({ instances: [{ prompt: "A tiny robot watering a windowsill garden" }], parameters: { durationSeconds: 4 }, }); - expect(String(fetchMock.mock.calls[1]?.[0])).toBe( + expect(fetchInputUrl(fetchMock, 1)).toBe( "https://generativelanguage.googleapis.com/v1beta/files/rest-video:download?alt=media&key=google-key", ); expect(downloadMock).not.toHaveBeenCalled(); diff --git a/extensions/google/web-search-provider.test.ts b/extensions/google/web-search-provider.test.ts index b5f15e821b8..e0b292efd24 100644 --- a/extensions/google/web-search-provider.test.ts +++ b/extensions/google/web-search-provider.test.ts @@ -38,13 +38,23 @@ function createGoogleModelProviderConfig( }; } +function requireFirstGeminiFetchCall( + mockFetch: ReturnType, +): [RequestInfo | URL | undefined, RequestInit | undefined] { + const [call] = mockFetch.mock.calls; + if (!call) { + throw new Error("expected Gemini web search fetch call"); + } + return call as [RequestInfo | URL | undefined, RequestInit | undefined]; +} + function getFetchHeaders(mockFetch: ReturnType): Record { - const init = mockFetch.mock.calls[0]?.[1] as { headers?: Record } | undefined; - return init?.headers ?? {}; + const [, init] = requireFirstGeminiFetchCall(mockFetch); + return (init?.headers as Record | undefined) ?? {}; } function getGeminiFetchUrl(mockFetch: ReturnType): string | undefined { - const input = mockFetch.mock.calls[0]?.[0]; + const [input] = requireFirstGeminiFetchCall(mockFetch); if (typeof input === "string") { return input; } @@ -57,7 +67,8 @@ function getGeminiFetchUrl(mockFetch: ReturnType): st function parseGeminiFetchBody(mockFetch: ReturnType): { tools?: Array<{ google_search?: { timeRangeFilter?: unknown } }>; } { - const body = mockFetch.mock.calls[0]?.[1]?.body; + const [, init] = requireFirstGeminiFetchCall(mockFetch); + const body = init?.body; if (typeof body !== "string") { throw new Error("Expected Gemini fetch body string"); } @@ -179,7 +190,7 @@ describe("google web search provider", () => { await tool?.execute({ query: "OpenClaw docs" }, { signal: controller.signal }); - const init = mockFetch.mock.calls[0]?.[1] as { signal?: AbortSignal } | undefined; + const [, init] = requireFirstGeminiFetchCall(mockFetch); expect(init?.signal?.aborted).toBe(true); });