diff --git a/src/agents/tools/web-tools.enabled-defaults.e2e.test.ts b/src/agents/tools/web-tools.enabled-defaults.e2e.test.ts index 030d6b668cf..a581798b437 100644 --- a/src/agents/tools/web-tools.enabled-defaults.e2e.test.ts +++ b/src/agents/tools/web-tools.enabled-defaults.e2e.test.ts @@ -29,6 +29,38 @@ function createPerplexitySearchTool(perplexityConfig?: { apiKey?: string; baseUr }); } +function parseFirstRequestBody(mockFetch: ReturnType) { + const request = mockFetch.mock.calls[0]?.[1] as RequestInit | undefined; + const requestBody = request?.body; + return JSON.parse(typeof requestBody === "string" ? requestBody : "{}") as Record< + string, + unknown + >; +} + +function installPerplexitySuccessFetch() { + return installMockFetch({ + choices: [{ message: { content: "ok" } }], + citations: [], + }); +} + +async function executePerplexitySearch( + query: string, + options?: { + perplexityConfig?: { apiKey?: string; baseUrl?: string }; + freshness?: string; + }, +) { + const mockFetch = installPerplexitySuccessFetch(); + const tool = createPerplexitySearchTool(options?.perplexityConfig); + await tool?.execute?.( + 1, + options?.freshness ? { query, freshness: options.freshness } : { query }, + ); + return mockFetch; +} + describe("web tools defaults", () => { it("enables web_fetch by default (non-sandbox)", () => { const tool = createWebFetchTool({ config: {}, sandboxed: false }); @@ -62,43 +94,30 @@ describe("web_search country and language parameters", () => { global.fetch = priorFetch; }); - it("should pass country parameter to Brave API", async () => { + async function runBraveSearchAndGetUrl( + params: Partial<{ + country: string; + search_lang: string; + ui_lang: string; + freshness: string; + }>, + ) { const mockFetch = installMockFetch({ web: { results: [] } }); const tool = createWebSearchTool({ config: undefined, sandboxed: true }); expect(tool).not.toBeNull(); - - await tool?.execute?.(1, { query: "test", country: "DE" }); - + await tool?.execute?.(1, { query: "test", ...params }); expect(mockFetch).toHaveBeenCalled(); - const url = new URL(mockFetch.mock.calls[0][0] as string); - expect(url.searchParams.get("country")).toBe("DE"); - }); + return new URL(mockFetch.mock.calls[0][0] as string); + } - it("should pass search_lang parameter to Brave API", async () => { - const mockFetch = installMockFetch({ web: { results: [] } }); - const tool = createWebSearchTool({ config: undefined, sandboxed: true }); - await tool?.execute?.(1, { query: "test", search_lang: "de" }); - - const url = new URL(mockFetch.mock.calls[0][0] as string); - expect(url.searchParams.get("search_lang")).toBe("de"); - }); - - it("should pass ui_lang parameter to Brave API", async () => { - const mockFetch = installMockFetch({ web: { results: [] } }); - const tool = createWebSearchTool({ config: undefined, sandboxed: true }); - await tool?.execute?.(1, { query: "test", ui_lang: "de" }); - - const url = new URL(mockFetch.mock.calls[0][0] as string); - expect(url.searchParams.get("ui_lang")).toBe("de"); - }); - - it("should pass freshness parameter to Brave API", async () => { - const mockFetch = installMockFetch({ web: { results: [] } }); - const tool = createWebSearchTool({ config: undefined, sandboxed: true }); - await tool?.execute?.(1, { query: "test", freshness: "pw" }); - - const url = new URL(mockFetch.mock.calls[0][0] as string); - expect(url.searchParams.get("freshness")).toBe("pw"); + it.each([ + { key: "country", value: "DE" }, + { key: "search_lang", value: "de" }, + { key: "ui_lang", value: "de" }, + { key: "freshness", value: "pw" }, + ])("passes $key parameter to Brave API", async ({ key, value }) => { + const url = await runBraveSearchAndGetUrl({ [key]: value }); + expect(url.searchParams.get(key)).toBe(value); }); it("rejects invalid freshness values", async () => { @@ -120,108 +139,72 @@ describe("web_search perplexity baseUrl defaults", () => { global.fetch = priorFetch; }); - it("defaults to Perplexity direct when PERPLEXITY_API_KEY is set", async () => { - vi.stubEnv("PERPLEXITY_API_KEY", "pplx-test"); - const mockFetch = installMockFetch({ - choices: [{ message: { content: "ok" } }], - citations: [], - }); - const tool = createPerplexitySearchTool(); - await tool?.execute?.(1, { query: "test-openrouter" }); - - expect(mockFetch).toHaveBeenCalled(); - expect(mockFetch.mock.calls[0]?.[0]).toBe("https://api.perplexity.ai/chat/completions"); - const request = mockFetch.mock.calls[0]?.[1] as RequestInit | undefined; - const requestBody = request?.body; - const body = JSON.parse(typeof requestBody === "string" ? requestBody : "{}") as { - model?: string; - }; - expect(body.model).toBe("sonar-pro"); - }); - it("passes freshness to Perplexity provider as search_recency_filter", async () => { vi.stubEnv("PERPLEXITY_API_KEY", "pplx-test"); - const mockFetch = installMockFetch({ - choices: [{ message: { content: "ok" } }], - citations: [], + const mockFetch = await executePerplexitySearch("perplexity-freshness-test", { + freshness: "pw", }); - const tool = createPerplexitySearchTool(); - await tool?.execute?.(1, { query: "perplexity-freshness-test", freshness: "pw" }); expect(mockFetch).toHaveBeenCalledOnce(); - const body = JSON.parse(mockFetch.mock.calls[0][1].body as string); + const body = parseFirstRequestBody(mockFetch); expect(body.search_recency_filter).toBe("week"); }); - it("defaults to OpenRouter when OPENROUTER_API_KEY is set", async () => { - vi.stubEnv("PERPLEXITY_API_KEY", ""); - vi.stubEnv("OPENROUTER_API_KEY", "sk-or-test"); - const mockFetch = installMockFetch({ - choices: [{ message: { content: "ok" } }], - citations: [], - }); - const tool = createPerplexitySearchTool(); - await tool?.execute?.(1, { query: "test-openrouter-env" }); + it.each([ + { + name: "defaults to Perplexity direct when PERPLEXITY_API_KEY is set", + env: { perplexity: "pplx-test" }, + query: "test-openrouter", + expectedUrl: "https://api.perplexity.ai/chat/completions", + expectedModel: "sonar-pro", + }, + { + name: "defaults to OpenRouter when OPENROUTER_API_KEY is set", + env: { perplexity: "", openrouter: "sk-or-test" }, + query: "test-openrouter-env", + expectedUrl: "https://openrouter.ai/api/v1/chat/completions", + expectedModel: "perplexity/sonar-pro", + }, + { + name: "prefers PERPLEXITY_API_KEY when both env keys are set", + env: { perplexity: "pplx-test", openrouter: "sk-or-test" }, + query: "test-both-env", + expectedUrl: "https://api.perplexity.ai/chat/completions", + }, + { + name: "uses configured baseUrl even when PERPLEXITY_API_KEY is set", + env: { perplexity: "pplx-test" }, + query: "test-config-baseurl", + perplexityConfig: { baseUrl: "https://example.com/pplx" }, + expectedUrl: "https://example.com/pplx/chat/completions", + }, + { + name: "defaults to Perplexity direct when apiKey looks like Perplexity", + query: "test-config-apikey", + perplexityConfig: { apiKey: "pplx-config" }, + expectedUrl: "https://api.perplexity.ai/chat/completions", + }, + { + name: "defaults to OpenRouter when apiKey looks like OpenRouter", + query: "test-openrouter-config", + perplexityConfig: { apiKey: "sk-or-v1-test" }, + expectedUrl: "https://openrouter.ai/api/v1/chat/completions", + }, + ])("$name", async ({ env, query, perplexityConfig, expectedUrl, expectedModel }) => { + if (env?.perplexity !== undefined) { + vi.stubEnv("PERPLEXITY_API_KEY", env.perplexity); + } + if (env?.openrouter !== undefined) { + vi.stubEnv("OPENROUTER_API_KEY", env.openrouter); + } + const mockFetch = await executePerplexitySearch(query, { perplexityConfig }); expect(mockFetch).toHaveBeenCalled(); - expect(mockFetch.mock.calls[0]?.[0]).toBe("https://openrouter.ai/api/v1/chat/completions"); - const request = mockFetch.mock.calls[0]?.[1] as RequestInit | undefined; - const requestBody = request?.body; - const body = JSON.parse(typeof requestBody === "string" ? requestBody : "{}") as { - model?: string; - }; - expect(body.model).toBe("perplexity/sonar-pro"); - }); - - it("prefers PERPLEXITY_API_KEY when both env keys are set", async () => { - vi.stubEnv("PERPLEXITY_API_KEY", "pplx-test"); - vi.stubEnv("OPENROUTER_API_KEY", "sk-or-test"); - const mockFetch = installMockFetch({ - choices: [{ message: { content: "ok" } }], - citations: [], - }); - const tool = createPerplexitySearchTool(); - await tool?.execute?.(1, { query: "test-both-env" }); - - expect(mockFetch).toHaveBeenCalled(); - expect(mockFetch.mock.calls[0]?.[0]).toBe("https://api.perplexity.ai/chat/completions"); - }); - - it("uses configured baseUrl even when PERPLEXITY_API_KEY is set", async () => { - vi.stubEnv("PERPLEXITY_API_KEY", "pplx-test"); - const mockFetch = installMockFetch({ - choices: [{ message: { content: "ok" } }], - citations: [], - }); - const tool = createPerplexitySearchTool({ baseUrl: "https://example.com/pplx" }); - await tool?.execute?.(1, { query: "test-config-baseurl" }); - - expect(mockFetch).toHaveBeenCalled(); - expect(mockFetch.mock.calls[0]?.[0]).toBe("https://example.com/pplx/chat/completions"); - }); - - it("defaults to Perplexity direct when apiKey looks like Perplexity", async () => { - const mockFetch = installMockFetch({ - choices: [{ message: { content: "ok" } }], - citations: [], - }); - const tool = createPerplexitySearchTool({ apiKey: "pplx-config" }); - await tool?.execute?.(1, { query: "test-config-apikey" }); - - expect(mockFetch).toHaveBeenCalled(); - expect(mockFetch.mock.calls[0]?.[0]).toBe("https://api.perplexity.ai/chat/completions"); - }); - - it("defaults to OpenRouter when apiKey looks like OpenRouter", async () => { - const mockFetch = installMockFetch({ - choices: [{ message: { content: "ok" } }], - citations: [], - }); - const tool = createPerplexitySearchTool({ apiKey: "sk-or-v1-test" }); - await tool?.execute?.(1, { query: "test-openrouter-config" }); - - expect(mockFetch).toHaveBeenCalled(); - expect(mockFetch.mock.calls[0]?.[0]).toBe("https://openrouter.ai/api/v1/chat/completions"); + expect(mockFetch.mock.calls[0]?.[0]).toBe(expectedUrl); + if (expectedModel) { + const body = parseFirstRequestBody(mockFetch); + expect(body.model).toBe(expectedModel); + } }); });