Files
oh-my-pi/packages/coding-agent/test/tools/web-search-gemini.test.ts
T
can1357 e689351597 fix(coding-agent): resolved OAuth token expiry flow in AuthStorage
- Centralized OAuth access lifecycle in `AuthStorage`, returning identity metadata and new access-result types.
- Added 60-second skew and strict expiry checks, returning undefined/throws for stale or expired OAuth credentials.
- Removed provider-local token refresh flows from Gemini, Gemini CLI, Antigravity, Kimi, and related OAuth helpers.
- Migrated web-search providers from `AgentStorage` to `AuthStorage` session-aware lookup with `authStorage`/`sessionId`/`signal` flow.
- Replaced `findAnthropicAuth`/DB auth lookup with `buildAnthropicAuthConfig` and explicit base-url override/env fallback ordering.
2026-05-26 03:59:13 +02:00

91 lines
2.5 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "bun:test";
import type { AuthStorage } from "@oh-my-pi/pi-ai";
import { hookFetch } from "@oh-my-pi/pi-utils";
import { searchGemini } from "../../src/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() {
capturedRequest = null;
return hookFetch((_url, init) => {
capturedRequest = {
body: init?.body ? (JSON.parse(init.body as string) as Record<string, unknown>) : null,
};
return new Response(SSE_RESPONSE, {
status: 200,
headers: { "Content-Type": "text/event-stream" },
});
});
}
afterEach(() => {
vi.restoreAllMocks();
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 () => {
using _hook = mockGeminiFetch();
await searchGemini(makeParams("default tools"));
expect(capturedRequest).not.toBeNull();
expect(capturedRequest?.body?.request).toMatchObject({
tools: [{ googleSearch: {} }],
});
});
it("passes through googleSearch payload into googleSearch tool", async () => {
using _hook = mockGeminiFetch();
await searchGemini({
...makeParams("google payload"),
google_search: { dynamicRetrievalConfig: { mode: "MODE_DYNAMIC" } },
});
expect(capturedRequest).not.toBeNull();
expect(capturedRequest?.body?.request).toMatchObject({
tools: [{ googleSearch: { dynamicRetrievalConfig: { mode: "MODE_DYNAMIC" } } }],
});
});
it("includes codeExecution and urlContext tools when provided", async () => {
using _hook = mockGeminiFetch();
await searchGemini({
...makeParams("extended tools"),
code_execution: {},
url_context: { allowedDomains: ["example.com"] },
});
expect(capturedRequest).not.toBeNull();
expect(capturedRequest?.body?.request).toMatchObject({
tools: [{ googleSearch: {} }, { codeExecution: {} }, { urlContext: { allowedDomains: ["example.com"] } }],
});
});
});