test(ci): OS-assigned ephemeral ports for subprocess suites — kill the port-collision flake (#176)

The subprocess-booting test suites hand-picked fixed ports in a cramped ~3955-4021 range, and
156-schedule-read-path deviated to a RANDOM port (3900 + rand%90) that overlapped those fixed
ports. Under CI load two servers could race on the same port, surfacing as flaky "no such table:
devices" / "FOREIGN KEY constraint failed" (a server answering a request against a half-migrated
or wrong DB). It's environmental — the suites pass locally and in isolation.

Fix: a shared test/helpers/free-port.js (bind :0 on loopback, read the OS-assigned port, release)
called in before() so every suite gets a guaranteed-unique ephemeral port — concurrent suites can
no longer collide, and no one has to hand-assign ports.

- Codemod converted 30 suites: const PORT = <fixed|random> -> let PORT (+ BASE) assigned via
  `PORT = await freePort()` at the top of before().
- 3 hand-fixed (different structure): 148-eviction-storm (lowercase `base`), boot-health (no
  before() — allocates PORT + a throwaway SEED_PORT inside the test, replacing the hardcoded
  3894), totp-keyrotation (no before() — allocates at the test start before bootServer()).

No fixed 39xx/40xx ports remain. Full server suite 435/435; the 4 hand-touched suites pass in
isolation. Pure test-infra change — no app code touched.
This commit is contained in:
screentinker 2026-07-13 09:51:40 -05:00 committed by GitHub
parent 40efaa1fed
commit 12c0004245
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 154 additions and 61 deletions

View file

