mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 13:36:48 +00:00
66 lines
2.3 KiB
TypeScript
66 lines
2.3 KiB
TypeScript
/**
|
|
* The schema_version bump must commit with the migration that earned it.
|
|
*
|
|
* If it lands in a statement of its own, a crash between the two leaves the
|
|
* schema advanced and the version stale, and the next boot replays a step that
|
|
* is not idempotent (an INSERT INTO app_settings, say) and exits 1 forever.
|
|
*/
|
|
import { describe, it, expect, vi } from 'vitest';
|
|
import Database from 'better-sqlite3';
|
|
|
|
vi.mock('../../../src/config', () => ({
|
|
ENCRYPTION_KEY: 'migration-atomicity-test-key',
|
|
JWT_SECRET: 'migration-atomicity-test-secret',
|
|
updateJwtSecret: vi.fn(),
|
|
}));
|
|
vi.mock('../../../src/db/database', () => {
|
|
throw new Error('Migration version atomicity tests must not import the global database');
|
|
});
|
|
|
|
import { createTables } from '../../../src/db/schema';
|
|
import { runMigrations } from '../../../src/db/migrations';
|
|
|
|
function migratedDb(): Database.Database {
|
|
const db = new Database(':memory:');
|
|
db.exec('PRAGMA journal_mode = WAL');
|
|
db.exec('PRAGMA busy_timeout = 5000');
|
|
db.exec('PRAGMA foreign_keys = ON');
|
|
createTables(db);
|
|
runMigrations(db);
|
|
return db;
|
|
}
|
|
|
|
describe('migration version bump atomicity', () => {
|
|
it('MIGRATE-ATOMIC-001: writes schema_version inside the migration transaction', () => {
|
|
const db = migratedDb();
|
|
try {
|
|
const { version } = db.prepare('SELECT version FROM schema_version LIMIT 1').get() as { version: number };
|
|
// Rewind one slot so exactly the last migration replays.
|
|
db.prepare('UPDATE schema_version SET version = ?').run(version - 1);
|
|
|
|
const realPrepare = db.prepare.bind(db);
|
|
const inTransaction: boolean[] = [];
|
|
vi.spyOn(db, 'prepare').mockImplementation(((sql: string) => {
|
|
const stmt = realPrepare(sql);
|
|
if (sql.includes('UPDATE schema_version SET version')) {
|
|
const realRun = stmt.run.bind(stmt);
|
|
stmt.run = ((...args: unknown[]) => {
|
|
inTransaction.push(db.inTransaction);
|
|
return realRun(...(args as never[]));
|
|
}) as typeof stmt.run;
|
|
}
|
|
return stmt;
|
|
}) as typeof db.prepare);
|
|
|
|
runMigrations(db);
|
|
|
|
expect(inTransaction).toEqual([true]);
|
|
const after = db.prepare('SELECT version FROM schema_version LIMIT 1').get() as { version: number };
|
|
expect(after.version).toBe(version);
|
|
} finally {
|
|
vi.restoreAllMocks();
|
|
db.close();
|
|
}
|
|
});
|
|
});
|