Connect native Guild and BG own-arrival continuations
This commit is contained in:
1 parent
e265af3cb3
commit
c10fe0b48c
19 files changed
+1051
-268
No files matched your search
@@ -2,81 +2,20 @@ import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { applyAction, type Action, type Game } from '../game/engine';
|
||||
import {
|
||||
addShipmentArrivalAmbassador,
|
||||
legacyGuildArrival,
|
||||
richeseNoFieldArrivalAction,
|
||||
shipmentArrivalAction,
|
||||
shipmentArrivalGame,
|
||||
shipmentArrivalPlayer,
|
||||
shipmentArrivalReload,
|
||||
shipmentArrivalResources,
|
||||
} from './fixture-deferred-shipment-arrival';
|
||||
|
||||
const overlap = /Ambassadors combined with another arrival reaction/;
|
||||
|
||||
function reject(g: Game, player: string, action: Action, pattern: RegExp) {
|
||||
function reject(g: Game, player: string, action: Action) {
|
||||
const before = structuredClone(g);
|
||||
assert.throws(() => applyAction(g, player, action), pattern);
|
||||
assert.throws(() => applyAction(g, player, action));
|
||||
assert.deepEqual(g, before);
|
||||
}
|
||||
|
||||
void test('an ordinary Guild shipment rejects a known Ambassador and advisor overlap before declaration or payment', (t) => {
|
||||
const g = shipmentArrivalGame();
|
||||
addShipmentArrivalAmbassador(g);
|
||||
const resources = structuredClone(shipmentArrivalResources(g));
|
||||
const uuid = t.mock.method(crypto, 'randomUUID', () => {
|
||||
throw Error('Unsupported shipment allocated an event');
|
||||
});
|
||||
reject(g, 'guild', shipmentArrivalAction, overlap);
|
||||
assert.deepEqual(shipmentArrivalResources(g), resources);
|
||||
assert.equal(g.pendingShipment, null);
|
||||
assert.equal(g.decision, null);
|
||||
assert.equal(uuid.mock.callCount(), 0);
|
||||
});
|
||||
|
||||
void test('an own Richese No-Field rejects the unsupported arrival before saving its private cancellation response', () => {
|
||||
const g = shipmentArrivalGame();
|
||||
g.active = 'richese';
|
||||
shipmentArrivalPlayer(g, 'richese').shipped = false;
|
||||
addShipmentArrivalAmbassador(g);
|
||||
const action = richeseNoFieldArrivalAction(g);
|
||||
const before = structuredClone(g);
|
||||
assert.throws(() => applyAction(g, 'richese', action), overlap);
|
||||
assert.deepEqual(g, before);
|
||||
assert.equal(g.pendingShipment, null);
|
||||
assert.equal(g.response, null);
|
||||
assert.equal(g.decision, null);
|
||||
assert.equal(shipmentArrivalPlayer(g, 'richese').noField!.deployed, null);
|
||||
});
|
||||
|
||||
void test('a saved Guild allowance returns only the uncommitted ordinary shipment and permits a safe replacement', () => {
|
||||
const g = legacyGuildArrival();
|
||||
const before = structuredClone(g);
|
||||
const resources = structuredClone(shipmentArrivalResources(g));
|
||||
const returned = applyAction(shipmentArrivalReload(g), 'guild', {
|
||||
type: 'decision',
|
||||
allow: true,
|
||||
});
|
||||
assert.deepEqual(g, before);
|
||||
assert.deepEqual(shipmentArrivalResources(returned), resources);
|
||||
assert.equal(returned.pendingShipment, null);
|
||||
assert.equal(returned.decision, null);
|
||||
assert.equal(shipmentArrivalPlayer(returned, 'guild').shipped, false);
|
||||
assert.deepEqual(shipmentArrivalPlayer(returned, 'guild').forces, {});
|
||||
assert.match(returned.log.at(-1)!.text, overlap);
|
||||
assert.match(
|
||||
returned.log.at(-1)!.text,
|
||||
/No forces, spice, cards or shipment allowance were spent/,
|
||||
);
|
||||
|
||||
const retried = applyAction(returned, 'guild', {
|
||||
...shipmentArrivalAction,
|
||||
territory: 'polar_sink',
|
||||
sector: 0,
|
||||
});
|
||||
assert.equal(retried.decision?.kind, 'guildShipment');
|
||||
assert.equal(retried.pendingShipment?.territory, 'polar_sink');
|
||||
});
|
||||
|
||||
void test('a supported Guild allowance still commits once and opens the advisor choice', () => {
|
||||
let g = shipmentArrivalGame();
|
||||
@@ -99,27 +38,6 @@ void test('a supported Guild allowance still commits once and opens the advisor
|
||||
void test('a malformed saved shipment remains rejected and is not treated as a recoverable arrival overlap', () => {
|
||||
const g = legacyGuildArrival();
|
||||
g.pendingShipment!.cost++;
|
||||
reject(
|
||||
g,
|
||||
'guild',
|
||||
{ type: 'decision', allow: true },
|
||||
/declared shipment price is no longer current/,
|
||||
);
|
||||
reject(g, 'guild', { type: 'decision', allow: true });
|
||||
});
|
||||
|
||||
void test('forged or null special receipts cannot enter the ordinary legacy return path', () => {
|
||||
for (const corrupt of [
|
||||
(g: Game) => {
|
||||
g.pendingShipment!.ambassadorEvent = 'forged';
|
||||
},
|
||||
(g: Game) => {
|
||||
g.pendingShipment!.guildSecretEvent = null as unknown as string;
|
||||
},
|
||||
]) {
|
||||
const g = legacyGuildArrival();
|
||||
corrupt(g);
|
||||
reject(g, 'guild', { type: 'decision', allow: true }, overlap);
|
||||
assert.ok(g.pendingShipment);
|
||||
assert.equal(g.decision?.kind, 'guildShipment');
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user