Files
diegosouzapw--omniroute/tests/unit/anthropic-compat-validation-v1-messages.test.ts
2026-07-13 13:39:12 +08:00

81 lines
3.2 KiB
TypeScript

// Regression test for the anthropic-compatible connection validator.
//
// Upstream fix: GET /models is not part of the Anthropic API spec; many
// compatible proxies either 404, 401, or 403 on /models even with a valid
// API key. The connection test must therefore not reject the credentials
// solely on a 401/403 from /models — it must fall back to POST /v1/messages
// (the canonical Anthropic auth probe) and treat any non-401/403 messages
// response as proof that the key was accepted.
//
// Ported from decolua/9router 584cf66a (Co-author: Rehan Choirul).
import test from "node:test";
import assert from "node:assert/strict";
const { validateProviderApiKey } = await import("../../src/lib/providers/validation.ts");
const originalFetch = globalThis.fetch;
test.afterEach(() => {
globalThis.fetch = originalFetch;
});
test(
"anthropic-compatible validation falls back to /messages when /models returns 403",
async () => {
const calls: { url: string; method: string }[] = [];
globalThis.fetch = async (url: any, init: any = {}) => {
const u = String(url);
const method = String(init?.method || "GET").toUpperCase();
calls.push({ url: u, method });
if (u.endsWith("/models")) {
return new Response(JSON.stringify({ error: "forbidden on models" }), { status: 403 });
}
// /messages: upstream accepts the key but rejects the toy payload with 400.
return new Response(JSON.stringify({ error: "bad request" }), { status: 400 });
};
const result = await validateProviderApiKey({
provider: "anthropic-compatible-403-on-models",
apiKey: "sk-test",
providerSpecificData: { baseUrl: "https://proxy.example.com/v1/messages" },
});
// BEFORE the fix this returned { valid: false, error: "Invalid API key" }
// because validateAnthropicCompatibleProvider short-circuited on the 403
// from GET /models without ever probing POST /v1/messages.
assert.equal(result.valid, true, "403 on /models alone must NOT mark the key invalid");
assert.equal(result.error, null);
// The validator must actually exercise the messages endpoint.
const messagesCall = calls.find(
(call) => call.url.endsWith("/messages") && call.method === "POST"
);
assert.ok(messagesCall, "expected a POST /messages probe after /models 403");
}
);
test(
"anthropic-compatible validation still rejects when /messages itself returns 401",
async () => {
// Symmetry guard: the fix must NOT make every 403/401 pass. Only the
// messages probe is authoritative — if it also rejects auth, the key is bad.
globalThis.fetch = async (url: any) => {
const u = String(url);
if (u.endsWith("/models")) {
return new Response(JSON.stringify({ error: "no models endpoint" }), { status: 403 });
}
return new Response(JSON.stringify({ error: "invalid_api_key" }), { status: 401 });
};
const result = await validateProviderApiKey({
provider: "anthropic-compatible-truly-bad-key",
apiKey: "sk-bad",
providerSpecificData: { baseUrl: "https://proxy.example.com/v1/messages" },
});
assert.equal(result.valid, false);
assert.equal(result.error, "Invalid API key");
}
);