Files
OmniRoute/tests/unit/provider-credential-requirement.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

94 lines
4.2 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import {
NOT_TOKEN_QUANTIFIABLE_BUT_CREDENTIALED,
checkKeylessCatalogConsistency,
getCredentialRequirement,
listNoCredentialProviders,
worksWithoutCredential,
} from "@/shared/utils/providerCredentialRequirement.ts";
import { FREE_MODEL_BUDGETS } from "@omniroute/open-sse/config/freeModelCatalog.data.ts";
test("classifies each credential model from the real registries", () => {
// noAuth: the connect form never asks for a key.
assert.equal(getCredentialRequirement("opencode"), "none");
// Literal anonymous token: routable with no user credential, key still honoured.
assert.equal(getCredentialRequirement("aihorde"), "optional");
assert.equal(getCredentialRequirement("kilocode"), "optional");
// Verified live 2026-07-20: answers with no Authorization header, 403s on a bad key.
assert.equal(getCredentialRequirement("ovhcloud"), "optional");
// Verified live 2026-08-11: HTTP 200 with no Authorization header (#10068).
assert.equal(getCredentialRequirement("kilo-gateway"), "optional");
// OAuth: nothing to paste, but the user still signs in.
assert.equal(getCredentialRequirement("agy"), "oauth");
// Ordinary key-gated provider.
assert.equal(getCredentialRequirement("groq"), "required");
// Unknown ids must fail closed, never be advertised as free access.
assert.equal(getCredentialRequirement("definitely-not-a-provider"), "required");
});
test("worksWithoutCredential excludes oauth — signing in is still a barrier", () => {
assert.equal(worksWithoutCredential("none"), true);
assert.equal(worksWithoutCredential("optional"), true);
assert.equal(worksWithoutCredential("oauth"), false);
assert.equal(worksWithoutCredential("required"), false);
});
test("listNoCredentialProviders is derived, not a hand-kept list", () => {
const ids = listNoCredentialProviders();
assert.ok(ids.length > 0);
assert.ok(ids.includes("opencode"));
assert.ok(ids.includes("ovhcloud"));
assert.ok(ids.includes("aihorde"));
assert.ok(!ids.includes("groq"), "key-gated providers must never be listed");
assert.deepEqual(ids, [...ids].sort(), "output must be stable for snapshotting");
});
test("free catalog's keyless label matches real routing behaviour", () => {
const report = checkKeylessCatalogConsistency(FREE_MODEL_BUDGETS);
assert.deepEqual(
report.unexpected,
[],
`these providers are labelled keyless but routing demands a credential: ${report.unexpected.join(", ")}. ` +
`Probe the endpoint and fix the registry instead of widening the recorded list.`
);
// Stale-allowlist enforcement: a frozen entry that stopped drifting must be
// removed, otherwise the debt list silently outlives the debt.
assert.deepEqual(
report.stale,
[],
`These now work without a credential — remove them from the recorded list: ${report.stale.join(", ")}`
);
});
test("providers that reject anonymous calls are never advertised as key-free", () => {
// Probed live 2026-07-20 — each returned 401/403 with no credential. They are
// freeType: "keyless" in the catalog (meaning "not token-quantifiable"), so a
// UI section built on that field would invite users to call providers that
// reject them. This is the regression guard for that bug.
for (const id of ["blackbox", "friendliai", "iflytek", "sparkdesk", "muse-spark-web"]) {
assert.equal(
worksWithoutCredential(getCredentialRequirement(id)),
false,
`${id} answers 401/403 without a credential — it must never be listed as key-free`
);
}
});
test("pollinations is genuinely key-free", () => {
// Probed live 2026-07-20: HTTP 200 with real choices and no credential.
assert.equal(worksWithoutCredential(getCredentialRequirement("pollinations")), true);
});
test("the credentialed-but-unquantifiable list only shrinks", () => {
// Frozen at 9 on 2026-07-20 (pollinations left it once its registry entry was
// corrected). Growing this means a mismatch was waved through instead of probed.
assert.ok(
NOT_TOKEN_QUANTIFIABLE_BUT_CREDENTIALED.length <= 9,
`list grew to ${NOT_TOKEN_QUANTIFIABLE_BUT_CREDENTIALED.length} — probe the endpoint and fix the registry instead`
);
});