82 lines
3.1 KiB
JavaScript
82 lines
3.1 KiB
JavaScript
// Test logEvent normalizer — canonical shape for UI/download/replay/analyze.
|
|||
|
|
// New design: legacy logs (no type) DROPPED. Not in pipeline. Kept only for
|
||
|
|
// human scroll in LogsView (raw). download/copy/replay/analyze = typed only.
|
||
|
|
const { normalizeEvent, serializeEvents } = require('../logEvent.js');
|
||
|
|
|
||
|
|
describe('logEvent normalizer', () => {
|
||
|
|
test('new schema passes through', () => {
|
||
|
|
const raw = {
|
||
|
|
id: 'l1', ts: 1000, type: 'next_turn', message: 'Bob turn',
|
||
|
|
encounterId: 'e1', encounterName: 'Enc', encounterPath: 'encounters/e1',
|
||
|
|
participantId: 'p1', participantName: 'Bob',
|
||
|
|
delta: { wrapped: true, prevRound: 1 },
|
||
|
|
undo: { currentTurnParticipantId: 'p0', round: 1, turnOrderIds: ['p0','p1'] },
|
||
|
|
undone: false, snapshot: { round: 2, currentTurnParticipantId: 'p1', turnOrderIds: ['p1'], activeIds: ['p1'] },
|
||
|
|
};
|
||
|
|
const e = normalizeEvent(raw);
|
||
|
|
expect(e.id).toBe('l1');
|
||
|
|
expect(e.type).toBe('next_turn');
|
||
|
|
expect(e.snapshot.round).toBe(2);
|
||
|
|
expect(e.delta.wrapped).toBe(true);
|
||
|
|
expect(e.undo.round).toBe(1);
|
||
|
|
expect(e.participantName).toBe('Bob');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('old bloated schema (payload/undo_payload) lifts undo, drops payload', () => {
|
||
|
|
const raw = {
|
||
|
|
id: 'l1b', ts: 1100, type: 'damage', message: 'dmg',
|
||
|
|
encounterPath: 'encounters/e1',
|
||
|
|
payload: { participants: [{ id: 'p1', currentHp: 30 }] },
|
||
|
|
undo_payload: { encounterPath: 'encounters/e1', updates: { round: 1 }, redo: { round: 2 } },
|
||
|
|
snapshot: { round: 2, currentTurnParticipantId: 'p1', turnOrderIds: ['p1'], activeIds: ['p1'] },
|
||
|
|
};
|
||
|
|
const e = normalizeEvent(raw);
|
||
|
|
expect(e.type).toBe('damage');
|
||
|
|
expect(e.undo).toBeTruthy();
|
||
|
|
expect(e.delta).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('legacy (no type) dropped → null', () => {
|
||
|
|
const raw = { id: 'l2', timestamp: 500, message: 'old', encounterName: 'Enc', undo: { encounterPath: 'enc/e1', updates: {} } };
|
||
|
|
expect(normalizeEvent(raw)).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('explicit unknown type dropped → null', () => {
|
||
|
|
expect(normalizeEvent({ id: 'l3', type: 'unknown' })).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('no type at all dropped → null', () => {
|
||
|
|
expect(normalizeEvent({ id: 'l4' })).toBeNull();
|
||
|
|
expect(normalizeEvent({ path: 'logs/abc' })).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('null in null out', () => {
|
||
|
|
expect(normalizeEvent(null)).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
test('serialize drops all-legacy input → empty', () => {
|
||
|
|
const raw = [
|
||
|
|
{ id: 'c', timestamp: 300 },
|
||
|
|
{ id: 'a', timestamp: 100 },
|
||
|
|
{ id: 'b', timestamp: 200 },
|
||
|
|
];
|
||
|
|
expect(JSON.parse(serializeEvents(raw))).toEqual([]);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('serialize sorts typed events ascending by ts', () => {
|
||
|
|
const raw = [
|
||
|
|
{ id: 'c', type: 'next_turn', ts: 300 },
|
||
|
|
{ id: 'a', type: 'start_encounter', ts: 100 },
|
||
|
|
{ id: 'b', type: 'damage', ts: 200 },
|
||
|
|
];
|
||
|
|
const arr = JSON.parse(serializeEvents(raw));
|
||
|
|
expect(arr.map(e => e.id)).toEqual(['a', 'b', 'c']);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('serialize filters nulls + legacy, keeps typed', () => {
|
||
|
|
const arr = JSON.parse(serializeEvents([null, { id: 'x', type: 'damage' }, { id: 'legacy' }, null]));
|
||
|
|
expect(arr).toHaveLength(1);
|
||
|
|
expect(arr[0].id).toBe('x');
|
||
|
|
});
|
||
|
|
});
|