Files
OmniRoute/tests/unit/apikeys-row-parsers-split.test.ts
Praveen K Palaniswamy 65e81158ab fix(ollama): route models by advertised capability (#11088)
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host.

Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean.

Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes #11087.
2026-08-23 11:45:01 -03:00

100 lines
4.2 KiB
TypeScript

// Characterization of the db/apiKeys.ts row-parsers split (god-file decomposition): the pure column
// parsers that coerce raw SQLite values into typed shapes moved into db/apiKeys/rowParsers.ts, and the
// two shared row types (AccessSchedule / RateLimitRule) into db/apiKeys/types.ts. Behavior-preserving
// move — these locks pin the coercion semantics (JSON lists, 0/1 flags, nullable timestamps, schedule
// validation) and that the host still re-exports the two public types. DB-backed hydration stays
// covered by api-key-policy / combo-provider-wildcard.
import { test } from "node:test";
import assert from "node:assert/strict";
const P = await import("../../src/lib/db/apiKeys/rowParsers.ts");
test("module exposes all sixteen parsers", () => {
for (const name of [
"parseAllowedModels",
"parseAllowedCombos",
"parseNoLog",
"parseAutoResolve",
"parseDisableNonPublicModels",
"parseAllowUsageCommand",
"parseIsActive",
"parseCompressionEnabled",
"parseAccessSchedule",
"parseRateLimits",
"parseAllowedConnections",
"parseAllowedQuotas",
"parseStringList",
"parseNullableTimestamp",
"parseIsBanned",
"parseStreamDefaultMode",
]) {
assert.equal(typeof (P as Record<string, unknown>)[name], "function", `missing ${name}`);
}
});
test("parseAllowedModels keeps only string entries, tolerates junk", () => {
assert.deepEqual(P.parseAllowedModels('["a","b",1,null]'), ["a", "b"]);
assert.deepEqual(P.parseAllowedModels(""), []);
assert.deepEqual(P.parseAllowedModels("not json"), []);
assert.deepEqual(P.parseAllowedModels(null), []);
});
test("flag parsers honor the 0/1/true/false matrix", () => {
assert.equal(P.parseNoLog(1), true);
assert.equal(P.parseNoLog("1"), true);
assert.equal(P.parseNoLog(0), false);
// isActive defaults to active unless explicitly falsy
assert.equal(P.parseIsActive(undefined), true);
assert.equal(P.parseIsActive(0), false);
assert.equal(P.parseIsActive("0"), false);
// compressionEnabled also defaults on for legacy/missing rows.
assert.equal(P.parseCompressionEnabled(undefined), true);
assert.equal(P.parseCompressionEnabled(null), true);
assert.equal(P.parseCompressionEnabled(1), true);
assert.equal(P.parseCompressionEnabled("1"), true);
assert.equal(P.parseCompressionEnabled(0), false);
assert.equal(P.parseCompressionEnabled("0"), false);
assert.equal(P.parseCompressionEnabled(false), false);
assert.equal(P.parseIsBanned(1), true);
assert.equal(P.parseIsBanned(0), false);
});
test("parseStreamDefaultMode collapses to legacy unless json", () => {
assert.equal(P.parseStreamDefaultMode("json"), "json");
assert.equal(P.parseStreamDefaultMode("legacy"), "legacy");
assert.equal(P.parseStreamDefaultMode("anything"), "legacy");
});
test("parseNullableTimestamp trims and nulls empties", () => {
assert.equal(P.parseNullableTimestamp(" 2026-01-01 "), "2026-01-01");
assert.equal(P.parseNullableTimestamp(" "), null);
assert.equal(P.parseNullableTimestamp(42), null);
});
test("parseAccessSchedule validates shape + clamps days, else null", () => {
const ok = P.parseAccessSchedule(
JSON.stringify({ enabled: true, from: "08:00", until: "18:00", days: [0, 3, 9], tz: "UTC" })
);
assert.ok(ok);
assert.deepEqual(ok?.days, [0, 3]); // 9 dropped (out of 0..6)
assert.equal(P.parseAccessSchedule('{"enabled":true}'), null);
assert.equal(P.parseAccessSchedule("[]"), null);
assert.equal(P.parseAccessSchedule(""), null);
});
test("parseRateLimits keeps well-formed numeric rules only", () => {
const out = P.parseRateLimits(
JSON.stringify([{ limit: 10, window: 60 }, { limit: "x", window: 1 }, null])
);
assert.deepEqual(out, [{ limit: 10, window: 60 }]);
assert.equal(P.parseRateLimits("not array"), null);
assert.equal(P.parseRateLimits(""), null);
});
test("host re-exports the two public row types (compile-time) and the parsers stay wired", async () => {
// type-only re-export can't be probed at runtime; assert the host module still loads + exposes its API
const HOST = await import("../../src/lib/db/apiKeys.ts");
assert.equal(typeof (HOST as Record<string, unknown>).getApiKeys, "function");
assert.equal(typeof (HOST as Record<string, unknown>).isModelAllowedForKey, "function");
});