Backend CI / type-check, build, tests (push) Successful in 1m0s
Co-authored-by: Cursor <cursoragent@cursor.com>
103 lines
2.8 KiB
JavaScript
103 lines
2.8 KiB
JavaScript
/**
|
|
* 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')
|