Files
oh-my-pi/packages/coding-agent/test/tools/web-search-parallel.test.ts
T
can1357 7040d58396 feat(coding-agent): added Parallel AI provider for web search and content extraction
- Added Parallel AI provider integration for web search with fast and research modes.
- Added Parallel extract API for URL content and YouTube video extraction with fallback support.
- Added /login parallel command and PARALLEL_API_KEY environment variable authentication.
- Added providers.parallelFetch configuration setting to control Parallel extract usage.
- Integrated Parallel provider into web search priority order between Exa and Kagi.
- Updated HTML-to-text and YouTube scrapers to prefer Parallel extract over fallback providers.
2026-03-12 17:08:35 +01:00

115 lines
3.2 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from "bun:test";
import { hookFetch } from "@oh-my-pi/pi-utils";
import { searchWithParallel } from "../../src/web/parallel";
import { getSearchProvider } from "../../src/web/search/provider";
import { searchParallel } from "../../src/web/search/providers/parallel";
describe("Parallel web search", () => {
let capturedRequestBody: unknown;
beforeEach(() => {
capturedRequestBody = undefined;
process.env.PARALLEL_API_KEY = "test-parallel-key";
});
afterEach(() => {
vi.restoreAllMocks();
delete process.env.PARALLEL_API_KEY;
});
function mockFetch(responseBody: unknown, status = 200): Disposable {
return hookFetch((_url, init) => {
if (typeof init?.body === "string") {
capturedRequestBody = JSON.parse(init.body);
}
return new Response(JSON.stringify(responseBody), {
status,
headers: { "Content-Type": "application/json" },
});
});
}
it("registers the Parallel provider", () => {
const provider = getSearchProvider("parallel");
expect(provider.id).toBe("parallel");
expect(provider.label).toBe("Parallel");
});
it("sends the expected Parallel search request and parses results", async () => {
using _hook = mockFetch({
search_id: "search-parallel-1",
results: [
{
title: "Parallel result",
url: "https://example.com/article",
publish_date: "2025-01-01",
excerpts: ["First excerpt", "Second excerpt"],
},
],
warnings: null,
usage: [{ name: "sku_search", count: 1 }],
});
const result = await searchWithParallel("parallel query", ["parallel query"]);
expect(capturedRequestBody).toEqual({
objective: "parallel query",
search_queries: ["parallel query"],
mode: "fast",
excerpts: { max_chars_per_result: 10_000 },
});
expect(result).toEqual({
requestId: "search-parallel-1",
sources: [
{
title: "Parallel result",
url: "https://example.com/article",
snippet: "First excerpt\n\nSecond excerpt",
publishedDate: "2025-01-01",
excerpts: ["First excerpt", "Second excerpt"],
},
],
warnings: [],
usage: [{ name: "sku_search", count: 1 }],
});
});
it("maps Parallel search responses into SearchResponse", async () => {
using _hook = mockFetch({
search_id: "search-parallel-2",
results: [
{
title: "Alpha",
url: "https://alpha.example",
publish_date: "2024-12-24",
excerpts: ["Alpha excerpt"],
},
],
errors: [],
warnings: null,
usage: null,
});
const result = await searchParallel({ query: "alpha search" });
expect(result.provider).toBe("parallel");
expect(result.requestId).toBe("search-parallel-2");
expect(result.sources).toEqual([
{
title: "Alpha",
url: "https://alpha.example",
snippet: "Alpha excerpt",
publishedDate: "2024-12-24",
ageSeconds: expect.any(Number),
},
]);
});
it("surfaces plain-text Parallel API errors", async () => {
using _hook = hookFetch(() => new Response("upstream unavailable", { status: 503 }));
await expect(searchParallel({ query: "broken" })).rejects.toMatchObject({
provider: "parallel",
status: 503,
message: "Parallel API error (503): upstream unavailable",
});
});
});