From 326b831c41d2634d2e86786023a01d1c710b36de Mon Sep 17 00:00:00 2001 From: Sean McLeod Date: Mon, 12 Oct 2026 17:13:08 +1100 Subject: [PATCH] Fix waitPort to ensure FRAMEWORK_PORT_TIMEOUT_MS is respected --- src/lib/wait-port.ts | 6 +-- src/utils/framework-server.ts | 1 - tests/unit/lib/wait-port.test.ts | 63 ++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 6 deletions(-) create mode 100644 tests/unit/lib/wait-port.test.ts diff --git a/src/lib/wait-port.ts b/src/lib/wait-port.ts index c3aa66e6b03..2d31c1534b9 100644 --- a/src/lib/wait-port.ts +++ b/src/lib/wait-port.ts @@ -4,12 +4,10 @@ export const waitPort = async ( port: number, host: string, timeout: number, - maxRetries?: number, ): Promise<{ open: boolean; ipVersion?: 4 | 6 }> => { const startTime = Date.now() - const retries = maxRetries ?? Math.ceil(timeout / 2000) - for (let attempt = 0; attempt < retries; attempt++) { + for (let attempt = 0; ; attempt++) { if (Date.now() - startTime > timeout) { return { open: false } } @@ -52,6 +50,4 @@ export const waitPort = async ( continue } } - - return { open: false } } diff --git a/src/utils/framework-server.ts b/src/utils/framework-server.ts index cfba2e19352..db4107c1223 100644 --- a/src/utils/framework-server.ts +++ b/src/utils/framework-server.ts @@ -61,7 +61,6 @@ export const startFrameworkServer = async function ({ settings.frameworkPort!, 'localhost', FRAMEWORK_PORT_TIMEOUT_MS, - 20, ) const timerId = setTimeout(() => { diff --git a/tests/unit/lib/wait-port.test.ts b/tests/unit/lib/wait-port.test.ts new file mode 100644 index 00000000000..859b361d037 --- /dev/null +++ b/tests/unit/lib/wait-port.test.ts @@ -0,0 +1,63 @@ +import net from 'node:net' + +import { afterEach, describe, expect, test } from 'vitest' + +import { waitPort } from '../../../src/lib/wait-port.js' + +const getFreePort = () => + new Promise((resolve, reject) => { + const server = net.createServer() + server.once('error', reject) + server.listen(0, () => { + const { port } = server.address() as net.AddressInfo + server.close(() => { + resolve(port) + }) + }) + }) + +describe('waitPort', () => { + const servers: net.Server[] = [] + + afterEach(async () => { + await Promise.all(servers.splice(0).map((server) => new Promise((resolve) => server.close(resolve)))) + }) + + test('resolves as open when the port starts listening later but within the timeout', async () => { + const port = await getFreePort() + setTimeout(() => { + const server = net.createServer() + servers.push(server) + server.listen(port) + }, 1500) + + const result = await waitPort(port, 'localhost', 5000) + + expect(result.open).toBe(true) + }) + + test('resolves as open without crashing when the server resets the connection', async () => { + const port = await getFreePort() + const server = net.createServer((socket) => { + socket.resetAndDestroy() + }) + servers.push(server) + await new Promise((resolve) => server.listen(port, resolve)) + + const result = await waitPort(port, 'localhost', 5000) + await new Promise((resolve) => setTimeout(resolve, 200)) + + expect(result.open).toBe(true) + }) + + test('keeps waiting until the timeout elapses when the port never opens', async () => { + const port = await getFreePort() + const timeout = 1500 + const startTime = Date.now() + + const result = await waitPort(port, 'localhost', timeout) + + expect(result).toEqual({ open: false }) + expect(Date.now() - startTime).toBeGreaterThanOrEqual(timeout) + }) +})