Files
Dune/tests/coalition-admission-joint-commitments.test.ts
T
Quintine 52d932002b
Verify and publish checkpoint container / container (push) Successful in 11m20s
Support group departures in joint commitment plans
2026-10-29 01:01:51 +11:00

60 lines
2.9 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { coalitionOf, emptyCoalitionState } from '../game/coalition-alliance';
import { applyAction, createGame, newPlayer, type Action, type Game } from '../game/engine';
const act = (game: Game, actor: string, action: Action) => applyAction(game, actor, action);
/** A player's retained partner and unrelated bribe remain jointly feasible.
* The empty joint ledger is significant: it delegates accepted-order
* reconciliation to the scalar alliance planner. */
function fixture(): Game {
let game = createGame('ADMISSIONJOINT', newPlayer('a', 'Atreides', 'atreides'), false);
game.players.push(newPlayer('b', 'Emperor', 'emperor'), newPlayer('c', 'Fremen', 'fremen'),
newPlayer('d', 'Harkonnen', 'harkonnen'), newPlayer('e', 'Guild', 'guild'));
Object.assign(game, {status:'playing', phase:1, turn:2, nexus:true,
order:['a','b','c','d','e'], ready:[]});
game.coalitionState = emptyCoalitionState('unlimited');
game.players.find(player => player.id === 'a')!.spice = 8;
game = act(game, 'a', {type:'alliance', target:'b'});
game = act(game, 'b', {type:'alliance', target:'a'});
const source = JSON.stringify(['ordinaryNexus', game.turn, game.spiceDiscard.map(pile => pile.length),
game.spiceSequence?.pile ?? null, game.spiceSequence?.skipped?.length ?? 0]);
game.alliancePromises = [{event:'alliance', source, player:'a', asker:'e', turn:game.turn,
partner:'b', answer:true}];
game.bribePromises = [{event:'bribe', player:'a', asker:'e', turn:game.turn,
recipient:'e', minimum:1, answer:true, progress:0}];
game.turnActionPromises = [];
return game;
}
function proposal(game: Game) {
const group = coalitionOf(game.players, game.coalitionState, 'a'); assert.ok(group);
return {type:'coalitionPropose', groupId:group.id, entrant:'c',
expectedRevision:game.coalitionState!.revision};
}
function answer(game: Game, accept: boolean) {
const offer = game.coalitionState!.proposal; assert.ok(offer);
return {type:'coalitionAnswer', event:offer.event, nexus:offer.nexus, turn:offer.turn,
accept, expectedRevision:game.coalitionState!.revision};
}
function commitmentsLive(game: Game) {
assert.equal(game.bribePromises![0].released ?? false, false);
assert.equal(game.alliancePromises![0].released ?? false, false);
}
void test('joint accepted-order promises remain binding after group admission', () => {
let game = fixture();
game = act(game, 'a', proposal(game));
commitmentsLive(game);
game = act(game, 'b', answer(game, true));
commitmentsLive(game);
game = act(game, 'c', answer(game, true));
commitmentsLive(game);
assert.equal(game.coalitionState!.proposal, null);
assert.deepEqual(coalitionOf(game.players, game.coalitionState, 'a')!.members, ['a','b','c']);
game = act(game, 'a', {type:'bribe', target:'e', amount:1});
assert.equal(game.bribePromises![0].fulfilled, true);
assert.equal(game.alliancePromises![0].released ?? false, false);
});