Files
Dune/tests/guild-advisor-source-arrival-engine.test.ts

84 lines
5.1 KiB
TypeScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { applyAction, viewGame, type Action, type Game } from '../game/engine';
import { isAdvisor } from '../game/advisors';
import { guildTransportQuote } from '../game/transport-quote';
import {
createSemutaAdvisorTransportMatchingFixture as fixture,
actSemutaAdvisorTransportMatching as act, allowSemutaAdvisorTransportMatching as allow,
finishSemutaAdvisorTransportMatching as finish, semutaAdvisorTransportMatchingPlayer as player,
semutaAdvisorTransportMatchingInventory as inventory,
} from './fixture-semuta-advisor-transport-matching';
const advisorEntry: Action = {type:'guildShip',from:'arrakeen:10',territory:'tueks_sietch',
sector:5,amount:1,elite:0,allyPayment:0};
function complete(game: Game) {
for (let count=0;game.response && ['guildTransportPermission','guildRate'].includes(game.response.kind) && count<3;count++)
game=allow(game);
return finish(game);
}
function reject(game: Game, actor: string, action: Action) {
const before=structuredClone(game);assert.throws(()=>applyAction(game,actor,action));assert.deepEqual(game,before);
}
function source() {
const f=fixture({kind:'guild'}),game=structuredClone(f.beforeDeclaration);
assert.equal(game.pendingAdvisorTransportMatching,undefined);
assert.ok((player(game,f.guild).forces['tueks_sietch:5']??0)>0);
assert.equal(player(game,f.owner).ally,f.guild);assert.equal(player(game,f.guild).ally,f.owner);
assert.equal(isAdvisor(player(game,f.owner),'arrakeen'),true);
assert.equal(player(game,f.owner).forces['arrakeen:10'],1);
return {f,game};
}
void test('fresh Guild board transport quotes and delivers the actual advisor into its allied foreign group',()=>{
const {f,game:before}=source();
assert.deepEqual(guildTransportQuote(viewGame(before,f.owner),advisorEntry).unavailableReasons,[]);
let game=act(before,f.owner,advisorEntry);
assert.equal(game.response?.kind,'guildTransportPermission');assert.equal(game.pendingAdvisorTransportMatching,undefined);
assert.equal(game.pendingGuildTransport?.advisorArrivalSource,'arrakeen');assert.equal(game.pendingGuildTransport?.advisors,true);
assert.deepEqual(player(game,f.owner).forces,player(before,f.owner).forces);
game=complete(game);
assert.equal(player(game,f.owner).forces['arrakeen:10']??0,0);
assert.equal(player(game,f.owner).forces['tueks_sietch:5'],1);assert.equal(isAdvisor(player(game,f.owner),'tueks_sietch'),true);
assert.deepEqual(player(game,f.guild).forces,player(before,f.guild).forces);
assert.equal(player(game,f.owner).spice,player(before,f.owner).spice-1);
assert.equal(player(game,f.guild).spice,player(before,f.guild).spice+1);
assert.equal(player(game,f.owner).shipped,true);assert.equal(player(game,f.owner).moved,0);
assert.equal(game.pendingAutomaticAdvisorFlips,undefined);assert.deepEqual(inventory(game),f.originalInventory);
reject(game,f.owner,advisorEntry);
});
void test('the source-stance cutover blocks fighter cross-entry and preserves the direct reserve default',()=>{
const {f,game}=source();
const fighter:Action={...advisorEntry,from:'imperial_basin:9'};
assert.ok(guildTransportQuote(viewGame(game,f.owner),fighter).unavailableReasons.length>0);
reject(game,f.owner,fighter);
const reserve=act(game,f.owner,{type:'ship',territory:'tueks_sietch',sector:5,amount:1,elite:0,allyPayment:0});
assert.ok(reserve.pendingShipment);assert.equal(reserve.pendingShipment.advisors,false);
assert.equal('advisorArrivalSource' in reserve.pendingShipment,false);
});
void test('a fresh Guild advisor declaration rejects a changed source before payment or transfer',()=>{
const {f,game:before}=source(),game=act(before,f.owner,advisorEntry);
assert.ok(game.pendingGuildTransport);game.pendingGuildTransport.advisorArrivalSource='polar_sink';
reject(game,f.holder,{type:'passResponse'});
});
void test('controlled legacy Guild permission bytes retain the original already-approved matching settlement',()=>{
const f=fixture({kind:'guild'});let game=allow(structuredClone(f.game));
assert.equal(game.response?.kind,'guildTransportPermission');const intent=game.pendingGuildTransport;assert.ok(intent);
// Explicit format supplement to a genuine approved prefix. This is not a
// historical saved-game capture and changes no physical source or approval.
delete intent.advisorArrivalSource;
const fields:unknown[]=[intent.event,intent.turn,intent.player,intent.fromReserves,intent.group,
intent.eliteGroup,intent.origin,intent.to,intent.sector,intent.amount,intent.elite,
intent.cost,intent.allyPayment,intent.advisors];
if(intent.permission!==undefined)fields.push(intent.permission);
intent.signature=JSON.stringify(fields);
game=complete(game);
assert.equal(player(game,f.owner).forces['imperial_basin:9'],1);
assert.equal(player(game,f.owner).forces['arrakeen:10'],2);assert.equal(isAdvisor(player(game,f.owner),'arrakeen'),true);
assert.equal(player(game,f.owner).shipped,true);assert.equal(player(game,f.owner).moved,0);
assert.deepEqual(inventory(game),f.originalInventory);assert.equal(game.pendingGuildTransport??null,null);
});