eb1a46baf5
- Added optional FetchImpl fields to compaction, proxy, AI, coding-agent, and mnemopi options. - Threaded injected fetch implementations through OAuth, discovery, and search/LLM request flows. - Removed exported hookFetch utility and its package entrypoint from utils. - Replaced global-fetch test monkeypatching with per-test FetchImpl mocks across test suites.
94 lines
2.6 KiB
TypeScript
94 lines
2.6 KiB
TypeScript
import { afterEach, describe, expect, it } from "bun:test";
|
|
import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
import type { FetchImpl } from "@oh-my-pi/pi-ai/types";
|
|
import { searchGemini } from "@oh-my-pi/pi-coding-agent/web/search/providers/gemini";
|
|
|
|
const SSE_RESPONSE =
|
|
'data: {"response":{"candidates":[{"content":{"role":"model","parts":[{"text":"Gemini answer"}]}}],"modelVersion":"gemini-2.5-flash"}}\n\n';
|
|
|
|
type CapturedRequest = {
|
|
body: Record<string, unknown> | null;
|
|
};
|
|
|
|
describe("searchGemini tools serialization", () => {
|
|
let capturedRequest: CapturedRequest | null = null;
|
|
|
|
const fakeAuthStorage = {
|
|
async getOAuthAccess() {
|
|
return {
|
|
accessToken: "test-access-token",
|
|
projectId: "test-project",
|
|
};
|
|
},
|
|
hasOAuth() {
|
|
return true;
|
|
},
|
|
} as unknown as AuthStorage;
|
|
|
|
function mockGeminiFetch(): FetchImpl {
|
|
capturedRequest = null;
|
|
return (_url, init) => {
|
|
capturedRequest = {
|
|
body: init?.body ? (JSON.parse(init.body as string) as Record<string, unknown>) : null,
|
|
};
|
|
return Promise.resolve(
|
|
new Response(SSE_RESPONSE, {
|
|
status: 200,
|
|
headers: { "Content-Type": "text/event-stream" },
|
|
}),
|
|
);
|
|
};
|
|
}
|
|
|
|
afterEach(() => {
|
|
capturedRequest = null;
|
|
});
|
|
|
|
function makeParams(query: string) {
|
|
return {
|
|
query,
|
|
authStorage: fakeAuthStorage,
|
|
systemPrompt: "Gemini test prompt",
|
|
} as const;
|
|
}
|
|
|
|
it("sends default googleSearch tool when no passthrough payloads are provided", async () => {
|
|
const fetchMock = mockGeminiFetch();
|
|
await searchGemini({ ...makeParams("default tools"), fetch: fetchMock });
|
|
|
|
expect(capturedRequest).not.toBeNull();
|
|
expect(capturedRequest?.body?.request).toMatchObject({
|
|
tools: [{ googleSearch: {} }],
|
|
});
|
|
});
|
|
|
|
it("passes through googleSearch payload into googleSearch tool", async () => {
|
|
const fetchMock = mockGeminiFetch();
|
|
await searchGemini({
|
|
...makeParams("google payload"),
|
|
google_search: { dynamicRetrievalConfig: { mode: "MODE_DYNAMIC" } },
|
|
fetch: fetchMock,
|
|
});
|
|
|
|
expect(capturedRequest).not.toBeNull();
|
|
expect(capturedRequest?.body?.request).toMatchObject({
|
|
tools: [{ googleSearch: { dynamicRetrievalConfig: { mode: "MODE_DYNAMIC" } } }],
|
|
});
|
|
});
|
|
|
|
it("includes codeExecution and urlContext tools when provided", async () => {
|
|
const fetchMock = mockGeminiFetch();
|
|
await searchGemini({
|
|
...makeParams("extended tools"),
|
|
code_execution: {},
|
|
url_context: { allowedDomains: ["example.com"] },
|
|
fetch: fetchMock,
|
|
});
|
|
|
|
expect(capturedRequest).not.toBeNull();
|
|
expect(capturedRequest?.body?.request).toMatchObject({
|
|
tools: [{ googleSearch: {} }, { codeExecution: {} }, { urlContext: { allowedDomains: ["example.com"] } }],
|
|
});
|
|
});
|
|
});
|