import { spawnSync } from 'node:child_process'; import { mkdtempSync, writeFileSync, existsSync, readFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join, resolve } from 'node:path'; import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; const root = resolve(import.meta.dirname, '..'); function runScript(script, args) { return spawnSync(process.execPath, [join(root, script), ...args], { cwd: root, encoding: 'utf8', }); } describe('production ops scripts', () => { it('prints a pg_dump command in dry-run mode without writing the target file', () => { const dir = mkdtempSync(join(tmpdir(), 'ftb-pg-backup-')); const output = join(dir, 'backup.dump'); const result = runScript('scripts/backup-postgres.mjs', [ '--dry-run', '--env-file', '.env.production.example', '--output', output, ]); assert.equal(result.status, 0, result.stderr); assert.match(result.stdout, /docker compose/); assert.match(result.stdout, /pg_dump/); assert.match(result.stdout, /backup\.dump/); assert.equal(existsSync(output), false); }); it('refuses restore by default unless --confirm-overwrite is supplied', () => { const dir = mkdtempSync(join(tmpdir(), 'ftb-pg-restore-')); const input = join(dir, 'backup.dump'); writeFileSync(input, 'not-a-real-dump'); const result = runScript('scripts/restore-postgres.mjs', [ '--env-file', '.env.production.example', '--input', input, ]); assert.notEqual(result.status, 0); assert.match(result.stderr, /--confirm-overwrite/); }); it('prints fresh database restore steps in dry-run mode after explicit overwrite confirmation', () => { const dir = mkdtempSync(join(tmpdir(), 'ftb-pg-restore-dry-')); const input = join(dir, 'backup.dump'); writeFileSync(input, 'not-a-real-dump'); const result = runScript('scripts/restore-postgres.mjs', [ '--dry-run', '--confirm-overwrite', '--env-file', '.env.production.example', '--input', input, ]); assert.equal(result.status, 0, result.stderr); assert.match(result.stdout, /dropdb/); assert.match(result.stdout, /createdb/); assert.match(result.stdout, /pg_restore/); }); it('prints a server_data volume tar backup command in dry-run mode', () => { const result = runScript('scripts/backup-server-data.mjs', [ '--dry-run', '--env-file', '.env.production.example', ]); assert.equal(result.status, 0, result.stderr); assert.match(result.stdout, /docker run/); assert.match(result.stdout, /ftb_pm_server_data/); assert.match(result.stdout, /tar -czf/); }); it('prints read-only release smoke checks in dry-run mode', () => { const result = runScript('scripts/smoke-test-release.mjs', [ '--dry-run', '--base-url', 'http://localhost', ]); assert.equal(result.status, 0, result.stderr); assert.match(result.stdout, /\/api\/v1\/health\/version/); assert.match(result.stdout, /\/products/); assert.match(result.stdout, /\/api\/v1\/products/); assert.match(result.stdout, /\/api\/v1\/v2\.2\/requirements\?productId=__smoke__/); assert.match(result.stdout, /\/api\/v1\/config\/ai/); }); it('declares the production monitoring baseline without real alert secrets', () => { const alertRules = readFileSync( join(root, 'deploy/monitoring/prometheus/alert-rules.yml'), 'utf8', ); const postgresQueries = readFileSync( join(root, 'deploy/monitoring/postgres/postgres-queries.yml'), 'utf8', ); const promtailConfig = readFileSync( join(root, 'deploy/monitoring/promtail/config.yml'), 'utf8', ); const dashboard = readFileSync( join(root, 'deploy/monitoring/grafana/dashboards/ftb-production-overview.json'), 'utf8', ); for (const alertName of [ 'FtbPostgresDown', 'FtbDiskPressure', 'FtbSlowApiLogBurst', 'FtbSlowPrismaLogBurst', 'FtbJobFailureLogBurst', 'FtbXiaobaoSummaryStale', ]) { assert.match(alertRules, new RegExp(alertName)); } assert.match(postgresQueries, /xiaobao_risk_summaries/); assert.match(postgresQueries, /dirty = true/); assert.match(promtailConfig, /Slow API request/); assert.match(promtailConfig, /Slow Prisma query/); assert.match(promtailConfig, /AppData relation sync failed/); assert.match(dashboard, /FTB Production Overview/); const combined = `${alertRules}\n${postgresQueries}\n${promtailConfig}\n${dashboard}`; assert.doesNotMatch(combined, /sk-ant-[A-Za-z0-9]/); assert.doesNotMatch(combined, /hooks\.slack\.com\/services\//); }); it('passes the runbook placeholder scan', () => { const result = runScript('scripts/check-runbook-placeholders.mjs', [ '--paths', 'docs/runbooks', 'docs/production-readiness.md', ]); assert.equal(result.status, 0, result.stderr); assert.match(result.stdout, /Runbook placeholder scan passed/); }); });