Files
Dune/tests/salusa-revival-pending-compat.test.ts
T

136 lines
6.3 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
applyAction, executeSpecialKaramaIntent, viewGame,
type Action, type Game, type SpecialKaramaIntent,
} from '../game/engine';
import { quoteRevivalResume } from '../game/revival-resume';
import {
holdRevivalCard, homeworldRevivalFixture, revivalInventory, revivalPlayer,
} from './fixture-homeworld-revival';
import { reachAdvancedRevivalOrderActor } from './fixture-advanced-revival-order';
const p = (game: Game) => revivalPlayer(game, 'e');
const reload = (game: Game): Game => JSON.parse(JSON.stringify(game));
function reject(game: Game, actor: string, action: Action) {
const before = structuredClone(game);
assert.throws(() => applyAction(game, actor, action));
assert.deepEqual(game, before);
}
/** Original Advanced Homeworld setup and native ordered phase progression.
* This controlled supplement stages four Imperial stars and two normal forces
* in Tanks, leaving Salusa at one, and transfers an actual Karama from the
* deck. Its later revivalStop is a real accepted action, not installed. */
function source() {
let game = homeworldRevivalFixture({ advanced: true, tleilaxu: true });
const owner = p(game);
assert.equal(owner.reserves, 20);
assert.equal(owner.elites!.reserves, 5);
owner.reserves -= 6; owner.tanks += 6;
owner.elites!.reserves -= 4; owner.elites!.tanks += 4;
game.homeworlds!.custody!.salusa!.elite = 1;
const card = holdRevivalCard(game, 'e', 'karama');
revivalInventory(game);
game = reachAdvancedRevivalOrderActor(game, 'e');
assert.equal(game.phase, 4);
assert.ok(!game.decision && !game.response && !game.phaseOpening);
assert.ok(viewGame(game, 'e').revival.sardaukarFreeBlock);
return { game, card };
}
function request(game: Game) {
const pending = applyAction(game, 'e', { type: 'revive', amount: 1, elite: 1 });
assert.equal(pending.decision?.kind, 'revivalStop');
assert.equal(pending.decision?.player, 't');
assert.ok(pending.pendingRevival);
return pending;
}
function allow(state: Game) {
let game = applyAction(reload(state), 't', { type: 'decision', decline: true });
for (let n = 0; game.response && n < 20; n++) {
const actor = game.players.find(player => !game.response!.passed.includes(player.id));
assert.ok(actor);
game = applyAction(game, actor.id, { type: 'passResponse' });
}
assert.equal(game.response ?? null, null);
assert.equal(game.pendingRevival ?? null, null);
revivalInventory(game);
return game;
}
void test('unstamped pre-policy pending free-star quote resumes its original accepted terms once', () => {
const { game } = source();
const accepted = request(game);
// Controlled historical receipt: represent an already accepted old-policy
// free star by changing only the frozen quote on the real pending stop.
// This is not presented as a naturally captured pre-upgrade room.
const legacy = reload(accepted), receipt = legacy.pendingRevival!;
delete receipt.salusaFreePolicy;
receipt.free = 1; receipt.cost = 0; receipt.normalCost = 0;
assert.equal(receipt.player, 'e');
assert.equal(receipt.kind, 'forces');
assert.equal(receipt.amount, 1);
assert.equal(receipt.elite, 1);
const before = structuredClone(legacy), quote = quoteRevivalResume(legacy, 'finish');
assert.equal(quote.outcome, 'revive');
assert.deepEqual(quote.pending, receipt);
assert.deepEqual(legacy, before);
const settled = allow(legacy);
assert.equal(p(settled).spice, p(legacy).spice);
assert.equal(p(settled).tanks, p(legacy).tanks - 1);
assert.equal(p(settled).elites!.tanks, p(legacy).elites!.tanks - 1);
assert.equal(p(settled).elites!.revived, 1);
assert.equal(p(settled).revived, 1);
assert.equal(p(settled).freeForcesRevived, 1);
assert.equal(settled.homeworlds!.custody!.salusa!.elite, 2);
reject(settled, 't', { type: 'decision', decline: true });
});
void test('new stamped Low pending quote settles the paid star, preserving the free allowance', () => {
const { game } = source();
const accepted = request(game), receipt = accepted.pendingRevival!;
assert.equal(receipt.salusaFreePolicy, 1);
assert.equal(receipt.free, 0);
assert.equal(receipt.normalCost, 2);
assert.equal(receipt.cost, 2);
const restored = reload(accepted), before = structuredClone(restored);
const quote = quoteRevivalResume(restored, 'finish');
assert.equal(quote.outcome, 'revive');
assert.deepEqual(quote.pending, receipt);
assert.deepEqual(restored, before);
const settled = allow(restored);
assert.equal(p(settled).spice, p(restored).spice - 2);
assert.equal(p(settled).tanks, p(restored).tanks - 1);
assert.equal(p(settled).elites!.tanks, p(restored).elites!.tanks - 1);
assert.equal(p(settled).elites!.revived, 1);
assert.equal(p(settled).revived, 1);
assert.equal(p(settled).freeForcesRevived, 0);
assert.equal(settled.homeworlds!.custody!.salusa!.elite, 2);
assert.equal(viewGame(settled, 'e').revival.freeRemaining, 1);
reject(settled, 't', { type: 'decision', decline: true });
});
void test('only an internal pre-policy special intent can finish its already accepted Low star selection', () => {
const { game, card } = source();
const action: Action = { type: 'card', mode: 'special', card, amount: 1, elite: 1 };
reject(game, 'e', action);
reject(game, 'e', { ...action, legacySalusaIntent: true });
reject(game, 'e', { ...action, salusaFreePolicy: undefined });
// The old normalized record simulates an already accepted internal Kull
// intent. It is not constructed from a player action and does not claim an
// actual Kull/Homeworld production path or captured persisted transaction.
const legacy: SpecialKaramaIntent = { kind: 'emperorForces', owner: 'e', card,
turn: game.turn, phase: game.phase, amount: 1, elite: 1 };
const resumed = reload(game), original = structuredClone(resumed);
executeSpecialKaramaIntent(resumed, legacy);
assert.equal(p(resumed).tanks, p(original).tanks - 1);
assert.equal(p(resumed).elites!.tanks, p(original).elites!.tanks - 1);
assert.equal(p(resumed).elites!.revived, p(original).elites!.revived);
assert.equal(p(resumed).spice, p(original).spice);
assert.equal(p(resumed).specialKaramaUsed, true);
assert.equal(resumed.discard.filter(held => held.id === card).length, 1);
assert.equal(resumed.homeworlds!.custody!.salusa!.elite, 2);
revivalInventory(resumed);
});