mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-13 18:32:12 +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.
129 lines
5.9 KiB
TypeScript
129 lines
5.9 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
|
|
import { OAUTH_ENDPOINTS, PROVIDERS as LEGACY_PROVIDERS } from "../../open-sse/config/constants.ts";
|
|
import { REGISTRY } from "../../open-sse/config/providerRegistry.ts";
|
|
import {
|
|
getAccessToken,
|
|
REFRESH_LEAD_MS,
|
|
supportsTokenRefresh,
|
|
} from "../../open-sse/services/tokenRefresh.ts";
|
|
import { OAUTH_PROVIDERS } from "../../src/shared/constants/providers.ts";
|
|
|
|
// The arc of this file, kept whole because each step is the reason the next made sense:
|
|
//
|
|
// #8232 Restored OAuth auto-refresh for stored `gemini-cli` connections — a real user
|
|
// report: the UI advertises automatic token rotation for OAuth providers, and
|
|
// these rows never rotated. It overshot, restoring a routable, UI-visible
|
|
// provider along the way.
|
|
// #8275 Narrowed that to the legacy refresh path ONLY, keeping the discontinued
|
|
// provider out of the public registries and out of routing.
|
|
// now Deprecated. What #8275 left was a refresh that WORKED (it redeemed against
|
|
// PROVIDERS.gemini's client — the same public Gemini CLI OAuth client) for a
|
|
// provider that is NOT routable. So the token stayed fresh and could never
|
|
// answer a request: periodic upstream calls maintaining a dead credential.
|
|
//
|
|
// The refresh assertions below therefore now assert the deprecation instead of the
|
|
// refresh. They were rewritten, not removed — the count is unchanged and the behavior is
|
|
// pinned harder than before (a silent skip would pass a weaker test; a classified code
|
|
// does not). Registry-exclusion coverage from #8275 is untouched, because that guarantee
|
|
// still holds and is still worth guarding.
|
|
//
|
|
// Companion: tests/unit/gemini-cli-deprecation.test.ts covers the notice itself, the
|
|
// routability of the migration target, and the untouched CLIENT identity (#7034).
|
|
|
|
test("Gemini CLI stays out of the chat and OAuth provider registries", () => {
|
|
assert.equal(REGISTRY["gemini-cli"], undefined);
|
|
assert.equal(LEGACY_PROVIDERS["gemini-cli"], undefined);
|
|
assert.equal((OAUTH_PROVIDERS as Record<string, unknown>)["gemini-cli"], undefined);
|
|
assert.ok(REGISTRY.gemini);
|
|
assert.ok(REGISTRY.antigravity);
|
|
});
|
|
|
|
test("legacy Gemini CLI connections are no longer refreshed at all", () => {
|
|
// Was: lead time equal to antigravity's, supportsTokenRefresh === true.
|
|
assert.equal(supportsTokenRefresh("gemini-cli"), false);
|
|
assert.equal(REFRESH_LEAD_MS["gemini-cli"], undefined);
|
|
// The sibling Google-backed providers must NOT be affected by the deprecation.
|
|
assert.equal(supportsTokenRefresh("gemini"), true);
|
|
assert.equal(REFRESH_LEAD_MS.antigravity, 15 * 60 * 1000);
|
|
});
|
|
|
|
test("Gemini CLI stays out of the provider translation snapshot", () => {
|
|
const snapshotPath = new URL("../snapshots/provider/translate-path.json", import.meta.url);
|
|
const snapshot = JSON.parse(readFileSync(snapshotPath, "utf8")) as Record<string, unknown>;
|
|
assert.equal(snapshot["gemini-cli"], undefined);
|
|
});
|
|
|
|
test("legacy Gemini CLI refresh never reaches Google's token endpoint anymore", async () => {
|
|
// Was: asserted a successful POST to OAUTH_ENDPOINTS.google.token carrying
|
|
// PROVIDERS.gemini's client_id/secret, returning a new access token. That call is the
|
|
// waste the deprecation removes — the token it produced could not route anywhere. Now
|
|
// the assertion is stronger: not "it fails", but "no upstream call happens at all".
|
|
const originalFetch = globalThis.fetch;
|
|
const calls: string[] = [];
|
|
|
|
globalThis.fetch = (async (url) => {
|
|
calls.push(String(url));
|
|
return new Response(JSON.stringify({ access_token: "should-never-be-requested" }), {
|
|
status: 200,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
}) as typeof fetch;
|
|
|
|
try {
|
|
const result = await getAccessToken(
|
|
"gemini-cli",
|
|
{ refreshToken: "legacy-gemini-cli-refresh-old" },
|
|
{}
|
|
);
|
|
|
|
assert.deepEqual(calls, [], `expected zero upstream calls, got ${calls.join(", ")}`);
|
|
assert.notEqual(
|
|
calls[0],
|
|
OAUTH_ENDPOINTS.google.token,
|
|
"the Google token endpoint must not be contacted for a deprecated provider"
|
|
);
|
|
assert.equal(result.accessToken, undefined, "no token may be handed back");
|
|
assert.equal(result.code, "provider_deprecated");
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("legacy Gemini CLI refresh reports deprecation, not a revoked token", async () => {
|
|
// Was: a 400 invalid_grant from upstream surfaced as
|
|
// { error: "unrecoverable_refresh_error", code: "invalid_grant" }. The envelope is
|
|
// deliberately unchanged — every existing caller keys on `error` and must keep
|
|
// stopping its retries (isUnrecoverableRefreshError, the manual-refresh route). Only
|
|
// the `code` differs, and that difference is the whole point: "your token was revoked"
|
|
// and "this provider no longer exists" demand different actions from the operator.
|
|
const originalFetch = globalThis.fetch;
|
|
globalThis.fetch = (async () =>
|
|
new Response(JSON.stringify({ error: "invalid_grant" }), {
|
|
status: 400,
|
|
headers: { "content-type": "application/json" },
|
|
})) as typeof fetch;
|
|
|
|
try {
|
|
const result = await getAccessToken(
|
|
"gemini-cli",
|
|
{ refreshToken: "legacy-gemini-cli-refresh-revoked" },
|
|
{}
|
|
);
|
|
assert.equal(result.error, "unrecoverable_refresh_error");
|
|
assert.equal(result.code, "provider_deprecated");
|
|
assert.equal(result.migrateTo, "gemini");
|
|
assert.match(result.reason, /gemini/i);
|
|
|
|
// The pre-deprecation behavior for a genuinely revoked token still works for the
|
|
// provider that IS routable — proof the deprecation did not blunt the real path.
|
|
const geminiResult = await getAccessToken("gemini", { refreshToken: "revoked" }, {});
|
|
assert.equal(geminiResult.error, "unrecoverable_refresh_error");
|
|
assert.equal(geminiResult.code, "invalid_grant");
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|