Files
OmniRoute/tests/unit/model-deprecation.test.ts
MumuTW ae7a843e86 fix(autoCombo,sse): catalog hygiene — retire dead FITNESS_TABLE rows, fix 7 BUILT_IN_ALIASES targets, add model-lifecycle gate (#11503) (#11507)
Validated in a combined 4-PR batch worktree off release/v3.8.51 tip. This PR's diff overlapped taskFitness.ts and autoCombo.test.ts with the already-merged #11492/#11506 — git's merge auto-resolved both hunks cleanly (non-overlapping layers: #11492/#11506 touch layer 2 arena lookup, this PR touches layer 4 static-table hygiene); verified no conflict markers remained and re-ran the full suite after boarding.
- npm run check:model-lifecycle — PASS, 68 retired ids, 1327 catalog ids, 0 violations (re-ran with the correct `node --import tsx/esm` loader after an initial bare-node invocation mistakenly failed on path-alias resolution — that was my invocation error, not the gate)
- Focused tests: fitness-table-hygiene-11503.test.ts, taskFitness-pattern-order-8603.test.ts, model-deprecation-aliases-11503.test.ts, check-model-lifecycle-gate.test.ts, model-deprecation.test.ts, autoCombo.test.ts — part of batch's 126/126 vitest + 246/246 node:test runs
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity, check:cycles — all OK
- Full-repo lint: 228 problems remaining, all pre-existing dashboard react-hooks/* findings unrelated to this diff (zero errors in any file this PR touches)

Thanks for this — genuinely thorough methodology (segment-boundary matching, provider-scoped alias guard, offline lifecycle gate with a documented burn-down list for the 6 remaining catalog offenders).
2026-08-25 13:52:23 -03:00

115 lines
4.8 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
const {
resolveModelAlias,
getDeprecationNotice,
isDeprecated,
setCustomAliases,
getCustomAliases,
addCustomAlias,
removeCustomAlias,
getAllAliases,
getBuiltInAliases,
} = await import("../../open-sse/services/modelDeprecation.ts");
// ─── resolveModelAlias ──────────────────────────────────────────────────────
test("resolveModelAlias: returns original for non-deprecated model", () => {
assert.equal(resolveModelAlias("claude-opus-4-6"), "claude-opus-4-6");
});
test("resolveModelAlias: resolves deprecated Gemini model", () => {
assert.equal(resolveModelAlias("gemini-pro"), "gemini-2.5-pro");
assert.equal(resolveModelAlias("gemini-1.5-pro"), "gemini-2.5-pro");
assert.equal(resolveModelAlias("gemini-1.5-flash"), "gemini-2.5-flash");
// Retired 2.0 Flash-Lite (Google shutdown 2026-06-01) + renamed flash-lite preview
// both forward to the live GA gemini-3.1-flash-lite.
assert.equal(resolveModelAlias("gemini-2.0-flash-lite"), "gemini-3.1-flash-lite");
assert.equal(resolveModelAlias("gemini-3.1-flash-lite-preview"), "gemini-3.1-flash-lite");
// Retired free Gemma (was in the gemini-free pool) forwards to the current
// gemini-free model instead of erroring with model-not-found.
assert.equal(resolveModelAlias("gemma-4"), "gemini-3.1-flash-lite");
});
test("resolveModelAlias: resolves deprecated Claude model", () => {
// #11503: the previous targets (claude-opus-4-20250514 / claude-sonnet-4-20250514)
// were themselves retired on 2026-06-15. These are the replacements Anthropic publishes.
assert.equal(resolveModelAlias("claude-3-opus-20240229"), "claude-opus-4-8");
assert.equal(resolveModelAlias("claude-3-5-sonnet-latest"), "claude-sonnet-4-6");
});
test("resolveModelAlias: does not silently reroute retired OpenAI models", () => {
assert.equal(resolveModelAlias("gpt-4-turbo-preview"), "gpt-4-turbo-preview");
assert.equal(resolveModelAlias("gpt-3.5-turbo-0125"), "gpt-3.5-turbo-0125");
});
test("resolveModelAlias: handles null/empty", () => {
assert.equal(resolveModelAlias(""), "");
assert.equal(resolveModelAlias(null), null);
assert.equal(resolveModelAlias(undefined), undefined);
});
// ─── getDeprecationNotice ───────────────────────────────────────────────────
test("getDeprecationNotice: returns message for deprecated model", () => {
const notice = getDeprecationNotice("gemini-pro");
assert.ok(notice);
assert.ok(notice.includes("deprecated"));
assert.ok(notice.includes("gemini-2.5-pro"));
});
test("getDeprecationNotice: returns null for non-deprecated model", () => {
assert.equal(getDeprecationNotice("claude-opus-4-6"), null);
});
test("getDeprecationNotice: returns null for empty/null", () => {
assert.equal(getDeprecationNotice(""), null);
assert.equal(getDeprecationNotice(null), null);
});
// ─── isDeprecated ───────────────────────────────────────────────────────────
test("isDeprecated: true for deprecated model", () => {
assert.equal(isDeprecated("gemini-pro"), true);
});
test("isDeprecated: false for current model", () => {
assert.equal(isDeprecated("claude-opus-4-6"), false);
});
// ─── Custom Aliases ─────────────────────────────────────────────────────────
test("custom aliases override built-in", () => {
setCustomAliases({ "gemini-pro": "gemini-3.1-pro" });
assert.equal(resolveModelAlias("gemini-pro"), "gemini-3.1-pro"); // custom wins
setCustomAliases({}); // reset
});
test("addCustomAlias and removeCustomAlias", () => {
addCustomAlias("my-old-model", "my-new-model");
assert.equal(resolveModelAlias("my-old-model"), "my-new-model");
const removed = removeCustomAlias("my-old-model");
assert.equal(removed, true);
assert.equal(resolveModelAlias("my-old-model"), "my-old-model");
});
test("removeCustomAlias: returns false for non-existent", () => {
assert.equal(removeCustomAlias("nonexistent"), false);
});
test("getAllAliases: includes both built-in and custom", () => {
addCustomAlias("test-from", "test-to");
const all = getAllAliases();
assert.ok(all["gemini-pro"]); // built-in
assert.equal(all["test-from"], "test-to"); // custom
removeCustomAlias("test-from"); // cleanup
});
test("getBuiltInAliases: returns built-in aliases", () => {
const builtIn = getBuiltInAliases();
assert.ok(builtIn["gemini-pro"]);
assert.ok(builtIn["claude-3-opus-20240229"]);
});