Files
Codeman/test/webserver-bound-port.test.ts
T

46 lines
1.8 KiB
TypeScript
Raw Permalink Normal View History

/**
* @fileoverview `new WebServer(0, …)`: the OS picks the port and every reader of
* `this.port` sees that number, not the 0 that was asked for (#440).
*
* Port: ephemeral.
*/
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import type { AddressInfo } from 'node:net';
import { WebServer } from '../src/web/server.js';
describe('WebServer on an ephemeral port', () => {
let server: WebServer;
/** Built BEFORE start(), as setupRoutes() builds it, so a by-value snapshot would read 0. */
let ctx: { port: number };
const savedApiUrl = process.env.CODEMAN_API_URL;
/** The socket's own answer, so each test stands on its own instead of trusting boundPort. */
const socketPort = () =>
((server as unknown as { app: { server: { address(): AddressInfo } } }).app.server.address() as AddressInfo).port;
beforeAll(async () => {
server = new WebServer(0, false, true);
ctx = (server as unknown as { createRouteContext(): { port: number } }).createRouteContext();
await server.start();
});
afterAll(async () => {
await server.stop();
if (savedApiUrl === undefined) delete process.env.CODEMAN_API_URL;
else process.env.CODEMAN_API_URL = savedApiUrl;
});
it('reports the port the OS assigned', async () => {
expect(socketPort()).toBeGreaterThan(0);
expect(server.boundPort).toBe(socketPort());
const res = await fetch(`http://127.0.0.1:${server.boundPort}/api/status`);
expect(res.status).toBe(200);
});
it('the route context reads it live (the tunnel start gets the real port, not 0)', () => {
expect(ctx.port).toBe(socketPort());
});
it('exports the real port to the panes as CODEMAN_API_URL', () => {
expect(process.env.CODEMAN_API_URL).toBe(`http://127.0.0.1:${socketPort()}`);
});
});