Files
Dune/tests/native-guild-rate-betrayal.test.ts

54 lines
3.3 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { applyAction, type Game } from '../game/engine';
import { createGuildBetrayalFixture, settleGuildBetrayalFixture } from './fixture-nexus-guild-betrayal';
import { passNativeGuildTransportPermission } from './fixture-guild-ambassador-income-arrival';
const player = (g: Game, id: string) => g.players.find(p => p.id === id)!;
for (const source of ['reserve', 'guildTransport', 'homeworld'] as const) {
for (const use of [false, true]) {
void test(`native ${source} full-price cancellation survives deferred Guild Betrayal ${use ? 'Use' : 'Pass'} until physical settlement`, () => {
const f = createGuildBetrayalFixture({ source, payer: 'guild', homeworlds: source === 'homeworld' });
const before = structuredClone(f.before);
// Explicit controlled extension of the existing native source fixture:
// conserve a real undealt Karama into the future invoice holder's hand.
const index = before.deck.findIndex(card => card.effect === 'karama');
assert.ok(index >= 0);
const card = before.deck.splice(index, 1)[0];
player(before, f.holder).hand.push(card);
let g = applyAction(structuredClone(before), f.guild, f.declaration);
if (g.decision?.kind === 'guildShipment' || g.decision?.kind === 'homeworldShipmentGuild')
g = applyAction(g, f.guild, { type: 'decision', allow: true,
...(g.decision.kind === 'homeworldShipmentGuild' ? { event: g.decision.event } : {}) });
g = passNativeGuildTransportPermission(g).game;
assert.equal(g.response?.kind, 'guildRate');
const amount = g.pendingShipment?.amount ?? g.pendingGuildTransport?.amount ?? g.pendingHomeworldShipment!.amount;
assert.ok(amount > 1);
assert.ok(player(before, f.guild).spice >= amount);
assert.equal(player(g, f.guild).spice, player(before, f.guild).spice);
g = applyAction(g, f.holder, { type: 'card', mode: 'cancel', card: card.id });
const frame = g.pendingGuildBetrayal!;
assert.ok(frame);
assert.equal(frame.invoice.price, amount);
assert.equal(player(g, f.guild).spice, player(before, f.guild).spice);
assert.deepEqual(player(g, f.guild).forces, player(before, f.guild).forces);
assert.equal(player(g, f.guild).reserves, player(before, f.guild).reserves);
assert.equal(player(g, f.guild).shipped, false);
assert.deepEqual(g.homeworlds, before.homeworlds);
if (use) g = applyAction(g, f.holder, { type: 'guildBetrayalUse', event: frame.invoice.event });
g = settleGuildBetrayalFixture(g);
assert.equal(player(g, f.guild).spice, player(before, f.guild).spice - amount);
assert.equal(player(g, f.holder).spice, player(before, f.holder).spice + (use ? amount : 0));
assert.equal(player(g, f.guild).shipped, true);
assert.deepEqual(player(g, f.guild).forces, player(f.original, f.guild).forces);
assert.equal(player(g, f.guild).reserves, player(f.original, f.guild).reserves);
assert.deepEqual(g.homeworlds, f.original.homeworlds);
assert.equal(g.guildBetrayal?.status, 'completed');
assert.ok(!g.pendingGuildBetrayal);
assert.equal(g.discard.filter(c => c.id === card.id).length, 1);
assert.throws(() => applyAction(structuredClone(g), f.holder,
{ type: 'guildBetrayalUse', event: frame.invoice.event }));
});
}
}