mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-14 10:52:17 +03:00
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.
94 lines
4.2 KiB
TypeScript
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`
|
|
);
|
|
});
|