openclaw/src/agents/provider-attribution.test.ts
OpenClaw ddecd5226b fix(openrouter): use correct X-Title header for app attribution
OpenRouter requires 'X-Title' header for app attribution, not
'X-OpenRouter-Title'. The incorrect header name caused OpenRouter to not
recognize requests as coming from OpenClaw, resulting in free models
(e.g., xiaomi/mimo-v2-pro) being charged at full price.

Changes:
- Replace 'X-OpenRouter-Title' with 'X-Title' per OpenRouter docs
- Remove unnecessary 'X-OpenRouter-Categories' header
- Update tests to match corrected headers

Reference: https://openrouter.ai/docs/app-attribution
Fixes #50738
2026-03-20 08:38:52 +07:00

113 lines
3.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
listProviderAttributionPolicies,
resolveProviderAttributionHeaders,
resolveProviderAttributionIdentity,
resolveProviderAttributionPolicy,
} from "./provider-attribution.js";
describe("provider attribution", () => {
it("resolves the canonical OpenClaw product and runtime version", () => {
const identity = resolveProviderAttributionIdentity({
OPENCLAW_VERSION: "2026.3.99",
});
expect(identity).toEqual({
product: "OpenClaw",
version: "2026.3.99",
});
});
it("returns a documented OpenRouter attribution policy", () => {
const policy = resolveProviderAttributionPolicy("openrouter", {
OPENCLAW_VERSION: "2026.3.14",
});
expect(policy).toEqual({
provider: "openrouter",
enabledByDefault: true,
verification: "vendor-documented",
hook: "request-headers",
docsUrl: "https://openrouter.ai/docs/app-attribution",
reviewNote: "Documented app attribution headers. Verified in OpenClaw runtime wrapper.",
product: "OpenClaw",
version: "2026.3.14",
headers: {
"HTTP-Referer": "https://openclaw.ai",
"X-Title": "OpenClaw",
},
});
});
it("normalizes aliases when resolving provider headers", () => {
expect(
resolveProviderAttributionHeaders("OpenRouter", {
OPENCLAW_VERSION: "2026.3.14",
}),
).toEqual({
"HTTP-Referer": "https://openclaw.ai",
"X-Title": "OpenClaw",
});
});
it("returns a hidden-spec OpenAI attribution policy", () => {
expect(resolveProviderAttributionPolicy("openai", { OPENCLAW_VERSION: "2026.3.14" })).toEqual({
provider: "openai",
enabledByDefault: true,
verification: "vendor-hidden-api-spec",
hook: "request-headers",
reviewNote:
"OpenAI native traffic supports hidden originator/User-Agent attribution. Verified against the Codex wire contract.",
product: "OpenClaw",
version: "2026.3.14",
headers: {
originator: "openclaw",
"User-Agent": "openclaw/2026.3.14",
},
});
expect(resolveProviderAttributionHeaders("openai", { OPENCLAW_VERSION: "2026.3.14" })).toEqual({
originator: "openclaw",
"User-Agent": "openclaw/2026.3.14",
});
});
it("returns a hidden-spec OpenAI Codex attribution policy", () => {
expect(
resolveProviderAttributionPolicy("openai-codex", { OPENCLAW_VERSION: "2026.3.14" }),
).toEqual({
provider: "openai-codex",
enabledByDefault: true,
verification: "vendor-hidden-api-spec",
hook: "request-headers",
reviewNote:
"OpenAI Codex ChatGPT-backed traffic supports the same hidden originator/User-Agent attribution contract.",
product: "OpenClaw",
version: "2026.3.14",
headers: {
originator: "openclaw",
"User-Agent": "openclaw/2026.3.14",
},
});
});
it("lists the current attribution support matrix", () => {
expect(
listProviderAttributionPolicies({ OPENCLAW_VERSION: "2026.3.14" }).map((policy) => [
policy.provider,
policy.enabledByDefault,
policy.verification,
policy.hook,
]),
).toEqual([
["openrouter", true, "vendor-documented", "request-headers"],
["openai", true, "vendor-hidden-api-spec", "request-headers"],
["openai-codex", true, "vendor-hidden-api-spec", "request-headers"],
["anthropic", false, "vendor-sdk-hook-only", "default-headers"],
["google", false, "vendor-sdk-hook-only", "user-agent-extra"],
["groq", false, "vendor-sdk-hook-only", "default-headers"],
["mistral", false, "vendor-sdk-hook-only", "custom-user-agent"],
["together", false, "vendor-sdk-hook-only", "default-headers"],
]);
});
});