File size: 3,708 Bytes
5448d8b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
/**

 * Regression tests for `isUsableCombo` (release/v3.8.2 code review, finding C1).

 *

 * The combo member refs returned by `/api/combos` do NOT carry a separate

 * `providerId` field β€” OmniRoute's `normalizeComboRecord` folds the provider

 * id INTO the full model string (e.g. "cc/claude-opus-4-7"). The previous

 * implementation read `step.providerId` (always `undefined`), so the

 * `usableOnly` combo filter silently never dropped anything. These tests pin

 * the corrected behavior: the verdict is derived from the `step.model` prefix,

 * mirroring `isUsableRawModelId`'s subtract-filter semantics.

 */

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

import { isUsableCombo, type OmniRouteRawCombo } from "../src/index.js";

/** Build a `usable` set bundle for the tests. */
function buildUsable(opts: { aliases?: string[]; canonicals?: string[]; known?: string[] }): {
  aliases: Set<string>;
  canonicals: Set<string>;
  knownAliases: Set<string>;
} {
  return {
    aliases: new Set(opts.aliases ?? []),
    canonicals: new Set(opts.canonicals ?? []),
    // knownAliases is the union of every prefix the universe is aware of β€”
    // usable or not. Default to including the usable aliases too.
    knownAliases: new Set([...(opts.known ?? []), ...(opts.aliases ?? [])]),
  };
}

function combo(models: OmniRouteRawCombo["models"]): OmniRouteRawCombo {
  return { id: "c1", name: "Test Combo", models };
}

test("isUsableCombo: member with a usable alias prefix β†’ keep", () => {
  const usable = buildUsable({ aliases: ["cc"], known: ["cc", "dead"] });
  const c = combo([{ kind: "model", model: "cc/claude-opus-4-7" }]);
  assert.equal(isUsableCombo(c, usable), true);
});

test("isUsableCombo: all members known-but-NOT-usable β†’ drop (the C1 regression)", () => {
  // Before the fix this returned true unconditionally because step.providerId
  // was always undefined. Now the known-but-unusable "dead" prefix is dropped.
  const usable = buildUsable({ aliases: ["cc"], known: ["cc", "dead"] });
  const c = combo([
    { kind: "model", model: "dead/legacy-model" },
    { kind: "model", model: "dead/another" },
  ]);
  assert.equal(isUsableCombo(c, usable), false);
});

test("isUsableCombo: unknown prefix β†’ keep (cannot prove unroutable)", () => {
  const usable = buildUsable({ aliases: ["cc"], known: ["cc", "dead"] });
  const c = combo([{ kind: "model", model: "agentrouter/mystery" }]);
  assert.equal(isUsableCombo(c, usable), true);
});

test("isUsableCombo: mixed non-usable + usable member β†’ keep", () => {
  const usable = buildUsable({ aliases: ["cc"], known: ["cc", "dead"] });
  const c = combo([
    { kind: "model", model: "dead/legacy" },
    { kind: "model", model: "cc/claude-opus-4-7" },
  ]);
  assert.equal(isUsableCombo(c, usable), true);
});

test("isUsableCombo: zero members β†’ keep", () => {
  const usable = buildUsable({ aliases: ["cc"], known: ["cc"] });
  assert.equal(isUsableCombo(combo([]), usable), true);
  assert.equal(isUsableCombo(combo(undefined), usable), true);
});

test("isUsableCombo: only combo-ref steps (no resolvable model) β†’ keep", () => {
  const usable = buildUsable({ aliases: ["cc"], known: ["cc", "dead"] });
  const c = combo([{ kind: "combo-ref", comboName: "nested" }]);
  assert.equal(isUsableCombo(c, usable), true);
});

test("isUsableCombo: usable canonical prefix β†’ keep", () => {
  const usable = buildUsable({ canonicals: ["anthropic"], known: ["anthropic", "dead"] });
  const c = combo([{ kind: "model", model: "anthropic/claude-opus-4-7" }]);
  assert.equal(isUsableCombo(c, usable), true);
});