import assert from 'node:assert/strict'; import test from 'node:test'; import { createGame, joinGame, newPlayer, type Game } from '../game/engine'; import { truthQuestionText, validateTruthQuestionReceipt, type TruthQuestion } from '../game/truthtrance'; /** Parser-only game; this does not claim a legal Ghola completion or bind an answer. */ function gameAt(phase: number): Game { const g = createGame('GHOLATRUTHPARSER', newPlayer('a', 'Asker', 'atreides')); joinGame(g, newPlayer('p', 'Respondent', 'harkonnen')); g.status = 'playing'; g.turn = 2; g.phase = phase; return g; } const question = { kind: 'gholaLeader', target: 'p', turn: 2, leader: 'named-leader' } as const; void test('Ghola named-leader questions parse through quiet phases until turn end', () => { for (const phase of [0, 4, 7]) { const game = gameAt(phase); assert.doesNotThrow(() => validateTruthQuestionReceipt(game, 'a', question)); } assert.match(truthQuestionText(question, id => id === 'named-leader' ? 'Named Commander' : id, id => id), /revive Named Commander with Ghola.*end of this turn/); assert.match(truthQuestionText({ ...question, leader: 'kwisatz' }, id => id, id => id), /Kwisatz Haderach/); assert.throws(() => validateTruthQuestionReceipt(gameAt(8), 'a', question)); }); void test('Ghola question rejects stale, self-targeted and noncanonical receipts', () => { const game = gameAt(4); for (const malformed of [ { ...question, turn: 1 }, { ...question, target: 'a' }, { ...question, target: 'absent' }, { ...question, leader: '' }, { ...question, leader: ' named-leader' }, { ...question, extra: true }, ]) assert.throws(() => validateTruthQuestionReceipt(game, 'a', malformed as TruthQuestion)); const busy = gameAt(4); busy.phaseOpening = { passed: [], initialize: false }; assert.throws(() => validateTruthQuestionReceipt(busy, 'a', question)); });