mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 22:16:26 +00:00
0a4c6fbace
db/database.ts opened the connection as a side effect of being imported, so the moment anything reached it the database was open. index.ts had to defer loading the app until a first-boot restore was done, and nothing in the container decided when the connection opened or closed. The module now opens the connection on first use and opens nothing at import. A connection closed on purpose (a restore, the shutdown) stays closed until it is reopened, as before. The new DatabaseLifecycle provider owns the decisions: buildApp() opens through it, which also binds the ORM to later swaps (the hook attachOrm used to register) and runs the schema bootstrap in the same order as before, and the shutdown closes through it. Its file property is the one resolved database path. The module keeps every export, so the test suites that replace it with vi.mock keep working; buildDbMock gains the new openDb.
48 lines
1.8 KiB
TypeScript
48 lines
1.8 KiB
TypeScript
import crypto from 'crypto';
|
|
import fs from 'fs';
|
|
import os from 'os';
|
|
import path from 'path';
|
|
|
|
/**
|
|
* A migrated-but-empty database, serialised once per test run.
|
|
*
|
|
* Migrations are async and `database.ts::openDb()` is not: it runs on the
|
|
* connection's first read, which cannot await. Most suites do not care (they
|
|
* mock the module or build their own database), but a handful use the
|
|
* singleton directly and need a schema to be there the moment they touch it.
|
|
*
|
|
* So the vitest global setup migrates one database for the whole run and writes
|
|
* it here; every worker opens a copy. `better-sqlite3` deserialises a 1.5 MiB
|
|
* snapshot in well under a millisecond, against ~800ms to replay 242 migrations,
|
|
* so this is also what keeps the suite fast.
|
|
*
|
|
* Production never reads this — there `buildApp()` provides the await point.
|
|
*/
|
|
|
|
/** Keyed by the migration set, so a snapshot from another branch is never reused. */
|
|
function snapshotKey(): string {
|
|
const dir = path.join(__dirname, 'migrations');
|
|
// A missing directory (e.g. a partial dist) degrades to a constant key; the
|
|
// global setup rewrites the file either way.
|
|
const names = fs.existsSync(dir) ? fs.readdirSync(dir).sort() : [];
|
|
return crypto.createHash('sha256').update(names.join('\n')).digest('hex').slice(0, 16);
|
|
}
|
|
|
|
/** Outside the repo on purpose: nothing here belongs in a working tree or an image. */
|
|
export function schemaSnapshotPath(): string {
|
|
return path.join(os.tmpdir(), `trek-schema-${snapshotKey()}.db`);
|
|
}
|
|
|
|
export function writeSchemaSnapshot(snapshot: Buffer): void {
|
|
fs.writeFileSync(schemaSnapshotPath(), snapshot);
|
|
}
|
|
|
|
/** The snapshot bytes, or null when no run has produced one. */
|
|
export function readSchemaSnapshot(): Buffer | null {
|
|
try {
|
|
return fs.readFileSync(schemaSnapshotPath());
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|