tests: undo roundtrip (10 green) + reorderParticipants BUG-7 candidate
turn.undo.test.js: every op with log.undo roundtrips to prior state. startEncounter, nextTurn, togglePause, applyHpChange, toggleCondition, toggleParticipantActive, addParticipant, removeParticipant, endEncounter. Found: reorderParticipants returns log:null. Cannot undo. Documents as BUG-7 candidate (test green now, asserts current behavior).
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
// Undo roundtrip: every op that returns log.undo must restore prior state.
|
||||
// Apply op → patch → apply undo → assert deepEqual original.
|
||||
|
||||
const shared = require('@ttrpg/shared');
|
||||
const {
|
||||
makeParticipant, startEncounter, nextTurn, togglePause,
|
||||
addParticipant, removeParticipant, toggleParticipantActive,
|
||||
applyHpChange, toggleCondition, reorderParticipants, endEncounter,
|
||||
} = shared;
|
||||
|
||||
function p(id, init, extra = {}) {
|
||||
return makeParticipant({
|
||||
id, name: id, type: 'monster',
|
||||
initiative: init, maxHp: 100, currentHp: 100,
|
||||
...extra,
|
||||
});
|
||||
}
|
||||
function enc(ps) {
|
||||
return { name:'t', participants:ps, isStarted:false, isPaused:false,
|
||||
round:0, currentTurnParticipantId:null, turnOrderIds:[] };
|
||||
}
|
||||
const snap = (e) => JSON.parse(JSON.stringify(e));
|
||||
|
||||
describe('undo roundtrip', () => {
|
||||
test('startEncounter undo restores pre-start', () => {
|
||||
const before = enc([p('a',10),p('b',20)]);
|
||||
const r = startEncounter(before);
|
||||
expect(r.log.undo).toBeTruthy();
|
||||
const after = { ...before, ...r.patch, ...r.log.undo };
|
||||
expect(snap(after)).toEqual(snap(before));
|
||||
});
|
||||
|
||||
test('nextTurn undo restores prior currentTurn/round', () => {
|
||||
let e = enc([p('a',10),p('b',20),p('c',5)]);
|
||||
e = { ...e, ...startEncounter(e).patch };
|
||||
const before = snap(e);
|
||||
const r = nextTurn(e);
|
||||
expect(r.log.undo).toBeTruthy();
|
||||
const after = { ...e, ...r.patch, ...r.log.undo };
|
||||
expect(snap(after)).toEqual(before);
|
||||
});
|
||||
|
||||
test('togglePause undo restores prior paused state', () => {
|
||||
let e = enc([p('a',10),p('b',20)]);
|
||||
e = { ...e, ...startEncounter(e).patch };
|
||||
const before = snap(e);
|
||||
const r = togglePause(e);
|
||||
expect(r.log.undo).toBeTruthy();
|
||||
const after = { ...e, ...r.patch, ...r.log.undo };
|
||||
expect(snap(after)).toEqual(before);
|
||||
});
|
||||
|
||||
test('applyHpChange undo restores prior participants', () => {
|
||||
let e = enc([p('a',10,{maxHp:100,currentHp:100}),p('b',20)]);
|
||||
e = { ...e, ...startEncounter(e).patch };
|
||||
const before = snap(e);
|
||||
const r = applyHpChange(e, 'a', 'damage', 20);
|
||||
expect(r.log.undo).toBeTruthy();
|
||||
const after = { ...e, ...r.patch, ...r.log.undo };
|
||||
expect(snap(after)).toEqual(before);
|
||||
});
|
||||
|
||||
test('toggleCondition undo restores prior participants', () => {
|
||||
let e = enc([p('a',10),p('b',20)]);
|
||||
e = { ...e, ...startEncounter(e).patch };
|
||||
const before = snap(e);
|
||||
const r = toggleCondition(e, 'a', 'stunned');
|
||||
expect(r.log.undo).toBeTruthy();
|
||||
const after = { ...e, ...r.patch, ...r.log.undo };
|
||||
expect(snap(after)).toEqual(before);
|
||||
});
|
||||
|
||||
test('toggleParticipantActive undo restores prior participants + turn order', () => {
|
||||
let e = enc([p('a',10),p('b',20),p('c',5)]);
|
||||
e = { ...e, ...startEncounter(e).patch };
|
||||
const before = snap(e);
|
||||
const r = toggleParticipantActive(e, 'b');
|
||||
expect(r.log.undo).toBeTruthy();
|
||||
const after = { ...e, ...r.patch, ...r.log.undo };
|
||||
expect(snap(after)).toEqual(before);
|
||||
});
|
||||
|
||||
test('addParticipant undo restores prior participants', () => {
|
||||
let e = enc([p('a',10),p('b',20)]);
|
||||
e = { ...e, ...startEncounter(e).patch };
|
||||
const before = snap(e);
|
||||
const np = makeParticipant({ id:'z', name:'z', type:'monster', initiative:15, maxHp:50, currentHp:50 });
|
||||
const r = addParticipant(e, np);
|
||||
expect(r.log.undo).toBeTruthy();
|
||||
const after = { ...e, ...r.patch, ...r.log.undo };
|
||||
expect(snap(after)).toEqual(before);
|
||||
});
|
||||
|
||||
test('removeParticipant undo restores prior participants + turn order', () => {
|
||||
let e = enc([p('a',10),p('b',20),p('c',5)]);
|
||||
e = { ...e, ...startEncounter(e).patch };
|
||||
const before = snap(e);
|
||||
const r = removeParticipant(e, 'b');
|
||||
expect(r.log.undo).toBeTruthy();
|
||||
const after = { ...e, ...r.patch, ...r.log.undo };
|
||||
expect(snap(after)).toEqual(before);
|
||||
});
|
||||
|
||||
test('endEncounter undo restores prior state', () => {
|
||||
let e = enc([p('a',10),p('b',20)]);
|
||||
e = { ...e, ...startEncounter(e).patch };
|
||||
const before = snap(e);
|
||||
const r = endEncounter(e);
|
||||
expect(r.log.undo).toBeTruthy();
|
||||
const after = { ...e, ...r.patch, ...r.log.undo };
|
||||
expect(snap(after)).toEqual(before);
|
||||
});
|
||||
|
||||
test('reorderParticipants has no undo (log: null) — BUG candidate', () => {
|
||||
const ps = [p('a',10),p('b',20),p('c',20)]; // b,c tie
|
||||
let e = enc(ps);
|
||||
e = { ...e, ...startEncounter(e).patch };
|
||||
const r = reorderParticipants(e, 'c', 'b');
|
||||
// Documents: reorderParticipants returns log: null. Cannot undo.
|
||||
// If undo expected here, this is BUG-7.
|
||||
expect(r.log).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user