Files
Dune/tests/turn-action-shipment-sector.test.ts

85 lines
4.6 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { compileMixedActionExpression, type MixedActionExpression } from '../game/mixed-action-question';
import {
TurnActionError,
parseTurnActionLeaf,
recordTurnActionShipment,
turnActionBranches,
turnActionValue,
validateTurnActionPromises,
type TurnActionLeaf,
type TurnActionPromise,
} from '../game/turn-action-promises';
type Expression = MixedActionExpression<TurnActionLeaf>;
const ship = (sector?: number, minimum = 3, territory = 'wind_pass'): Expression => ({
kind: 'action', action: { kind: 'ship', territory, minimum, ...(sector === undefined ? {} : { sector }) },
});
const group = (kind: 'and' | 'or', ...terms: Expression[]): Expression => ({ kind, terms });
function promise(mixed: Expression, answer = true): TurnActionPromise {
return {
turn: 1, mixed, event: 'whole:1', player: 'p', asker: 'a', answer,
compiled: compileMixedActionExpression(mixed, () => 'yes', answer),
shipment: { pending: true, event: null }, progress: [], payments: [],
transportClosed: false, paymentClosed: false, fulfilled: false, released: false,
};
}
void test('whole shipment atoms preserve exact printed sectors and reject invalid ones', () => {
assert.deepEqual(parseTurnActionLeaf({ kind: 'ship', territory: 'wind_pass', minimum: 3, sector: 15 }),
{ kind: 'ship', territory: 'wind_pass', minimum: 3, sector: 15 });
for (const input of [
{ kind: 'ship', territory: 'wind_pass', minimum: 3, sector: 4 },
{ kind: 'ship', territory: 'wind_pass', minimum: 3, sector: 15.5 },
{ kind: 'move', territory: 'wind_pass', minimum: 3, sector: 15 },
{ kind: 'ship', territory: 'wind_pass', minimum: 3, sector: null },
]) assert.throws(() => parseTurnActionLeaf(input), TurnActionError);
});
void test('one actual matching sector fulfills the signed Yes or rejects the signed No', () => {
const exact = promise(ship(15));
const [done] = recordTurnActionShipment([exact], 'p', 1, { territory: 'wind_pass', sector: 15, amount: 3 });
assert.deepEqual(done.shipment.event, { territory: 'wind_pass', sector: 15, amount: 3 });
assert.equal(done.fulfilled, true);
assert.equal(turnActionValue(done), true);
validateTurnActionPromises([done], ['p', 'a']);
assert.throws(() => recordTurnActionShipment([promise(ship(15), false)], 'p', 1,
{ territory: 'wind_pass', sector: 15, amount: 3 }), TurnActionError);
const [other] = recordTurnActionShipment([promise(ship(15), false)], 'p', 1,
{ territory: 'wind_pass', sector: 14, amount: 3 });
assert.equal(other.fulfilled, true);
assert.equal(turnActionValue(other), false);
const [unknown] = recordTurnActionShipment([promise(ship(15), false)], 'p', 1,
{ territory: 'wind_pass', amount: 3 });
assert.equal(turnActionValue(unknown), false);
});
void test('sector and territory-wide atom identity remain distinct across AND and OR', () => {
const broad = ship(), exact14 = ship(14), exact15 = ship(15);
const alternatives = promise(group('or', broad, exact14));
validateTurnActionPromises([alternatives], ['p', 'a']);
const branches = [...turnActionBranches(alternatives)];
assert.equal(branches.length, 2);
assert.ok(branches.some(row => row.some(leaf => leaf.kind === 'ship' && leaf.sector === undefined)));
assert.ok(branches.some(row => row.some(leaf => leaf.kind === 'ship' && leaf.sector === 14)));
assert.deepEqual([...turnActionBranches(promise(group('and', exact14, exact15)))], []);
assert.equal([...turnActionBranches(promise(group('and', broad, exact14)))].length, 1);
const [done] = recordTurnActionShipment([alternatives], 'p', 1, { territory: 'wind_pass', sector: 15, amount: 3 });
assert.equal(done.fulfilled, true);
});
void test('mixed exact and broad receipts retain one real event without changing legacy bytes', () => {
const legacy = promise(ship());
const [legacyDone] = recordTurnActionShipment([legacy], 'p', 1, { territory: 'wind_pass', sector: 15, amount: 3 });
assert.deepEqual(legacyDone.shipment.event, { territory: 'wind_pass', amount: 3 });
assert.equal(JSON.stringify(legacyDone.shipment.event), '{"territory":"wind_pass","amount":3}');
validateTurnActionPromises([legacyDone], ['p', 'a']);
const row = promise(group('or', ship(), ship(14)));
const [done] = recordTurnActionShipment([row], 'p', 1, { territory: 'wind_pass', sector: 15, amount: 3 });
assert.deepEqual(done.shipment.event, { territory: 'wind_pass', amount: 3, sector: 15 });
validateTurnActionPromises([done], ['p', 'a']);
assert.throws(() => recordTurnActionShipment([promise(ship(14))], 'p', 1,
{ territory: 'wind_pass', sector: 19, amount: 3 }), TurnActionError);
});