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 = `
`;
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 < 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 < 10 | `;
const [r] = parseDuckDuckGoLite(html);
assert.equal(r.snippet, "5 < 10");
assert.doesNotMatch(r.snippet, /5 < 10/, "must not double-unescape < into a real '<'");
});
test("parseDuckDuckGoLite never emits a live