import { describe, expect, it } from "vitest"; import { buildProviderRequestDispatcherPolicy, mergeProviderRequestOverrides, resolveProviderRequestPolicyConfig, resolveProviderRequestConfig, resolveProviderRequestHeaders, sanitizeConfiguredModelProviderRequest, sanitizeConfiguredProviderRequest, sanitizeRuntimeProviderRequestOverrides, } from "./provider-request-config.js"; describe("provider request config", () => { it("merges discovered, provider, and model headers in precedence order", () => { const resolved = resolveProviderRequestConfig({ provider: "custom-openai", api: "openai-responses", baseUrl: "https://proxy.example.com/v1", discoveredHeaders: { "X-Discovered": "1", "X-Shared": "discovered", }, providerHeaders: { "X-Provider": "2", "X-Shared": "provider", }, modelHeaders: { "X-Model": "3", "X-Shared": "model", }, capability: "llm", transport: "stream", }); expect(resolved.headers).toEqual({ "X-Discovered": "1", "X-Provider": "2", "X-Model": "3", "X-Shared": "model", }); }); it("surfaces authHeader intent without mutating headers yet", () => { const resolved = resolveProviderRequestConfig({ provider: "google", api: "google-generative-ai", baseUrl: "https://generativelanguage.googleapis.com/v1beta", authHeader: true, capability: "llm", transport: "stream", }); expect(resolved.auth).toEqual({ configured: false, mode: "authorization-bearer", injectAuthorizationHeader: true, }); expect(resolved.headers).toBeUndefined(); }); it("keeps future proxy and tls slots stable for current callers", () => { const resolved = resolveProviderRequestConfig({ provider: "openrouter", api: "openai-responses", baseUrl: "https://openrouter.ai/api/v1", capability: "llm", transport: "stream", }); expect(resolved.proxy).toEqual({ configured: false }); expect(resolved.tls).toEqual({ configured: false }); expect(resolved.policy.endpointClass).toBe("openrouter"); expect(resolved.policy.attributionProvider).toBe("openrouter"); expect(resolved.extraHeaders).toEqual({ configured: false, headers: undefined, }); }); it("normalizes transport overrides into auth, extra headers, proxy, and tls slots", () => { const resolved = resolveProviderRequestConfig({ provider: "custom-openai", api: "openai-responses", baseUrl: "https://proxy.example.com/v1", request: { headers: { "X-Tenant": "acme", }, auth: { mode: "header", headerName: "api-key", value: "secret", }, proxy: { mode: "explicit-proxy", url: "http://proxy.internal:8443", tls: { ca: "proxy-ca", }, }, tls: { cert: "client-cert", key: "client-key", serverName: "gateway.internal", }, }, capability: "llm", transport: "stream", }); expect(resolved.extraHeaders).toEqual({ configured: true, headers: { "X-Tenant": "acme", "api-key": "secret", }, }); expect(resolved.auth).toEqual({ configured: true, mode: "header", headerName: "api-key", value: "secret", injectAuthorizationHeader: false, }); expect(resolved.proxy).toEqual({ configured: true, mode: "explicit-proxy", proxyUrl: "http://proxy.internal:8443", tls: { configured: true, ca: "proxy-ca", }, }); expect(resolved.tls).toEqual({ configured: true, cert: "client-cert", key: "client-key", serverName: "gateway.internal", }); }); it("drops legacy Authorization when a custom auth header override is configured", () => { const resolved = resolveProviderRequestConfig({ provider: "custom-openai", api: "openai-responses", baseUrl: "https://proxy.example.com/v1", providerHeaders: { Authorization: "Bearer stale-token", "X-Tenant": "acme", }, request: { auth: { mode: "header", headerName: "api-key", value: "secret", }, }, capability: "llm", transport: "stream", }); expect(resolved.headers).toEqual({ "X-Tenant": "acme", "api-key": "secret", }); }); it("builds explicit proxy dispatcher policy from normalized transport config", () => { const resolved = resolveProviderRequestConfig({ provider: "custom-openai", baseUrl: "https://proxy.example.com/v1", request: { proxy: { mode: "explicit-proxy", url: "http://proxy.internal:8443", tls: { ca: "proxy-ca", }, }, tls: { cert: "client-cert", key: "client-key", }, }, }); expect(buildProviderRequestDispatcherPolicy(resolved)).toEqual({ mode: "explicit-proxy", proxyUrl: "http://proxy.internal:8443", proxyTls: { ca: "proxy-ca", }, }); }); it("does not copy target TLS into env proxy TLS", () => { const resolved = resolveProviderRequestConfig({ provider: "custom-openai", baseUrl: "https://proxy.example.com/v1", request: { proxy: { mode: "env-proxy", }, tls: { cert: "client-cert", key: "client-key", serverName: "gateway.internal", }, }, }); expect(buildProviderRequestDispatcherPolicy(resolved)).toEqual({ mode: "env-proxy", connect: { cert: "client-cert", key: "client-key", servername: "gateway.internal", }, }); }); it("rejects insecure TLS transport overrides", () => { expect(() => resolveProviderRequestConfig({ provider: "custom-openai", baseUrl: "https://proxy.example.com/v1", request: { tls: { insecureSkipVerify: true, }, }, }), ).toThrow(/insecureskipverify/i); }); it("rejects proxy and tls runtime auth overrides", () => { expect(() => sanitizeRuntimeProviderRequestOverrides({ headers: { "X-Tenant": "acme", }, proxy: { mode: "explicit-proxy", url: "http://proxy.internal:8443", }, }), ).toThrow(/runtime auth request overrides do not allow proxy or tls/i); }); it("sanitizes configured request overrides into runtime transport overrides", () => { expect( sanitizeConfiguredProviderRequest({ headers: { "X-Tenant": "acme", }, auth: { mode: "authorization-bearer", token: "secret", }, proxy: { mode: "explicit-proxy", url: "http://proxy.internal:8443", tls: { ca: "proxy-ca", }, }, tls: { cert: "client-cert", key: "client-key", serverName: "gateway.internal", }, }), ).toEqual({ headers: { "X-Tenant": "acme", }, auth: { mode: "authorization-bearer", token: "secret", }, proxy: { mode: "explicit-proxy", url: "http://proxy.internal:8443", tls: { ca: "proxy-ca", }, }, tls: { cert: "client-cert", key: "client-key", serverName: "gateway.internal", }, }); }); it("fails fast when configured request overrides still contain unresolved SecretRefs", () => { expect(() => sanitizeConfiguredProviderRequest({ headers: { "X-Tenant": { source: "env", provider: "default", id: "MEDIA_AUDIO_TENANT" }, }, auth: { mode: "authorization-bearer", token: { source: "env", provider: "default", id: "MEDIA_AUDIO_TOKEN" }, }, tls: { cert: { source: "env", provider: "default", id: "MEDIA_AUDIO_CERT" }, }, }), ).toThrow(/request\.(headers\.X-Tenant|auth\.token|tls\.cert): unresolved SecretRef/i); }); it("keeps model-provider transport overrides once the llm path can carry them", () => { expect( sanitizeConfiguredModelProviderRequest({ headers: { "X-Tenant": "acme", }, proxy: { mode: "explicit-proxy", url: "http://proxy.internal:8443", }, }), ).toEqual({ headers: { "X-Tenant": "acme", }, proxy: { mode: "explicit-proxy", url: "http://proxy.internal:8443", }, }); }); it("merges configured request overrides with later entries winning", () => { expect( mergeProviderRequestOverrides( { headers: { "X-Provider": "1", "X-Shared": "provider", }, auth: { mode: "authorization-bearer", token: "provider-token", }, }, { headers: { "X-Entry": "2", "X-Shared": "entry", }, auth: { mode: "header", headerName: "api-key", value: "entry-key", }, }, ), ).toEqual({ headers: { "X-Provider": "1", "X-Shared": "entry", "X-Entry": "2", }, auth: { mode: "header", headerName: "api-key", value: "entry-key", }, }); }); it("lets defaults override caller headers when requested", () => { const resolved = resolveProviderRequestHeaders({ provider: "openai", api: "openai-responses", baseUrl: "https://api.openai.com/v1", capability: "llm", transport: "stream", callerHeaders: { originator: "spoofed", "User-Agent": "spoofed/0.0.0", "X-Custom": "1", }, precedence: "defaults-win", }); expect(resolved).toMatchObject({ originator: "openclaw", version: expect.any(String), "User-Agent": expect.stringMatching(/^openclaw\//), "X-Custom": "1", }); }); it("lets caller headers override defaults when requested", () => { const resolved = resolveProviderRequestHeaders({ provider: "openrouter", api: "openai-completions", capability: "llm", transport: "stream", callerHeaders: { "HTTP-Referer": "https://example.com", "X-Custom": "1", }, precedence: "caller-wins", }); expect(resolved).toEqual({ "HTTP-Referer": "https://openclaw.ai", "X-OpenRouter-Title": "OpenClaw", "X-OpenRouter-Categories": "cli-agent", "X-Custom": "1", }); }); it("merges header names case-insensitively", () => { const resolved = resolveProviderRequestHeaders({ provider: "openai", api: "openai-responses", baseUrl: "https://api.openai.com/v1", capability: "llm", transport: "stream", callerHeaders: { "user-agent": "custom-agent/1.0", }, precedence: "caller-wins", }); expect( Object.keys(resolved ?? {}).filter((key) => key.toLowerCase() === "user-agent"), ).toHaveLength(1); expect(resolved?.["User-Agent"]).toMatch(/^openclaw\//); }); it("drops forbidden header keys while merging", () => { const resolved = resolveProviderRequestHeaders({ provider: "custom-openai", callerHeaders: { __proto__: "polluted", constructor: "polluted", "X-Custom": "1", } as Record, defaultHeaders: { prototype: "polluted", } as Record, }); expect(resolved).toEqual({ "X-Custom": "1", }); expect(Object.getPrototypeOf(resolved ?? {})).toBeNull(); }); it("unifies policy, capabilities, headers, base URL, and private-network posture", () => { const resolved = resolveProviderRequestPolicyConfig({ provider: "openai", api: "openai-responses", baseUrl: "https://api.openai.com/v1/", defaultBaseUrl: "https://fallback.example/v1/", callerHeaders: { "User-Agent": "custom-agent/1.0", "X-Custom": "1", }, providerHeaders: { authorization: "Bearer test-key", }, compat: { supportsStore: true, }, capability: "llm", transport: "stream", precedence: "defaults-win", }); expect(resolved.baseUrl).toBe("https://api.openai.com/v1"); expect(resolved.allowPrivateNetwork).toBe(true); expect(resolved.policy.endpointClass).toBe("openai-public"); expect(resolved.capabilities.allowsResponsesStore).toBe(true); expect(resolved.headers).toMatchObject({ authorization: "Bearer test-key", originator: "openclaw", version: expect.any(String), "User-Agent": expect.stringMatching(/^openclaw\//), "X-Custom": "1", }); }); });