feat: v0.6.0 — data foundation: Drizzle ORM + SQLite (sql.js/WASM) persistence, first-run seed, read-only GET /api/state snapshot
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import fs from 'node:fs/promises';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
|
||||
import { openGameDb, persist } from './client';
|
||||
import * as schema from './schema';
|
||||
|
||||
let dir: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
dir = await fs.mkdtemp(path.join(os.tmpdir(), 'flight-db-'));
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await fs.rm(dir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
describe('openGameDb', () => {
|
||||
it('seeds a fresh DB file on first open and persists it to disk', async () => {
|
||||
const file = path.join(dir, 'fresh.db');
|
||||
const inst = await openGameDb(file);
|
||||
|
||||
const planes = await inst.db.select().from(schema.planes);
|
||||
expect(planes).toHaveLength(1);
|
||||
expect(planes[0].id).toBe('plane-starter');
|
||||
expect(planes[0].status).toBe('available');
|
||||
expect(planes[0].locationIcao).toBe('EDDF'); // Frankfurt hub from the CSV
|
||||
expect(planes[0].fuel).toBe(planes[0].maxFuel);
|
||||
|
||||
const contracts = await inst.db.select().from(schema.contracts);
|
||||
expect(contracts.length).toBeGreaterThanOrEqual(8);
|
||||
expect(contracts.every((c) => c.status === 'open')).toBe(true);
|
||||
|
||||
const account = (await inst.db.select().from(schema.account))[0];
|
||||
expect(account?.cash).toBeGreaterThan(0);
|
||||
const sim = (await inst.db.select().from(schema.simState))[0];
|
||||
expect(sim?.paused).toBe(false);
|
||||
expect(sim?.timeScale).toBe(1);
|
||||
|
||||
// The seed was durably written: the file exists and is non-empty.
|
||||
const st = await fs.stat(file);
|
||||
expect(st.size).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('does not re-seed an existing DB — state survives reopen', async () => {
|
||||
const file = path.join(dir, 'reopen.db');
|
||||
const first = await openGameDb(file);
|
||||
const contracts1 = (await first.db.select().from(schema.contracts)).map((c) => c.id).sort();
|
||||
const account1 = (await first.db.select().from(schema.account))[0];
|
||||
|
||||
// Simulate a process restart: brand-new instance over the same file.
|
||||
const second = await openGameDb(file);
|
||||
const contracts2 = (await second.db.select().from(schema.contracts)).map((c) => c.id).sort();
|
||||
expect(contracts2).toEqual(contracts1);
|
||||
expect((await second.db.select().from(schema.account))[0]).toEqual(account1);
|
||||
});
|
||||
|
||||
it('persist() durably saves writes made after open', async () => {
|
||||
const file = path.join(dir, 'write.db');
|
||||
const inst = await openGameDb(file);
|
||||
|
||||
await inst.db.update(schema.account).set({ cash: 12345 });
|
||||
await persist(inst);
|
||||
|
||||
// A fresh instance over the same file sees the update.
|
||||
const reloaded = await openGameDb(file);
|
||||
expect((await reloaded.db.select().from(schema.account))[0]?.cash).toBe(12345);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user