import { test } from "node:test"; import assert from "node:assert/strict"; import { parseDuckDuckGoLite } from "../../open-sse/services/freeWebSearch.ts"; // Real DuckDuckGo lite shape (captured from lite.duckduckgo.com/lite/): // double-quoted href BEFORE single-quoted class='result-link', direct URLs, // highlight tags inside titles/snippets, link[i] aligns with snippet[i]. const REAL_LITE_HTML = `
1.  API overview - Claude API Docs - Anthropic
    Claude API Documentation. New to Claude?
2.  Claude Platform | Claude - Anthropic
    Build with the most capable models.
`; test("parseDuckDuckGoLite extracts aligned title/url/snippet from real lite HTML", () => { const results = parseDuckDuckGoLite(REAL_LITE_HTML); assert.equal(results.length, 2); assert.equal(results[0].url, "https://platform.claude.com/docs/en/api/overview"); assert.equal(results[0].title, "API overview - Claude API Docs - Anthropic"); assert.ok(results[0].snippet.includes("Claude API Documentation")); assert.equal(results[1].url, "https://www.anthropic.com/api"); assert.equal(results[1].title, "Claude Platform | Claude - Anthropic"); assert.ok(results[1].snippet.includes("most capable models")); }); test("parseDuckDuckGoLite strips highlight tags and collapses whitespace", () => { const results = parseDuckDuckGoLite(REAL_LITE_HTML); assert.doesNotMatch(results[0].title, /|<\/b>/); assert.doesNotMatch(results[0].snippet, /|<\/b>/); }); test("parseDuckDuckGoLite decodes a uddg redirect href (defensive)", () => { const html = `Example snippet`; const results = parseDuckDuckGoLite(html); assert.equal(results[0].url, "https://example.com/a?b=1"); }); test("parseDuckDuckGoLite upgrades protocol-relative hrefs to https", () => { const html = `Proto rel s`; const results = parseDuckDuckGoLite(html); assert.equal(results[0].url, "https://example.org/path"); }); test("parseDuckDuckGoLite returns [] when there are no result links", () => { assert.deepEqual(parseDuckDuckGoLite("no results"), []); assert.deepEqual(parseDuckDuckGoLite(""), []); }); test("parseDuckDuckGoLite drops non-http(s) result URLs (javascript:/data:/file:)", () => { const html = `XSS bad Safe good`; const results = parseDuckDuckGoLite(html); assert.equal(results.length, 1, "the javascript: link must be discarded"); assert.equal(results[0].url, "https://ok.example.com"); assert.doesNotMatch(JSON.stringify(results), /javascript:/); }); test("parseDuckDuckGoLite is bounded on adversarial HTML (no catastrophic backtracking)", () => { // Many unclosed result-link anchors + a huge filler tail must return promptly. const pathological = ``.repeat(2000) + "x".repeat(2_000_000); const start = Date.now(); const results = parseDuckDuckGoLite(pathological); assert.ok(Date.now() - start < 1000, "must not hang on adversarial HTML"); assert.ok(Array.isArray(results)); }); test("parseDuckDuckGoLite tolerates a missing snippet (empty string, not crash)", () => { const html = `Only a link`; const results = parseDuckDuckGoLite(html); assert.equal(results.length, 1); assert.equal(results[0].snippet, ""); }); test("parseDuckDuckGoLite does not double-unescape entities (CodeQL js/double-escaping)", () => { // A snippet whose author literally wrote "5 < 10" reaches us HTML-escaped as // "5 &lt; 10". Correct single-level decoding must yield the literal "5 < 10", // NOT collapse it into a real "<" ("5 < 10") by unescaping & before <. const html = `T 5 &lt; 10`; const [r] = parseDuckDuckGoLite(html); assert.equal(r.snippet, "5 < 10"); assert.doesNotMatch(r.snippet, /5 < 10/, "must not double-unescape &lt; into a real '<'"); }); test("parseDuckDuckGoLite never emits a live