// BUG-7: reorderParticipants not logged. // Every mutating func is now async, takes ctx last, and writes encounter + // log internally via commit(). reorderParticipants must emit a log entry on a // real move (so the drag shows up in the combat log + carries an undo payload) // and stay a silent no-op (no write) when blocked. // // RED was: returned { patch, log } with log:null → handler skipped logAction. // Now: a successful reorder writes exactly one log entry; no-ops write none. 'use strict'; const { makeParticipant, reorderParticipants, expandUndo } = require('@ttrpg/shared'); const { mockCtx } = require('./_helpers'); function p(id, init) { return makeParticipant({ id, name: id, type: 'monster', initiative: init, maxHp: 100, currentHp: 100 }); } function enc(ps) { return { name:'t', participants:ps, isStarted:false, isPaused:false, round:0, currentTurnParticipantId:null, turnOrderIds:[] }; } describe('BUG-7: reorderParticipants logged', () => { test('returns log object (not null) with message + undo', async () => { const { storage, ctx } = mockCtx(); const e = enc([p('a', 10), p('b', 10), p('c', 10)]); await reorderParticipants(e, 'c', 'b', ctx); expect(storage.logs()).toHaveLength(1); const entry = storage.logs()[0]; expect(typeof entry.message).toBe('string'); expect(entry.message.length).toBeGreaterThan(0); expect(entry.undo).toBeDefined(); }); test('undo restores original participants order', async () => { const { storage, ctx } = mockCtx(); const e = enc([p('a', 10), p('b', 10), p('c', 10)]); const orig = e.participants.map(p => p.id); await reorderParticipants(e, 'c', 'b', ctx); const entry = storage.logs()[0]; const restored = { ...e, ...expandUndo(entry, e).updates }; expect(restored.participants.map(p => p.id)).toEqual(orig); }); test('no-op (same id) returns null log', async () => { const { storage, ctx } = mockCtx(); const e = enc([p('a', 10), p('b', 10)]); const newEnc = await reorderParticipants(e, 'a', 'a', ctx); expect(newEnc).toBe(e); // same ref = no write expect(storage.logs()).toHaveLength(0); }); test('no-op (cross-init blocked) returns null log', async () => { const { storage, ctx } = mockCtx(); const e = enc([p('a', 10), p('b', 5)]); const newEnc = await reorderParticipants(e, 'a', 'b', ctx); expect(newEnc).toBe(e); // same ref = no write expect(storage.logs()).toHaveLength(0); }); });