-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathindex.test.ts
More file actions
85 lines (72 loc) · 2.84 KB
/
Copy pathindex.test.ts
File metadata and controls
85 lines (72 loc) · 2.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import { afterEach, describe, expect, it, vi } from "vitest"
import { SerpApiPlugin } from "./index"
const originalApiKey = process.env.SERPAPI_API_KEY
async function pluginHooks() {
return SerpApiPlugin({} as never)
}
afterEach(() => {
vi.unstubAllGlobals()
if (originalApiKey === undefined) delete process.env.SERPAPI_API_KEY
else process.env.SERPAPI_API_KEY = originalApiKey
})
describe("serpapi_search", () => {
it("calls the REST API with mapped parameters and the trusted API key", async () => {
process.env.SERPAPI_API_KEY = "trusted-key"
const fetchMock = vi.fn(async (_input: string | URL | Request) => new Response('{"ok":true}', { status: 200 }))
vi.stubGlobal("fetch", fetchMock)
const search = (await pluginHooks()).tool?.serpapi_search
if (!search) throw new Error("serpapi_search was not registered")
const result = await search.execute(
{ engine: "amazon", q: "headphones", params: { api_key: "untrusted-key", page: 2 } },
{} as never,
)
expect(result).toBe('{"ok":true}')
expect(fetchMock).toHaveBeenCalledOnce()
const url = new URL(String(fetchMock.mock.calls[0][0]))
expect(url.origin + url.pathname).toBe("https://serpapi.com/search.json")
expect(Object.fromEntries(url.searchParams)).toEqual({
engine: "amazon",
page: "2",
k: "headphones",
api_key: "trusted-key",
})
})
it("returns HTTP failures as tool output", async () => {
process.env.SERPAPI_API_KEY = "test-key"
vi.stubGlobal("fetch", vi.fn(async () => new Response("rate limited", { status: 429 })))
const search = (await pluginHooks()).tool?.serpapi_search
if (!search) throw new Error("serpapi_search was not registered")
await expect(search.execute({ engine: "google_light", q: "news" }, {} as never)).resolves.toBe(
"SerpApi search failed: HTTP 429: rate limited",
)
})
it("does not make a request without an API key", async () => {
delete process.env.SERPAPI_API_KEY
const fetchMock = vi.fn()
vi.stubGlobal("fetch", fetchMock)
const search = (await pluginHooks()).tool?.serpapi_search
if (!search) throw new Error("serpapi_search was not registered")
await expect(search.execute({ q: "news" }, {} as never)).resolves.toBe(
"SERPAPI_API_KEY is not available to the OpenCode process.",
)
expect(fetchMock).not.toHaveBeenCalled()
})
})
describe("MCP configuration", () => {
it("resolves the API key into the authorization header", async () => {
process.env.SERPAPI_API_KEY = "test-key"
const hooks = await pluginHooks()
const config = {}
await hooks.config?.(config)
expect(config).toMatchObject({
mcp: {
serpapi: {
type: "remote",
url: "https://mcp.serpapi.com/mcp",
oauth: false,
headers: { Authorization: "Bearer test-key" },
},
},
})
})
})