Files
Dune/tests/advanced-emperor-payment-overlays.test.ts
T
Quintine 599af2475d
Verify and publish checkpoint container / container (push) Canceled after 0s
Route Advanced Emperor allied auction funding
2026-10-29 02:48:11 +11:00

85 lines
4.8 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { applyAction, viewGame, type Game } from '../game/engine';
import {
allowE3Responses, chooseBureaucratE3Payment, createBureaucratE3AuctionFixture,
payBureaucratE3Auction,
} from './fixture-bureaucrat-e3-funding';
import { createSpiceBankerIncomeFixture } from './fixture-spice-banker-income';
import { freshBiddingGame, biddingPlayer } from './fixture-homeworld-occupied-bidding';
import { qualifyDefensePosition } from './fixture-homeworld-occupied-defenses';
import { advanceOriginal } from './fixture-homeworld-occupied-producers';
const seat = (game: Game, id: string) => {
const player = game.players.find(candidate => candidate.id === id);
assert.ok(player);
return player;
};
void test('Advanced Emperor ally bank share does not inflate a Bureaucrat diversion from the buyer-funded income', () => {
// Real E3 setup, original Bureaucrat assignment and turn-two alliance;
// the fixture uses the original wallets and physical card.
const f = createBureaucratE3AuctionFixture({ advanced: true, donor: 'emperor', payerShare: 5, donorShare: 3 });
const paid = payBureaucratE3Auction(f);
assert.deepEqual(paid.currentAuctionSale?.paymentRouting, [
{ payer: f.payer, amount: 5, recipient: 'emperor' },
{ payer: f.donor, amount: 3, recipient: 'bank' },
]);
const pending = allowE3Responses(paid);
assert.equal(pending.decision?.kind, 'bureaucratPayment');
assert.equal(pending.decision?.player, f.trainer);
const done = allowE3Responses(chooseBureaucratE3Payment(pending, true));
assert.equal(seat(done, f.payer).spice, seat(f.beforePledge, f.payer).spice - 5);
assert.equal(seat(done, f.donor).spice, seat(f.beforePledge, f.donor).spice - 3 + 3);
assert.equal(done.bureaucratPayments?.used.length, 1);
assert.ok(seat(done, f.payer).hand.some(card => card.id === f.card));
});
void test('Advanced Emperor ally bank share creates a Spice Banker grant from the actual bank leg', () => {
// Real Skills setup and native bidding; the existing fixture stages the
// reciprocal alliance and its original aid before the physical purchase.
const f = createSpiceBankerIncomeFixture({ advanced: true, kind: 'auction',
emperor: true, amount: 6, allySplit: 4 });
const emperor = f.game.players.find(player => player.faction === 'emperor')!;
let game = applyAction(f.beforePayment, f.actor, f.paymentAction);
game = allowE3Responses(game);
const sources = game.spiceBankerIncome!.sources.slice(f.beforePayment.spiceBankerIncome!.sources.length);
assert.equal(sources.length, 1);
assert.deepEqual(sources[0].source.bankLegs, [{ payer: emperor.id, amount: 4 }]);
assert.equal(sources[0].grant?.owner, f.owner);
assert.equal(seat(game, emperor.id).spice, seat(f.beforePayment, emperor.id).spice + 2);
assert.equal(seat(game, f.payer).spice, seat(f.beforePayment, f.payer).spice - 2);
});
void test('occupied Kaitain allocates only the buyer-funded Emperor income from an Advanced split bid', () => {
let game = freshBiddingGame('emperor', 'harkonnen');
// Existing physical occupation fixture stages Kaitain's qualifying position;
// this test also stages the pair and its wallets, then uses native actions.
qualifyDefensePosition(game, 'homeworld:emperor');
biddingPlayer(game, 'ally').ally = 'native';
biddingPlayer(game, 'native').ally = 'ally';
game = advanceOriginal(game, state => state.phase === 3 && !!state.auction && !state.response && !state.decision);
for (let n = 0; game.auction?.active !== 'ally' && n < game.players.length; n++)
game = applyAction(game, game.auction!.active, { type: 'passBid' });
assert.equal(game.auction?.active, 'ally');
biddingPlayer(game, 'ally').spice = 4;
biddingPlayer(game, 'native').spice = 10;
const occupierBefore = biddingPlayer(game, 'occupier').spice;
game = applyAction(game, 'native', { type: 'pledgeAid', amount: 2 });
game = applyAction(game, 'ally', { type: 'bid', amount: 6, allyPayment: 2 });
for (let n = 0; !game.currentAuctionSale && n < game.players.length + 2; n++) {
if (game.decision?.kind === 'auctionPayment')
game = applyAction(game, 'ally', { type: 'decision', karama: false });
else game = applyAction(game, game.auction!.active, { type: 'passBid' });
}
game = advanceOriginal(game, state => state.decision?.kind === 'homeworldOccupiedPercentage');
const offer = viewGame(game, 'occupier').homeworldOccupiedPercentage!;
assert.equal(offer.amount, 2);
const receipt = game.homeworldOccupiedPercentageLedger!.receipts.at(-1)!;
assert.equal(receipt.source.amount, 4);
game = applyAction(game, 'occupier', { type: 'decision', event: offer.event, ownAmount: 2 });
assert.equal(seat(game, 'native').spice, 10);
assert.equal(seat(game, 'ally').spice, 0);
assert.equal(seat(game, 'occupier').spice, occupierBefore + 2);
});