import { describe, it, expect, vi, beforeEach } from 'vitest'; import { createServer } from 'node:net'; // Mock the docker-client module to avoid actual Docker calls vi.mock('./docker-client.js', () => ({ getPreviewPorts: vi.fn(), })); import { allocatePort } from './port-allocator.js'; import { getPreviewPorts } from './docker-client.js'; const mockedGetPreviewPorts = vi.mocked(getPreviewPorts); describe('allocatePort', () => { beforeEach(() => { vi.clearAllMocks(); }); it('returns BASE_PORT (9100) when no ports are in use', async () => { mockedGetPreviewPorts.mockResolvedValue([]); const port = await allocatePort(); expect(port).toBe(9100); }); it('skips ports already used by previews', async () => { mockedGetPreviewPorts.mockResolvedValue([9100, 9101]); const port = await allocatePort(); expect(port).toBe(9102); }); it('skips non-contiguous used ports', async () => { mockedGetPreviewPorts.mockResolvedValue([9100, 9103]); const port = await allocatePort(); expect(port).toBe(9101); }); it('skips a port that is bound by another process', async () => { mockedGetPreviewPorts.mockResolvedValue([]); // Bind port 9100 to simulate external use const server = createServer(); await new Promise((resolve) => { server.listen(9100, '127.0.0.1', () => resolve()); }); try { const port = await allocatePort(); expect(port).toBe(9101); } finally { await new Promise((resolve) => { server.close(() => resolve()); }); } }); });