import assert from 'node:assert/strict'; import test from 'node:test'; import { createGame, newPlayer, type Game } from '../game/engine'; import { richeseRevealInterruptSupported as supported } from '../game/richese-reveal-interruption'; /** Quote-only staged response; native action and physical continuation are * exercised separately by the integration owner. */ function source(target: 'richese' | 'atreides' = 'atreides'): Game { const game = createGame('REVEALEXTRAREVIVAL', newPlayer('richese', 'Richese', 'richese'), false, ['choam']); game.players.push(newPlayer('emperor', 'Emperor', 'emperor'), newPlayer('atreides', 'Atreides', 'atreides')); game.status = 'playing'; game.phase = 4; game.turn = 2; const emperor = game.players.find(player => player.id === 'emperor')!; const recipient = game.players.find(player => player.id === target)!; emperor.ally = target; recipient.ally = emperor.id; recipient.tanks = 2; emperor.spice = 10; game.response = { kind: 'emperorRevival', owner: emperor.id, recipient: target, amount: 2, elite: 0, passed: [] }; return game; } void test('a clean Emperor extra revival preserves the accepted response for either ally', () => { for (const recipient of ['richese', 'atreides'] as const) { const game = source(recipient), before = structuredClone(game); assert.equal(supported(game), true, recipient); assert.deepEqual(game, before); } }); void test('extra revival admission checks its original funding, physical forces and mutual ally', () => { for (const mutate of [ (g: Game) => { g.phase = 3; }, (g: Game) => { g.response!.owner = 'atreides'; }, (g: Game) => { g.response!.recipient = 'emperor'; }, (g: Game) => { g.players.find(p => p.id === 'atreides')!.ally = null; }, (g: Game) => { g.players.find(p => p.id === 'atreides')!.tanks = 1; }, (g: Game) => { g.players.find(p => p.id === 'emperor')!.spice = 3; }, (g: Game) => { g.response!.amount = 0; }, (g: Game) => { g.response!.elite = 1; }, (g: Game) => { g.emperorExtra.atreides = 2; }, (g: Game) => { g.revivalPrevention = {player:'atreides',turn:g.turn}; }, (g: Game) => { g.response!.passed = ['atreides','atreides']; }, (g: Game) => { g.response!.intent = 'other'; }, (g: Game) => { g.decision = {kind:'auctionPayment',player:'emperor'}; }, (g: Game) => { g.pendingRevival = {} as NonNullable; }, ]) { const game = source(); mutate(game); const before = structuredClone(game); assert.equal(supported(game), false); assert.deepEqual(game, before); } }); void test('Ixian extra revivals retain the special-force price and typed tanks', () => { const game = source(); game.expansions.push('ix'); const recipient = game.players.find(player => player.id === 'atreides')!; recipient.faction = 'ixians'; recipient.elites = {reserves:0,tanks:1,forces:{},revived:0}; game.response!.elite = 1; const emperor = game.players.find(player => player.id === 'emperor')!; emperor.spice = 5; assert.equal(supported(game), true); emperor.spice = 4; assert.equal(supported(game), false); emperor.spice = 5; game.response!.elite = 2; assert.equal(supported(game), false); });