Files
Dune/tests/prototype-room.test.ts
T
Quintine 6e6fecc9d4 Prototype Leader Skills setup, battle disciplines and capture
Connect all fourteen physical skills and private setup to five card-role battle
modifiers, public posture, Harkonnen skilled capture, death return and optional
own-leader revival through controls, legal AI and persisted room actions.
Nine skill effects and combined-mode/publication acceptance remain unfinished.
Captured replacement entitlement and foreign ghola integration stay guarded.

Fresh publisher/designer research supports skilled-capture publicity through
its unique known physical card; this is recorded as a component inference.
Independent review found and fixed Voice/must-use-leader and invalid saved
assignment/offer cases. A pre-existing Discovery privacy assertion confused
card ID prefixes; the corrected deterministic physical-pair scenario was
independently reviewed. The first broad run was 4410/4411; corrected final
verification passes types/lint, all 4411 offline tests, build and 43 HTTP tests.
Final durations: check 149741ms, build 6659ms, integration 28274ms.
Source fingerprint: aef655ac1ebc0eb2f64294e13d3d27337167312cf8ab17568a166ee9c39f6a85.
Private final report: 20260913-leader-skills/checks-final/report.json.

Three seeded prototype games completed with no rejected bot candidates. Browser
setup/card inspection/refresh passed. Another user-reported power outage stopped
the server; backup and necessary restart restored the same private seat. All
292 games at restart and their original application records remain unchanged;
33 isolated HTTP-test rooms bring the current total to 325. No database reset.

Efficiency: one shared setup/lifecycle implementation supports five connected
role effects; selective existing agents handled catalog/UI/tests/research and
independent reviews. One repeated broad check was required by the reproduced
ID-prefix assertion failure. Aggregate goal counter rose from 65529766 to
67313673 through this checkpoint (1783907), including inherited context and
coordination; this is not a billing or savings estimate. Full goal remains active.
2026-09-13 17:31:37 +10:00

149 lines
4.7 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { DatabaseSync } from 'node:sqlite';
import { createGame, joinGame, newPlayer } from '../game/engine';
import {
startIxPrototypeRoom,
startPrototypeRoom,
} from '../tools/prototype-room';
function fixture(t: test.TestContext) {
const db = new DatabaseSync(':memory:');
t.after(() => db.close());
db.exec(
'CREATE TABLE rooms(code TEXT PRIMARY KEY, state TEXT, version INTEGER, updated_at INTEGER); CREATE TABLE seats(room_code TEXT, player_id TEXT, token_hash TEXT);',
);
const game = createGame(
'PROTOTYP',
newPlayer('i', 'Ix player', 'ixians'),
true,
);
game.expansions = ['ix'];
joinGame(game, newPlayer('t', 'Tleilaxu player', 'tleilaxu'));
game.players.forEach((player) => {
player.ready = true;
});
db.prepare('INSERT INTO rooms VALUES(?,?,?,?)').run(
game.code,
JSON.stringify(game),
7,
100,
);
db.prepare('INSERT INTO rooms VALUES(?,?,?,?)').run(
'KEEPME00',
'{"unchanged":true}',
3,
50,
);
db.prepare('INSERT INTO seats VALUES(?,?,?)').run(
game.code,
'i',
'unchanged-private-session',
);
return { db, game };
}
void test('local prototype starts the named ready lobby once, retaining every seat and other room', (t) => {
const { db } = fixture(t);
const seats = db.prepare('SELECT * FROM seats').all();
const other = db
.prepare('SELECT * FROM rooms WHERE code = ?')
.get('KEEPME00');
const result = startIxPrototypeRoom(db, 'PROTOTYP', 7);
assert.equal(result.version, 8);
assert.equal(result.status, 'setup');
const saved = db
.prepare('SELECT state, version FROM rooms WHERE code = ?')
.get('PROTOTYP')!;
assert.equal(JSON.parse(saved.state as string).version, saved.version);
assert.deepEqual(db.prepare('SELECT * FROM seats').all(), seats);
assert.deepEqual(
db.prepare('SELECT * FROM rooms WHERE code = ?').get('KEEPME00'),
other,
);
const rows = db.prepare('SELECT * FROM rooms ORDER BY code').all();
assert.throws(() => startIxPrototypeRoom(db, 'PROTOTYP', 7), /changed/);
assert.throws(() => startIxPrototypeRoom(db, 'PROTOTYP', 8), /fresh lobby/);
assert.deepEqual(db.prepare('SELECT * FROM rooms ORDER BY code').all(), rows);
});
void test('unready, wrong identity and incompatible lobbies fail without writing any room or seat', (t) => {
const { db, game } = fixture(t);
for (const mutate of [
() => {
game.players[0].ready = false;
},
() => {
game.players[0].ready = true;
game.code = 'NOTMATCH';
},
() => {
game.code = 'PROTOTYP';
game.expansions = [];
},
]) {
mutate();
db.prepare('UPDATE rooms SET state = ? WHERE code = ?').run(
JSON.stringify(game),
'PROTOTYP',
);
const before = db.prepare('SELECT * FROM rooms ORDER BY code').all();
assert.throws(() => startIxPrototypeRoom(db, 'PROTOTYP', 7));
assert.deepEqual(
db.prepare('SELECT * FROM rooms ORDER BY code').all(),
before,
);
assert.equal(
db.prepare('SELECT count(*) AS count FROM seats').get()!.count,
1,
);
}
});
void test('Leader Skills prototype saves the real private setup once without changing other rooms or seats', (t) => {
const { db } = fixture(t);
const game = createGame(
'PROTOTYP',
newPlayer('i', 'Atreides', 'atreides'),
true,
);
joinGame(game, newPlayer('h', 'Harkonnen', 'harkonnen'));
game.players.forEach((p) => {
p.ready = true;
});
db.prepare('UPDATE rooms SET state = ? WHERE code = ?').run(
JSON.stringify(game),
game.code,
);
const seats = db.prepare('SELECT * FROM seats').all();
const other = db
.prepare('SELECT * FROM rooms WHERE code = ?')
.get('KEEPME00');
const result = startPrototypeRoom(db, game.code, 7, 'leader-skills');
assert.equal(result.version, 8);
const saved = JSON.parse(
db.prepare('SELECT state FROM rooms WHERE code = ?').get(game.code)!
.state as string,
);
assert.equal(saved.version, 8);
assert.equal(saved.setupStage, 'leaderSkills');
assert.equal(saved.leaderSkills.offers.i.cards.length, 2);
assert.equal(saved.leaderSkills.offers.h.cards.length, 2);
assert.equal(saved.players[0].hand.length, 1);
assert.equal(saved.players[1].hand.length, 2);
const rooms = db.prepare('SELECT * FROM rooms ORDER BY code').all();
assert.throws(
() => startPrototypeRoom(db, game.code, 8, 'leader-skills'),
/redeal/,
);
assert.deepEqual(
db.prepare('SELECT * FROM rooms ORDER BY code').all(),
rooms,
);
assert.deepEqual(db.prepare('SELECT * FROM seats').all(), seats);
assert.deepEqual(
db.prepare('SELECT * FROM rooms WHERE code = ?').get('KEEPME00'),
other,
);
});