import test from "node:test"; import assert from "node:assert/strict"; const originalFetch = globalThis.fetch; const originalTimeoutEnv = process.env.FETCH_TIMEOUT_MS; async function loadFetchTimeoutModule(tag) { return import(`../../src/shared/utils/fetchTimeout.ts?case=${tag}-${Date.now()}`); } test.afterEach(() => { globalThis.fetch = originalFetch; if (originalTimeoutEnv === undefined) { delete process.env.FETCH_TIMEOUT_MS; } else { process.env.FETCH_TIMEOUT_MS = originalTimeoutEnv; } }); test("fetchWithTimeout forwards options and exposes the configured timeout", async () => { process.env.FETCH_TIMEOUT_MS = "3210"; const mod = await loadFetchTimeoutModule("configured-timeout"); let seenUrl = null; let seenOptions = null; const expectedResponse = { ok: true, status: 204 }; globalThis.fetch = async (url, options) => { seenUrl = url; seenOptions = options; return expectedResponse; }; const response = await mod.fetchWithTimeout("https://example.test/ping", { method: "POST", headers: { "x-test": "1" }, }); assert.equal(mod.getConfiguredTimeout(), 3210); assert.equal(response, expectedResponse); assert.equal(seenUrl, "https://example.test/ping"); assert.equal(seenOptions.method, "POST"); assert.equal(seenOptions.headers["x-test"], "1"); assert.equal(seenOptions.signal instanceof AbortSignal, true); }); test("fetchWithTimeout converts both pre-aborted and externally aborted requests into FetchTimeoutError", async () => { const mod = await loadFetchTimeoutModule("abort"); const preAborted = new AbortController(); preAborted.abort(); globalThis.fetch = async (_url, options) => { assert.equal(options.signal.aborted, true); const error = new Error("aborted"); error.name = "AbortError"; throw error; }; await assert.rejects( mod.fetchWithTimeout("https://example.test/pre-aborted", { signal: preAborted.signal, timeoutMs: 9, }), (error) => { assert.equal(error instanceof mod.FetchTimeoutError, true); assert.equal((error as any).timeoutMs, 9); (assert as any).equal((error as any).url, "https://example.test/pre-aborted"); (assert as any).match((error as any).message, /timed out after 9ms/); return true; } ); const external = new AbortController(); let fetchSawSignal = false; globalThis.fetch = async (_url, options) => { fetchSawSignal = true; setTimeout(() => external.abort(), 0); await new Promise((resolve) => setTimeout(resolve, 5)); const error = new Error("aborted later"); error.name = "AbortError"; throw error; }; await assert.rejects( mod.fetchWithTimeout("https://example.test/external-abort", { signal: external.signal, timeoutMs: 15, }), (error) => { assert.equal(fetchSawSignal, true); assert.equal(error instanceof mod.FetchTimeoutError, true); assert.equal((error as any).timeoutMs, 15); assert.equal((error as any).url, "https://example.test/external-abort"); return true; } ); }); test("fetchWithTimeout rethrows non-timeout failures unchanged", async () => { const mod = await loadFetchTimeoutModule("generic-error"); const failure = new Error("network down"); globalThis.fetch = async () => { throw failure; }; await assert.rejects( mod.fetchWithTimeout("https://example.test/fail", { timeoutMs: 5 }), (error) => error === failure ); });