Files
Dune/tests/homeworld-phase-occupation-income.test.ts
T

76 lines
5.5 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { homeworldGameIntegrity } from '../game/homeworld-game';
import { TECH_TOKENS } from '../game/tech-tokens';
import {
completeHomeworldPhaseOccupation, createHomeworldPhaseOccupationFixture,
homeworldPhaseOccupationInventory as inventory, homeworldPhaseOccupationPlayer as player,
} from './fixture-homeworld-phase-occupation';
for (const advanced of [false, true]) {
void test(`public selected-family ${advanced ? 'Advanced Worlds Tech Strongholds' : 'Basic Worlds Tech'} pays actual sole Caladan occupation separately from phase rewards`, () => {
const f = createHomeworldPhaseOccupationFixture({ advanced });
assert.equal(f.initial.status, 'lobby'); assert.equal(f.setup.status, 'setup');
assert.deepEqual(f.setup.players.map(seat => seat.faction), ['atreides', 'ecaz', 'ixians']);
assert.deepEqual(f.setup.expansions, ['ix', 'choam', 'ecaz']);
assert.ok(f.setup.homeworlds && f.setup.ecazTreachery && f.setup.techTokens);
assert.equal(!!f.setup.strongholdCards, advanced);
assert.ok(f.game.homeworldOccupationPreview);
assert.ok(!f.game.nexusCards && !f.game.leaderSkills && !f.game.discoveryEnabled && !f.game.semutaPreview);
assert.equal(f.originalInventory.length, 50); assert.deepEqual(inventory(f.game), f.originalInventory);
assert.deepEqual(f.staging, []); assert.equal(f.sourceActionCount, f.sourceActions.length);
assert.equal(f.evacuation.steps[0].action.type, 'ship');
assert.equal(f.evacuation.steps[0].action.amount, 10);
assert.deepEqual(f.evacuationSources, { 'homeworld:atreides': { normal: 10, elite: 0 } });
assert.equal(player(f.evacuation.before, f.native).reserves, 10);
assert.equal(player(f.evacuation.after, f.native).reserves, 0);
assert.equal(player(f.evacuation.after, f.native).forces['arrakeen:10'], 20);
assert.equal(player(f.evacuation.before, f.native).spice - player(f.evacuation.after, f.native).spice, 10);
assert.equal(f.invasionAction.type, 'homeworldShip'); assert.equal(f.invasionQuote.cost, 1);
assert.deepEqual(f.incomingSources, { 'homeworld:ecaz': { normal: 1, elite: 0 } });
assert.deepEqual(f.invasion.after.homeworlds?.custody?.visitors[f.world]?.[f.owner], { normal: 1, elite: 0 });
assert.equal(player(f.invasion.before, f.owner).reserves - player(f.invasion.after, f.owner).reserves, 1);
assert.equal(f.invasion.before.homeworldOccupationHistory?.qualifications.some(q => q.world === f.world), false);
assert.equal(f.qualification.world, f.world); assert.equal(f.qualification.player, f.owner);
assert.ok(f.invasion.after.homeworldOccupationHistory?.sources.some(source => source.event === f.qualification.event));
assert.equal(f.entitlement.qualification, f.qualification.event); assert.equal(f.entitlement.ally, null);
assert.equal(f.entitlement.spice, 2);
assert.equal(f.evacuation.after.techTokens?.heighliners.spice, 1);
assert.equal(f.invasion.after.techTokens?.heighliners.spice, 1, 'The second paid shipment does not duplicate the per-phase token accrual');
assert.equal(f.game.techTokens?.heighliners.owner, f.techOwner);
assert.equal(f.game.homeworldOccupiedIncomeHistory?.length, 0);
homeworldGameIntegrity(f.game);
const before = structuredClone(f.game), techBefore = structuredClone(f.game.techTokens);
const result = completeHomeworldPhaseOccupation(f);
assert.deepEqual(f.game, before, 'The qualified pre-Collection source remains reusable for the actual callback');
assert.deepEqual(result.collection.steps[0], { actor: f.current, action: f.action });
assert.equal(result.collection.before.phase, 5); assert.equal(result.collection.after.phase, 7);
assert.equal(player(result.collection.after, f.techOwner).spice - player(result.collection.before, f.techOwner).spice, 1);
assert.equal(result.collection.after.techTokens?.heighliners.spice, 0);
assert.equal(player(result.collection.after, f.owner).spice - player(result.collection.before, f.owner).spice, 2);
assert.equal(player(result.collection.after, f.native).spice,
player(result.collection.before, f.native).spice + (advanced ? 2 : 0),
'Advanced native Arrakeen Collection remains separate from the Ecaz occupier bank income');
assert.equal(result.collection.after.pendingHomeworldOccupiedIncome, null);
assert.equal(result.incomeHistory.queue.length, 1);
assert.equal(result.incomeHistory.queue[0].qualification, f.qualification.event);
assert.deepEqual(result.incomeReceipt, { world: f.world, status: 'settled', ownAmount: 2,
credits: [{ player: f.owner, amount: 2 }] });
for (const { id } of TECH_TOKENS)
assert.equal(result.game.techTokens![id].owner, techBefore![id].owner,
'Occupied-bank Collection transfers no original Tech custody');
assert.equal(result.game.turn, 2); assert.equal(result.game.phase, 0);
if (advanced) {
assert.equal(f.game.strongholdCards?.claimedTurn, 0);
assert.ok(Object.values(f.game.strongholdCards!.owners).every(owner => owner === null));
assert.equal(result.mentat.after.strongholdCards?.claimedTurn, 1);
assert.equal(result.mentat.after.strongholdCards?.owners.arrakeen, f.native);
assert.equal(result.mentat.after.strongholdCards?.owners.hidden_mobile_stronghold, f.techOwner);
}
assert.deepEqual(inventory(result.game), f.originalInventory); homeworldGameIntegrity(result.game);
assert.deepEqual(result.game.homeworlds?.custody?.visitors[f.world]?.[f.owner], { normal: 1, elite: 0 });
assert.equal(player(result.game, f.native).reserves, 0);
});
}