Files
diegosouzapw--omniroute/tests/unit/provider-alias-transitive-5918.test.ts
2026-07-13 13:39:12 +08:00

57 lines
2.8 KiB
TypeScript

/**
* Regression tests for #5918: resolveProviderAlias must follow the alias chain
* transitively.
*
* Root cause: resolveProviderAlias() did a single-hop lookup
* (`ALIAS_TO_PROVIDER_ID[alias] || alias`). The registry has genuine two-hop chains:
* the parent OpenCode provider registers `id: "opencode", alias: "oc"` (so
* `oc -> opencode`), and a manual override maps `opencode -> opencode-zen` (the
* main free tier). With single-hop, `resolveProviderAlias("oc")` returned the
* intermediate `"opencode"` instead of the final `"opencode-zen"`, so `oc/<model>`
* resolved to the wrong provider.
*
* Fix: resolve transitively with a depth limit AND a seen-set so cycles cannot loop.
* These assertions FAIL on the old single-hop implementation and pass on the fix.
*
* RECONCILED at v3.8.44 with the #2901 contract: the chain STOPS as soon as a hop
* lands on a REGISTERED provider id. "oc" is the registry alias of the no-auth
* "opencode" provider, so `oc/<model>` must resolve to it — continuing through the
* manual "opencode" → "opencode-zen" slug override (which exists only for
* user-typed `opencode/` prefixes) would leave the no-auth provider unreachable by
* any prefix and misroute its combo entries (see combo-builder-opencode-prefix
* test #2901). Transitivity still applies across alias-only hops, and the
* loop/depth guards from #5918 are retained.
*/
import test from "node:test";
import assert from "node:assert/strict";
import { resolveProviderAlias } from "../../open-sse/services/model.ts";
test("resolveProviderAlias stops the oc chain at the registered no-auth opencode provider (#2901)", () => {
// "opencode" is a REGISTERED provider id (the no-auth tier, alias "oc"), so the
// chain must stop there instead of following the manual opencode→opencode-zen
// slug override — otherwise the no-auth provider is unreachable by any prefix.
assert.equal(resolveProviderAlias("oc"), "opencode");
});
test("resolveProviderAlias resolves a direct one-hop alias", () => {
assert.equal(resolveProviderAlias("opencode"), "opencode-zen");
});
test("resolveProviderAlias returns a terminal id unchanged (id === alias)", () => {
// opencode-zen registers id === alias === "opencode-zen"; must not loop or drift.
assert.equal(resolveProviderAlias("opencode-zen"), "opencode-zen");
});
test("resolveProviderAlias falls back to identity for an unknown provider/id", () => {
assert.equal(resolveProviderAlias("gpt-4"), "gpt-4");
assert.equal(resolveProviderAlias("some-unregistered-provider"), "some-unregistered-provider");
});
test("resolveProviderAlias returns null for non-string input", () => {
assert.equal(resolveProviderAlias(null), null);
assert.equal(resolveProviderAlias(undefined), null);
// @ts-expect-error — exercising the runtime guard against non-string input
assert.equal(resolveProviderAlias(123), null);
});