@ -10,13 +10,16 @@ const { spawn } = require('node:child_process');
const ioClient = require('socket.io-client');
const path = require('node:path'); const os = require('node:os'); const fs = require('node:fs'); const crypto = require('node:crypto');
const PORT = 3955;
const base = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT;
let base;
const DATA_DIR = path.join(os.tmpdir(), 'st-storm-' + crypto.randomBytes(4).toString('hex'));
let proc;
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
before(async () => {
PORT = await freePort();
base = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(path.join(os.tmpdir(), 'st-storm.log'), 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -13,13 +13,15 @@ const WebSocket = require('../node_modules/ws');
const ioClient = require('socket.io-client');
const path = require('node:path'); const os = require('node:os'); const fs = require('node:fs'); const crypto = require('node:crypto');
const PORT = 3957;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-ho-' + crypto.randomBytes(4).toString('hex'));
let proc;
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(path.join(os.tmpdir(), 'st-ho.log'), 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -23,8 +23,9 @@ const os = require('node:os');
const fs = require('node:fs');
const crypto = require('node:crypto');
const PORT = 3900 + (crypto.randomBytes(1)[0] % 90); // avoid clashes with sibling subprocess suites
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT; // avoid clashes with sibling subprocess suites
let BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-156-test-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-156-test-' + crypto.randomBytes(4).toString('hex') + '.log');
@ -60,6 +61,8 @@ async function loadItem(itemId) {
}
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -12,12 +12,14 @@ const os = require('node:os');
const fs = require('node:fs');
const crypto = require('node:crypto');
const PORT = 3992;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-agency-' + crypto.randomBytes(4).toString('hex'));
let proc;
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(path.join(os.tmpdir(), 'st-agency.log'), 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -20,8 +20,8 @@ const crypto = require('node:crypto');
const ioClient = require('socket.io-client');
const { PUBLIC_ROUTERS, JWT_ONLY_ROUTERS } = require('../config/api-surface');
const PORT = 3978;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-api-test-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-api-test-' + crypto.randomBytes(4).toString('hex') + '.log');
@ -38,6 +38,8 @@ const auth = (tok, extra = {}) => ({ headers: { Authorization: 'Bearer ' + tok,
const post = (tok, obj, extra) => ({ method: 'POST', ...auth(tok, extra), body: JSON.stringify(obj) });
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -14,8 +14,8 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const Database = require('better-sqlite3');
const PORT = 4011;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-billauthz-' + crypto.randomBytes(4).toString('hex'));
let proc, db;
@ -30,6 +30,8 @@ const setRole = (email, role) => db.prepare('UPDATE users SET role = ? WHERE ema
let adminJwt, userJwt, billingToken, billingTokenId, readToken;
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(path.join(os.tmpdir(), 'st-billauthz.log'), 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -13,12 +13,14 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const Database = require('better-sqlite3');
const PORT = 3999;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-billing-ep-' + crypto.randomBytes(4).toString('hex'));
let proc, db;
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(path.join(os.tmpdir(), 'st-billing-ep.log'), 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -14,8 +14,8 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const Database = require('better-sqlite3');
const PORT = 4021;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-billmint-' + crypto.randomBytes(4).toString('hex'));
process.env.DATA_DIR = DATA_DIR; // so requiring lib/billing-token's deps resolves this db too
@ -25,6 +25,8 @@ const { hashToken } = require('../middleware/apiToken');
let proc, db, minted;
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(path.join(os.tmpdir(), 'st-billmint.log'), 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -14,12 +14,14 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const Database = require('better-sqlite3');
const PORT = 3993;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-blockauthz-' + crypto.randomBytes(4).toString('hex'));
let proc, db;
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(path.join(os.tmpdir(), 'st-blockauthz.log'), 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -14,17 +14,19 @@ const os = require('node:os');
const fs = require('node:fs');
const crypto = require('node:crypto');
const Database = require('better-sqlite3');
const { freePort } = require('./helpers/free-port');
const PORT = 3995;
const BASE = `http://127.0.0.1:${PORT}`;
const DATA_DIR = path.join(os.tmpdir(), 'st-boot-' + crypto.randomBytes(4).toString('hex'));
const DBPATH = path.join(DATA_DIR, 'db', 'remote_display.db');
test('boots + serves /api/status quickly against a pre-bloated table; prune drains in background', async () => {
const PORT = await freePort();
const BASE = `http://127.0.0.1:${PORT}`;
const SEED_PORT = await freePort();
// 1) Create + migrate the DB in a throwaway boot, then seed a large backlog.
{
const p = spawn('node', ['server.js'], { cwd: path.join(__dirname, '..'), env: { ...process.env, DATA_DIR, SELF_HOSTED: 'true', PORT: '3894', NODE_ENV: 'test' }, stdio: 'ignore' });
for (let i = 0; i < 60; i++) { try { const r = await fetch('http://127.0.0.1:3894/api/status'); if (r.ok) break; } catch { /* */ } await new Promise(r => setTimeout(r, 200)); }
const p = spawn('node', ['server.js'], { cwd: path.join(__dirname, '..'), env: { ...process.env, DATA_DIR, SELF_HOSTED: 'true', PORT: String(SEED_PORT), NODE_ENV: 'test' }, stdio: 'ignore' });
for (let i = 0; i < 60; i++) { try { const r = await fetch(`http://127.0.0.1:${SEED_PORT}/api/status`); if (r.ok) break; } catch { /* */ } await new Promise(r => setTimeout(r, 200)); }
p.kill('SIGKILL');
await new Promise(r => setTimeout(r, 300));
}

View file

@ -14,8 +14,8 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const ioClient = require('socket.io-client');
const PORT = 3984;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-ack-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-ack-' + crypto.randomBytes(4).toString('hex') + '.log');
const DEDUP_MS = 600;
@ -24,6 +24,8 @@ let proc;
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -15,14 +15,16 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const ioClient = require('socket.io-client');
const PORT = 3985;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-flood-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-flood-' + crypto.randomBytes(4).toString('hex') + '.log');
let proc;
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -15,14 +15,16 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const ioClient = require('socket.io-client');
const PORT = 3986;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-valve-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-valve-' + crypto.randomBytes(4).toString('hex') + '.log');
let proc;
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -19,8 +19,8 @@ const crypto = require('node:crypto');
const ioClient = require('socket.io-client');
const Database = require('better-sqlite3');
const PORT = 3987;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-block-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-block-' + crypto.randomBytes(4).toString('hex') + '.log');
const DB_PATH = path.join(DATA_DIR, 'db', 'remote_display.db');
@ -30,6 +30,8 @@ let tdb; // ONE long-lived operator-style connection (mirrors how the server hol
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -8,11 +8,14 @@ const assert = require('node:assert/strict');
const { spawn } = require('node:child_process');
const sleep = ms => new Promise(r => setTimeout(r, ms));
const PORT = 3977; const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-doqr-' + crypto.randomBytes(4).toString('hex'));
let proc, JWT;
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(path.join(os.tmpdir(), 'st-doqr.log'), 'w');
proc = spawn('node', ['server.js'], { cwd: path.join(__dirname, '..'), env: { ...process.env, DATA_DIR, SELF_HOSTED: 'true', PORT: String(PORT), NODE_ENV: 'test' }, stdio: ['ignore', logFd, logFd] });
let up = false; for (let i = 0; i < 80; i++) { try { if ((await fetch(BASE + '/api/status')).ok) { up = true; break; } } catch {} await sleep(250); }

View file

@ -16,14 +16,16 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const ioClient = require('socket.io-client');
const PORT = 3989;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-pair-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-pair-' + crypto.randomBytes(4).toString('hex') + '.log');
let proc, JWT;
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], { cwd: path.join(__dirname, '..'), env: { ...process.env, DATA_DIR, SELF_HOSTED: 'true', PORT: String(PORT), NODE_ENV: 'test' }, stdio: ['ignore', logFd, logFd] });
let up = false;

View file

@ -25,8 +25,8 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const Database = require('better-sqlite3');
const PORT = 3996;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-zone-test-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-zone-' + crypto.randomBytes(4).toString('hex') + '.log');
const PW = 'Passw0rd123';
@ -55,6 +55,8 @@ async function getOrphanCount() {
}
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -16,8 +16,8 @@ const crypto = require('node:crypto');
const ioClient = require('socket.io-client');
const Database = require('better-sqlite3');
const PORT = 3988;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-recl-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-recl-' + crypto.randomBytes(4).toString('hex') + '.log');
const DB_PATH = path.join(DATA_DIR, 'db', 'remote_display.db');
@ -25,6 +25,8 @@ let proc, tdb;
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -10,11 +10,14 @@ const { spawn } = require('node:child_process');
const ioClient = require('../node_modules/socket.io-client');
const sleep = ms => new Promise(r => setTimeout(r, ms));
const PORT = 3976; const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-gsync-' + crypto.randomBytes(4).toString('hex'));
let proc, JWT;
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(path.join(os.tmpdir(), 'st-gsync.log'), 'w');
proc = spawn('node', ['server.js'], { cwd: path.join(__dirname, '..'), env: { ...process.env, DATA_DIR, SELF_HOSTED: 'true', PORT: String(PORT), NODE_ENV: 'test' }, stdio: ['ignore', logFd, logFd] });
let up = false; for (let i = 0; i < 80; i++) { try { if ((await fetch(BASE + '/api/status')).ok) { up = true; break; } } catch {} await sleep(250); }

View file

@ -0,0 +1,21 @@
'use strict';
const net = require('net');
// Allocate a free TCP port from the OS (bind :0 on loopback, read it back, release it).
// Subprocess test suites call this in before() instead of hand-picking a fixed/random port —
// the old 39xx scheme collided under CI load (a random port in the shared range, or a new
// suite reusing one), surfacing as flaky "no such table: devices" / FK errors when two servers
// raced on the same port. An OS-assigned ephemeral port per suite can't collide.
function freePort() {
return new Promise((resolve, reject) => {
const srv = net.createServer();
srv.unref();
srv.on('error', reject);
srv.listen(0, '127.0.0.1', () => {
const port = srv.address().port;
srv.close(() => resolve(port));
});
});
}
module.exports = { freePort };

View file

@ -13,13 +13,15 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const Database = require('better-sqlite3');
const PORT = 3982;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-lag-int-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-lag-int-' + crypto.randomBytes(4).toString('hex') + '.log');
let proc;
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -15,8 +15,8 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const Database = require('better-sqlite3');
const PORT = 3994;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-mute-test-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-mute-' + crypto.randomBytes(4).toString('hex') + '.log');
const PW = 'Passw0rd123';
@ -33,6 +33,8 @@ const post = (tok, obj) => ({ method: 'POST', ...auth(tok), body: JSON.stringify
const put = (tok, obj) => ({ method: 'PUT', ...auth(tok), body: JSON.stringify(obj || {}) });
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -12,8 +12,8 @@ const os = require('node:os');
const fs = require('node:fs');
const crypto = require('node:crypto');
const PORT = 3991;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-ota-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-ota-' + crypto.randomBytes(4).toString('hex') + '.log');
let proc, LATEST;
@ -25,6 +25,8 @@ const check = async (version, deviceId) => {
};
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
// the breaker only reports update_available when an APK actually exists — give the
// test server a dummy one (resolveApkPath checks DATA_DIR/ScreenTinker.apk).
fs.mkdirSync(DATA_DIR, { recursive: true });

View file

@ -28,8 +28,9 @@ const crypto = require('node:crypto');
const ioClient = require('socket.io-client');
const Database = require('better-sqlite3');
const PORT = 3997; // must be unique across the suite (files run concurrently under `node --test`)
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT; // must be unique across the suite (files run concurrently under `node --test`)
let BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-storm-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-storm-' + crypto.randomBytes(4).toString('hex') + '.log');
let proc, rdb;
@ -37,6 +38,8 @@ let proc, rdb;
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -16,13 +16,15 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const ioClient = require('socket.io-client');
const PORT = 3983;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-thr-int-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-thr-int-' + crypto.randomBytes(4).toString('hex') + '.log');
let proc;
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -13,12 +13,14 @@ const crypto = require('node:crypto');
const Database = require('better-sqlite3');
const ioClient = require('socket.io-client');
const PORT = 3998;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-statusdbg-' + crypto.randomBytes(4).toString('hex'));
let proc, db;
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(path.join(os.tmpdir(), 'st-statusdbg.log'), 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -18,8 +18,8 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const Database = require('better-sqlite3');
const PORT = 3990;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-thumb-test-' + crypto.randomBytes(4).toString('hex'));
const CONTENT_DIR = path.join(DATA_DIR, 'uploads', 'content');
const LOG = path.join(os.tmpdir(), 'st-thumb-' + crypto.randomBytes(4).toString('hex') + '.log');
@ -47,6 +47,8 @@ function makeContent(thumbnailPath, { mime = 'image/png' } = {}) {
}
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
// Mock upstream standing in for img.youtube.com. /missing/* -> 404 to exercise the
// clean-failure path; everything else -> a 200 image/png.
upstream = http.createServer((req, res) => {

View file

@ -16,8 +16,8 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const { authenticator } = require('otplib');
const PORT = 3980;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-totp-rot-' + crypto.randomBytes(4).toString('hex'));
function bootServer(jwtSecret) {
@ -44,6 +44,8 @@ const post = (o) => ({ method: 'POST', headers: { 'Content-Type': 'application/j
const postAuth = (tok, o) => ({ method: 'POST', headers: { Authorization: 'Bearer ' + tok, 'Content-Type': 'application/json' }, body: JSON.stringify(o || {}) });
test('#100 key rotation does NOT brick TOTP: recovery survives; TOTP fails cleanly (no 500)', async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
let proc = bootServer('keyA-' + crypto.randomBytes(8).toString('hex'));
try {
await waitUp();

View file

@ -13,8 +13,8 @@ const fs = require('node:fs');
const crypto = require('node:crypto');
const { authenticator } = require('otplib');
const PORT = 3979;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-totp-test-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-totp-' + crypto.randomBytes(4).toString('hex') + '.log');
let proc;
@ -28,6 +28,8 @@ const auth = (tok, extra = {}) => ({ headers: { Authorization: 'Bearer ' + tok,
const post = (tok, obj, extra) => ({ method: 'POST', ...auth(tok, extra), body: JSON.stringify(obj || {}) });
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], {
cwd: path.join(__dirname, '..'),

View file

@ -78,14 +78,16 @@ test('cross-client conformance FINDING: APK socket.io TRANSPORT backoff diverges
});
// ============================ E2E: MIXED FLEET against the real server ============================
const PORT = 3968;
const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-v4core-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-v4core.log');
let proc, JWT;
const sleep = ms => new Promise(r => setTimeout(r, ms));
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], { cwd: path.join(__dirname, '..'), env: { ...process.env, DATA_DIR, SELF_HOSTED: 'true', PORT: String(PORT), NODE_ENV: 'test' }, stdio: ['ignore', logFd, logFd] });
let up = false;

View file

@ -48,12 +48,15 @@ test('A2 window: reconnects older than 60s drop out (a past flap does not stay D
});
// ================= E2E: the shared !isPlaylistRefresh gate + change-detection =================
const PORT = 3972; const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-rg-e2e-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-rg-e2e.log');
let proc, JWT;
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], { cwd: path.join(__dirname, '..'), env: { ...process.env, DATA_DIR, SELF_HOSTED: 'true', PORT: String(PORT), NODE_ENV: 'test' }, stdio: ['ignore', logFd, logFd] });
let up = false; for (let i = 0; i < 80; i++) { try { if ((await fetch(BASE + '/api/status')).ok) { up = true; break; } } catch { /* */ } await sleep(250); }

View file

@ -83,10 +83,13 @@ test('B/wgt CLEAN-CLOSE: pagehide(false) -> clean_exit; BACKGROUNDING pagehide(t
});
// ============ PART A — server-side socket / #148 / reconnect-vs-exit safety ============
const PORT = 3975; const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-exit3-' + crypto.randomBytes(4).toString('hex'));
let proc, JWT;
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(path.join(os.tmpdir(), 'st-exit3.log'), 'w');
proc = spawn('node', ['server.js'], { cwd: path.join(__dirname, '..'), env: { ...process.env, DATA_DIR, SELF_HOSTED: 'true', PORT: String(PORT), NODE_ENV: 'test' }, stdio: ['ignore', logFd, logFd] });
let up = false; for (let i = 0; i < 80; i++) { try { if ((await fetch(BASE + '/api/status')).ok) { up = true; break; } } catch {} await sleep(250); }

View file

@ -22,12 +22,15 @@ test('sanitizeExitReason: only crashed/clean_exit accepted; silent + unknown REJ
});
// ===== E2E =====
const PORT = 3974; const BASE = `http://127.0.0.1:${PORT}`;
const { freePort } = require('./helpers/free-port');
let PORT, BASE;
const DATA_DIR = path.join(os.tmpdir(), 'st-exit-' + crypto.randomBytes(4).toString('hex'));
const LOG = path.join(os.tmpdir(), 'st-exit.log');
let proc, JWT;
before(async () => {
PORT = await freePort();
BASE = `http://127.0.0.1:${PORT}`;
const logFd = fs.openSync(LOG, 'w');
proc = spawn('node', ['server.js'], { cwd: path.join(__dirname, '..'), env: { ...process.env, DATA_DIR, SELF_HOSTED: 'true', PORT: String(PORT), NODE_ENV: 'test' }, stdio: ['ignore', logFd, logFd] });
let up = false; for (let i = 0; i < 80; i++) { try { if ((await fetch(BASE + '/api/status')).ok) { up = true; break; } } catch { /* */ } await sleep(250); }