From 42f6de850995205165c32a1e625a41682a834537 Mon Sep 17 00:00:00 2001 From: JPeer264 Date: Wed, 13 May 2026 15:22:20 +0200 Subject: [PATCH] test(cloudflare): Retry until ready --- .../cloudflare-integration-tests/runner.ts | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/dev-packages/cloudflare-integration-tests/runner.ts b/dev-packages/cloudflare-integration-tests/runner.ts index 0d19e2e4384b..2024f67a3e6b 100644 --- a/dev-packages/cloudflare-integration-tests/runner.ts +++ b/dev-packages/cloudflare-integration-tests/runner.ts @@ -18,6 +18,29 @@ export function cleanupChildProcesses(): void { process.on('exit', cleanupChildProcesses); +// Wrangler can report "Ready" before it can actually handle requests. +// This retries fetch on connection errors to handle this race condition. +async function fetchWithRetry(url: string, init: RequestInit, maxRetries = 10, retryDelayMs = 200): Promise { + for (let attempt = 0; attempt < maxRetries; attempt++) { + try { + return await fetch(url, init); + } catch (e) { + const isConnectionError = + e instanceof Error && (e.message.includes('ECONNREFUSED') || e.message.includes('fetch failed')); + + if (isConnectionError && attempt < maxRetries - 1) { + if (process.env.DEBUG) log(`Request failed, retrying (attempt ${attempt + 1}/${maxRetries})...`); + await new Promise(r => setTimeout(r, retryDelayMs)); + continue; + } + + throw e; + } + } + + throw new Error('fetchWithRetry: unreachable'); +} + function deferredPromise( done?: () => void, ): { resolve: (val: T) => void; reject: (reason?: unknown) => void; promise: Promise } { @@ -316,7 +339,7 @@ export function createRunner(...paths: string[]) { if (process.env.DEBUG) log('making request', method, url, headers, body); try { - const res = await fetch(url, { headers, method, body }); + const res = await fetchWithRetry(url, { headers, method, body }); if (!res.ok) { if (!expectError) {