import assert from 'node:assert/strict'; import test from 'node:test'; import { chooseDiscoveryStorm, discoveryStormSaphoModes, discoveryStormSignature, finishDiscoveryStorm, reorderDiscoveryStorm, validateDiscoveryStorm, } from '../game/discovery-storm'; import {createTestingStationContestFixture} from './fixture-testing-station-contest'; // Real revealed station, original storm source and both occupants. The helper // tests ordering only; physical Sapho ownership and disposal belong to engine. const source = () => createTestingStationContestFixture().game; void test('legacy station signature stays unchanged and offers only meaningful two-seat moves', () => { const game = source(), frame = game.ecologicalStorm!; const [first, second] = frame.queue!.order; assert.equal(frame.signature, JSON.stringify([frame.event, frame.turn, frame.owner, frame.token, frame.from, frame.base, frame.delta, frame.stage, frame.queue!.order, frame.queue!.choices])); assert.deepEqual(discoveryStormSaphoModes(game, frame, first), ['last']); assert.deepEqual(discoveryStormSaphoModes(game, frame, second), ['first']); assert.deepEqual(discoveryStormSaphoModes(game, frame, 'nonoccupant'), []); }); for (const mode of ['first', 'last'] as const) void test( `Sapho ${mode} binds a fresh station event and preserves both cumulative choices`, () => { const game = source(), original = structuredClone(game.ecologicalStorm!); const holder = original.queue!.order[mode === 'first' ? 1 : 0]; const frame = reorderDiscoveryStorm(game, original, {holder, event: original.event, mode, nextEvent: 'sapho-fresh-event'}); assert.deepEqual(original, game.ecologicalStorm); assert.deepEqual(frame.queue!.order, [...original.queue!.order].reverse()); assert.equal(frame.owner, frame.queue!.order[0]); assert.deepEqual(frame.sapho, {event: original.event, nextEvent: 'sapho-fresh-event', originalOrder: original.queue!.order, holder, mode}); assert.deepEqual(discoveryStormSaphoModes(game, frame, holder), []); assert.throws(() => chooseDiscoveryStorm(game, frame, frame.owner, original.event, 0)); let next = chooseDiscoveryStorm(game, frame, frame.owner, frame.event, 0); assert.equal(next.event, 'sapho-fresh-event:1'); validateDiscoveryStorm(game, next); next = chooseDiscoveryStorm(game, next, next.owner, next.event, 0); assert.equal(next.stage, 'traversal'); validateDiscoveryStorm(game, next); next = finishDiscoveryStorm(next); validateDiscoveryStorm(game, next); assert.deepEqual(next.sapho, frame.sapho); assert.deepEqual(next.queue!.choices.map(choice => choice.owner), frame.queue!.order); }, ); void test('stale, ineffective, replayed and tampered station reorder requests fail closed', () => { const game = source(), frame = game.ecologicalStorm!; const [first, second] = frame.queue!.order, before = structuredClone(frame); for (const input of [ {holder: first, event: 'stale', mode: 'last' as const, nextEvent: 'fresh'}, {holder: first, event: frame.event, mode: 'first' as const, nextEvent: 'fresh'}, {holder: second, event: frame.event, mode: 'first' as const, nextEvent: frame.event}, {holder: 'other', event: frame.event, mode: 'last' as const, nextEvent: 'fresh'}, ]) assert.throws(() => reorderDiscoveryStorm(game, frame, input)); assert.deepEqual(frame, before); const reordered = reorderDiscoveryStorm(game, frame, {holder: first, event: frame.event, mode: 'last', nextEvent: 'fresh'}); assert.throws(() => reorderDiscoveryStorm(game, reordered, {holder: second, event: 'fresh', mode: 'last', nextEvent: 'again'})); const changed = structuredClone(reordered); changed.sapho!.originalOrder.reverse(); changed.signature = discoveryStormSignature(changed); assert.throws(() => validateDiscoveryStorm(game, changed)); const afterFirst = chooseDiscoveryStorm(game, frame, first, frame.event, 0); assert.deepEqual(discoveryStormSaphoModes(game, afterFirst, second), []); });