Files
ca-marketplace-scraper/packages/mcp-server/test/protocol.test.ts

159 lines
4.6 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
import { handleMcpRequest } from "../src/protocol/handler";
import { tools } from "../src/protocol/tools";
const originalFetch = global.fetch;
describe("MCP protocol cookie inputs", () => {
beforeEach(() => {
global.fetch = mock(() =>
Promise.resolve(new Response(JSON.stringify([]), { status: 200 })),
) as unknown as typeof fetch;
});
afterEach(() => {
global.fetch = originalFetch;
});
test("search tools should not expose Facebook or eBay cookie inputs", () => {
const searchFacebookTool = tools.find(
(tool) => tool.name === "search_facebook",
);
const searchEbayTool = tools.find((tool) => tool.name === "search_ebay");
expect(searchFacebookTool?.inputSchema.properties).not.toHaveProperty(
"cookiesSource",
);
expect(searchEbayTool?.inputSchema.properties).not.toHaveProperty(
"cookies",
);
});
test("search_facebook should not forward cookies query parameters", async () => {
await handleMcpRequest(
new Request("http://localhost", {
method: "POST",
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: {
name: "search_facebook",
arguments: {
query: "laptop",
cookiesSource: "c_user=1",
},
},
}),
}),
);
const calledUrl = (global.fetch as unknown as ReturnType<typeof mock>).mock
.calls[0]?.[0];
expect(String(calledUrl)).toContain("/facebook?q=laptop");
expect(String(calledUrl)).not.toContain("cookies=");
});
});
describe("MCP protocol unstableFilter", () => {
beforeEach(() => {
global.fetch = mock(() =>
Promise.resolve(new Response(JSON.stringify([]), { status: 200 })),
) as unknown as typeof fetch;
});
afterEach(() => {
global.fetch = originalFetch;
});
test("all search tools should document the unstableFilter property", () => {
const toolNames = ["search_kijiji", "search_facebook", "search_ebay"];
for (const toolName of toolNames) {
const tool = tools.find((t) => t.name === toolName);
expect(tool).toBeDefined();
expect(tool?.inputSchema.properties).toHaveProperty("unstableFilter");
const prop = tool?.inputSchema.properties.unstableFilter as {
type: string;
description: string;
};
expect(prop.type).toBe("boolean");
expect(prop.description).toContain("optional");
expect(prop.description).toContain("20%");
expect(prop.description).toContain("median");
expect(prop.description).toContain("unstableResults");
}
});
test("handler should forward unstableFilter=true for search_kijiji", async () => {
await handleMcpRequest(
new Request("http://localhost", {
method: "POST",
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: {
name: "search_kijiji",
arguments: {
query: "laptop",
unstableFilter: true,
},
},
}),
}),
);
const calledUrl = (global.fetch as unknown as ReturnType<typeof mock>).mock
.calls[0]?.[0];
expect(String(calledUrl)).toContain("unstableFilter=true");
});
test("handler should forward unstableFilter=true for search_facebook", async () => {
await handleMcpRequest(
new Request("http://localhost", {
method: "POST",
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: {
name: "search_facebook",
arguments: {
query: "laptop",
unstableFilter: true,
},
},
}),
}),
);
const calledUrl = (global.fetch as unknown as ReturnType<typeof mock>).mock
.calls[0]?.[0];
expect(String(calledUrl)).toContain("unstableFilter=true");
});
test("handler should forward unstableFilter=true for search_ebay", async () => {
await handleMcpRequest(
new Request("http://localhost", {
method: "POST",
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "tools/call",
params: {
name: "search_ebay",
arguments: {
query: "laptop",
unstableFilter: true,
},
},
}),
}),
);
const calledUrl = (global.fetch as unknown as ReturnType<typeof mock>).mock
.calls[0]?.[0];
expect(String(calledUrl)).toContain("unstableFilter=true");
});
});