Flight-Ops plumbing minus game code: in-memory WASM DB, persist() via tmp+rename, globalThis singleton. Schema is empty at scaffold stage; _meta plumbing table stamps created_at on first open so the ./db volume holds a valid SQLite file from boot (a brand-new sql.js database exports as zero bytes). instrumentation.ts opens it at server start behind a runtime-guarded dynamic import (keeps node:fs out of the Edge instrumentation bundle).
52 lines
1.9 KiB
TypeScript
52 lines
1.9 KiB
TypeScript
import { mkdtemp, rm, stat } from 'node:fs/promises';
|
|
import { tmpdir } from 'node:os';
|
|
import path from 'node:path';
|
|
import { afterAll, describe, expect, it } from 'vitest';
|
|
import { openGameDb, persist } from './client';
|
|
|
|
const dir = await mkdtemp(path.join(tmpdir(), 'sw-db-test-'));
|
|
const file = path.join(dir, 'test.db');
|
|
|
|
afterAll(async () => {
|
|
await rm(dir, { recursive: true, force: true });
|
|
});
|
|
|
|
function metaValue(db: Awaited<ReturnType<typeof openGameDb>>, key: string): string | undefined {
|
|
const rows = db.raw.exec('SELECT value FROM _meta WHERE key = ?', [key]);
|
|
return rows[0]?.values[0][0] as string | undefined;
|
|
}
|
|
|
|
describe('db client (sql.js/Drizzle plumbing)', () => {
|
|
it('creates a valid DB file on disk when opening a fresh path', async () => {
|
|
const db = await openGameDb(file);
|
|
expect(db.file).toBe(file);
|
|
const s = await stat(file);
|
|
expect(s.size).toBeGreaterThan(0); // persisted at first open, not left in-memory only
|
|
expect(metaValue(db, 'created_at')).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
});
|
|
|
|
it('preserves state across reopen (volume persistence)', async () => {
|
|
const created = metaValue(await openGameDb(file), 'created_at');
|
|
expect(created).toBeDefined();
|
|
const db = await openGameDb(file); // second process would see the same file
|
|
expect(db.raw.exec('PRAGMA integrity_check')[0].values[0][0]).toBe('ok');
|
|
expect(metaValue(db, 'created_at')).toBe(created); // not re-stamped on reopen
|
|
});
|
|
|
|
it('persist() rewrites the file atomically (tmp + rename)', async () => {
|
|
const db = await openGameDb(file);
|
|
await persist(db);
|
|
const s = await stat(file);
|
|
expect(s.size).toBeGreaterThan(0);
|
|
// no leftover tmp file after a clean persist
|
|
let tmpGone = true;
|
|
try {
|
|
await stat(`${file}.tmp`);
|
|
tmpGone = false;
|
|
} catch {
|
|
/* expected */
|
|
}
|
|
expect(tmpGone).toBe(true);
|
|
});
|
|
});
|