mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
* feat(dashboard): replace free-text model inputs with hidePaid-aware Selects (#6540) Swap RoutingTab.webSearchRouteModel, ComboDefaultsTab.handoffModel, and BackgroundDegradationTab's from/to fields from free-text inputs to a new shared ModelSelectField fed by the already hidePaidModels-aware GET /api/models, with an off-catalog "(custom)" fallback so an existing saved value is never silently dropped. ModelRoutingSection's glob pattern field gets a fail-open "matches only paid models" warning instead, since it's a wildcard matcher rather than a single model id. Adds save-time paid-target rejection (PAID_MODEL_TARGET_BLOCKED, 400) on PATCH /api/settings, PATCH /api/settings/combo-defaults, and PUT /api/settings/background-degradation when hidePaidModels is on, failing open for aliases/combo names/unrecognized providers. globToRegex is extracted from lib/db/modelComboMappings.ts into a new dependency-free shared/utils/globPattern.ts so both the DB module and the new client-side pattern heuristic reuse the same regex-building logic. * refactor(6540): extract paid-target guard in background-degradation PUT The inline hidePaid check nested if>if>for>if inside the PUT handler, pushing its cognitive complexity to 16 (>15) — a NEW violation that broke the check:complexity-ratchets gate (891 > baseline 890). Extracted the check into a module-local hasBlockedPaidTarget() helper; behaviour and response body are unchanged. cognitive-complexity back to 890 = baseline. * fix(i18n): mirror paidModelPatternWarning into pt-BR.json (#6540) The new key landed only in en.json; the i18n pt-BR integrity test (no drift, #6695) requires pt-BR.json to carry every en.json key.
36 lines
1.2 KiB
TypeScript
36 lines
1.2 KiB
TypeScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { globToRegex } from "@/shared/utils/globPattern";
|
|
|
|
test("globToRegex — * matches any sequence of characters", () => {
|
|
const re = globToRegex("claude-sonnet*");
|
|
assert.equal(re.test("claude-sonnet-4"), true);
|
|
assert.equal(re.test("claude-sonnet"), true);
|
|
assert.equal(re.test("claude-opus-4"), false);
|
|
});
|
|
|
|
test("globToRegex — ? matches exactly one character", () => {
|
|
const re = globToRegex("gpt-?");
|
|
assert.equal(re.test("gpt-4"), true);
|
|
assert.equal(re.test("gpt-40"), false);
|
|
assert.equal(re.test("gpt-"), false);
|
|
});
|
|
|
|
test("globToRegex — case-insensitive", () => {
|
|
const re = globToRegex("Claude-Sonnet*");
|
|
assert.equal(re.test("claude-sonnet-4"), true);
|
|
assert.equal(re.test("CLAUDE-SONNET-4"), true);
|
|
});
|
|
|
|
test("globToRegex — anchored (no partial match)", () => {
|
|
const re = globToRegex("sonnet");
|
|
assert.equal(re.test("claude-sonnet-4"), false);
|
|
assert.equal(re.test("sonnet"), true);
|
|
});
|
|
|
|
test("globToRegex — escapes regex special characters", () => {
|
|
const re = globToRegex("gpt-4.1");
|
|
assert.equal(re.test("gpt-4.1"), true);
|
|
assert.equal(re.test("gpt-4X1"), false); // literal dot, not "any char"
|
|
});
|