import assert from 'node:assert/strict'; import test from 'node:test'; import { TERRITORIES } from '../game/board'; import { applyAction, viewGame, type Action } from '../game/engine'; import { junctionTransportActions, junctionTransportChoice } from '../game/junction-transport-options'; import { TERROR_STRONGHOLDS } from '../game/moritani-terror'; import { createJunctionPurchasedPrice, type PurchasedJunctionRoute } from './fixture-junction-purchased-price'; /** Original public setup, physical purchased rate and Guild's actual Junction * offer come from the native fixture. To examine the later persisted state, * Atomics' removed token, original victim and hand-limit marker are staged; * any excess held card is moved into the original deck, not duplicated. */ function staged(route: PurchasedJunctionRoute) { const f=createJunctionPurchasedPrice({route}); const g=structuredClone(f.beforeAction); const available=TERRITORIES.filter(t=>TERROR_STRONGHOLDS.includes(t.id)).flatMap(t=> t.sectors.map(sector=>`${t.id}:${sector}`)).find(destination=> !!junctionTransportChoice(viewGame(g,f.shipper),destination,f.sources).action); assert.ok(available,'An original offered Junction route needs an eligible stronghold.'); const target=available.split(':')[0]; const owner=g.players.find(player=>player.faction==='moritani'); assert.ok(owner); const token=g.moritaniTerror?.tokens.find(candidate=>candidate.kind==='atomics'); assert.ok(token); token.status='removed'; token.location=null; owner.atomicsHandLimitPenalty=true; if (owner.hand.length>3) g.deck.push(...owner.hand.splice(3)); g.moritaniAtomics={territory:target,turn:g.turn,moritaniId:owner.id, allyAtActivation:null,alliancePolicy:'activation-targets'}; return {f,g,destination:available,target}; } for (const route of ['nativeArrival','arrakisReturn'] as const) { void test(`Junction ${route==='nativeArrival'?'Homeworld-to-Arrakis':'Arrakis-to-Arrakis'} cannot enter Atomics Aftermath`,()=>{ const {f,g,destination,target}=staged(route); const view=viewGame(g,f.shipper); const quoted=junctionTransportChoice(view,destination,f.sources); assert.match(quoted.blocked??'',/Atomics Aftermath/); assert.equal(quoted.action,null); const offer=view.junctionTransport?.offer; assert.ok(offer); const declaration: Action={type:'junctionShip',event:view.junctionTransport!.event, offer:offer.event,destination,sources:f.sources,allyPayment:0,forfeitPurchasedRate:true}; const before=structuredClone(g); assert.throws(()=>applyAction(g,f.shipper,declaration),/Atomics Aftermath/); assert.deepEqual(g,before); for (let level=0;level<4;level++) assert.ok(!junctionTransportActions(view,level).some(action=> action.type==='junctionShip' && typeof action.destination==='string' && action.destination.split(':')[0]===target)); // A Homeworld destination remains legal because the marker affects only Arrakis. const world=junctionTransportChoice(view,'homeworld:ecaz',f.sources); assert.ok(!world.blocked?.includes('Atomics')); }); }