Skip to content

Commit a40d7ae

Browse files
refactor(test): extract prod-DB cross-check to scripts/verify-prod-db.mjs
continuous.live-db-write.test.js Part 4 read the on-disk audit.db and asserted content thresholds (distinct_nodes > 100, finished batches, error codes). That asserts facts about whatever DB happens to be on disk, so it only passed on a populated production DB and FAILED on dev boxes / CI / fresh clones with a sparse fixture DB — a permanent red that masks real regressions. It is an operational health check, not a regression test. - Moved Part 4 verbatim into scripts/verify-prod-db.mjs (read-only, closes in finally, SKIPs when audit.db absent). Run on demand: npm run verify:prod-db. - continuous.live-db-write.test.js is now hermetic (in-memory DB only): Parts 1-3 still verify the real insertBatch → insertBatchResult → updateBatchOnFinish write path. 25/0, deterministic on any machine. - npm run test:integration now goes fully green regardless of local DB state. The two in-gate smoke tests already deliberately never assert against prod-DB contents; this brings the lone violator in line with that pattern.
1 parent 8de32a3 commit a40d7ae

3 files changed

Lines changed: 116 additions & 57 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"test": "node test/smoke.test.js && node test/db.smoke.test.js && node test/continuous.smoke.test.js && node test/security.test.js && node test/failure-log-ux.test.js && node test/public-no-action-buttons.test.js && node test/onchain-decode.test.js",
4242
"test:ui": "node test/ui.smoke.test.js",
4343
"test:integration": "node test/continuous.audit-integration.test.js && node test/continuous.live-db-write.test.js && node test/node-detail.smoke.test.js",
44+
"verify:prod-db": "node scripts/verify-prod-db.mjs",
4445
"cleanup": "node scripts/cleanup.mjs"
4546
},
4647
"keywords": [

scripts/verify-prod-db.mjs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/usr/bin/env node
2+
/**
3+
* verify-prod-db.mjs — Operational health check for the live audit.db
4+
*
5+
* This is NOT a regression test. It is an on-demand operational audit the
6+
* operator runs against their real instrument to confirm the live
7+
* continuous-loop write path has populated production data sanely:
8+
*
9+
* - finished batches exist with finished_at > started_at
10+
* - the most recent batch has passed+failed > 0 and batch_results rows
11+
* - the corpus covers a meaningful number of distinct nodes (>100)
12+
* - failure rows carry error codes (proves the failure-log path works)
13+
* - batch mode distribution looks reasonable
14+
*
15+
* It was extracted from test/continuous.live-db-write.test.js (Part 4). That
16+
* cross-check asserts facts about whatever audit.db is on disk, so it can only
17+
* pass on a genuinely-populated production DB — it FAILED on dev boxes / CI /
18+
* fresh clones with a sparse fixture DB, masking real regressions. Automated
19+
* suites must be deterministic and environment-independent; an "is my live DB
20+
* healthy" question is an ops check, so it lives here instead.
21+
*
22+
* The hermetic write-path test (Parts 1-3) stays in test/continuous.live-db-write.test.js
23+
* and runs in `npm run test:integration`.
24+
*
25+
* Run: node scripts/verify-prod-db.mjs
26+
* Exit: 0 = all health checks passed (or prod DB absent → SKIP)
27+
* 1 = prod DB present but one or more health checks failed
28+
*
29+
* DB-lock note (per CLAUDE.md): opens audit.db read-only and closes it in a
30+
* finally block. Don't run this in parallel with a starting server.
31+
*/
32+
33+
import path from 'path';
34+
import { fileURLToPath } from 'url';
35+
import { existsSync } from 'fs';
36+
import Database from 'better-sqlite3';
37+
38+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
39+
const PROD_DB = path.join(__dirname, '..', 'data', 'audit.db');
40+
41+
const out = { pass: 0, fail: 0, errors: [] };
42+
function ok(cond, name) {
43+
if (cond) { out.pass++; console.log(` PASS ${name}`); }
44+
else { out.fail++; out.errors.push(name); console.log(` FAIL ${name}`); }
45+
}
46+
47+
function main() {
48+
console.log(`Production DB health check — ${PROD_DB}`);
49+
50+
if (!existsSync(PROD_DB)) {
51+
console.log(' SKIP prod audit.db not present — nothing to verify.');
52+
console.log(`\n${'='.repeat(60)}\nRESULTS: SKIPPED (no prod DB)\n${'='.repeat(60)}`);
53+
process.exit(0);
54+
}
55+
56+
const prod = new Database(PROD_DB, { readonly: true });
57+
try {
58+
// Most recent finished batch
59+
const last = prod.prepare(`
60+
SELECT id, started_at, finished_at, snapshot_size, passed, failed, mode,
61+
(SELECT COUNT(*) FROM batch_results WHERE batch_id = batches.id) AS rows_count
62+
FROM batches WHERE finished_at IS NOT NULL ORDER BY id DESC LIMIT 1
63+
`).get();
64+
ok(last && last.id > 0, `prod has finished batches (most recent id=${last?.id})`);
65+
ok(last && last.finished_at > last.started_at, 'prod batch finished_at > started_at');
66+
ok(last && (last.passed + last.failed) > 0, `prod batch has passed+failed > 0 (${last?.passed}+${last?.failed})`);
67+
ok(last && last.rows_count > 0, `prod batch has batch_results rows (${last?.rows_count})`);
68+
69+
// Aggregate
70+
const agg = prod.prepare(`
71+
SELECT COUNT(DISTINCT b.id) AS batches,
72+
COUNT(br.id) AS results,
73+
COUNT(DISTINCT br.node_address) AS distinct_nodes
74+
FROM batches b LEFT JOIN batch_results br ON br.batch_id = b.id
75+
`).get();
76+
ok(agg.batches > 0, `prod has ${agg.batches} batches recorded`);
77+
ok(agg.results > 0, `prod has ${agg.results} batch_results rows`);
78+
ok(agg.distinct_nodes > 100,
79+
`prod has ${agg.distinct_nodes} distinct nodes tested across all batches`);
80+
81+
// Schema sanity — error_code distinct values prove failure-log path works
82+
const codes = prod.prepare(`
83+
SELECT error_code, COUNT(*) c FROM batch_results
84+
WHERE error_code IS NOT NULL GROUP BY error_code ORDER BY c DESC LIMIT 5
85+
`).all();
86+
console.log(' prod error_code distribution (top 5):');
87+
for (const r of codes) console.log(` ${r.error_code.padEnd(30)} ${r.c}`);
88+
ok(codes.length > 0, 'prod batch_results contains failure rows with error codes');
89+
90+
// Mode distribution
91+
const modes = prod.prepare(`SELECT mode, COUNT(*) c FROM batches GROUP BY mode`).all();
92+
console.log(' prod batch mode distribution:');
93+
for (const m of modes) console.log(` ${m.mode.padEnd(15)} ${m.c}`);
94+
} finally {
95+
prod.close();
96+
}
97+
98+
console.log(`\n${'='.repeat(60)}\nRESULTS: ${out.pass} passed, ${out.fail} failed`);
99+
if (out.errors.length) for (const e of out.errors) console.log(` FAIL: ${e}`);
100+
console.log('='.repeat(60));
101+
process.exit(out.fail ? 1 : 0);
102+
}
103+
104+
main();

test/continuous.live-db-write.test.js

Lines changed: 11 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,16 @@
1515
* SAME sequence and shape, simulating one full iteration.
1616
* 3. Reads back the rows and asserts schema + content match.
1717
*
18-
* Then it cross-checks the production audit.db — proving prior continuous
19-
* runs wrote real data through this same path.
18+
* Hermetic and deterministic — uses only an in-memory DB. The read-only
19+
* cross-check of the live production audit.db that used to follow these steps
20+
* has moved to scripts/verify-prod-db.mjs: it asserted facts about whatever
21+
* audit.db was on disk, so it could only pass on a populated production DB and
22+
* failed on dev/CI/fresh clones. That is an operational health check, not a
23+
* regression test.
2024
*
2125
* Run: NODE_ENV=test node test/continuous.live-db-write.test.js
2226
*/
2327

24-
import path from 'path';
25-
import { fileURLToPath } from 'url';
26-
import Database from 'better-sqlite3';
27-
import { existsSync } from 'fs';
28-
29-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
30-
const PROD_DB = path.join(__dirname, '..', 'data', 'audit.db');
31-
3228
const out = { pass: 0, fail: 0, errors: [] };
3329
function ok(cond, name) {
3430
if (cond) { out.pass++; console.log(` PASS ${name}`); }
@@ -121,53 +117,11 @@ async function main() {
121117
ok(closedRow.failed === 1, 'batches.failed=1');
122118
ok(closedRow.snapshot_size === 3, 'batches.snapshot_size unchanged');
123119

124-
// ─── 4. Cross-check production DB — prove this same code path ran live ──
125-
console.log('[4] Cross-check production audit.db (read-only)');
126-
if (!existsSync(PROD_DB)) {
127-
console.log(' SKIP prod audit.db not present');
128-
} else {
129-
const prod = new Database(PROD_DB, { readonly: true });
130-
try {
131-
// Most recent finished batch
132-
const last = prod.prepare(`
133-
SELECT id, started_at, finished_at, snapshot_size, passed, failed, mode,
134-
(SELECT COUNT(*) FROM batch_results WHERE batch_id = batches.id) AS rows_count
135-
FROM batches WHERE finished_at IS NOT NULL ORDER BY id DESC LIMIT 1
136-
`).get();
137-
ok(last && last.id > 0, `prod has finished batches (most recent id=${last?.id})`);
138-
ok(last && last.finished_at > last.started_at, 'prod batch finished_at > started_at');
139-
ok(last && (last.passed + last.failed) > 0, `prod batch has passed+failed > 0 (${last?.passed}+${last?.failed})`);
140-
ok(last && last.rows_count > 0, `prod batch has batch_results rows (${last?.rows_count})`);
141-
142-
// Aggregate
143-
const agg = prod.prepare(`
144-
SELECT COUNT(DISTINCT b.id) AS batches,
145-
COUNT(br.id) AS results,
146-
COUNT(DISTINCT br.node_address) AS distinct_nodes
147-
FROM batches b LEFT JOIN batch_results br ON br.batch_id = b.id
148-
`).get();
149-
ok(agg.batches > 0, `prod has ${agg.batches} batches recorded`);
150-
ok(agg.results > 0, `prod has ${agg.results} batch_results rows`);
151-
ok(agg.distinct_nodes > 100,
152-
`prod has ${agg.distinct_nodes} distinct nodes tested across all batches`);
153-
154-
// Schema sanity — error_code distinct values prove failure-log path works
155-
const codes = prod.prepare(`
156-
SELECT error_code, COUNT(*) c FROM batch_results
157-
WHERE error_code IS NOT NULL GROUP BY error_code ORDER BY c DESC LIMIT 5
158-
`).all();
159-
console.log(' prod error_code distribution (top 5):');
160-
for (const r of codes) console.log(` ${r.error_code.padEnd(30)} ${r.c}`);
161-
ok(codes.length > 0, 'prod batch_results contains failure rows with error codes');
162-
163-
// Mode distribution
164-
const modes = prod.prepare(`SELECT mode, COUNT(*) c FROM batches GROUP BY mode`).all();
165-
console.log(' prod batch mode distribution:');
166-
for (const m of modes) console.log(` ${m.mode.padEnd(15)} ${m.c}`);
167-
} finally {
168-
prod.close();
169-
}
170-
}
120+
// The prod-DB cross-check that used to live here (Part 4) has moved to
121+
// scripts/verify-prod-db.mjs — it asserted facts about whatever audit.db was
122+
// on disk (distinct_nodes > 100, etc.), so it could only pass on a populated
123+
// production DB and failed on dev/CI/fresh clones. That is an operational
124+
// health check, not a regression test; this file stays hermetic.
171125

172126
console.log(`\n${'='.repeat(60)}\nRESULTS: ${out.pass} passed, ${out.fail} failed`);
173127
if (out.errors.length) for (const e of out.errors) console.log(` FAIL: ${e}`);

0 commit comments

Comments
 (0)