Files
backend/test/sqlite-regression.cjs
T

97 lines
3.6 KiB
JavaScript

// Full fresh-DB proof, including concurrent runners and failure/signal cleanup.
const assert = require('node:assert/strict');
const { spawn } = require('node:child_process');
const {
mkdtempSync,
writeFileSync,
readFileSync,
existsSync,
} = require('node:fs');
const { tmpdir } = require('node:os');
const { join, resolve } = require('node:path');
const cwd = resolve(__dirname, '..');
const evidence = mkdtempSync(join(tmpdir(), 'wealthledger-sqlite-regression-'));
const sentinel = join(evidence, 'user.db');
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Paths stay in runner-created temporary directories or explicitly selected local test builds.
writeFileSync(sentinel, 'fictional user database: must never be opened');
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Paths stay in runner-created temporary directories or explicitly selected local test builds.
const before = readFileSync(sentinel);
const databases = new Set();
async function check(name, overrides = {}, signal) {
const child = spawn(process.execPath, ['test/sqlite.cjs', '--all'], {
cwd,
env: {
...process.env,
DATABASE_URL: `file:${sentinel}`,
RUST_LOG: 'warn',
LOG_LEVEL: 'error',
...overrides,
},
stdio: ['ignore', 'pipe', 'pipe'],
});
let log = '';
let sent = false;
child.stdout.on('data', (chunk) => {
log += chunk;
if (signal && !sent && log.includes('SQLite ')) {
sent = true;
child.kill(signal);
}
});
child.stderr.on('data', (chunk) => {
log += chunk;
});
const code = await new Promise((resolve, reject) => {
child.once('error', reject);
child.once('close', resolve);
});
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Paths stay in runner-created temporary directories or explicitly selected local test builds.
writeFileSync(join(evidence, `${name}.log`), log);
const dirs = [...log.matchAll(/^SQLite .+: (.+)$/gm)].map(
(match) => match[1],
);
assert.ok(dirs.length > 0, 'runner must allocate a database');
for (const dir of dirs) {
assert.ok(!databases.has(dir), 'runners must never share databases');
databases.add(dir);
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Paths stay in runner-created temporary directories or explicitly selected local test builds.
assert.ok(!existsSync(dir), `runner must clean up ${dir}`);
}
assert.deepEqual(
// eslint-disable-next-line security/detect-non-literal-fs-filename -- Paths stay in runner-created temporary directories or explicitly selected local test builds.
readFileSync(sentinel),
before,
'user database must be untouched',
);
const expected =
signal === 'SIGINT'
? 130
: signal === 'SIGTERM'
? 143
: overrides.PRISMA_SCHEMA_ENGINE_BINARY
? 1
: 0;
assert.equal(code, expected, `See ${name}.log`);
console.log(`PASS ${name}: ${dirs.length} isolated databases`);
}
(async () => {
console.log(`Evidence: ${evidence}`);
for (let i = 1; i <= 3; i++) await check(`sequential-${i}`);
const parallel = await Promise.allSettled([
check('parallel-1'),
check('parallel-2', { RUST_LOG: 'error' }),
]);
for (const result of parallel) {
if (result.status === 'rejected') throw result.reason;
}
await check('migration-failure', {
PRISMA_SCHEMA_ENGINE_BINARY: join(evidence, 'missing-engine'),
});
await check('sigint', {}, 'SIGINT');
await check('sigterm', {}, 'SIGTERM');
console.log(`PASS regression: ${evidence}`);
})().catch((error) => {
console.error(error);
process.exitCode = 1;
});