Files
Dune/tests/saved-game-verification.test.ts
Quintine 6e26ba5daa Add repeatable verification and establish fresh save checkpoint
Consolidate current readiness and existing rule decisions, preserve selective
subagents and automatic verified Git pushes, and record the efficiency pilot.
Add source-bound compact checks, private saved-game snapshots/online backups,
and bounded HTTP seat-restoration checks with 15 meaningful regressions.

At the user's explicit request, archive and clear 3,644 old local rooms and
related records while retaining schema/migrations; preserve future games from
a fresh empty baseline. Private backups and credentials are outside Git.

Validation: npm run check passes types, lint and all 4,177 offline tests.
Full report: 20260913-efficiency-check-3, 130.147 seconds; source fingerprint
344ce1d44475b0cd4ff10a915f28331cb4e2fcacbc7595636fbaa645c0568f64.
Two earlier broad attempts stopped at new type/lint errors before tests;
both were fixed. Independent review found cancellation falsely passing;
SIGINT/SIGTERM regressions and review confirm the fix. Documentation links,
example test selection, private backup integrity and empty baseline verified.
No app/build/dependency/HTTP implementation changed; no production build,
HTTP suite, browser acceptance or AI calibration claimed for this checkpoint.
The full game goal and existing release gates remain unfinished.
2026-09-13 10:39:34 +10:00

103 lines
3.7 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import * as sqlite from 'node:sqlite';
import { mkdtempSync, mkdirSync, rmSync, existsSync, statSync } from 'node:fs';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import {
captureSavedGames,
compareSavedGames,
loadSavedGameSnapshot,
readSavedGames,
} from '../tools/saved-game-verification';
function fixture(t: test.TestContext) {
const area = mkdtempSync(join(tmpdir(), 'dune-preservation-'));
const root = join(area, 'repo');
mkdirSync(root);
const path = join(root, 'games.sqlite'),
db = new sqlite.DatabaseSync(path);
db.exec(
"PRAGMA journal_mode=WAL; CREATE TABLE rooms(code TEXT PRIMARY KEY, version INTEGER, state TEXT); INSERT INTO rooms VALUES('ABCDEFGH', 2, '{\"privateHand\":[1]}')",
);
t.after(() => {
db.close();
rmSync(area, { recursive: true, force: true });
});
return { area, root, path, db };
}
void test('preservation allows new rooms but detects changed state, versions and missing originals', (t) => {
const { path, db } = fixture(t),
baseline = readSavedGames(path);
db.exec("INSERT INTO rooms VALUES('BCDEFGHJ', 0, '{}')");
const added = compareSavedGames(baseline, readSavedGames(path));
assert.equal(added.preserved, true);
assert.equal(added.added, 1);
db.exec("UPDATE rooms SET version=3 WHERE code='ABCDEFGH'");
assert.equal(
compareSavedGames(baseline, readSavedGames(path)).preserved,
false,
);
db.exec("UPDATE rooms SET version=2, state='{}' WHERE code='ABCDEFGH'");
assert.deepEqual(compareSavedGames(baseline, readSavedGames(path)).changed, [
'ABCDEFGH',
]);
db.exec("DELETE FROM rooms WHERE code='ABCDEFGH'");
assert.deepEqual(compareSavedGames(baseline, readSavedGames(path)).missing, [
'ABCDEFGH',
]);
});
void test('private snapshot stores hashes and cannot overwrite an earlier baseline', async (t) => {
const { area, root, path } = fixture(t),
out = join(area, 'snapshot');
const result = await captureSavedGames(root, path, out);
assert.deepEqual(loadSavedGameSnapshot(join(out, 'snapshot.json')), result);
assert.equal(JSON.stringify(result).includes('privateHand'), false);
assert.equal(statSync(join(out, 'snapshot.json')).mode & 0o777, 0o600);
await assert.rejects(captureSavedGames(root, path, out));
assert.deepEqual(loadSavedGameSnapshot(join(out, 'snapshot.json')), result);
assert.throws(
() =>
compareSavedGames(
{ ...result, rooms: [...result.rooms, ...result.rooms] },
result,
),
/Invalid/,
);
});
void test('missing source never creates a database or output', async (t) => {
const { area, root } = fixture(t),
missing = join(area, 'missing.sqlite'),
out = join(area, 'out');
await assert.rejects(captureSavedGames(root, missing, out));
assert.equal(existsSync(missing), false);
assert.equal(existsSync(out), false);
});
void test(
'online backup includes WAL state and leaves the live database untouched',
{ skip: typeof sqlite.backup !== 'function' },
async (t) => {
const { area, root, path, db } = fixture(t),
out = join(area, 'backup');
db.exec("INSERT INTO rooms VALUES('CDEFGHJK', 4, '{\"pending\":true}')");
const before = readSavedGames(path),
snapshot = await captureSavedGames(root, path, out, true);
assert.equal(compareSavedGames(before, snapshot).preserved, true);
assert.equal(snapshot.rooms.length, 2);
assert.equal(
compareSavedGames(snapshot, readSavedGames(join(out, 'games.sqlite')))
.preserved,
true,
);
assert.equal(
compareSavedGames(before, readSavedGames(path)).preserved,
true,
);
assert.equal(statSync(join(out, 'games.sqlite')).mode & 0o777, 0o600);
},
);