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.
This commit is contained in:
1 parent
06d0a66fc5
commit
6e26ba5daa
17 files changed
+1242
-2
No files matched your search
@@ -0,0 +1,102 @@
|
||||
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);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,130 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
mkdtempSync,
|
||||
mkdirSync,
|
||||
writeFileSync,
|
||||
chmodSync,
|
||||
rmSync,
|
||||
} from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { tmpdir } from 'node:os';
|
||||
import {
|
||||
checkSeatRestoration,
|
||||
loadRestorationConfig,
|
||||
type RestorationConfig,
|
||||
} from '../tools/seat-restoration';
|
||||
|
||||
const config: RestorationConfig = {
|
||||
baseUrl: 'http://127.0.0.1:3000',
|
||||
isolatedQa: true,
|
||||
seats: [
|
||||
{ code: 'ABCDEFGH', playerId: 'p1', version: 4, token: 'private_token' },
|
||||
],
|
||||
};
|
||||
const view = () => ({
|
||||
code: 'ABCDEFGH',
|
||||
me: 'p1',
|
||||
version: 4,
|
||||
players: [{ id: 'p1', hand: [], traitors: [], spice: 10 }, { id: 'p2' }],
|
||||
});
|
||||
const respond =
|
||||
(body: unknown, status = 200): typeof fetch =>
|
||||
async () =>
|
||||
Response.json(body, { status });
|
||||
|
||||
void test('restoration authenticates the expected seat and prevents cookie redirects', async () => {
|
||||
const request: typeof fetch = async (url, options) => {
|
||||
assert.ok(url instanceof URL);
|
||||
assert.equal(url.href, 'http://127.0.0.1:3000/api/rooms/ABCDEFGH');
|
||||
assert.equal(
|
||||
new Headers(options?.headers).get('cookie'),
|
||||
'dune_ABCDEFGH=private_token',
|
||||
);
|
||||
assert.equal(options?.redirect, 'error');
|
||||
assert.ok(options?.signal);
|
||||
return Response.json(view());
|
||||
};
|
||||
assert.deepEqual(await checkSeatRestoration(config, request), {
|
||||
checked: 1,
|
||||
passed: true,
|
||||
});
|
||||
});
|
||||
|
||||
void test('restoration refuses wrong identity, stale versions, missing private state and rival secrets', async () => {
|
||||
for (const body of [
|
||||
{ ...view(), me: 'p2' },
|
||||
{ ...view(), code: 'BCDEFGHJ' },
|
||||
{ ...view(), version: 5 },
|
||||
{ ...view(), players: [{ id: 'p1' }] },
|
||||
{ ...view(), botsPending: true },
|
||||
{
|
||||
...view(),
|
||||
players: [...view().players, { id: 'p3', traitors: ['secret'] }],
|
||||
},
|
||||
])
|
||||
await assert.rejects(
|
||||
checkSeatRestoration(config, respond(body)),
|
||||
/^Error: Seat 1 failed/,
|
||||
);
|
||||
await assert.rejects(
|
||||
checkSeatRestoration(
|
||||
config,
|
||||
respond({ error: 'private_token secret response' }, 403),
|
||||
),
|
||||
(error) => {
|
||||
assert.equal(String(error).includes('private_token'), false);
|
||||
return true;
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
void test('invalid origins and unsafe credentials cause no network requests', async () => {
|
||||
let calls = 0;
|
||||
const request: typeof fetch = async () => {
|
||||
calls++;
|
||||
return Response.json(view());
|
||||
};
|
||||
for (const baseUrl of [
|
||||
'https://example.com',
|
||||
'http://127.0.0.1.evil.test',
|
||||
'http://user:password@localhost',
|
||||
'http://localhost/other',
|
||||
'http://localhost/?secret=token',
|
||||
])
|
||||
await assert.rejects(checkSeatRestoration({ ...config, baseUrl }, request));
|
||||
await assert.rejects(
|
||||
checkSeatRestoration(
|
||||
{ ...config, seats: [{ ...config.seats[0], token: 'a; other=cookie' }] },
|
||||
request,
|
||||
),
|
||||
);
|
||||
await assert.rejects(
|
||||
checkSeatRestoration(
|
||||
{ ...config, isolatedQa: false } as unknown as RestorationConfig,
|
||||
request,
|
||||
),
|
||||
);
|
||||
assert.equal(calls, 0);
|
||||
});
|
||||
|
||||
void test('seat manifests must be private files outside the checkout', (t) => {
|
||||
const area = mkdtempSync(join(tmpdir(), 'dune-seat-manifest-'));
|
||||
t.after(() => rmSync(area, { recursive: true, force: true }));
|
||||
const root = join(area, 'repo');
|
||||
mkdirSync(root);
|
||||
const outside = join(area, 'seats.json'),
|
||||
inside = join(root, 'seats.json');
|
||||
writeFileSync(outside, JSON.stringify(config), { mode: 0o600 });
|
||||
writeFileSync(inside, JSON.stringify(config), { mode: 0o600 });
|
||||
assert.deepEqual(loadRestorationConfig(outside, root), config);
|
||||
assert.throws(
|
||||
() => loadRestorationConfig(inside, root),
|
||||
/private file outside/,
|
||||
);
|
||||
chmodSync(outside, 0o644);
|
||||
assert.throws(
|
||||
() => loadRestorationConfig(outside, root),
|
||||
/private file outside/,
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,208 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { execFileSync, spawnSync } from 'node:child_process';
|
||||
import {
|
||||
mkdtempSync,
|
||||
mkdirSync,
|
||||
writeFileSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
symlinkSync,
|
||||
statSync,
|
||||
existsSync,
|
||||
} from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import {
|
||||
privateOutputDirectory,
|
||||
runVerification,
|
||||
sourceSnapshot,
|
||||
} from '../tools/verification';
|
||||
|
||||
void test('focused wrapper cannot turn a filename into a list-only or test-skipping option', (t) => {
|
||||
const { root, area } = fixture(t),
|
||||
out = join(area, 'run');
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[
|
||||
'--import',
|
||||
import.meta.resolve('tsx'),
|
||||
new URL('../tools/verify.ts', import.meta.url).pathname,
|
||||
'--out',
|
||||
out,
|
||||
'focused',
|
||||
'--focus=--list',
|
||||
],
|
||||
{ cwd: root, encoding: 'utf8', timeout: 15000 },
|
||||
);
|
||||
assert.equal(result.status, 1);
|
||||
assert.equal(existsSync(out), false);
|
||||
});
|
||||
|
||||
function fixture(t: test.TestContext) {
|
||||
const area = mkdtempSync(join(tmpdir(), 'dune-verification-'));
|
||||
t.after(() => rmSync(area, { recursive: true, force: true }));
|
||||
const root = join(area, 'repo');
|
||||
mkdirSync(root);
|
||||
const git = (...args: string[]) =>
|
||||
execFileSync('git', args, { cwd: root, stdio: 'pipe' });
|
||||
git('init', '-q');
|
||||
writeFileSync(join(root, '.gitignore'), 'private.txt\n');
|
||||
writeFileSync(join(root, 'source.txt'), 'original');
|
||||
git('add', '.');
|
||||
git(
|
||||
'-c',
|
||||
'user.name=Verification',
|
||||
'-c',
|
||||
'user.email=verification@example.invalid',
|
||||
'commit',
|
||||
'-qm',
|
||||
'fixture',
|
||||
);
|
||||
return { area, root };
|
||||
}
|
||||
|
||||
void test('interrupted checks fail even when a child handles the signal with exit zero', (t) => {
|
||||
const { root, area } = fixture(t);
|
||||
for (const [signal, exitCode] of [
|
||||
['SIGINT', 130],
|
||||
['SIGTERM', 143],
|
||||
] as const) {
|
||||
const out = join(area, signal),
|
||||
script = join(area, `${signal}.mts`);
|
||||
const steps = [
|
||||
{
|
||||
name: 'interrupted',
|
||||
command: process.execPath,
|
||||
args: [
|
||||
'-e',
|
||||
`process.on('${signal}',()=>process.exit(0));process.kill(process.ppid,'${signal}');setInterval(()=>{},1000);`,
|
||||
],
|
||||
},
|
||||
{ name: 'must-not-run', command: process.execPath, args: ['-e', ''] },
|
||||
];
|
||||
writeFileSync(
|
||||
script,
|
||||
`import { runVerification } from ${JSON.stringify(new URL('../tools/verification.ts', import.meta.url).href)};\nawait runVerification(${JSON.stringify(root)}, ${JSON.stringify(out)}, ${JSON.stringify(steps)});\n`,
|
||||
);
|
||||
execFileSync(
|
||||
process.execPath,
|
||||
['--import', import.meta.resolve('tsx'), script],
|
||||
{ cwd: root, stdio: 'pipe', timeout: 15000 },
|
||||
);
|
||||
const report = JSON.parse(readFileSync(join(out, 'report.json'), 'utf8'));
|
||||
assert.equal(report.status, 'failed');
|
||||
assert.equal(report.steps.length, 1);
|
||||
assert.equal(report.steps[0].exitCode, exitCode);
|
||||
}
|
||||
});
|
||||
|
||||
void test('fingerprints include untracked source and ignore private local material', (t) => {
|
||||
const { root } = fixture(t),
|
||||
before = sourceSnapshot(root);
|
||||
writeFileSync(join(root, 'private.txt'), 'never include this');
|
||||
assert.deepEqual(sourceSnapshot(root), before);
|
||||
writeFileSync(join(root, 'new.ts'), 'new source');
|
||||
assert.notEqual(sourceSnapshot(root).tree, before.tree);
|
||||
rmSync(join(root, 'new.ts'));
|
||||
rmSync(join(root, 'source.txt'));
|
||||
assert.notEqual(sourceSnapshot(root).tree, before.tree);
|
||||
});
|
||||
|
||||
void test('verification preserves real child failures, stops later work and stores private logs', async (t) => {
|
||||
const { root, area } = fixture(t),
|
||||
out = join(area, 'run');
|
||||
const result = await runVerification(root, out, [
|
||||
{
|
||||
name: 'first',
|
||||
command: process.execPath,
|
||||
args: ['-e', 'console.log("first complete")'],
|
||||
},
|
||||
{
|
||||
name: 'failure',
|
||||
command: process.execPath,
|
||||
args: ['-e', 'console.error("failure detail");process.exit(7)'],
|
||||
},
|
||||
{
|
||||
name: 'must-not-run',
|
||||
command: process.execPath,
|
||||
args: ['-e', 'process.exit(0)'],
|
||||
},
|
||||
]);
|
||||
assert.equal(result.status, 'failed');
|
||||
assert.deepEqual(
|
||||
result.steps.map((s) => s.exitCode),
|
||||
[0, 7],
|
||||
);
|
||||
assert.match(
|
||||
readFileSync(join(out, 'failure.log'), 'utf8'),
|
||||
/failure detail/,
|
||||
);
|
||||
assert.equal(
|
||||
JSON.parse(readFileSync(join(out, 'report.json'), 'utf8')).status,
|
||||
'failed',
|
||||
);
|
||||
assert.equal(statSync(out).mode & 0o777, 0o700);
|
||||
assert.equal(statSync(join(out, 'report.json')).mode & 0o777, 0o600);
|
||||
await assert.rejects(
|
||||
runVerification(root, out, [
|
||||
{ name: 'repeat', command: process.execPath, args: ['-e', ''] },
|
||||
]),
|
||||
);
|
||||
assert.match(
|
||||
readFileSync(join(out, 'failure.log'), 'utf8'),
|
||||
/failure detail/,
|
||||
);
|
||||
});
|
||||
|
||||
void test('green processes cannot certify source changed during verification', async (t) => {
|
||||
const { root, area } = fixture(t);
|
||||
const result = await runVerification(root, join(area, 'run'), [
|
||||
{
|
||||
name: 'edit',
|
||||
command: process.execPath,
|
||||
args: ['-e', 'require("node:fs").writeFileSync("source.txt", "changed")'],
|
||||
},
|
||||
]);
|
||||
assert.equal(result.steps[0].exitCode, 0);
|
||||
assert.equal(result.status, 'source-changed');
|
||||
assert.notEqual(result.before.tree, result.after?.tree);
|
||||
});
|
||||
|
||||
void test('unchanged successful checks pass and missing executables fail', async (t) => {
|
||||
const { root, area } = fixture(t);
|
||||
assert.equal(
|
||||
(
|
||||
await runVerification(root, join(area, 'good'), [
|
||||
{ name: 'ok', command: process.execPath, args: ['-e', ''] },
|
||||
])
|
||||
).status,
|
||||
'passed',
|
||||
);
|
||||
assert.equal(
|
||||
(
|
||||
await runVerification(root, join(area, 'bad'), [
|
||||
{ name: 'missing', command: join(area, 'missing-command'), args: [] },
|
||||
])
|
||||
).status,
|
||||
'failed',
|
||||
);
|
||||
});
|
||||
|
||||
void test('output refuses checkout paths, symlink redirects and existing results', (t) => {
|
||||
const { root, area } = fixture(t);
|
||||
assert.throws(
|
||||
() => privateOutputDirectory(root, join(root, 'results')),
|
||||
/outside/,
|
||||
);
|
||||
symlinkSync(root, join(area, 'redirect'), 'dir');
|
||||
assert.throws(
|
||||
() => privateOutputDirectory(root, join(area, 'redirect/results')),
|
||||
/outside/,
|
||||
);
|
||||
const out = join(area, 'out');
|
||||
privateOutputDirectory(root, out);
|
||||
writeFileSync(join(out, 'sentinel'), 'keep');
|
||||
assert.throws(() => privateOutputDirectory(root, out));
|
||||
assert.equal(readFileSync(join(out, 'sentinel'), 'utf8'), 'keep');
|
||||
});
|
||||
Reference in new issue
Block a user