Files
OmniRoute/tests/unit/provider-alias-uniqueness.test.ts
Diego Rodrigues de Sa e Souza f48e25194f fix(providers): retire Qwen Web pending provenance review (#11713)
Rebased onto the current release/v3.8.51 tip as part of a combined provider-retirement/provenance merge batch (Designer Web, Felo Web, Runtime, GPL-derived removal all landed together already). Conflicts resolved:

- `src/shared/constants/providerRetirement.ts`: add/add conflict — combined `felo-web`/`felo` (already-merged) with `qwen-web`/`qw` into one `RUNTIME_RETIRED_PROVIDER_IDS` set, kept both `assertRuntimeProviderAvailable`/`assertRuntimeModelProviderAvailable` helpers.
- `open-sse/config/providers/registry/minimax/web/index.ts`: modify/delete — kept deleted (file is hailuo-web's registry entry, already retired by #11691; this PR's own change to it was just a comment reword on a since-removed target).
- `open-sse/executors/index.ts`, `executorProxy.ts`, `virtualFactory.ts`, `autoStrategy.ts`, `model.ts`, `chat.ts`, `chatHelpers.ts`, `auth.ts`, `src/lib/db/providers.ts`, `reservedProviderPrefixes.ts`: combined the Designer + Runtime (Felo + Qwen) retirement guard calls at each shared chokepoint — compute-once-then-OR pattern, consistent with the prior Designer+Felo combination.
- `src/shared/constants/providers/web-cookie.ts`, `tests/snapshots/provider/translate-path.json`, `tests/snapshots/executors/executor-map.json`: both sides had inserted a different retired provider (qwen-web vs. already-retired raycast/hailuo-web) at the same dict position — resolved by dropping both. `executor-map.json`'s `keyCount` recomputed to 135 (matches actual merged `entries`).
- `tests/unit/chatcore-executor-proxy.test.ts`, `tests/unit/provider-node-reserved-prefix.test.ts`: split into independent Felo/Qwen test blocks (established pattern for coexisting retirement-mechanism tests); recomputed `RESERVED_PREFIX_COUNT` to 398 (Designer+Felo+Qwen tombstones on top of the post-#11691 REGISTRY, verified via direct module evaluation, not hand-derived).
- `config/quality/test-masking-allowlist.json`: additive merge of Qwen's `_deletedWithReplacement` entries alongside Designer's.
- `README.md` + all `docs/i18n/*/README.md` mirrors, `docs/getting-started/FREE-TIERS-GUIDE.md`, `docs/reference/FREE_TIERS.md`, `docs/diagrams/free-tier-budget.svg`, `docs/screenshots/free-tier-budget-card.svg`: recomputed the free-tier catalog counts (447 entries / 440 active / 7 discontinued) from the actual merged `freeModelCatalog.data.ts`, regenerated the budget-card SVG via its real generator (`scripts/research/gen-budget-card-svg.mjs`), and dropped the retired Qwen quick-start row / QWEN MODELS section from every i18n README (identical unlocalized block across all 34 locales).
- Also fixed a duplicate-import merge artifact in `src/lib/db/providers.ts` (`isRuntimeRetiredProviderId` imported twice) caught by `typecheck:core`, and rebaselined `file-size-baseline.json` for the combined retirement-guard growth (`virtualFactory.ts` +3, with justification).

Focused suite green (345/345 across executor-proxy, reserved-prefix, migration-167, qwen-web-retirement, virtual-auto-combo, web-cookie/session, executor-map-golden and siblings), plus `typecheck:core` and `check-file-size`/`check-changelog-integrity` clean. Thanks for the provenance-hold retirement work — appreciated.
2026-08-28 06:01:21 -03:00

94 lines
4.1 KiB
TypeScript

/**
* Provider alias uniqueness — no two provider IDs may share the same short alias.
*
* Before this guard, three aliases collided in the registry and the LAST entry in
* iteration order silently won, emitting a startup warning and shadowing a real
* provider:
* - "kimi" → kimi-web (shadowed the kimi provider that gained a dedicated executor)
* - "hc" → the provider that held it shadowed huggingchat (it was later
* removed entirely, #11176; huggingchat keeps its own id as alias)
*
* The decision: the primary provider keeps the short alias; the web/secondary
* variant takes its own id as alias. This test pins both the global uniqueness
* invariant (so future additions can't silently re-collide) and the specific
* resolutions for the affected providers, across BOTH alias sources
* (open-sse registry + src/shared providers map).
*/
import test from "node:test";
import assert from "node:assert/strict";
import { PROVIDER_ID_TO_ALIAS } from "../../open-sse/config/providerModels.ts";
import {
resolveProviderId,
getProviderAlias,
APIKEY_PROVIDERS,
WEB_COOKIE_PROVIDERS,
} from "../../src/shared/constants/providers.ts";
test("no two provider IDs share the same alias in the open-sse registry", () => {
const aliasToIds = new Map<string, string[]>();
for (const [id, alias] of Object.entries(PROVIDER_ID_TO_ALIAS)) {
const ids = aliasToIds.get(alias) ?? [];
ids.push(id);
aliasToIds.set(alias, ids);
}
const collisions = [...aliasToIds.entries()].filter(([, ids]) => ids.length > 1);
assert.deepEqual(
collisions,
[],
`Alias collisions detected (each alias must map to exactly one provider id): ${collisions
.map(([alias, ids]) => `"${alias}" → ${ids.join(", ")}`)
.join("; ")}`
);
});
test("primary providers keep the short alias; web variants use their own id", () => {
// open-sse registry (source of the startup warning + chat routing)
assert.equal(PROVIDER_ID_TO_ALIAS.kimi, "kimi");
assert.equal(PROVIDER_ID_TO_ALIAS["kimi-web"], "kimi-web");
assert.equal(PROVIDER_ID_TO_ALIAS.huggingchat, "huggingchat");
});
test("src/shared providers map resolves the same aliases unambiguously", () => {
// alias → id
assert.equal(resolveProviderId("kimi"), "kimi");
// ids used as aliases for the supported secondary variants
assert.equal(resolveProviderId("kimi-web"), "kimi-web");
assert.equal(resolveProviderId("huggingchat"), "huggingchat");
// id → alias
assert.equal(getProviderAlias("kimi"), "kimi");
});
test("retired Hailuo alias is absent while official MiniMax aliases remain distinct", () => {
assert.equal(PROVIDER_ID_TO_ALIAS["hailuo-web"], undefined);
assert.equal(PROVIDER_ID_TO_ALIAS.minimax, "minimax");
assert.equal(PROVIDER_ID_TO_ALIAS["minimax-cn"], "minimax-cn");
assert.equal(resolveProviderId("minimax"), "minimax");
assert.equal(resolveProviderId("minimax-cn"), "minimax-cn");
});
test("freepik is the Magnific Mystic legacy alias, not a second provider id", () => {
assert.equal(resolveProviderId("freepik"), "magnific");
assert.equal(resolveProviderId("magnific"), "magnific");
assert.equal(getProviderAlias("magnific"), "freepik");
assert.ok("magnific" in APIKEY_PROVIDERS);
assert.ok(!("freepik" in APIKEY_PROVIDERS));
});
test("no provider id is registered in both the API-key and web-cookie catalogs", () => {
// A provider belongs to exactly one auth category; the same id in both catalogs
// renders the provider twice in the dashboard (once per section). huggingchat
// regressed this way (its API-key counterpart is the separate `huggingface`
// Inference API id), so it must live ONLY in WEB_COOKIE_PROVIDERS.
const apikeyIds = new Set(Object.keys(APIKEY_PROVIDERS));
const overlap = Object.keys(WEB_COOKIE_PROVIDERS).filter((id) => apikeyIds.has(id));
assert.deepEqual(overlap, [], `Providers duplicated across catalogs: ${overlap.join(", ")}`);
assert.ok("huggingchat" in WEB_COOKIE_PROVIDERS, "huggingchat must be in the web-cookie catalog");
assert.ok(
!("huggingchat" in APIKEY_PROVIDERS),
"huggingchat must NOT be in the API-key catalog (use `huggingface` for the API key path)"
);
});