/** * Tests for findAvailablePort utility * Critical for port discovery reliability */ import { findAvailablePort, findAvailablePortSequential, isPortAvailable } from '../findAvailablePort'; import net from 'node:net'; describe('findAvailablePort (enhanced with randomized search)', () => { it('should find an available port within configured range', async () => { const port = await findAvailablePort({ minPort: 9000, maxPort: 9100 }); expect(port).toBeGreaterThanOrEqual(9000); expect(port).toBeLessThanOrEqual(9100); }); it('should use default port range when no options provided', async () => { const port = await findAvailablePort(); expect(port).toBeGreaterThanOrEqual(2000); expect(port).toBeLessThanOrEqual(2500); }); it('should retry when the random port is in use', async () => { const occupiedPort = 9200; const server = net.createServer(); await new Promise((resolve, reject) => { server.once('error', reject); server.listen(occupiedPort, '127.0.0.1', resolve); }); try { // Use custom random that always picks the occupied port first const createSequenceRandom = (values: number[]): (() => number) => { let index = 0; return () => { const value = values[index] ?? values[values.length - 1] ?? 0; index = Math.min(index + 1, values.length - 1); return value; }; }; const port = await findAvailablePort({ minPort: 9200, maxPort: 9300, random: createSequenceRandom([0, 0.5]) // Will try port 9200 first, then retry }); expect(port).not.toBe(occupiedPort); expect(port).toBeGreaterThanOrEqual(9200); expect(port).toBeLessThanOrEqual(9300); } finally { await new Promise((resolve) => server.close(() => resolve())); } }); it('should throw error when no ports available in range', async () => { // Block all ports in the range by creating servers on them const blockers = [net.createServer(), net.createServer()]; await Promise.all([ new Promise((resolve, reject) => blockers[0].once('error', reject).listen(9999, '0.0.0.0', resolve) ), new Promise((resolve, reject) => blockers[1].once('error', reject).listen(10000, '0.0.0.0', resolve) ) ]); try { await expect( findAvailablePort({ minPort: 9999, maxPort: 10000 }) ).rejects.toThrow(/No available port found/); } finally { await Promise.all( blockers.map( (server) => new Promise((resolve) => server.close(() => resolve())) ) ); } }); it('should validate port range', async () => { await expect( findAvailablePort({ minPort: 100000, maxPort: 100000 }) ).rejects.toThrow(/must be valid integers/); await expect( findAvailablePort({ minPort: 5000, maxPort: 4000 }) ).rejects.toThrow(/Minimum port must be less than or equal to maximum port/); }); }); describe('findAvailablePortSequential (legacy API)', () => { it('should find an available port searching up', async () => { const port = await findAvailablePortSequential(9000, 'up'); expect(port).toBeGreaterThanOrEqual(9000); expect(port).toBeLessThan(10000); }); it('should find an available port searching down', async () => { const port = await findAvailablePortSequential(9000, 'down'); expect(port).toBeGreaterThanOrEqual(2000); expect(port).toBeLessThanOrEqual(9000); }); it('should handle port bounds correctly', async () => { // Test down direction doesn't go below 2000 const port = await findAvailablePortSequential(2001, 'down'); expect(port).toBeGreaterThanOrEqual(2000); expect(port).toBeLessThanOrEqual(2001); }); it('should return different ports on consecutive calls', async () => { const port1 = await findAvailablePortSequential(8000, 'up'); const port2 = await findAvailablePortSequential(port1 + 1, 'up'); expect(port2).toBeGreaterThan(port1); }); }); describe('isPortAvailable', () => { it('should return false for occupied port', async () => { const port = 9500; const server = net.createServer(); await new Promise((resolve, reject) => { server.once('error', reject); server.listen(port, '127.0.0.1', resolve); }); try { const available = await isPortAvailable(port, '127.0.0.1'); expect(available).toBe(false); } finally { await new Promise((resolve) => server.close(() => resolve())); } }); it('should return true for available port', async () => { const available = await isPortAvailable(9501, '127.0.0.1'); expect(available).toBe(true); }); });