Files
OmniRoute/tests/unit/provider-onboarding-wizard.test.ts
Thiago Reis 3a28b3b5e8 feat: add Kiro API key authentication (#6587)
* feat(oauth): add Kiro long-lived API key auth (#6587)

New /api/oauth/kiro/api-key route + KiroService.validateApiKey let a
Kiro account be linked with a long-lived AWS CodeWhisperer/Kiro API
key instead of the interactive OAuth device flow, with live
per-account model discovery (ListAvailableModels, 5-minute cache)
layered over the existing static registry fallback.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* fix(changelog): re-restore #6587 bullet after release sync

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* fix(merge): restore #6126 clinepass files reverted by release auto-resolve + baseline re-merge

The release sync's auto-resolve reverted sibling PR #6126's clinepass work
(registry, catalog, oauth constants, clineAuth.ts, token-refresh case, tests)
and the file-size baseline — all outside this PR's scope. Restored to the
release versions, re-applied only this PR's own baseline entries, restored the
#6126 CHANGELOG bullet (re-inserting only this PR's own).

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* chore(quality): freeze public-creds FP — AWS region default in validateApiKey signature

Same class as the existing minimax fn-param FPs: CRED_KEY_RE matches the
apiKey: param annotation and captures the region default "us-east-1",
which is not a credential.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* fix(kiro): keep hard-failure reject semantics + kill public-creds fn-param FP at the source

- getKiroUsage: exhausted non-auth attempts now REJECT with the last HTTP-status
  failure in the pre-#6587 format (usage-service-hardening relies on it); auth
  failures keep the soft social-auth message.
- validateApiKey: region default moved out of the parameter list (the
  check-public-creds CRED_KEY_RE matches the apiKey: annotation and flags any
  literal in the signature); drops the brittle line-keyed allowlist entry.

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: strangersp <strangersp@users.noreply.github.com>
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
2026-07-09 22:49:06 -03:00

331 lines
10 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
const catalog =
await import("../../src/app/(dashboard)/dashboard/providers/components/onboarding/providerOnboardingCatalog.ts");
const api =
await import("../../src/app/(dashboard)/dashboard/providers/components/onboarding/providerOnboardingApi.ts");
test("provider onboarding catalog exposes API-key and OAuth providers for the wizard", () => {
const apiKeyOptions = catalog.getWizardApiKeyProviderOptions();
const oauthOptions = catalog.getWizardOAuthProviderOptions();
assert.ok(apiKeyOptions.some((option) => option.id === "openai"));
assert.ok(apiKeyOptions.some((option) => option.id === "openrouter"));
assert.ok(!apiKeyOptions.some((option) => option.id === "kiro"));
assert.ok(!apiKeyOptions.some((option) => option.id === "amazon-q"));
assert.ok(oauthOptions.some((option) => option.id === "claude"));
assert.ok(oauthOptions.some((option) => option.id === "kiro"));
assert.ok(oauthOptions.some((option) => option.id === "amazon-q"));
assert.ok(oauthOptions.some((option) => option.id === "cursor"));
assert.ok(!oauthOptions.some((option) => option.id === "zed"));
assert.ok(!oauthOptions.some((option) => option.id === "windsurf"));
assert.ok(!oauthOptions.some((option) => option.id === "devin-cli"));
assert.ok(!oauthOptions.some((option) => option.id === "qoder"));
assert.ok(apiKeyOptions.every((option) => option.authKind === "apikey"));
assert.ok(oauthOptions.every((option) => option.authKind === "oauth"));
});
test("provider onboarding option filter matches id, name, alias, and description", () => {
const options = [
{
id: "openrouter",
name: "OpenRouter",
icon: "router",
alias: "or",
description: "multi-model routing gateway",
authKind: "apikey",
apiKeyOptional: false,
deprecated: false,
},
{
id: "claude",
name: "Claude Code",
icon: "smart_toy",
alias: "cc",
description: "OAuth coding provider",
authKind: "oauth",
apiKeyOptional: false,
deprecated: false,
},
];
assert.deepEqual(
catalog.filterWizardProviderOptions(options, "router").map((option) => option.id),
["openrouter"]
);
assert.deepEqual(
catalog.filterWizardProviderOptions(options, "coding").map((option) => option.id),
["claude"]
);
assert.equal(catalog.filterWizardProviderOptions(options, " ").length, 2);
});
test("provider onboarding builds providerSpecificData without empty fields", () => {
assert.deepEqual(
catalog.buildProviderSpecificData({
baseUrl: " https://gateway.example/v1 ",
region: "",
cx: " cx-123 ",
customUserAgent: " ",
}),
{
baseUrl: "https://gateway.example/v1",
cx: "cx-123",
}
);
assert.equal(catalog.buildProviderSpecificData({}), null);
});
test("provider onboarding API sends validate, create, and test requests with expected payloads", async () => {
const calls = [];
const originalFetch = globalThis.fetch;
globalThis.fetch = async (url, init) => {
calls.push({ url: String(url), init });
if (String(url) === "/api/providers/validate") {
return Response.json({ valid: true });
}
if (String(url) === "/api/providers") {
return Response.json(
{ connection: { id: "conn-1", provider: "openai", name: "OpenAI Primary" } },
{ status: 201 }
);
}
if (String(url) === "/api/providers/conn-1/test") {
return Response.json({ valid: true, latencyMs: 42 });
}
return Response.json({ error: "unexpected" }, { status: 500 });
};
try {
await api.validateOnboardingApiKey({
provider: "openai",
apiKey: "sk-test",
baseUrl: "https://api.example/v1",
});
const connection = await api.createOnboardingConnection({
provider: "openai",
name: "OpenAI Primary",
apiKey: "sk-test",
providerSpecificData: { baseUrl: "https://api.example/v1" },
});
const result = await api.testOnboardingConnection(connection.id);
assert.equal(connection.id, "conn-1");
assert.equal(result.valid, true);
assert.equal(calls.length, 3);
assert.equal(calls[0].url, "/api/providers/validate");
assert.deepEqual(JSON.parse(String(calls[0].init.body)), {
provider: "openai",
apiKey: "sk-test",
baseUrl: "https://api.example/v1",
});
assert.equal(calls[1].url, "/api/providers");
assert.deepEqual(JSON.parse(String(calls[1].init.body)), {
provider: "openai",
name: "OpenAI Primary",
apiKey: "sk-test",
priority: 1,
testStatus: "unknown",
providerSpecificData: { baseUrl: "https://api.example/v1" },
});
assert.equal(calls[2].url, "/api/providers/conn-1/test");
assert.equal(calls[2].init.method, "POST");
} finally {
globalThis.fetch = originalFetch;
}
});
test("provider onboarding validation rejects HTTP 200 responses with valid false", async () => {
const originalFetch = globalThis.fetch;
let createCalled = false;
globalThis.fetch = async (url) => {
if (String(url) === "/api/providers/validate") {
return Response.json({ valid: false, error: "Invalid API key" });
}
if (String(url) === "/api/providers") {
createCalled = true;
return Response.json({ connection: { id: "conn-1", provider: "openai" } });
}
return Response.json({ error: "unexpected" }, { status: 500 });
};
try {
await assert.rejects(
() => api.validateOnboardingApiKey({ provider: "openai", apiKey: "bad-key" }),
/Invalid API key/
);
assert.equal(createCalled, false);
} finally {
globalThis.fetch = originalFetch;
}
});
test("#5692 onboarding validation treats unsupported providers as non-blocking (save proceeds)", async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async (url) => {
if (String(url) === "/api/providers/validate") {
// #5565/#5567: providers with no live validator (lmarena, piapi, …) return
// HTTP 400 + { unsupported: true }. The wizard must NOT treat this as a hard
// failure — otherwise the connection is never created (#5692).
return Response.json(
{ error: "Provider validation not supported", unsupported: true },
{ status: 400 }
);
}
return Response.json({ error: "unexpected" }, { status: 500 });
};
try {
const data = await api.validateOnboardingApiKey({ provider: "lmarena", apiKey: "test-key" });
assert.equal(data.unsupported, true);
} finally {
globalThis.fetch = originalFetch;
}
});
test("provider onboarding API ignores non-object error JSON", async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () => Response.json(null, { status: 500 });
try {
await assert.rejects(() => api.fetchOnboardingProviderNodes(), /Failed to load provider nodes/);
} finally {
globalThis.fetch = originalFetch;
}
});
test("provider onboarding validates API payloads before sending requests", async () => {
const originalFetch = globalThis.fetch;
let fetchCalled = false;
globalThis.fetch = async () => {
fetchCalled = true;
return Response.json({ valid: true });
};
try {
await assert.rejects(
() =>
api.validateOnboardingApiKey({
provider: "openai",
apiKey: "sk-test",
baseUrl: "not-a-url",
}),
/Provider credentials are not valid/
);
await assert.rejects(
() =>
api.createOnboardingConnection({
provider: "openai",
name: "OpenAI Primary",
}),
/Provider connection data is invalid/
);
assert.equal(fetchCalled, false);
} finally {
globalThis.fetch = originalFetch;
}
});
test("provider onboarding loads provider node feature flags through the API helper", async () => {
const calls = [];
const originalFetch = globalThis.fetch;
globalThis.fetch = async (url, init) => {
calls.push({ url: String(url), init });
return Response.json({ ccCompatibleProviderEnabled: true });
};
try {
const result = await api.fetchOnboardingProviderNodes();
assert.deepEqual(result, { ccCompatibleProviderEnabled: true });
assert.equal(calls.length, 1);
assert.equal(calls[0].url, "/api/provider-nodes");
} finally {
globalThis.fetch = originalFetch;
}
});
test("provider onboarding can create compatible connections without an API key", async () => {
const calls = [];
const originalFetch = globalThis.fetch;
globalThis.fetch = async (url, init) => {
calls.push({ url: String(url), init });
return Response.json(
{ connection: { id: "conn-custom", provider: "openai-compatible-local" } },
{ status: 201 }
);
};
try {
const connection = await api.createOnboardingConnection({
provider: "openai-compatible-local",
name: "Local Gateway",
});
assert.equal(connection.id, "conn-custom");
assert.deepEqual(JSON.parse(String(calls[0].init.body)), {
provider: "openai-compatible-local",
name: "Local Gateway",
priority: 1,
testStatus: "unknown",
});
} finally {
globalThis.fetch = originalFetch;
}
});
test("provider onboarding API builds compatible provider node request bodies", () => {
assert.deepEqual(
api.buildCompatibleNodeRequest({
mode: "openai",
name: "Local Gateway",
prefix: "local-gw",
baseUrl: "https://gateway.example/v1",
apiType: "responses",
modelsPath: "/models",
}),
{
name: "Local Gateway",
prefix: "local-gw",
baseUrl: "https://gateway.example/v1",
type: "openai-compatible",
chatPath: "",
apiType: "responses",
modelsPath: "/models",
}
);
assert.deepEqual(
api.buildCompatibleNodeRequest({
mode: "cc",
name: "CC Gateway",
prefix: "cc-gw",
baseUrl: "https://cc.example",
}),
{
name: "CC Gateway",
prefix: "cc-gw",
baseUrl: "https://cc.example",
type: "anthropic-compatible",
chatPath: "/v1/messages?beta=true",
compatMode: "cc",
}
);
});
test("provider onboarding validates compatible provider node request bodies", () => {
assert.throws(
() =>
api.buildCompatibleNodeRequest({
mode: "openai",
name: "Local Gateway",
prefix: "local-gw",
baseUrl: "https://gateway.example/v1",
chatPath: "chat/completions",
}),
/Compatible provider data is invalid/
);
});