mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
OC 1.17.8+ native-adapter gate (Ji() in bundled client) rejects any
model whose providerID is not exactly 'openai', 'anthropic', or does
not start with 'opencode'. The plugin's default providerId ('omniroute')
fails this gate, so every model dispatch returns 'Unable to determine
provider'. Auto-prefix in resolveOmniRoutePluginOptions so existing
opencode.json configs (providerId: 'omniroute') keep working without
user-facing changes — internally every catalog key, ModelV2 field,
and auth hook sees 'opencode-omniroute'.
The prefix also fixes the auth.json key mismatch: the resolved
(opencode-omniroute) key is what OC 1.17.8+ dispatcher reads when
looking up credentials.
148 lines
5.7 KiB
TypeScript
148 lines
5.7 KiB
TypeScript
/**
|
|
* T-02 auth-hook contract tests.
|
|
*
|
|
* Covers the `createOmniRouteAuthHook(opts)` factory and its loader behaviour
|
|
* against every Auth flavor (`api`, `oauth`, null, empty key). Validates the
|
|
* multi-instance fix: provider id flows from plugin options, not a module
|
|
* constant.
|
|
*/
|
|
|
|
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { createOmniRouteAuthHook } from "../src/index.js";
|
|
|
|
test("createOmniRouteAuthHook: default providerId is 'omniroute'", () => {
|
|
const hook = createOmniRouteAuthHook();
|
|
assert.equal(hook.provider, "opencode-omniroute");
|
|
});
|
|
|
|
test("createOmniRouteAuthHook: custom providerId binds to hook.provider (multi-instance)", () => {
|
|
const hook = createOmniRouteAuthHook({ providerId: "omniroute-preprod" });
|
|
assert.equal(hook.provider, "opencode-omniroute-preprod");
|
|
});
|
|
|
|
test("createOmniRouteAuthHook: methods[0] is type 'api' with label including displayName", () => {
|
|
const hook = createOmniRouteAuthHook();
|
|
assert.equal(Array.isArray(hook.methods), true);
|
|
assert.equal(hook.methods.length, 1);
|
|
const m = hook.methods[0];
|
|
assert.equal(m.type, "api");
|
|
assert.equal(m.label, "OmniRoute API Key");
|
|
|
|
const custom = createOmniRouteAuthHook({ providerId: "omniroute-preprod" });
|
|
assert.equal(custom.methods[0].label, "OmniRoute (opencode-omniroute-preprod) API Key");
|
|
});
|
|
|
|
test("createOmniRouteAuthHook: prompts[0] uses key='apiKey' per @opencode-ai/plugin contract", () => {
|
|
// NOTE: spec referenced `name: "apiKey"`; the official
|
|
// @opencode-ai/plugin@1.15.6 prompt shape uses `key` + `message` (no
|
|
// `name`/`label`/`mask` fields). Asserting against the real type contract.
|
|
const hook = createOmniRouteAuthHook();
|
|
const m = hook.methods[0];
|
|
assert.equal(m.type, "api");
|
|
// narrow: api method may carry prompts
|
|
const prompts = "prompts" in m ? m.prompts : undefined;
|
|
assert.ok(Array.isArray(prompts) && prompts.length === 1, "expected one prompt");
|
|
const p = prompts![0];
|
|
assert.equal(p.type, "text");
|
|
assert.equal((p as { key: string }).key, "apiKey");
|
|
assert.ok(
|
|
typeof (p as { message: string }).message === "string" &&
|
|
(p as { message: string }).message.includes("omniroute"),
|
|
"prompt message should mention provider id"
|
|
);
|
|
});
|
|
|
|
test("loader: valid api auth → {apiKey} when no baseURL option (T-04: fetch omitted)", async () => {
|
|
// T-04 changed the loader return shape: without a resolvable baseURL the
|
|
// interceptor cannot gate-keep requests, so the loader falls back to
|
|
// apiKey-only and the AI-SDK uses its default fetch. See fetch-interceptor
|
|
// tests for the wired-fetch branches.
|
|
const hook = createOmniRouteAuthHook();
|
|
assert.ok(hook.loader, "loader must be defined");
|
|
const result = await hook.loader!(
|
|
async () => ({ type: "api", key: "sk-test" }) as never,
|
|
{} as never
|
|
);
|
|
assert.deepEqual(result, { apiKey: "sk-test" });
|
|
});
|
|
|
|
test("loader: valid api auth → {apiKey, baseURL, fetch} when baseURL option set (T-04)", async () => {
|
|
const hook = createOmniRouteAuthHook({ baseURL: "https://or.example.com/v1" });
|
|
const result = await hook.loader!(
|
|
async () => ({ type: "api", key: "sk-x" }) as never,
|
|
{} as never
|
|
);
|
|
assert.equal((result as { apiKey: string }).apiKey, "sk-x");
|
|
assert.equal((result as { baseURL: string }).baseURL, "https://or.example.com/v1");
|
|
assert.equal(
|
|
typeof (result as { fetch?: unknown }).fetch,
|
|
"function",
|
|
"T-04: loader must wire fetch interceptor when baseURL resolves"
|
|
);
|
|
});
|
|
|
|
test("loader: features.fetchInterceptor=false AND geminiSanitization=false → no custom fetch (flags honored)", async () => {
|
|
// Regression: both fetch-layer flags were documented + schema-validated but
|
|
// silently ignored. Disabling both must fall back to the SDK default fetch.
|
|
const hook = createOmniRouteAuthHook({
|
|
baseURL: "https://or.example.com/v1",
|
|
features: { fetchInterceptor: false, geminiSanitization: false },
|
|
});
|
|
const result = await hook.loader!(
|
|
async () => ({ type: "api", key: "sk-x" }) as never,
|
|
{} as never
|
|
);
|
|
assert.deepEqual(result, { apiKey: "sk-x", baseURL: "https://or.example.com/v1" });
|
|
assert.equal(
|
|
(result as { fetch?: unknown }).fetch,
|
|
undefined,
|
|
"both flags off must omit the custom fetch"
|
|
);
|
|
});
|
|
|
|
test("loader: features.fetchInterceptor=false but geminiSanitization=true → fetch still wired (sanitizer only)", async () => {
|
|
const hook = createOmniRouteAuthHook({
|
|
baseURL: "https://or.example.com/v1",
|
|
features: { fetchInterceptor: false, geminiSanitization: true },
|
|
});
|
|
const result = await hook.loader!(
|
|
async () => ({ type: "api", key: "sk-x" }) as never,
|
|
{} as never
|
|
);
|
|
assert.equal(
|
|
typeof (result as { fetch?: unknown }).fetch,
|
|
"function",
|
|
"geminiSanitization alone must still provide a fetch wrapper"
|
|
);
|
|
});
|
|
|
|
test("loader: null/undefined auth → {} (no creds yet, OC surfaces /connect)", async () => {
|
|
const hook = createOmniRouteAuthHook();
|
|
const r1 = await hook.loader!(async () => null as never, {} as never);
|
|
assert.deepEqual(r1, {});
|
|
const r2 = await hook.loader!(async () => undefined as never, {} as never);
|
|
assert.deepEqual(r2, {});
|
|
});
|
|
|
|
test("loader: oauth-flavored auth → {} (wrong method type, ignored)", async () => {
|
|
const hook = createOmniRouteAuthHook();
|
|
const result = await hook.loader!(
|
|
async () =>
|
|
({
|
|
type: "oauth",
|
|
refresh: "r",
|
|
access: "a",
|
|
expires: 0,
|
|
}) as never,
|
|
{} as never
|
|
);
|
|
assert.deepEqual(result, {});
|
|
});
|
|
|
|
test("loader: api auth with empty key → {} (empty creds rejected)", async () => {
|
|
const hook = createOmniRouteAuthHook();
|
|
const result = await hook.loader!(async () => ({ type: "api", key: "" }) as never, {} as never);
|
|
assert.deepEqual(result, {});
|
|
});
|