// Start the dev relay for an e2e run, and tell the difference between "one is
// already running" and "ours could not start".
//
// The pattern this replaces resolved BOTH cases to the same `false`:
//
// proc.on("exit", () => resolve(false)); // port in use → external relay
//
// A relay that dies on startup — board's had never had `npm install` run in
// deploy/relay, so it exited with ERR_MODULE_NOT_FOUND every single time — is
// indistinguishable from a healthy external one. The suite then ran with NO
// relay at all and still passed, because a release dist points at the
// PRODUCTION relay (config.cljs: goog.DEBUG is false outside `npm run dev`).
// Two independent faults concealing each other.
//
// So: on exit, check whether the port is actually accepting connections. If it
// is, someone else's relay is there and that is fine. If it is not, fail with
// what the process actually said.
import { spawn } from "node:child_process";
import { connect } from "node:net";
const listening = (port) =>
new Promise((resolve) => {
const s = connect(port, "127.0.0.1");
s.on("connect", () => (s.destroy(), resolve(true)));
s.on("error", () => resolve(false));
setTimeout(() => (s.destroy(), resolve(false)), 1500);
});
/** Resolves { proc, external } or throws with the relay's own output. */
export async function startDevRelay(relayPath, { port = 9090, banner = "relay up" } = {}) {
const proc = spawn(process.execPath, [relayPath, "--dev"], { stdio: ["ignore", "pipe", "pipe"] });
let err = "";
proc.stderr.on("data", (d) => (err += String(d)));
const up = await new Promise((resolve) => {
const t = setTimeout(() => resolve(false), 10000);
proc.stdout.on("data", (d) => String(d).includes(banner) && (clearTimeout(t), resolve(true)));
proc.on("exit", () => (clearTimeout(t), resolve(false)));
});
if (up) return { proc, external: false };
if (await listening(port)) {
console.log(`⚠️ dev relay already running externally on :${port}`);
return { proc: null, external: true };
}
throw new Error(
`the dev relay did not start and nothing is listening on :${port}. ` +
`Without it these tests would silently fall back to the PRODUCTION relay.\n` +
(err.trim().split("\n").slice(0, 6).join("\n") || "(no stderr)") +
`\nIf this is ERR_MODULE_NOT_FOUND, run: npm install in deploy/relay`,
);
}