Files
pi-web-search/src/config.test.ts

194 lines
5.4 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { mkdtemp, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { loadWebSearchConfig, WebSearchConfigError } from "./config.ts";
async function writeTempConfig(contents: unknown) {
const dir = await mkdtemp(join(tmpdir(), "pi-web-search-config-"));
const file = join(dir, "web-search.json");
const body = typeof contents === "string" ? contents : JSON.stringify(contents, null, 2);
await writeFile(file, body, "utf8");
return file;
}
test("loadWebSearchConfig returns a normalized default provider and provider lookup", async () => {
const file = await writeTempConfig({
defaultProvider: "exa-main",
providers: [
{
name: "exa-main",
type: "exa",
apiKey: "exa-test-key",
options: {
defaultSearchLimit: 7,
defaultFetchTextMaxCharacters: 9000,
},
},
],
});
const config = await loadWebSearchConfig(file);
assert.equal(config.defaultProviderName, "exa-main");
assert.equal(config.defaultProvider.name, "exa-main");
assert.equal(config.providersByName.get("exa-main")?.apiKey, "exa-test-key");
assert.equal(config.providers[0]?.options?.defaultSearchLimit, 7);
});
test("loadWebSearchConfig normalizes a Tavily default with Exa fallback", async () => {
const file = await writeTempConfig({
defaultProvider: "tavily-main",
providers: [
{
name: "tavily-main",
type: "tavily",
apiKey: "tvly-test-key",
},
{
name: "exa-fallback",
type: "exa",
apiKey: "exa-test-key",
},
],
});
const config = await loadWebSearchConfig(file);
assert.equal(config.defaultProviderName, "tavily-main");
assert.equal(config.defaultProvider.type, "tavily");
assert.equal(config.providersByName.get("exa-fallback")?.type, "exa");
});
test("loadWebSearchConfig rejects a missing default provider target", async () => {
const file = await writeTempConfig({
defaultProvider: "missing",
providers: [
{
name: "exa-main",
type: "exa",
apiKey: "exa-test-key",
},
],
});
await assert.rejects(
() => loadWebSearchConfig(file),
(error) =>
error instanceof WebSearchConfigError &&
/defaultProvider \"missing\"/.test(error.message),
);
});
test("loadWebSearchConfig rejects a missing file with a helpful example message", async () => {
const file = join(tmpdir(), "pi-web-search-does-not-exist.json");
await assert.rejects(
() => loadWebSearchConfig(file),
(error) =>
error instanceof WebSearchConfigError &&
error.message.includes(file) &&
error.message.includes('"defaultProvider"') &&
error.message.includes('"providers"'),
);
});
test("loadWebSearchConfig accepts self-hosted Firecrawl without an apiKey and normalizes its baseUrl", async () => {
const file = await writeTempConfig({
defaultProvider: "firecrawl-main",
providers: [
{
name: "firecrawl-main",
type: "firecrawl",
baseUrl: "https://firecrawl.internal.example/v2/",
fallbackProviders: ["exa-fallback"],
},
{
name: "exa-fallback",
type: "exa",
apiKey: "exa-test-key",
},
],
});
const config = await loadWebSearchConfig(file);
const provider = config.providersByName.get("firecrawl-main");
assert.equal(provider?.type, "firecrawl");
assert.equal(provider?.baseUrl, "https://firecrawl.internal.example/v2");
assert.equal(provider?.apiKey, undefined);
assert.deepEqual(provider?.fallbackProviders, ["exa-fallback"]);
});
test("loadWebSearchConfig rejects Firecrawl cloud config without an apiKey", async () => {
const file = await writeTempConfig({
defaultProvider: "firecrawl-main",
providers: [
{
name: "firecrawl-main",
type: "firecrawl",
},
],
});
await assert.rejects(
() => loadWebSearchConfig(file),
(error) =>
error instanceof WebSearchConfigError &&
/Firecrawl provider \"firecrawl-main\"/.test(error.message) &&
/apiKey/.test(error.message),
);
});
test("loadWebSearchConfig rejects unknown fallback providers", async () => {
const file = await writeTempConfig({
defaultProvider: "firecrawl-main",
providers: [
{
name: "firecrawl-main",
type: "firecrawl",
apiKey: "fc-test-key",
fallbackProviders: ["missing-provider"],
},
],
});
await assert.rejects(
() => loadWebSearchConfig(file),
(error) =>
error instanceof WebSearchConfigError &&
/fallback provider/.test(error.message) &&
/missing-provider/.test(error.message),
);
});
test("loadWebSearchConfig rejects fallback cycles", async () => {
const file = await writeTempConfig({
defaultProvider: "firecrawl-main",
providers: [
{
name: "firecrawl-main",
type: "firecrawl",
apiKey: "fc-test-key",
fallbackProviders: ["exa-fallback"],
},
{
name: "exa-fallback",
type: "exa",
apiKey: "exa-test-key",
fallbackProviders: ["firecrawl-main"],
},
],
});
await assert.rejects(
() => loadWebSearchConfig(file),
(error) =>
error instanceof WebSearchConfigError &&
/cycle/i.test(error.message) &&
/firecrawl-main/.test(error.message) &&
/exa-fallback/.test(error.message),
);
});