Make the test workflow wait for the API server to be ready

Original task (#127): the `test` workflow ran `pnpm --filter
@workspace/api-server test` directly, which fires HTTP requests at
localhost:8080. On a freshly-started environment the API server
isn't up yet, so every test fails with ECONNREFUSED, drowning real
failures in noise.

Changes:
- Added `artifacts/api-server/scripts/wait-for-server.mjs`, a small
  pure-Node poller that hits `${TEST_API_BASE ?? "http://localhost:8080"}/api/healthz`
  every 500ms until it returns `{status: "ok"}` or the timeout
  (default 30s) elapses. On timeout it exits 1 with a clear
  "Start the API Server workflow (or set TEST_API_BASE) before
  running tests" message instead of a wall of fetch errors.
  Configurable via `TEST_API_BASE`, `TEST_API_WAIT_TIMEOUT_MS`,
  `TEST_API_WAIT_INTERVAL_MS`.
- Added `test:wait` script to `artifacts/api-server/package.json`.
- Updated the `test` workflow to run
  `pnpm --filter @workspace/api-server test:wait` before the
  api-server tests and the tx-os e2e tests.

Verified:
- `node ./scripts/wait-for-server.mjs` against a running server
  reports "ready ... after 86ms" and exits 0.
- Same script with TEST_API_BASE pointed at a dead port exits 1
  with the friendly message.
- The full `test` workflow now flows past the readiness gate and
  runs all 158 tests; 153 pass, 3 skip, and 2 fail for real reasons
  (PDF export endpoints returning 500). Filed follow-up #179 for
  the PDF bugs.

No deviations from the task. `.replit` was edited via the workflow
configuration tool (direct edits are blocked).
This commit is contained in:
Riyadh
2026-04-29 19:28:14 +00:00
parent 8e06c229ce
commit aaefeb878a
2 changed files with 49 additions and 0 deletions
+1
View File
@@ -8,6 +8,7 @@
"build": "node ./build.mjs",
"start": "node --enable-source-maps ./dist/index.mjs",
"typecheck": "tsc -p tsconfig.json --noEmit",
"test:wait": "node ./scripts/wait-for-server.mjs",
"test": "node --test 'tests/**/*.test.mjs'"
},
"dependencies": {
@@ -0,0 +1,48 @@
#!/usr/bin/env node
const API_BASE = process.env.TEST_API_BASE ?? "http://localhost:8080";
const TIMEOUT_MS = Number(process.env.TEST_API_WAIT_TIMEOUT_MS ?? 30_000);
const INTERVAL_MS = Number(process.env.TEST_API_WAIT_INTERVAL_MS ?? 500);
const URL = `${API_BASE.replace(/\/$/, "")}/api/healthz`;
const started = Date.now();
let attempt = 0;
let lastError = null;
while (Date.now() - started < TIMEOUT_MS) {
attempt += 1;
try {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 2_000);
const res = await fetch(URL, { signal: controller.signal });
clearTimeout(timer);
if (res.ok) {
const body = await res.json().catch(() => ({}));
if (body && body.status === "ok") {
const elapsed = Date.now() - started;
console.log(
`API server ready at ${URL} after ${elapsed}ms (attempt ${attempt})`,
);
process.exit(0);
}
lastError = new Error(
`Unexpected health payload: ${JSON.stringify(body)}`,
);
} else {
lastError = new Error(`HTTP ${res.status} from ${URL}`);
}
} catch (err) {
lastError = err;
}
await new Promise((r) => setTimeout(r, INTERVAL_MS));
}
console.error(
`API server not reachable at ${URL} after ${TIMEOUT_MS}ms (${attempt} attempts).`,
);
console.error(
`Last error: ${lastError ? lastError.message ?? String(lastError) : "unknown"}`,
);
console.error(
`Start the API Server workflow (or set TEST_API_BASE) before running tests.`,
);
process.exit(1);