Support staged group shipment funding
Verify and publish checkpoint container / container (push) Canceled after 0s

This commit is contained in:
Quintine committed 2026-10-28 23:14:44 +11:00
1 parent 624f95dde4
commit 34aa64cdef
13 files changed
+828 -16

No files matched your search

+26
View File
@@ -43,6 +43,32 @@ void test('staged group mode renders its real Nexus invitation and hides conflic
assert.match(markup(game, 'h', true), /disabled=""[^>]*>Accept invitation/);
});
void test('staged group shipment funding appears on the live table for the donor and recipient', () => {
let game = nexusOfferGame();
game.coalitionState = emptyCoalitionState();
game = applyAction(game, 'a', {type:'alliance', target:'e'});
game = applyAction(game, 'e', {type:'alliance', target:'a'});
const pair = coalitionOf(game.players, game.coalitionState, 'a'); assert.ok(pair);
game = applyAction(game, 'a', {type:'coalitionPropose', groupId:pair.id,
entrant:'h', expectedRevision:game.coalitionState!.revision});
for (const id of ['e', 'h']) {
const offer = game.coalitionState!.proposal!;
game = applyAction(game, id, {type:'coalitionAnswer', event:offer.event, nexus:offer.nexus,
turn:offer.turn, accept:true, expectedRevision:game.coalitionState!.revision});
}
// Stage the next movement boundary; the group negotiation above used real actions.
Object.assign(game, {phase:5, nexus:false, active:'a', ready:[], movementRemaining:['a','e','h']});
const emperor = game.players.find(player => player.id === 'e')!;
assert.ok(emperor.reserves > 0);
emperor.reserves--;
emperor.forces['arrakeen:10'] = (emperor.forces['arrakeen:10'] ?? 0) + 1;
game = applyAction(game, 'e', {type:'coalitionPledge', recipient:'a', amount:1});
assert.match(markup(game, 'a'), /Emperor recipient: 1 spice/);
assert.match(markup(game, 'a'), /Use from Emperor recipient for your next shipment/);
assert.match(markup(game, 'a'), /Basic allied transit: ship forces/);
assert.match(markup(game, 'e'), /Pledge to Atreides offerer · 1 spice currently set aside/);
});
void test('live outgoing offer is named and withdrawable only by its sender, separately from an incoming invitation', () => {
const g = applyAction(nexusOfferGame(), 'a', { type: 'alliance', target: 'e' });
const html = markup(g);