Files
Dune/tests/moritani-duke-captured-runtime.test.ts

91 lines
5.0 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { dukeController } from '../game/duke-vidal';
import { viewGame } from '../game/engine';
import {
assassinateAfterCapturedDukeUse, cancelMoritaniDukeReclaim,
createMoritaniDukeCaptureFixture, reclaimMoritaniDuke, useMoritaniReclaimedDuke,
} from './fixture-moritani-duke-capture';
import { nextNexusDukeStep, nexusDukePlayer, stepNexusDuke } from './fixture-nexus-duke-capture';
void test('original Moritani qualification takes its real Harkonnen captive and releases after one native battle', () => {
const f = createMoritaniDukeCaptureFixture();
assert.equal(dukeController(f.game.dukeVidal!), f.captor);
const reclaimed = reclaimMoritaniDuke(f);
const duke = reclaimed.after.dukeVidal!;
assert.equal(dukeController(duke), f.owner);
assert.equal(duke.leader.capturedBy, undefined);
assert.equal(duke.leader.concealed, undefined);
assert.equal(duke.captureVisibility, undefined);
assert.equal(duke.leader.dead, false);
assert.equal(duke.leader.deaths, f.game.dukeVidal!.leader.deaths);
assert.equal(duke.source, 'moritani');
assert.equal(nexusDukePlayer(reclaimed.after, f.owner).spice, nexusDukePlayer(reclaimed.before, f.owner).spice);
const used = useMoritaniReclaimedDuke(reclaimed.after, f);
assert.equal(dukeController(used.dukeVidal!), null);
assert.equal(used.dukeVidal!.leader.dead, false);
assert.equal(used.dukeVidal!.leader.usedAt, 'carthag');
assert.equal(used.dukeVidal!.leader.capturedBy, undefined);
});
void test('unused original Moritani reclamation expires without restoring Harkonnen custody', () => {
const f = createMoritaniDukeCaptureFixture();
const reclaimed = reclaimMoritaniDuke(f);
const expired = useMoritaniReclaimedDuke(reclaimed.after, f, false);
assert.equal(dukeController(expired.dukeVidal!), null);
assert.equal(expired.dukeVidal!.leader.dead, false);
assert.equal(expired.dukeVidal!.leader.capturedBy, undefined);
assert.equal(expired.dukeVidal!.leader.deaths, f.game.dukeVidal!.leader.deaths);
});
void test('a qualified public acquisition cannot revive an actually executed captive Duke', () => {
const f = createMoritaniDukeCaptureFixture({ execute: true });
assert.equal(f.game.dukeVidal!.leader.dead, true);
const resolved = reclaimMoritaniDuke(f);
assert.equal(dukeController(resolved.after.dukeVidal!), null);
assert.equal(resolved.after.dukeVidal!.leader.dead, true);
assert.equal(resolved.after.dukeVidal!.leader.deaths, 2);
assert.equal(resolved.after.dukeVidal!.leader.concealed, undefined);
assert.equal(nexusDukePlayer(resolved.after, f.owner).spice, nexusDukePlayer(resolved.before, f.owner).spice);
});
void test('original Karama preserves the captive and its actual winning use still opens native Harkonnen Assassinate', () => {
const f = createMoritaniDukeCaptureFixture({ karama: true });
const karama = nexusDukePlayer(f.game, f.captor).hand.find(c => c.effect === 'karama')!;
const canceled = cancelMoritaniDukeReclaim(f);
assert.equal(dukeController(canceled.after.dukeVidal!), f.captor);
assert.deepEqual(canceled.after.dukeVidal, canceled.before.dukeVidal);
assert.ok(canceled.after.discard.some(c => c.id === karama.id));
const used = assassinateAfterCapturedDukeUse(canceled.after, f);
assert.equal(dukeController(used.window.dukeVidal!), null);
assert.equal(used.window.dukeVidal!.leader.dead, false);
const targetBefore = nexusDukePlayer(used.window, f.captor).leaders.find(l => l.id === f.target)!;
const targetAfter = nexusDukePlayer(used.after, f.captor).leaders.find(l => l.id === f.target)!;
assert.equal(targetAfter.dead, true);
assert.equal(targetAfter.deaths, targetBefore.deaths + 1);
assert.equal(nexusDukePlayer(used.after, f.owner).spice,
nexusDukePlayer(used.window, f.owner).spice + targetBefore.strength);
assert.equal(nexusDukePlayer(used.after, f.owner).traitors.includes(f.target), true);
assert.equal(nexusDukePlayer(used.replaced, f.owner).traitors.includes(f.target), false);
assert.equal(used.replaced.moritaniAssassinate!.opportunities.filter(r =>
r.card === f.target && r.stage === 'replaced').length, 1);
});
void test('a private execution does not remove or identify the public qualifying acquisition window', () => {
const living = createMoritaniDukeCaptureFixture({ karama: true });
const executed = createMoritaniDukeCaptureFixture({ karama: true, execute: true });
const open = (f: typeof living) => {
const next = nextNexusDukeStep(f.game, f.plan);
assert.ok(next);
return stepNexusDuke(f.game, f.plan, next, f.actions);
};
const livePending = open(living), deadPending = open(executed);
assert.equal(livePending.response?.kind, 'moritaniDuke');
assert.equal(deadPending.response?.kind, 'moritaniDuke');
const livePublic = viewGame(livePending, living.guild);
const deadPublic = viewGame(deadPending, executed.guild);
assert.deepEqual(deadPublic.response, livePublic.response);
assert.deepEqual(deadPublic.dukeVidal, livePublic.dukeVidal);
assert.equal(deadPending.dukeVidal!.leader.dead, true);
});