diff --git a/src/infra/net/proxy-fetch.test.ts b/src/infra/net/proxy-fetch.test.ts index 1be1488776f..e3a4f635702 100644 --- a/src/infra/net/proxy-fetch.test.ts +++ b/src/infra/net/proxy-fetch.test.ts @@ -92,6 +92,32 @@ function requireProxyFetch( return fetchFn; } +function requireUndiciFetchCall(index = 0): unknown[] { + const call = undiciFetch.mock.calls[index]; + if (!call) { + throw new Error(`expected undici fetch call at index ${index}`); + } + return call; +} + +function requireUndiciFetchInit(index = 0): Record { + const init = requireUndiciFetchCall(index)[1]; + if (!init || typeof init !== "object" || Array.isArray(init)) { + throw new Error(`expected undici fetch init at index ${index}`); + } + return init as Record; +} + +function requireHeadersInit(value: unknown, label: string): HeadersInit { + if (value === undefined || value instanceof Headers || Array.isArray(value)) { + return value as HeadersInit; + } + if (value && typeof value === "object") { + return value as HeadersInit; + } + throw new Error(`expected ${label} headers`); +} + function clearProxyEnv(): void { for (const key of PROXY_ENV_KEYS) { delete process.env[key]; @@ -128,9 +154,10 @@ describe("makeProxyFetch", () => { expect(proxyAgentSpy).toHaveBeenCalledWith(proxyUrl); expect(undiciFetch).toHaveBeenCalledOnce(); - const [input, init] = undiciFetch.mock.calls[0] ?? []; + const [input] = requireUndiciFetchCall(); + const init = requireUndiciFetchInit(); expect(input).toBe("https://api.example.com/v1/audio"); - expect(init?.dispatcher).toBe(getLastAgent()); + expect(init.dispatcher).toBe(getLastAgent()); }); it("reuses the same ProxyAgent across calls", async () => { @@ -139,9 +166,9 @@ describe("makeProxyFetch", () => { const proxyFetch = makeProxyFetch("http://proxy.test:8080"); await proxyFetch("https://api.example.com/one"); - const firstDispatcher = undiciFetch.mock.calls[0]?.[1]?.dispatcher; + const firstDispatcher = requireUndiciFetchInit().dispatcher; await proxyFetch("https://api.example.com/two"); - const secondDispatcher = undiciFetch.mock.calls[1]?.[1]?.dispatcher; + const secondDispatcher = requireUndiciFetchInit(1).dispatcher; expect(proxyAgentSpy).toHaveBeenCalledOnce(); expect(secondDispatcher).toBe(firstDispatcher); @@ -164,13 +191,13 @@ describe("makeProxyFetch", () => { body: form, }); - const passedInit = undiciFetch.mock.calls[0]?.[1]; - expect(passedInit?.body).toBeInstanceOf(MockUndiciFormData); - const passedBody = passedInit?.body as InstanceType; + const passedInit = requireUndiciFetchInit(); + expect(passedInit.body).toBeInstanceOf(MockUndiciFormData); + const passedBody = passedInit.body as InstanceType; expect(passedBody.get("model")).toBe("whisper-1"); expect(passedBody.get("file")).toBeInstanceOf(Blob); expect(passedBody.entriesList.find(([key]) => key === "file")?.[2]).toBe("voice.ogg"); - const sentHeaders = new Headers(passedInit?.headers); + const sentHeaders = new Headers(requireHeadersInit(passedInit.headers, "FormData proxy")); expect(sentHeaders.has("content-length")).toBe(false); expect(sentHeaders.has("content-type")).toBe(false); }); @@ -186,7 +213,7 @@ describe("makeProxyFetch", () => { body, }); - expect(undiciFetch.mock.calls[0]?.[1]?.body).toBe(body); + expect(requireUndiciFetchInit().body).toBe(body); }); it("drops symbol metadata from plain header dictionaries before undici fetch", async () => { @@ -207,10 +234,12 @@ describe("makeProxyFetch", () => { body: "{}", }); - const passedHeaders = undiciFetch.mock.calls[0]?.[1]?.headers; + const passedHeaders = requireUndiciFetchInit().headers; expect(passedHeaders).not.toBe(headers); expect(Object.getOwnPropertySymbols(passedHeaders as object)).toStrictEqual([]); - expect(new Headers(passedHeaders).get("content-type")).toBe("application/json"); + expect( + new Headers(requireHeadersInit(passedHeaders, "plain dictionary proxy")).get("content-type"), + ).toBe("application/json"); expect(Object.getOwnPropertySymbols(headers)).toHaveLength(1); }); @@ -226,7 +255,7 @@ describe("makeProxyFetch", () => { body: form as unknown as BodyInit, }); - expect(undiciFetch.mock.calls[0]?.[1]?.body).toBe(form); + expect(requireUndiciFetchInit().body).toBe(form); }); it("converts FormData-like bodies from another implementation", async () => { @@ -245,9 +274,9 @@ describe("makeProxyFetch", () => { body: formLike as unknown as BodyInit, }); - const passedInit = undiciFetch.mock.calls[0]?.[1]; - expect(passedInit?.body).toBeInstanceOf(MockUndiciFormData); - expect(passedInit?.body.get("model")).toBe("whisper-1"); + const passedBody = requireUndiciFetchInit().body; + expect(passedBody).toBeInstanceOf(MockUndiciFormData); + expect((passedBody as InstanceType).get("model")).toBe("whisper-1"); }); }); @@ -302,9 +331,10 @@ describe("resolveProxyFetchFromEnv", () => { await fetchFn("https://api.example.com"); expect(undiciFetch).toHaveBeenCalledOnce(); - const [input, init] = undiciFetch.mock.calls[0] ?? []; + const [input] = requireUndiciFetchCall(); + const init = requireUndiciFetchInit(); expect(input).toBe("https://api.example.com"); - expect(init?.dispatcher).toBe(EnvHttpProxyAgent.lastCreated); + expect(init.dispatcher).toBe(EnvHttpProxyAgent.lastCreated); }); it("converts global FormData bodies when using proxy env fetch", async () => { @@ -326,10 +356,12 @@ describe("resolveProxyFetchFromEnv", () => { body: form, }); - const passedInit = undiciFetch.mock.calls[0]?.[1]; - expect(passedInit?.body).toBeInstanceOf(MockUndiciFormData); - expect(passedInit?.body.get("model")).toBe("test-model"); - expect(passedInit?.body.get("file")).toBeInstanceOf(Blob); + const passedBody = requireUndiciFetchInit().body; + expect(passedBody).toBeInstanceOf(MockUndiciFormData); + expect((passedBody as InstanceType).get("model")).toBe("test-model"); + expect((passedBody as InstanceType).get("file")).toBeInstanceOf( + Blob, + ); }); it("returns proxy fetch when HTTP_PROXY is set", () => { diff --git a/src/plugin-sdk/test-helpers/provider-http-mocks.ts b/src/plugin-sdk/test-helpers/provider-http-mocks.ts index 6d6f6ea7a5f..73a8dcd9204 100644 --- a/src/plugin-sdk/test-helpers/provider-http-mocks.ts +++ b/src/plugin-sdk/test-helpers/provider-http-mocks.ts @@ -20,7 +20,7 @@ type ResolveProviderHttpRequestConfigResult = { dispatcherPolicy: undefined; }; -type AnyMock = Mock<(...args: any[]) => any>; +type AnyMock = Mock<(...args: unknown[]) => unknown>; interface ProviderHttpMocks { resolveApiKeyForProviderMock: Mock<() => Promise<{ apiKey: string }>>;