397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
41 lines
2.5 KiB
JavaScript
41 lines
2.5 KiB
JavaScript
// tools/mentor-journal-store.test.mjs
|
|
import { describe, it, expect } from 'vitest';
|
|
import { loadMentorJournal, persistMentorJournal, loadMentorVerdict, persistMentorVerdict } from './mentor-journal-store.mjs';
|
|
|
|
function memFs(files = {}) {
|
|
return {
|
|
files,
|
|
readFileSync(p) { if (!(p in files)) { const e = new Error('ENOENT'); e.code = 'ENOENT'; throw e; } return files[p]; },
|
|
writeFileSync(p, d) { files[p] = String(d); },
|
|
renameSync(a, b) { files[b] = files[a]; delete files[a]; },
|
|
};
|
|
}
|
|
|
|
describe('mentor-journal-store (F-C2-4 персист цепи + вердикта)', () => {
|
|
it('нет файлов → {entries:[], headSig:null} / null-вердикт', () => {
|
|
const fs = memFs();
|
|
expect(loadMentorJournal({ sessionId: 's1', runtimeDir: '/r', fsImpl: fs })).toEqual({ entries: [], headSig: null });
|
|
expect(loadMentorVerdict({ sessionId: 's1', runtimeDir: '/r', fsImpl: fs })).toBe(null);
|
|
});
|
|
it('persist → load круговая (журнал: jsonl+head; вердикт: json)', () => {
|
|
const fs = memFs();
|
|
const journal = { entries: [{ seq: 1, ts: 5, payload: { task_id: 't', round: 1, side: 'mentor', utterance: 'u', justification: 'j' }, prev_hash: '0'.repeat(64), chain_hash: 'h1' }], headSig: 'SIG' };
|
|
persistMentorJournal({ journal, sessionId: 's1', runtimeDir: '/r', fsImpl: fs });
|
|
expect(loadMentorJournal({ sessionId: 's1', runtimeDir: '/r', fsImpl: fs })).toEqual({ entries: journal.entries, headSig: 'SIG' });
|
|
const v = { ok: true, wired: true, planHash: 'PH', verdict: { recommendation: 'r' } };
|
|
persistMentorVerdict({ record: v, sessionId: 's1', runtimeDir: '/r', fsImpl: fs });
|
|
expect(loadMentorVerdict({ sessionId: 's1', runtimeDir: '/r', fsImpl: fs })).toEqual(v);
|
|
});
|
|
it('atomic: после persist временных файлов не остаётся', () => {
|
|
const fs = memFs();
|
|
persistMentorVerdict({ record: { ok: false }, sessionId: 's1', runtimeDir: '/r', fsImpl: fs });
|
|
persistMentorJournal({ journal: { entries: [], headSig: null }, sessionId: 's1', runtimeDir: '/r', fsImpl: fs });
|
|
expect(Object.keys(fs.files).some((k) => k.includes('.tmp'))).toBe(false);
|
|
});
|
|
it('битый sessionId → throw (path-injection guard N3)', () => {
|
|
const fs = memFs();
|
|
expect(() => loadMentorJournal({ sessionId: '../x', runtimeDir: '/r', fsImpl: fs })).toThrow();
|
|
expect(() => persistMentorVerdict({ record: {}, sessionId: 'a/b', runtimeDir: '/r', fsImpl: fs })).toThrow();
|
|
});
|
|
});
|