test: guard google provider mock calls

This commit is contained in:
Peter Steinberger
2026-05-12 00:03:51 +01:00
parent e847e0f2dd
commit 93b1e7e282
4 changed files with 74 additions and 14 deletions

View File

@@ -37,6 +37,14 @@ function fetchJsonBody(fetchMock: ReturnType<typeof vi.fn>, index: number): unkn
return JSON.parse(body) as unknown;
}
function requireFirstFetchInput(fetchMock: ReturnType<typeof vi.fn>): 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({

View File

@@ -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);

View File

@@ -37,7 +37,11 @@ type MockWithCalls = {
};
function firstObjectArg(mock: MockWithCalls): Record<string, unknown> {
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<string, unknown> {
return recordField(firstObjectArg(createGoogleGenAIMock).httpOptions, "httpOptions");
}
function requireFetchCall(
fetchMock: ReturnType<typeof vi.fn>,
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<typeof vi.fn>, 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<typeof vi.fn>, 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();

View File

@@ -38,13 +38,23 @@ function createGoogleModelProviderConfig(
};
}
function requireFirstGeminiFetchCall(
mockFetch: ReturnType<typeof installGeminiFetch>,
): [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<typeof installGeminiFetch>): Record<string, string> {
const init = mockFetch.mock.calls[0]?.[1] as { headers?: Record<string, string> } | undefined;
return init?.headers ?? {};
const [, init] = requireFirstGeminiFetchCall(mockFetch);
return (init?.headers as Record<string, string> | undefined) ?? {};
}
function getGeminiFetchUrl(mockFetch: ReturnType<typeof installGeminiFetch>): 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<typeof installGeminiFetch>): st
function parseGeminiFetchBody(mockFetch: ReturnType<typeof installGeminiFetch>): {
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);
});