import assert from 'node:assert/strict'; import { test } from 'node:test'; import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); // Inline compare logic by importing via dynamic eval of exported function - // the script only has main(); duplicate core compare here for unit tests. function indexMemories(manifest) { const byId = new Map(); for (const item of manifest.memories ?? []) { byId.set(item.id, item); } return byId; } function indexCandidates(manifest) { const byId = new Map(); for (const item of manifest.candidates ?? []) { byId.set(item.id, item); } return byId; } function compareManifests(baseline, current) { const drift = { summary: { candidateCountDelta: (current.summary?.candidateCount ?? 0) - (baseline.summary?.candidateCount ?? 0), }, addedMemoryIds: [], removedMemoryIds: [], changedMemories: [], addedCandidateIds: [], removedCandidateIds: [], changedCandidates: [], }; const base = indexMemories(baseline); const cur = indexMemories(current); for (const [id] of cur) { if (!base.has(id)) drift.addedMemoryIds.push(id); } for (const [id] of base) { if (!cur.has(id)) drift.removedMemoryIds.push(id); } for (const [id, baseItem] of base) { const curItem = cur.get(id); if (!curItem) continue; const fields = [ 'userId', 'memoryHash', 'status', 'sourceSessionId', 'evidenceMessageId', ]; const changes = {}; for (const field of fields) { if (String(baseItem[field] ?? '') !== String(curItem[field] ?? '')) { changes[field] = { baseline: baseItem[field] ?? null, current: curItem[field] ?? null }; } } if (Object.keys(changes).length) { drift.changedMemories.push({ id, changes }); } } const baseCandidates = indexCandidates(baseline); const curCandidates = indexCandidates(current); for (const [id] of curCandidates) { if (!baseCandidates.has(id)) drift.addedCandidateIds.push(id); } for (const [id] of baseCandidates) { if (!curCandidates.has(id)) drift.removedCandidateIds.push(id); } for (const [id, baseItem] of baseCandidates) { const curItem = curCandidates.get(id); if (!curItem) continue; const fields = ['userId', 'status', 'sourceSessionId']; const changes = {}; for (const field of fields) { if (String(baseItem[field] ?? '') !== String(curItem[field] ?? '')) { changes[field] = { baseline: baseItem[field] ?? null, current: curItem[field] ?? null }; } } if (Object.keys(changes).length) { drift.changedCandidates.push({ id, changes }); } } drift.ok = drift.addedMemoryIds.length === 0 && drift.removedMemoryIds.length === 0 && drift.changedMemories.length === 0 && drift.addedCandidateIds.length === 0 && drift.removedCandidateIds.length === 0 && drift.changedCandidates.length === 0 && drift.summary.candidateCountDelta === 0; return drift; } test('memory drift fails when userId changes for the same memory id', () => { const baseline = { summary: { candidateCount: 1 }, memories: [{ id: 'm1', userId: 'u1', memoryHash: 'h1', status: 'active' }], candidates: [{ id: 'c1', userId: 'u1', status: 'pending' }], }; const current = { summary: { candidateCount: 6 }, memories: [{ id: 'm1', userId: 'u2', memoryHash: 'h1', status: 'active' }], candidates: [ { id: 'c1', userId: 'u1', status: 'pending' }, { id: 'c2', userId: 'u1', status: 'pending' }, { id: 'c3', userId: 'u1', status: 'pending' }, { id: 'c4', userId: 'u1', status: 'pending' }, { id: 'c5', userId: 'u1', status: 'pending' }, { id: 'c6', userId: 'u1', status: 'pending' }, ], }; const report = compareManifests(baseline, current); assert.equal(report.ok, false); assert.equal(report.changedMemories.length, 1); assert.equal(report.changedMemories[0].changes.userId.baseline, 'u1'); assert.equal(report.summary.candidateCountDelta, 5); assert.ok(report.addedCandidateIds.length >= 5); }); test('memory drift passes for identical manifests', () => { const manifest = { summary: { candidateCount: 0 }, memories: [{ id: 'm1', userId: 'u1', memoryHash: 'h1', status: 'active' }], candidates: [], }; const report = compareManifests(manifest, structuredClone(manifest)); assert.equal(report.ok, true); });