diff --git a/test/imf.test.ts b/test/imf.test.ts index fafa915..f8efb7b 100644 --- a/test/imf.test.ts +++ b/test/imf.test.ts @@ -195,15 +195,18 @@ describe("registry", () => { const indexUrl = `http://127.0.0.1:${port}/index.yaml` indexBody = `version: 1\nmodels:\n tiny-1.0:\n filename: tiny.zip\n url: http://127.0.0.1:${port}/tiny.zip\n sha256: ${sha}\n` - const store = new Map() + // store raw bytes: Response.clone() stream semantics differ across + // undici versions (node 20 vs 24) and are irrelevant to the contract + const store = new Map() const fakeCache = { async match(req: RequestInfo) { const key = String(req instanceof Request ? req.url : req) - return store.get(key)?.clone() + const bytes = store.get(key) + return bytes === undefined ? undefined : new Response(bytes) }, async put(req: RequestInfo, res: Response) { const key = String(req instanceof Request ? req.url : req) - store.set(key, res.clone()) + store.set(key, new Uint8Array(await res.arrayBuffer())) }, async delete(req: RequestInfo) { const key = String(req instanceof Request ? req.url : req) @@ -212,29 +215,38 @@ describe("registry", () => { } const g = globalThis as Record g["caches"] = { open: async () => fakeCache } - // simulate a browser host: no Node fs, so the Cache API path runs - const versions = process.versions as { node?: string } - const realNode = versions.node - delete versions.node + // Simulate a browser host by swapping the globalThis.process + // reference (the registry detects Node via globalThis). Deleting + // process.versions.node instead breaks node 20's bundled undici, + // which splits it inside fetch(). + const realProcess = globalThis.process + const withBrowserHost = async (fn: () => Promise): Promise => { + const gg = globalThis as { process?: unknown } + gg.process = { env: {}, versions: {} } + try { + return await fn() + } finally { + gg.process = realProcess + } + } try { process.env["SECRYST_CACHE"] = undefined - const first = await resolve("tiny-1.0", indexUrl) + const first = await withBrowserHost(() => resolve("tiny-1.0", indexUrl)) expect([...first.bytes]).toEqual([...fixtureZip]) expect(store.size).toBe(1) // channel dies; the cached copy serves, still sha-verified channelUp = false - const second = await resolve("tiny-1.0", indexUrl) + const second = await withBrowserHost(() => resolve("tiny-1.0", indexUrl)) expect([...second.bytes]).toEqual([...fixtureZip]) // a corrupted cache entry falls through to a fresh download channelUp = true const key = store.keys().next().value as string - store.set(key, new Response(new Uint8Array([1, 2, 3]))) - const third = await resolve("tiny-1.0", indexUrl) + store.set(key, new Uint8Array([1, 2, 3])) + const third = await withBrowserHost(() => resolve("tiny-1.0", indexUrl)) expect([...third.bytes]).toEqual([...fixtureZip]) } finally { - if (realNode !== undefined) versions.node = realNode delete g["caches"] delete process.env["SECRYST_CACHE"] await new Promise((r) => server.close(() => r()))