Files
OmniRoute/tests/unit/glob-pattern-6540.test.ts
Diego Rodrigues de Sa e Souza 8143d8e3a6 feat: replace free-text model inputs with hidePaid-aware Selects (#6540) (#7229)
* 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.
2026-07-17 10:40:18 -03:00

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"
});