summaryrefslogtreecommitdiff
path: root/test/format.test.ts
blob: 74e37e13479574651a08922b1ff0207cf9443d6c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import assert from "node:assert/strict";
import { test } from "node:test";

import { formatResults } from "../src/format.ts";
import { SearchChallengeError } from "../src/errors.ts";

const results = [
  { title: "First", url: "https://example.com/1", snippet: "A snippet." },
  { title: "Second", url: "https://example.com/2", snippet: "" },
];

test("results are numbered, with the URL on its own line", () => {
  const text = formatResults({
    query: "q",
    searchUrl: "https://www.google.com/search?q=q",
    finalUrl: "https://www.google.com/search?q=q",
    endpoint: "10.88.0.25:9223 (built-in default)",
    engine: "Google",
    engineSource: "built-in default",
    results,
  });
  // The engine has to be named on every result set: the agent may have switched
  // engines to get around a block, and two indexes are not interchangeable.
  assert.match(
    text,
    /^Search results for "q" — 2 hits from Google \(built-in default\), via Chrome at 10\.88\.0\.25:9223 \(built-in default\)$/m,
  );
  assert.match(text, /^1\. First$/m);
  assert.match(text, /^ {3}https:\/\/example\.com\/1$/m);
  assert.match(text, /^2\. Second$/m);
  // A missing snippet must not leave a stray blank indented line.
  assert.ok(!/\n {3}\n/.test(text));
});

test("a redirect is reported, because the query URL is then not where we looked", () => {
  const text = formatResults({
    query: "q",
    searchUrl: "https://www.google.com/search?q=q",
    finalUrl: "https://www.google.com/search?q=q&sei=abc",
    endpoint: "e",
    engine: "Bing",
    engineSource: "/search-engine",
    results,
  });
  assert.match(text, /^Landed on: https:\/\/www\.google\.com\/search\?q=q&sei=abc$/m);
});

test("the challenge error tells the agent to hand off to a human, with the URL", () => {
  const err = new SearchChallengeError({
    challenge: "captcha-widget",
    pageUrl: "https://www.google.com/sorry/index?continue=x",
    endpoint: "10.88.0.25:9223 (built-in default)",
    engine: "Google",
    alternatives: ["duckduckgo", "bing", "brave"],
    detail: "Our systems have detected unusual traffic",
  });
  assert.equal(err.name, "SearchChallengeError");
  assert.ok(err instanceof Error, "must be throwable as an Error");
  assert.match(err.message, /captcha-widget/);
  assert.match(err.message, /https:\/\/www\.google\.com\/sorry\/index\?continue=x/);
  assert.match(err.message, /Ask the user/);
  assert.match(err.message, /10\.88\.0\.25:9223/);
  // Switching engines is the agent's own move and must be offered before the
  // human handoff, since a block on one engine says nothing about the others.
  for (const alt of ["duckduckgo", "bing", "brave"]) assert.match(err.message, new RegExp(alt));
  assert.ok(
    err.message.indexOf("retry the same query with engine") < err.message.indexOf("Ask the user"),
    "the self-service fallback must come before the human handoff",
  );
});