Files
LinkPort/scripts/verify-random-port.mjs

68 lines
3.1 KiB
JavaScript

// Real executable/listener smoke test with isolated single-file databases.
// No production data, desktop DPAPI material or upstream servers are used.
import assert from 'node:assert/strict';
import { spawn } from 'node:child_process';
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { basename, join, resolve, sep } from 'node:path';
import { setTimeout as delay } from 'node:timers/promises';
import { resolveProjectPath } from './project-paths.mjs';
assert(process.argv[2], 'Provide the freshly built linkport executable');
const binary = resolveProjectPath(process.argv[2]);
const temporaryRoot = resolve(tmpdir());
const directory = mkdtempSync(join(temporaryRoot, 'linkport-random-port-'));
const inherited = Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.toUpperCase().startsWith('LINKPORT_')));
const children = [];
async function stop(child) {
if (child.exitCode !== null || child.signalCode !== null) return;
await new Promise(resolve => { child.once('exit', resolve); child.kill(); });
}
async function start(name, address) {
const env = {
...inherited, LINKPORT_AUTH_ENABLED: 'false',
LINKPORT_DATA: join(directory, name, 'test.sqlite'), LINKPORT_NO_BROWSER: '1',
};
if (address !== undefined) env.LINKPORT_HTTP_ADDR = address;
const child = spawn(binary, [], {cwd: directory, env, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe']});
children.push(child);
let output = '', failure;
child.stdout.on('data', data => { output += data.toString(); });
child.stderr.on('data', data => { output += data.toString(); });
child.on('error', error => { failure = error; });
for (let i = 0; i < 200; i++) {
if (failure) throw failure;
assert(child.exitCode === null, `Startup failed: ${output}`);
const match = /http:\/\/127\.0\.0\.1:(\d+)/.exec(output);
if (match) {
const url = new URL(match[0]);
assert(Number(url.port) > 0, 'Startup must report the bound port, not port 0');
const health = await fetch(`${url.origin}/healthz`, {signal: AbortSignal.timeout(5000)});
assert.equal(health.status, 200);
const page = await fetch(url, {signal: AbortSignal.timeout(5000)});
assert.equal(page.status, 200);
assert.match(await page.text(), /<html/i);
return {child, url};
}
await delay(50);
}
throw new Error(`Startup did not report its actual URL: ${output}`);
}
try {
const first = await start('first');
const second = await start('second');
assert.notEqual(first.url.port, second.url.port, 'Simultaneous default launches must not collide');
await stop(first.child);
const fixed = await start('first', first.url.host);
assert.equal(fixed.url.origin, first.url.origin, 'Explicit fixed address must be respected');
console.log(`Random-port startup verified: ${first.url.origin}, ${second.url.origin}; fixed-port override and UI/health endpoints passed.`);
} finally {
await Promise.all(children.map(stop));
const target = resolve(directory);
assert(target.startsWith(temporaryRoot + sep) && basename(target).startsWith('linkport-random-port-'), 'Unexpected cleanup path');
rmSync(target, {recursive: true, force: true});
}