|
| 1 | +// Build-time smoke test: boot the BUNDLED dist/cli.js with persistence enabled and |
| 2 | +// confirm the node:sqlite-backed DB layer loads. Tests run against TS source, so a |
| 3 | +// bundler mangling the `node:sqlite` import (e.g. stripping the `node:` prefix) only |
| 4 | +// surfaces at runtime in the built artifact — exactly what broke v0.2.0. Chaining this |
| 5 | +// into `pnpm build` makes such a regression fail the build (and the Docker release). |
| 6 | +import { spawn } from "node:child_process"; |
| 7 | +import { mkdtempSync, rmSync } from "node:fs"; |
| 8 | +import { tmpdir } from "node:os"; |
| 9 | +import { join } from "node:path"; |
| 10 | +import { randomBytes } from "node:crypto"; |
| 11 | + |
| 12 | +const dir = mkdtempSync(join(tmpdir(), "lnurl-smoke-")); |
| 13 | +const SUCCESS = "persistence: enabled"; // cli logs this only after the DB layer loads + migrates |
| 14 | +const TIMEOUT_MS = 20_000; |
| 15 | + |
| 16 | +const child = spawn(process.execPath, ["--experimental-sqlite", "dist/cli.js"], { |
| 17 | + stdio: ["ignore", "pipe", "pipe"], |
| 18 | + env: { |
| 19 | + ...process.env, |
| 20 | + DB_PATH: join(dir, "smoke.db"), |
| 21 | + TOKEN_ENCRYPTION_KEY: randomBytes(32).toString("hex"), |
| 22 | + BOOTSTRAP_DOMAIN: "smoke.test", |
| 23 | + // High, unlikely-to-conflict ports (PORT=0 would fall back to 3000 via `|| default`). |
| 24 | + PORT: "39080", |
| 25 | + ADMIN_PORT: "39081", |
| 26 | + ADMIN_BIND: "127.0.0.1", |
| 27 | + }, |
| 28 | +}); |
| 29 | + |
| 30 | +let out = ""; |
| 31 | +let settled = false; |
| 32 | +const finish = (ok, msg) => { |
| 33 | + if (settled) return; |
| 34 | + settled = true; |
| 35 | + clearTimeout(timer); |
| 36 | + try { child.kill("SIGKILL"); } catch { /* already gone */ } |
| 37 | + // Best-effort cleanup: on Windows the just-killed child may still hold the SQLite |
| 38 | + // file handle briefly, so retry and never let a cleanup failure change the result. |
| 39 | + try { rmSync(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 }); } catch { /* temp dir; OS will reap */ } |
| 40 | + if (ok) { |
| 41 | + console.log("✓ dist smoke: bundled CLI initialized node:sqlite persistence"); |
| 42 | + process.exit(0); |
| 43 | + } |
| 44 | + console.error(`✗ dist smoke FAILED: ${msg}\n--- cli output ---\n${out}`); |
| 45 | + process.exit(1); |
| 46 | +}; |
| 47 | + |
| 48 | +const timer = setTimeout(() => finish(false, "timed out waiting for startup"), TIMEOUT_MS); |
| 49 | +const onData = (d) => { out += d.toString(); if (out.includes(SUCCESS)) finish(true); }; |
| 50 | +child.stdout.on("data", onData); |
| 51 | +child.stderr.on("data", onData); // capture errors (e.g. ERR_MODULE_NOT_FOUND) |
| 52 | +child.on("exit", (code) => finish(false, `CLI exited before startup (code ${code})`)); |
| 53 | +child.on("error", (err) => finish(false, `failed to spawn CLI: ${err.message}`)); |
0 commit comments