import assert from 'node:assert/strict'; import test from 'node:test'; import { GholaLeaderPromiseError, gholaLeaderClaimText, gholaLeaderRequirements, liveGholaLeaderPromises, parseGholaLeaderClaim, recordPromisedGholaLeader, validateGholaLeaderPromises, type GholaLeaderPromise } from '../game/ghola-leader-promises'; const promise = (event: string, leader = 'leader-a', changes: Partial = {}): GholaLeaderPromise => ({ event, player: 'target', asker: 'asker', turn: 2, leader, answer: true, ...changes }); void test('named Ghola claims require a current turn and exact identity without coercion', () => { assert.deepEqual(parseGholaLeaderClaim({ kind: 'gholaLeader', target: 'target', turn: 2, leader: 'kwisatz' }), { turn: 2, leader: 'kwisatz' }); assert.match(gholaLeaderClaimText({ turn: 2, leader: 'kwisatz' }), /Kwisatz Haderach.*Ghola/); for (const value of [null, [], {}, { turn: 0, leader: 'a' }, { turn: 2.5, leader: 'a' }, { turn: '2', leader: 'a' }, { turn: 2, leader: '' }, { turn: 2, leader: ' a' }]) assert.throws(() => parseGholaLeaderClaim(value), GholaLeaderPromiseError); }); void test('live obligations combine duplicates, flag conflicts, and do not grant extra Ghola uses', () => { const records = [promise('a'), promise('a-again'), promise('no', 'leader-b', { answer: false }), promise('old', 'leader-c', { turn: 1 }), promise('other', 'leader-d', { player: 'other' }), promise('done', 'leader-e', { fulfilled: true }), promise('released', 'leader-f', { released: true })]; assert.deepEqual(liveGholaLeaderPromises(records, 'target', 2), records.slice(0, 3)); assert.deepEqual(gholaLeaderRequirements(records, 'target', 2), { required: ['leader-a'], forbidden: ['leader-b'] }); assert.equal(gholaLeaderRequirements([...records, promise('contradiction', 'leader-a', { answer: false })], 'target', 2), null); }); void test('only an actual matching return fulfills Yes, and No rejects without mutation', () => { const records = [promise('a'), promise('a-again'), promise('no', 'leader-b', { answer: false }), promise('other', 'leader-a', { player: 'other' }), promise('earlier', 'leader-a', { fulfilled: true })]; const before = structuredClone(records); assert.throws(() => recordPromisedGholaLeader(records, 'target', 2, 'leader-b'), GholaLeaderPromiseError); assert.deepEqual(records, before); const after = recordPromisedGholaLeader(records, 'target', 2, 'leader-a'); assert.equal(after[0].fulfilled, true); assert.equal(after[1].fulfilled, true); assert.equal(after[2], records[2]); assert.equal(after[3], records[3]); assert.equal(after[4], records[4]); assert.deepEqual(records, before); const unrelated = recordPromisedGholaLeader(records, 'target', 2, 'kwisatz'); assert.equal(unrelated[0], records[0]); assert.throws(() => recordPromisedGholaLeader(records, 'target', 2, ' leader-a'), GholaLeaderPromiseError); }); void test('ledger validation enforces participants, unique events and terminal state', () => { const valid = [promise('a'), promise('b', 'kwisatz', { answer: false }), promise('c', 'leader-c', { released: true })]; assert.doesNotThrow(() => validateGholaLeaderPromises(JSON.parse(JSON.stringify(valid)), ['target', 'asker'])); for (const invalid of [null, [null], [promise('a'), promise('a')], [promise('bad', 'a', { asker: 'target' })], [promise('bad', 'a', { player: 'absent' })], [promise('bad', 'a', { fulfilled: true, released: true })], [{ ...promise('bad'), answer: 'yes' }]]) assert.throws(() => validateGholaLeaderPromises(invalid as GholaLeaderPromise[], ['target', 'asker']), GholaLeaderPromiseError); });