/** * T-04 fetch-interceptor contract tests. * * Covers `createOmniRouteFetchInterceptor` (URL-prefix gating, header merge, * Content-Type defaulting, input-shape polymorphism) plus the loader * integration that wires it into the AuthHook return shape. * * Strategy: replace `globalThis.fetch` with a closure-based recorder for the * duration of each test (saved-and-restored in try/finally — node:test has * no built-in spy/restore lifecycle). The recorder captures `(input, init)` * as observed by the wrapped global call so we can assert on what was * forwarded after header injection. */ import test from "node:test"; import assert from "node:assert/strict"; import { createOmniRouteAuthHook, createOmniRouteFetchInterceptor } from "../src/index.js"; type FetchCall = { input: Parameters[0]; init?: RequestInit }; function installFetchRecorder(response: Response = new Response("ok")) { const calls: FetchCall[] = []; const original = globalThis.fetch; globalThis.fetch = (async (input: any, init?: any) => { calls.push({ input, init }); return response; }) as typeof fetch; const restore = () => { globalThis.fetch = original; }; return { calls, restore }; } const BASE = "https://or.example.com/v1"; const KEY = "sk-test-fetch"; test("createOmniRouteFetchInterceptor: targets baseURL → Authorization header injected", async () => { const { calls, restore } = installFetchRecorder(); try { const f = createOmniRouteFetchInterceptor({ apiKey: KEY, baseURL: BASE }); await f(`${BASE}/chat/completions`, { method: "POST", body: JSON.stringify({ x: 1 }), }); assert.equal(calls.length, 1); const sent = calls[0]!; const sentHeaders = new Headers((sent.init as RequestInit).headers); assert.equal(sentHeaders.get("Authorization"), `Bearer ${KEY}`); } finally { restore(); } }); test("createOmniRouteFetchInterceptor: targets baseURL → Authorization OVERRIDES caller-supplied Bearer", async () => { const { calls, restore } = installFetchRecorder(); try { const f = createOmniRouteFetchInterceptor({ apiKey: KEY, baseURL: BASE }); await f(`${BASE}/chat/completions`, { method: "POST", body: "{}", headers: { Authorization: "Bearer attacker-key" }, }); const sent = calls[0]!; const sentHeaders = new Headers((sent.init as RequestInit).headers); // We own the apiKey for this provider — caller-supplied Bearer must lose. assert.equal(sentHeaders.get("Authorization"), `Bearer ${KEY}`); } finally { restore(); } }); test("createOmniRouteFetchInterceptor: targets baseURL + body → Content-Type defaults to application/json", async () => { const { calls, restore } = installFetchRecorder(); try { const f = createOmniRouteFetchInterceptor({ apiKey: KEY, baseURL: BASE }); await f(`${BASE}/chat/completions`, { method: "POST", body: JSON.stringify({ m: "x" }), }); const sent = calls[0]!; const sentHeaders = new Headers((sent.init as RequestInit).headers); assert.equal(sentHeaders.get("Content-Type"), "application/json"); } finally { restore(); } }); test("createOmniRouteFetchInterceptor: caller-set Content-Type is NOT overwritten", async () => { const { calls, restore } = installFetchRecorder(); try { const f = createOmniRouteFetchInterceptor({ apiKey: KEY, baseURL: BASE }); await f(`${BASE}/v2/whatever`, { method: "POST", body: "raw", headers: { "Content-Type": "text/plain; charset=utf-8" }, }); const sent = calls[0]!; const sentHeaders = new Headers((sent.init as RequestInit).headers); assert.equal(sentHeaders.get("Content-Type"), "text/plain; charset=utf-8"); } finally { restore(); } }); test("createOmniRouteFetchInterceptor: non-baseURL host → passthrough, no Authorization injected", async () => { const { calls, restore } = installFetchRecorder(); try { const f = createOmniRouteFetchInterceptor({ apiKey: KEY, baseURL: BASE }); await f("https://third-party.example.org/v1/chat", { method: "POST", body: "{}", headers: { "X-Caller": "yes" }, }); const sent = calls[0]!; // Init forwarded verbatim — no header injection. const sentHeaders = new Headers((sent.init as RequestInit | undefined)?.headers); assert.equal(sentHeaders.get("Authorization"), null, "MUST NOT leak apiKey"); assert.equal(sentHeaders.get("X-Caller"), "yes"); } finally { restore(); } }); test("createOmniRouteFetchInterceptor: refuses suffix-spoof — `${base}-attacker.evil` does NOT match baseURL", async () => { const { calls, restore } = installFetchRecorder(); try { const f = createOmniRouteFetchInterceptor({ apiKey: KEY, baseURL: BASE }); // baseURL is `https://or.example.com/v1`. A spoofed // `https://or.example.com/v1-attacker.evil/chat` shares the literal prefix // but is NOT under our origin path — must be treated as passthrough. await f("https://or.example.com/v1-attacker.evil/chat", { method: "POST", body: "{}", }); const sent = calls[0]!; const sentHeaders = new Headers((sent.init as RequestInit | undefined)?.headers); assert.equal(sentHeaders.get("Authorization"), null); } finally { restore(); } }); test("createOmniRouteFetchInterceptor: URL object input is handled", async () => { const { calls, restore } = installFetchRecorder(); try { const f = createOmniRouteFetchInterceptor({ apiKey: KEY, baseURL: BASE }); await f(new URL(`${BASE}/models`), {}); const sent = calls[0]!; const sentHeaders = new Headers((sent.init as RequestInit).headers); assert.equal(sentHeaders.get("Authorization"), `Bearer ${KEY}`); } finally { restore(); } }); test("createOmniRouteFetchInterceptor: Request input is handled (reads .url)", async () => { const { calls, restore } = installFetchRecorder(); try { const f = createOmniRouteFetchInterceptor({ apiKey: KEY, baseURL: BASE }); const req = new Request(`${BASE}/chat/completions`, { method: "POST", body: JSON.stringify({ a: 1 }), headers: { "X-Caller": "preserved" }, }); await f(req); const sent = calls[0]!; // The interceptor forwards the original Request as `input` but layers our // headers into the `init`. We assert against the init view since fetch() // resolves headers from init first when both are present. const sentHeaders = new Headers((sent.init as RequestInit).headers); assert.equal(sentHeaders.get("Authorization"), `Bearer ${KEY}`); assert.equal( sentHeaders.get("X-Caller"), "preserved", "Request-attached headers must survive the merge" ); } finally { restore(); } }); test("createOmniRouteFetchInterceptor: trailing slash in baseURL is normalized", async () => { const { calls, restore } = installFetchRecorder(); try { const f = createOmniRouteFetchInterceptor({ apiKey: KEY, baseURL: `${BASE}////`, }); await f(`${BASE}/models`, {}); const sent = calls[0]!; const sentHeaders = new Headers((sent.init as RequestInit).headers); assert.equal(sentHeaders.get("Authorization"), `Bearer ${KEY}`); } finally { restore(); } }); test("createOmniRouteFetchInterceptor: GET without body does NOT set Content-Type", async () => { const { calls, restore } = installFetchRecorder(); try { const f = createOmniRouteFetchInterceptor({ apiKey: KEY, baseURL: BASE }); await f(`${BASE}/models`); // no init at all const sent = calls[0]!; const sentHeaders = new Headers((sent.init as RequestInit).headers); assert.equal(sentHeaders.get("Authorization"), `Bearer ${KEY}`); assert.equal( sentHeaders.get("Content-Type"), null, "Content-Type should only default when a body exists" ); } finally { restore(); } }); // ---------------------------------------------------------------------------- // loader integration // ---------------------------------------------------------------------------- test("loader: returns fetch fn when apiKey + baseURL both present (via opts)", async () => { const hook = createOmniRouteAuthHook({ baseURL: BASE }); const result = await hook.loader!(async () => ({ type: "api", key: KEY }) as never, {} as never); assert.equal((result as { apiKey: string }).apiKey, KEY); assert.equal((result as { baseURL: string }).baseURL, BASE); assert.equal( typeof (result as { fetch?: unknown }).fetch, "function", "loader must wire fetch interceptor when baseURL resolves" ); }); test("loader: returns fetch fn when baseURL is stashed on the auth credential", async () => { // Some auth backends attach baseURL alongside the key (post-/connect flow). // The loader should pick it up even when plugin opts.baseURL is unset. const hook = createOmniRouteAuthHook(); const result = await hook.loader!( async () => ({ type: "api", key: KEY, baseURL: BASE }) as never, {} as never ); assert.equal((result as { baseURL?: string }).baseURL, BASE); assert.equal(typeof (result as { fetch?: unknown }).fetch, "function"); }); test("loader: omits fetch fn when baseURL missing (apiKey-only return)", async () => { const hook = createOmniRouteAuthHook(); // no baseURL opt const result = await hook.loader!(async () => ({ type: "api", key: KEY }) as never, {} as never); // Interceptor needs a baseURL to gate-keep; without one, fall back to // apiKey-only and let the SDK use its default fetch. assert.deepEqual(result, { apiKey: KEY }); }); test("loader integration: wired interceptor actually injects Bearer when invoked", async () => { // End-to-end: pull the fetch fn out of the loader return and exercise it, // proving the wiring matches the standalone interceptor's contract. const { calls, restore } = installFetchRecorder(); try { const hook = createOmniRouteAuthHook({ baseURL: BASE }); const result = await hook.loader!( async () => ({ type: "api", key: KEY }) as never, {} as never ); const wiredFetch = (result as { fetch: typeof fetch }).fetch; await wiredFetch(`${BASE}/v1/models`, {}); assert.equal(calls.length, 1); const sentHeaders = new Headers((calls[0]!.init as RequestInit).headers); assert.equal(sentHeaders.get("Authorization"), `Bearer ${KEY}`); } finally { restore(); } });