evOo-local/packages/adapters/src/keyless-http-web.test.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

138 lines
5.1 KiB
TypeScript

import type { AdapterContext } from "@rakazo/adapter-kit";
import { describe, expect, it } from "vitest";
import { FakeWebProvider } from "./fake-web.js";
import {
extractReadableText,
KeylessHttpWebProvider,
parseDuckDuckGoResults,
} from "./keyless-http-web.js";
import { clampMaxChars, clampMaxResults } from "./web-limits.js";
import { createWebProvider, resolveWebProviderKind } from "./web-provider-factory.js";
import { webFetchFromTool, webSearchFromTool } from "./web-tools.js";
const ctx: AdapterContext = {
operationId: "1",
traceId: "1",
spaceId: "w",
userId: "u",
signal: new AbortController().signal,
};
const publicResolver = async () => [{ address: "203.0.113.10", family: 4 as const }];
describe("web provider factory", () => {
it("defaults to keyless-http", () => {
expect(resolveWebProviderKind({})).toBe("keyless-http");
expect(createWebProvider().describe().id).toBe("keyless-http");
expect(createWebProvider("fake").describe().id).toBe("fake");
});
});
describe("keyless HTTP web provider", () => {
it("searches via the HTML backend without a vendor API key", async () => {
const html = `
<div class="result">
<a class="result__a" href="https://duckduckgo.com/l/?uddg=https%3A%2F%2Fexample.test%2Fa">Alpha</a>
<a class="result__snippet">Snippet A</a>
</div>
<div class="result">
<a class="result__a" href="https://example.test/b">Beta</a>
<div class="result__snippet">Snippet B</div>
</div>
`;
const fetchMock: typeof fetch = async () =>
new Response(html, { status: 200, headers: { "content-type": "text/html" } });
const provider = new KeylessHttpWebProvider({
fetch: fetchMock,
resolveHostname: publicResolver,
});
const results = await provider.search({ query: "alpha", maxResults: 5 }, ctx);
expect(results).toEqual([
{ title: "Alpha", url: "https://example.test/a", snippet: "Snippet A" },
{ title: "Beta", url: "https://example.test/b", snippet: "Snippet B" },
]);
});
it("fetches readable page text with truncation", async () => {
const long = "word ".repeat(100);
const html = `<html><head><title>Doc</title></head><body><article><p>${long}</p></article></body></html>`;
const fetchMock: typeof fetch = async () =>
new Response(html, { status: 200, headers: { "content-type": "text/html" } });
const provider = new KeylessHttpWebProvider({
fetch: fetchMock,
resolveHostname: publicResolver,
});
const result = await provider.fetch({ url: "https://example.test/doc", maxChars: 120 }, ctx);
expect(result.title).toBeTruthy();
expect(result.url).toBe("https://example.test/doc");
expect(result.truncated).toBe(true);
expect(result.text.endsWith("[Content truncated]")).toBe(true);
expect(result.text.length).toBeLessThanOrEqual(120 + "\n\n[Content truncated]".length);
});
it("falls back to HTML strip when Readability finds nothing", () => {
const extracted = extractReadableText(
"<html><title>Plain</title><body><div>just text here</div></body></html>",
"https://example.test/",
);
expect(extracted.title).toBe("Plain");
expect(extracted.text).toContain("just text here");
});
it("parses HTML search results with a cap", () => {
const html = Array.from({ length: 12 }, (_, i) => {
return `<div class="result"><a class="result__a" href="https://example.test/${i}">T${i}</a><div class="result__snippet">S${i}</div></div>`;
}).join("");
expect(parseDuckDuckGoResults(html, 3)).toHaveLength(3);
});
});
describe("web tool apply with fake provider", () => {
it("returns injected search and fetch results", async () => {
const fake = new FakeWebProvider();
fake.searchHits = [
{ title: "One", url: "https://example.test/1", snippet: "s1" },
{ title: "Two", url: "https://example.test/2", snippet: "s2" },
];
fake.fetchResult = {
url: "https://example.test/1",
title: "One",
text: "Body text",
truncated: false,
};
await expect(webSearchFromTool(fake, ctx, { query: "q", maxResults: 1 })).resolves.toEqual({
results: [{ title: "One", url: "https://example.test/1", snippet: "s1" }],
});
expect(fake.lastSearch?.query).toBe("q");
await expect(
webFetchFromTool(fake, ctx, { url: "https://example.test/1", maxChars: 100 }),
).resolves.toEqual({
url: "https://example.test/1",
title: "One",
text: "Body text",
truncated: false,
});
});
it("surfaces provider errors as tool errors", async () => {
const fake = new FakeWebProvider();
fake.searchError = new Error("boom");
await expect(webSearchFromTool(fake, ctx, { query: "q" })).resolves.toEqual({
error: "boom",
});
});
});
describe("web tool clamps", () => {
it("clamps maxResults and maxChars", () => {
expect(clampMaxResults(undefined)).toBe(5);
expect(clampMaxResults(100)).toBe(10);
expect(clampMaxResults(0)).toBe(1);
expect(clampMaxChars(undefined)).toBe(8_000);
expect(clampMaxChars(10)).toBe(100);
expect(clampMaxChars(999_999)).toBe(50_000);
});
});