test(database): require one SQL statement per exec during initialization

The performance capture counts one exec call as one statement, because
SQL cannot be split reliably in the counter (trigger bodies contain
semicolons). The historical-upgrade driver now wraps exec on every
connection initDatabase opens and fails on a batch, so that counting
assumption holds for the fresh profile and all historical schemas.
Documents the definition in the counter and the architecture docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-09-27 18:48:29 +02:00
1 parent 37ac72b951
commit 757166b43a
4 files changed
+45 -4

No files matched your search

@@ -3,6 +3,7 @@ import { readFileSync } from 'node:fs';
import Database from 'better-sqlite3';
import { getTableConfig } from 'drizzle-orm/sqlite-core';
import { closeDatabase, getDatabasePath, initDatabase } from '../connection';
import { setDatabaseConnectionObserver } from '../connection-observer';
import * as currentSchema from '../schema';
const tables = [
@@ -38,6 +39,31 @@ function seed(sqlite: Database.Database) {
}
}
/**
* The performance capture counts one `exec` call as one SQL statement
* (apps/electron-backend/src/app/workers/database-worker-sql-statement-count.ts),
* so initialization must pass exactly one statement per `exec`. better-sqlite3
* refuses to prepare a string with a second statement; other prepare errors
* are left to `exec` itself (for example an idempotent ALTER TABLE).
*/
function requireSingleStatementExec(): string[] {
const batches: string[] = [];
setDatabaseConnectionObserver((connection) => {
const exec = connection.exec;
connection.exec = function singleStatementExec(sql: string) {
try {
connection.prepare(sql);
} catch (error) {
if (error instanceof RangeError) {
batches.push(sql.replace(/\s+/g, ' ').slice(0, 120));
}
}
return exec.call(this, sql);
};
});
return batches;
}
function snapshot(sqlite: Database.Database) {
return tables.map((table) => {
const columns = sqlite.pragma(`table_info(${table})`) as {
@@ -98,6 +124,7 @@ async function main() {
console.warn = (...args) => warnings.push(args);
console.log = () => undefined;
const databasePath = getDatabasePath();
const execBatches = requireSingleStatementExec();
if (fixture === 'fresh') {
await initDatabase();
@@ -157,6 +184,11 @@ async function main() {
[],
'Initialization must not silently skip failed migrations'
);
assert.deepEqual(
execBatches,
[],
'Initialization must pass one SQL statement per exec call'
);
process.stdout.write('upgrade verified');
}