Files
Dune/tests/coalition-bribe-truth.test.ts
T
Quintine 6e26ceb216
Verify and publish checkpoint container / container (push) Canceled after 0s
Recognize group allies in bribes and deals
2026-10-28 23:57:55 +11:00

76 lines
4.1 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { createElement } from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { BribePromiseGuideButton } from '../components/bribe-promises';
import { mixedBribeInputError } from '../components/mixed-bribe-fields';
import { baseDeck } from '../game/cards';
import { coalitionOf, emptyCoalitionState } from '../game/coalition-alliance';
import { applyAction, createGame, newPlayer, viewGame, type Action, type Game } from '../game/engine';
const act = (game:Game, id:string, action:Action) => applyAction(game,id,action);
/** Group entry is staged at a Basic Nexus. Pair/group formation, physical
* Truthtrance transfer, declaration, priority and questions use native actions. */
function source() {
let game = createGame('GROUPTRUTHBRIBE',newPlayer('a','Atreides','atreides'),false);
game.players.push(newPlayer('e','Emperor','emperor'),newPlayer('f','Fremen','fremen'),
newPlayer('h','Harkonnen','harkonnen'));
Object.assign(game,{status:'playing',phase:1,turn:2,nexus:true,order:['a','e','f','h'],
ready:[],response:null,decision:null,phaseOpening:null,deck:baseDeck()});
game.coalitionState = emptyCoalitionState();
game = act(game,'a',{type:'alliance',target:'e'});
game = act(game,'e',{type:'alliance',target:'a'});
const pair = coalitionOf(game.players,game.coalitionState,'a'); assert.ok(pair);
game = act(game,'a',{type:'coalitionPropose',groupId:pair.id,entrant:'f',expectedRevision:game.coalitionState!.revision});
for (const id of ['e','f']) {
const invitation = game.coalitionState!.proposal!;
game = act(game,id,{type:'coalitionAnswer',event:invitation.event,nexus:invitation.nexus,
turn:invitation.turn,accept:true,expectedRevision:game.coalitionState!.revision});
}
Object.assign(game,{phase:3,nexus:false,active:'h',ready:[]});
for (const player of game.players) { player.spice = 10; player.hand = []; }
const index = game.deck.findIndex(card => card.effect === 'truthtrance'); assert.ok(index >= 0);
const [truth] = game.deck.splice(index,1);
game.players.find(player => player.id === 'h')!.hand.push(truth);
game = act(game,'h',{type:'card',card:truth.id});
for (let count=0; game.truthtrance?.stage === 'priority' && count < game.players.length; count++) {
const next = game.players.find(player => !game.truthtrance!.passed.includes(player.id)); assert.ok(next);
game = act(game,next.id,{type:'truthPass'});
}
assert.equal(game.truthtrance?.stage,'ask');
return game;
}
const question = (recipient:string) => ({type:'truthAsk',question:{kind:'bribe',target:'a',turn:2,recipient,minimum:3}} as Action);
void test('group allies cannot be named in a native Truthtrance bribe question, but an outsider can', () => {
let game = source();
for (const recipient of ['e','f']) {
const before = structuredClone(game);
assert.throws(() => act(game,'h',question(recipient)));
assert.deepEqual(game,before);
}
game = act(game,'h',question('h'));
assert.deepEqual(viewGame(game,'a').truthBribeAnswers,['yes','no']);
game = act(game,'a',{type:'truthAnswer',answer:'yes'});
const view = viewGame(game,'a');
assert.equal(view.bribePromiseGuide?.action.target,'h');
const html = renderToStaticMarkup(createElement(BribePromiseGuideButton,{game:view,busy:false,send() {}}));
assert.match(html,/Pay 3 spice to Harkonnen/);
assert.doesNotMatch(html,/disabled=""/);
game = act(game,'a',view.bribePromiseGuide!.action);
assert.equal(game.players.find(player => player.id === 'a')!.spice,7);
assert.equal(game.players.find(player => player.id === 'h')!.bribes,3);
});
void test('mixed bribe fields reject group allies while accepting the outsider', () => {
const view = viewGame(source(),'h');
const expression = (recipient:string) => ({kind:'and',terms:[
{kind:'fact',fact:{kind:'spice',compare:'gte',value:1}},
{kind:'action',action:{recipient,minimum:3}},
]});
assert.ok(mixedBribeInputError(expression('e'),view,'a'));
assert.ok(mixedBribeInputError(expression('f'),view,'a'));
assert.equal(mixedBribeInputError(expression('h'),view,'a'),null);
});