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); });