Files
diegosouzapw--omniroute/tests/unit/anthropic-header-dedupe-1475.test.ts
2026-07-13 13:39:12 +08:00

68 lines
3.1 KiB
TypeScript

// Regression test for #1475 — duplicate case-variant Anthropic headers.
//
// Node/undici's fetch lowercases and MERGES same-name headers, so an outbound
// set carrying both "anthropic-version" and "Anthropic-Version" collapses into
// a single "anthropic-version: 2023-06-01, 2023-06-01" value, which the
// Anthropic API rejects. normalizeAnthropicHeaderVariants() reconciles the two
// case variants down to one canonical lowercase header with a single value
// (and a deduped, joined comma-list for anthropic-beta) before the request is
// dispatched in BaseExecutor.buildHeaders / DefaultExecutor.buildHeaders.
import test from "node:test";
import assert from "node:assert/strict";
import { normalizeAnthropicHeaderVariants } from "../../open-sse/config/anthropicHeaders.ts";
import { DefaultExecutor } from "../../open-sse/executors/default.ts";
test("normalizeAnthropicHeaderVariants collapses case-variant anthropic-version into one value", () => {
const headers: Record<string, string> = {
"anthropic-version": "2023-06-01",
"Anthropic-Version": "2023-06-01",
};
normalizeAnthropicHeaderVariants(headers);
// The mixed-case variant must be gone, and the lowercase one must carry a
// single value (no "v, v" duplication that Anthropic rejects).
assert.equal(headers["Anthropic-Version"], undefined);
assert.equal(headers["anthropic-version"], "2023-06-01");
assert.ok(!headers["anthropic-version"].includes(","));
});
test("normalizeAnthropicHeaderVariants dedupes and joins anthropic-beta variants", () => {
const headers: Record<string, string> = {
"anthropic-beta": "oauth-2025-04-20, prompt-caching",
"Anthropic-Beta": "prompt-caching, fine-grained",
};
normalizeAnthropicHeaderVariants(headers);
assert.equal(headers["Anthropic-Beta"], undefined);
assert.equal(headers["anthropic-beta"], "oauth-2025-04-20,prompt-caching,fine-grained");
});
test("normalizeAnthropicHeaderVariants leaves a single lowercase header untouched", () => {
const headers: Record<string, string> = { "anthropic-version": "2023-06-01" };
normalizeAnthropicHeaderVariants(headers);
assert.equal(headers["anthropic-version"], "2023-06-01");
assert.equal(headers["Anthropic-Version"], undefined);
});
test("normalizeAnthropicHeaderVariants is a no-op when no anthropic headers are present", () => {
const headers: Record<string, string> = { "Content-Type": "application/json" };
normalizeAnthropicHeaderVariants(headers);
assert.deepEqual(headers, { "Content-Type": "application/json" });
});
test("DefaultExecutor.buildHeaders does not emit duplicate anthropic-version variants for an anthropic-compatible provider", () => {
const executor = new DefaultExecutor("anthropic-compatible-test");
const headers = executor.buildHeaders({ apiKey: "sk-test" }, true) as Record<string, string>;
// Whatever case the upstream registry/auth path used, only the canonical
// lowercase header survives, with exactly one value.
assert.equal(headers["Anthropic-Version"], undefined);
if (headers["anthropic-version"] !== undefined) {
assert.ok(!headers["anthropic-version"].includes(","));
}
});