mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-15 19:32:20 +03:00
* fix(usage): console-aware Token Plan guidance and subscription hint on bailian 401 The personal Token Plan is sold through two consoles with different portals, gateway hosts and login tickets. Two operator-facing messages ignored the split: - The quota guidance always said 'get the cookie at home.qwencloud.com', even for connections served by the Alibaba Model Studio console — following it verbatim produces a cookie the gateway rejects (console mismatch → BailianGateway.Login.NotLogined). The guidance now derives the console from the provider via resolveConsoleSite, matching what the fetcher will do with the pasted cookie. - Key validation mapped upstream 401 to a bare 'Invalid API key'. An expired Token Plan subscription produces the exact same upstream 401 (observed live 2026-09-01: subscription ended 08-23, the working key started failing), so the message now names the subscription as a cause worth checking. * test(providers): align the remaining bailian 401/403 message pins to prefix match search-provider-validation.test.ts pinned the exact 'Invalid API key' string for the bailian validator; the message now also names an expired Token Plan subscription. Same property asserted (401/403 => invalid), prefix match.
167 lines
4.9 KiB
TypeScript
167 lines
4.9 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
|
|
const { validateProviderApiKey } = await import("../../src/lib/providers/validation.ts");
|
|
|
|
test("serper validation accepts authenticated non-auth upstream errors", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
globalThis.fetch = async () =>
|
|
new Response(JSON.stringify({ error: "credits_exhausted" }), {
|
|
status: 402,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
|
|
try {
|
|
const result = await validateProviderApiKey({
|
|
provider: "serper-search",
|
|
apiKey: "valid-serper-key",
|
|
});
|
|
|
|
assert.equal(result.valid, true);
|
|
assert.equal(result.error, null);
|
|
assert.equal(result.unsupported, false);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("serper validation still rejects unauthorized keys", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
globalThis.fetch = async () =>
|
|
new Response(JSON.stringify({ error: "Unauthorized" }), {
|
|
status: 403,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
|
|
try {
|
|
const result = await validateProviderApiKey({
|
|
provider: "serper-search",
|
|
apiKey: "bad-serper-key",
|
|
});
|
|
|
|
assert.equal(result.valid, false);
|
|
assert.equal(result.error, "Invalid API key");
|
|
assert.equal(result.unsupported, false);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("Kimi Code API-key validation uses the messages endpoint for both provider ids", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
let calls = [];
|
|
|
|
globalThis.fetch = async (url, init = {}) => {
|
|
calls.push({
|
|
url: String(url),
|
|
method: init.method || "GET",
|
|
headers: init.headers || {},
|
|
});
|
|
|
|
return new Response(JSON.stringify({ ok: true }), {
|
|
status: 400,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
};
|
|
|
|
try {
|
|
for (const provider of ["kimi-coding", "kimi-coding-apikey"]) {
|
|
calls = [];
|
|
const result = await validateProviderApiKey({
|
|
provider,
|
|
apiKey: "sk-kimi-test",
|
|
});
|
|
|
|
assert.equal(result.valid, true);
|
|
assert.equal(result.error, null);
|
|
// The Anthropic-like validator first probes /models then falls back to the messages endpoint.
|
|
assert.equal(calls.length, 2);
|
|
|
|
// calls[0] is the models probe; calls[1] is the POST to the messages endpoint.
|
|
assert.equal(calls[1].url, "https://api.kimi.com/coding/v1/messages?beta=true");
|
|
assert.equal(calls[1].method, "POST");
|
|
assert.equal(calls[1].headers["x-api-key"], "sk-kimi-test");
|
|
assert.equal(calls[1].headers["Anthropic-Version"], "2023-06-01");
|
|
|
|
for (const call of calls) {
|
|
assert.equal(call.url.includes("?beta=true/messages"), false);
|
|
assert.equal(call.url.includes("?beta=true/models"), false);
|
|
}
|
|
}
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("bailian-coding-plan validation accepts 400 as valid auth path", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
globalThis.fetch = async () =>
|
|
new Response(JSON.stringify({ error: "invalid request" }), {
|
|
status: 400,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
|
|
try {
|
|
const result = await validateProviderApiKey({
|
|
provider: "bailian-coding-plan",
|
|
apiKey: "valid-bailian-key",
|
|
});
|
|
|
|
assert.equal(result.valid, true);
|
|
assert.equal(result.error, null);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("bailian-coding-plan validation rejects 401 as invalid key", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
globalThis.fetch = async () =>
|
|
new Response(JSON.stringify({ error: "Unauthorized" }), {
|
|
status: 401,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
|
|
try {
|
|
const result = await validateProviderApiKey({
|
|
provider: "bailian-coding-plan",
|
|
apiKey: "bad-bailian-key",
|
|
});
|
|
|
|
assert.equal(result.valid, false);
|
|
// Prefix match: the message now also names an expired Token Plan subscription,
|
|
// which yields the identical upstream 401/403.
|
|
assert.match(String(result.error), /^Invalid API key/);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
test("bailian-coding-plan validation rejects 403 as invalid key", async () => {
|
|
const originalFetch = globalThis.fetch;
|
|
|
|
globalThis.fetch = async () =>
|
|
new Response(JSON.stringify({ error: "Forbidden" }), {
|
|
status: 403,
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
|
|
try {
|
|
const result = await validateProviderApiKey({
|
|
provider: "bailian-coding-plan",
|
|
apiKey: "bad-bailian-key",
|
|
});
|
|
|
|
assert.equal(result.valid, false);
|
|
// Prefix match: the message now also names an expired Token Plan subscription,
|
|
// which yields the identical upstream 401/403.
|
|
assert.match(String(result.error), /^Invalid API key/);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|