summaryrefslogtreecommitdiff
path: root/test/engine-target.test.ts
blob: e5ced6d2ef1307e4964ce6dad33860f16e119cf2 (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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/**
 * Engine selection, and the promise that an unsupported recency window is
 * refused rather than dropped.
 */

import assert from "node:assert/strict";
import { test } from "node:test";

import { ENGINE_ENV_VAR, DEFAULT_ENGINE, resolveEngine } from "../src/engine-target.ts";
import { enginesSupportingRecency, parseEngineId } from "../src/engines.ts";
import { selectEngine } from "../src/search.ts";

const ok = (r: ReturnType<typeof resolveEngine>) => {
  assert.equal(r.kind, "ok");
  if (r.kind !== "ok") throw new Error("unreachable");
  return r;
};

test("nothing configured means the built-in default", () => {
  const r = ok(resolveEngine(null, {}, null));
  assert.equal(r.id, DEFAULT_ENGINE);
  assert.equal(r.source, "default");
});

test("precedence is parameter, then env, then stored", () => {
  assert.equal(ok(resolveEngine(null, {}, "bing")).id, "bing");
  assert.equal(ok(resolveEngine(null, { [ENGINE_ENV_VAR]: "brave" }, "bing")).id, "brave");
  // The parameter beating the env var is the deliberate difference from the CDP
  // target: it is what lets the agent fall back when an engine is blocked.
  const r = ok(resolveEngine("duckduckgo", { [ENGINE_ENV_VAR]: "brave" }, "bing"));
  assert.equal(r.id, "duckduckgo");
  assert.equal(r.source, "parameter");
});

test("aliases resolve, including the short ones a human would type", () => {
  for (const [raw, expected] of [
    ["ddg", "duckduckgo"],
    ["duck", "duckduckgo"],
    ["DuckDuckGo", "duckduckgo"],
    ["  bing  ", "bing"],
    ["g", "google"],
    ["BRAVE", "brave"],
  ] as const) {
    assert.equal(parseEngineId(raw), expected, raw);
  }
});

test("an unknown name is reported, never quietly skipped for the next source", () => {
  // A typo in the env var must not silently search Google instead.
  const r = resolveEngine(null, { [ENGINE_ENV_VAR]: "gooogle" }, "bing");
  assert.equal(r.kind, "invalid");
  if (r.kind !== "invalid") throw new Error("unreachable");
  assert.equal(r.source, "env");
  assert.equal(r.raw, "gooogle");
});

test("blank values fall through rather than counting as a choice", () => {
  assert.equal(ok(resolveEngine("  ", { [ENGINE_ENV_VAR]: "  " }, "bing")).id, "bing");
});

test("an unsupported recency window is refused, and names engines that support it", async () => {
  // Bing has no year window and Brave has no time filter; both must say so
  // rather than returning unfiltered results that look filtered.
  for (const engine of ["bing", "brave"] as const) {
    const error = await selectEngine({ query: "q", numResults: 5, recency: "year", engine }).then(
      () => null,
      (e: Error) => e,
    );
    assert.ok(error, `${engine} should have refused`);
    assert.equal(error.name, "RecencyUnsupportedError", engine);
    for (const alt of enginesSupportingRecency("year")) assert.match(error.message, new RegExp(alt), engine);
  }
});

test("windows an engine does support are accepted", async () => {
  for (const [engine, recency] of [
    ["google", "year"],
    ["duckduckgo", "year"],
    ["bing", "month"],
  ] as const) {
    const chosen = await selectEngine({ query: "q", numResults: 5, recency, engine });
    assert.equal(chosen.engine.id, engine);
  }
});

test("brave with no recency is fine — the refusal is about the window, not the engine", async () => {
  const chosen = await selectEngine({ query: "q", numResults: 5, engine: "brave" });
  assert.equal(chosen.engine.id, "brave");
});