diff --git a/src/main/node-runtime/pinned-runtime-materializer-node.test.ts b/src/main/node-runtime/pinned-runtime-materializer-node.test.ts index 440072ece77..0910c99b996 100644 --- a/src/main/node-runtime/pinned-runtime-materializer-node.test.ts +++ b/src/main/node-runtime/pinned-runtime-materializer-node.test.ts @@ -156,39 +156,28 @@ describe('pinned runtime archive request ownership', () => { // used to see only Chromium's bare `net::ERR_CONNECTION_REFUSED` under "couldn't start its server". describe('a pinned runtime archive this computer cannot download', () => { it('says this computer could not reach the download, and where an offline copy goes', async () => { - vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] }) - try { - const cacheRoot = join(root, 'cache') - const fetcher = vi.fn(async () => { - throw new Error('net::ERR_CONNECTION_REFUSED') - }) - let settled = false - const failure = materializeNodeRuntimeArchive(TARGET, cacheRoot, { fetcher }).catch( - (error: unknown) => error - ) - void failure.finally(() => (settled = true)) - // Real file I/O runs between the retry pauses, so yield to it before skipping each pause. - for (let turn = 0; !settled && turn < 1_000; turn += 1) { - await new Promise((resolve) => setImmediate(resolve)) - vi.advanceTimersByTime(10_000) - } - const error = await failure - expect(fetcher).toHaveBeenCalledTimes(4) - const offlinePath = join(cacheRoot, 'node', 'archives', originalAsset.archive) + const cacheRoot = join(root, 'cache') + const fetcher = vi.fn(async () => { + throw new Error('net::ERR_CONNECTION_REFUSED') + }) + // Every attempt still runs; only the pauses between them are skipped. + const error = await materializeNodeRuntimeArchive(TARGET, cacheRoot, { + fetcher, + retryDelaysMs: [0, 0, 0] + }).catch((caught: unknown) => caught) + expect(fetcher).toHaveBeenCalledTimes(4) + const offlinePath = join(cacheRoot, 'node', 'archives', originalAsset.archive) - expect(error).toBeInstanceOf(Error) - const message = error instanceof Error ? error.message : '' - expect(message).toContain("This computer couldn't download Node from https://") - expect(message).toContain(`/v${NODE_RUNTIME_PIN.version}/${originalAsset.archive}`) - expect(message).toContain('(net::ERR_CONNECTION_REFUSED)') - expect(message).toContain("the host doesn't need internet access") - expect(message).toContain(`copy it to ${offlinePath}`) - expect(error instanceof Error ? error.cause : null).toMatchObject({ - message: 'net::ERR_CONNECTION_REFUSED' - }) - } finally { - vi.useRealTimers() - } + expect(error).toBeInstanceOf(Error) + const message = error instanceof Error ? error.message : '' + expect(message).toContain("This computer couldn't download Node from https://") + expect(message).toContain(`/v${NODE_RUNTIME_PIN.version}/${originalAsset.archive}`) + expect(message).toContain('(net::ERR_CONNECTION_REFUSED)') + expect(message).toContain("the host doesn't need internet access") + expect(message).toContain(`copy it to ${offlinePath}`) + expect(error instanceof Error ? error.cause : null).toMatchObject({ + message: 'net::ERR_CONNECTION_REFUSED' + }) }) it.skipIf(process.platform === 'win32')( diff --git a/src/main/node-runtime/pinned-runtime-materializer.ts b/src/main/node-runtime/pinned-runtime-materializer.ts index 3a94e584001..133f8ecb55e 100644 --- a/src/main/node-runtime/pinned-runtime-materializer.ts +++ b/src/main/node-runtime/pinned-runtime-materializer.ts @@ -21,6 +21,7 @@ import { export type PinnedRuntimeMaterializeOptions = { fetcher?: MainHttpClient['fetch'] signal?: AbortSignal + retryDelaysMs?: readonly number[] } export type PinnedRuntimeArchive = { @@ -69,7 +70,8 @@ export async function materializeVerifiedRuntimeArchive( archive, downloaded, options.fetcher ?? getMainHttpClient().fetch, - options.signal + options.signal, + options.retryDelaysMs ).catch((error: unknown) => { throw unreachableArchiveError(archive, cached.path, error, options.signal) }) @@ -138,7 +140,8 @@ export async function materializeCachedRuntimeExecutable( runtime, archivePath, options.fetcher ?? getMainHttpClient().fetch, - options.signal + options.signal, + options.retryDelaysMs ) options.signal?.throwIfAborted() const extractedDir = join(temporaryDir, 'extracted')