89 lines
2.9 KiB
JavaScript
89 lines
2.9 KiB
JavaScript
import { test } from 'node:test';
|
|||
|
|
import assert from 'node:assert/strict';
|
||
|
|
|
||
|
|
import { createSessionReaderStateCache } from '../app/src/renderer/src/session-reader-state.mjs';
|
||
|
|
import { resolveReaderAnchorIndex } from '../app/src/renderer/src/session-timeline-viewport.mjs';
|
||
|
|
|
||
|
|
test('reader state cache stores only semantic state in independent session snapshots', () => {
|
||
|
|
const cache = createSessionReaderStateCache({ maxEntries: 4 });
|
||
|
|
|
||
|
|
cache.set('session-a', {
|
||
|
|
mode: 'anchor',
|
||
|
|
anchor: {
|
||
|
|
itemKey: 'message:a-120',
|
||
|
|
messageUuid: 'a-120',
|
||
|
|
offset: 18,
|
||
|
|
fallbackIndex: 120,
|
||
|
|
},
|
||
|
|
disclosures: [{ key: 'tool:a-call', messageUuid: 'a-120', open: true, raw: false }],
|
||
|
|
expandedMessageIds: ['a-120'],
|
||
|
|
scrollTop: 98_000,
|
||
|
|
currentMsgIdx: 120,
|
||
|
|
});
|
||
|
|
|
||
|
|
const restored = cache.get('session-a');
|
||
|
|
assert.deepEqual(restored, {
|
||
|
|
mode: 'anchor',
|
||
|
|
anchor: {
|
||
|
|
itemKey: 'message:a-120',
|
||
|
|
messageUuid: 'a-120',
|
||
|
|
offset: 18,
|
||
|
|
fallbackIndex: 120,
|
||
|
|
},
|
||
|
|
disclosures: [{ key: 'tool:a-call', messageUuid: 'a-120', open: true, raw: false }],
|
||
|
|
expandedMessageIds: ['a-120'],
|
||
|
|
});
|
||
|
|
assert.equal(cache.get('session-b'), null);
|
||
|
|
|
||
|
|
restored.anchor.offset = 999;
|
||
|
|
restored.disclosures[0].open = false;
|
||
|
|
restored.expandedMessageIds.push('mutated');
|
||
|
|
assert.equal(cache.get('session-a').anchor.offset, 18);
|
||
|
|
assert.equal(cache.get('session-a').disclosures[0].open, true);
|
||
|
|
assert.deepEqual(cache.get('session-a').expandedMessageIds, ['a-120']);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('reader state cache evicts the least recently used session', () => {
|
||
|
|
const cache = createSessionReaderStateCache({ maxEntries: 2 });
|
||
|
|
const state = messageUuid => ({
|
||
|
|
mode: 'anchor',
|
||
|
|
anchor: { itemKey: `message:${messageUuid}`, messageUuid, offset: 0, fallbackIndex: 0 },
|
||
|
|
});
|
||
|
|
|
||
|
|
cache.set('session-a', state('a'));
|
||
|
|
cache.set('session-b', state('b'));
|
||
|
|
cache.get('session-a');
|
||
|
|
cache.set('session-c', state('c'));
|
||
|
|
|
||
|
|
assert.equal(cache.get('session-b'), null);
|
||
|
|
assert.equal(cache.get('session-a').anchor.messageUuid, 'a');
|
||
|
|
assert.equal(cache.get('session-c').anchor.messageUuid, 'c');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('reader anchors resolve by stable identity before falling back to position', () => {
|
||
|
|
const items = [
|
||
|
|
{ key: 'message:first', messageUuid: 'first' },
|
||
|
|
{ key: 'workflow:shared', messageUuid: 'shared' },
|
||
|
|
{ key: 'message:shared', messageUuid: 'shared' },
|
||
|
|
{ key: 'message:last', messageUuid: 'last' },
|
||
|
|
];
|
||
|
|
|
||
|
|
assert.equal(resolveReaderAnchorIndex({
|
||
|
|
itemKey: 'message:shared',
|
||
|
|
messageUuid: 'shared',
|
||
|
|
fallbackIndex: 0,
|
||
|
|
}, items), 2);
|
||
|
|
assert.equal(resolveReaderAnchorIndex({
|
||
|
|
itemKey: 'missing',
|
||
|
|
messageUuid: 'shared',
|
||
|
|
fallbackIndex: 0,
|
||
|
|
}, items), 1);
|
||
|
|
assert.equal(resolveReaderAnchorIndex({
|
||
|
|
itemKey: 'missing',
|
||
|
|
messageUuid: 'missing',
|
||
|
|
fallbackIndex: 99,
|
||
|
|
}, items), 3);
|
||
|
|
assert.equal(resolveReaderAnchorIndex(null, items), 0);
|
||
|
|
assert.equal(resolveReaderAnchorIndex(null, []), null);
|
||
|
|
});
|