/** * Smoke-test production env on a temp DB (no deploy). * Requires: npm run build first. */ import { spawn, spawnSync } from 'node:child_process' import { mkdtempSync, rmSync, existsSync } from 'node:fs' import { tmpdir } from 'node:os' import { join, dirname } from 'node:path' import { fileURLToPath } from 'node:url' const backendRoot = join(dirname(fileURLToPath(import.meta.url)), '..') if (!existsSync(join(backendRoot, 'dist', 'main.js'))) { console.error('Run npm run build before trial-production-smoke.mjs') process.exit(1) } const tempDir = mkdtempSync(join(tmpdir(), 'cgl-prod-smoke-')) const dbPath = join(tempDir, 'app.db') const port = String(3020 + Math.floor(Math.random() * 100)) const secret = `trial-smoke-${'x'.repeat(40)}` function fail(msg) { console.error(msg) rmSync(tempDir, { recursive: true, force: true }) process.exit(1) } const bad = spawnSync(process.execPath, ['dist/main.js'], { cwd: backendRoot, env: { ...process.env, NODE_ENV: 'production', PORT: port, DB_PATH: dbPath, JWT_SECRET: 'gold-land-dev-secret-change-in-production', CORS_ORIGIN: 'http://127.0.0.1:8080', }, stdio: 'pipe', encoding: 'utf8', timeout: 8000, }) if (bad.status === 0) { fail('Expected startup failure with dev JWT placeholder in production') } async function waitForReady() { await new Promise((resolve, reject) => { const child = spawn(process.execPath, ['dist/main.js'], { cwd: backendRoot, env: { ...process.env, NODE_ENV: 'production', PORT: port, DB_PATH: dbPath, JWT_SECRET: secret, CORS_ORIGIN: 'http://127.0.0.1:8080', }, stdio: 'pipe', }) const timer = setTimeout(() => { child.kill() reject(new Error('startup timeout')) }, 20_000) const poll = async () => { try { const live = await fetch(`http://127.0.0.1:${port}/health`) const ready = await fetch(`http://127.0.0.1:${port}/health/ready`) if (live.ok && ready.ok) { clearTimeout(timer) child.kill() resolve(undefined) return } } catch { // retry } setTimeout(poll, 300) } setTimeout(poll, 500) }) } await waitForReady().catch((e) => fail(String(e))) const demoCheck = spawnSync( process.execPath, [ '-e', `const D=require('better-sqlite3');const d=new D(${JSON.stringify(dbPath)});const row=d.prepare("SELECT COUNT(*) as c FROM auth_accounts WHERE username='demo'").get();console.log(row.c);d.close();`, ], { cwd: backendRoot, encoding: 'utf8' }, ) const demoCount = Number(demoCheck.stdout.trim()) if (demoCount !== 0) { fail(`Expected no demo account in production DB, found ${demoCount}`) } rmSync(tempDir, { recursive: true, force: true }) console.log('trial-production-smoke passed')