← back to Photo Cleanup
test: use ephemeral port + verify own server so stale processes never collide
a8c7c07ff9365c0d3c3cdca6a09d306712c8c335 · 2026-05-18 20:23:43 -0700 · SteveStudio2
Files touched
Diff
commit a8c7c07ff9365c0d3c3cdca6a09d306712c8c335
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date: Mon May 18 20:23:43 2026 -0700
test: use ephemeral port + verify own server so stale processes never collide
---
test.js | 39 +++++++++++++++++++++++++++++----------
1 file changed, 29 insertions(+), 10 deletions(-)
diff --git a/test.js b/test.js
index eb448be..a6dcee5 100644
--- a/test.js
+++ b/test.js
@@ -11,7 +11,19 @@ const os = require('os');
const path = require('path');
const cp = require('child_process');
-const PORT = 9778;
+// Grab a free ephemeral port so the test never collides with a stale server
+// from a prior run (a hardcoded port silently routes POSTs to a stranger).
+function freePort() {
+ const srv = http.createServer();
+ return new Promise((resolve, reject) => {
+ srv.once('error', reject);
+ srv.listen(0, '127.0.0.1', () => {
+ const p = srv.address().port;
+ srv.close(() => resolve(p));
+ });
+ });
+}
+
const TMP = fs.mkdtempSync(path.join(os.tmpdir(), 'photo-cleanup-test-'));
const SANDBOX = fs.mkdtempSync(path.join(os.homedir(), '.photo-cleanup-sandbox-'));
const CAND_FILE = path.join(TMP, 'candidates.txt');
@@ -29,13 +41,9 @@ const fakeC = path.join(SANDBOX, 'steve-test-c.png');
[fakeA, fakeB, fakeC].forEach(makeFakeImage);
fs.writeFileSync(CAND_FILE, [fakeA, fakeB, fakeC].join('\n') + '\n');
-const child = cp.spawn(process.execPath, [path.join(__dirname, 'server.js')], {
- env: { ...process.env, PORT: String(PORT), CANDIDATES: CAND_FILE },
- stdio: ['ignore', 'pipe', 'pipe'],
-});
+let PORT; // resolved to a free ephemeral port before the server spawns
+let child; // server child process
let serverOut = '';
-child.stdout.on('data', (c) => { serverOut += c.toString(); });
-child.stderr.on('data', (c) => { serverOut += c.toString(); });
function get(p) {
return new Promise((resolve, reject) => {
@@ -61,10 +69,14 @@ function post(p, payload) {
async function waitReady(retries = 20) {
for (let i = 0; i < retries; i++) {
- try { const r = await get('/api/list'); if (r.status === 200) return; } catch (_) {}
+ try {
+ const r = await get('/api/list');
+ // Confirm it is OUR server: it must serve the 3-file sandbox, not a stranger.
+ if (r.status === 200 && JSON.parse(r.body).rows.length === 3) return;
+ } catch (_) {}
await new Promise((r) => setTimeout(r, 150));
}
- throw new Error('server never came up\n' + serverOut);
+ throw new Error('server never came up (or wrong server on port)\n' + serverOut);
}
const tests = [];
@@ -167,6 +179,13 @@ t('Name sort produces deterministic ordering for the sandbox', async () => {
(async () => {
try {
+ PORT = await freePort();
+ child = cp.spawn(process.execPath, [path.join(__dirname, 'server.js')], {
+ env: { ...process.env, PORT: String(PORT), CANDIDATES: CAND_FILE },
+ stdio: ['ignore', 'pipe', 'pipe'],
+ });
+ child.stdout.on('data', (c) => { serverOut += c.toString(); });
+ child.stderr.on('data', (c) => { serverOut += c.toString(); });
await waitReady();
let pass = 0, fail = 0;
for (const test of tests) {
@@ -176,7 +195,7 @@ t('Name sort produces deterministic ordering for the sandbox', async () => {
console.log(`\n${pass}/${tests.length} photo-cleanup tests passed`);
process.exitCode = fail ? 1 : 0;
} finally {
- child.kill();
+ if (child) child.kill();
try { fs.rmSync(SANDBOX, { recursive: true, force: true }); } catch (_) {}
try { fs.rmSync(TMP, { recursive: true, force: true }); } catch (_) {}
}
← 04d578c photo-cleanup: web reviewer for 42-file Steve/Abrams candida
·
back to Photo Cleanup
·
server: add timeout to sips dimension probe + always clean u f1d9287 →