a8489a22c7
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
67 lines
3.0 KiB
JavaScript
67 lines
3.0 KiB
JavaScript
import { describe, it, expect } from 'vitest';
|
|
import { mkdtempSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { pushVerdict, drainVerdicts, markSurfaced, readPendingAck, clearPendingAck } from './verdict-surface-store.mjs';
|
|
|
|
const dir = () => mkdtempSync(join(tmpdir(), 'vsurf-'));
|
|
|
|
describe('verdict-surface-store', () => {
|
|
it('push → drain one-shot (drain чистит)', () => {
|
|
const d = dir();
|
|
pushVerdict('s1', { outcome: 'NO-GO', gate: 'judge' }, d);
|
|
expect(drainVerdicts('s1', d)).toHaveLength(1);
|
|
expect(drainVerdicts('s1', d)).toHaveLength(0);
|
|
});
|
|
it('пер-сессия: чужие не видны', () => {
|
|
const d = dir();
|
|
pushVerdict('s1', { outcome: 'GO' }, d);
|
|
expect(drainVerdicts('s2', d)).toHaveLength(0);
|
|
});
|
|
it('pending-ack: mark → read → clear', () => {
|
|
const d = dir();
|
|
markSurfaced('s1', ['NO-GO'], d);
|
|
expect(readPendingAck('s1', d)).toEqual(['NO-GO']);
|
|
clearPendingAck('s1', d);
|
|
expect(readPendingAck('s1', d)).toBeNull();
|
|
});
|
|
it('fail-quiet: битый baseDir не кидает', () => {
|
|
expect(() => drainVerdicts('s1', '\0bad')).not.toThrow();
|
|
expect(drainVerdicts('s1', '\0bad')).toEqual([]);
|
|
});
|
|
});
|
|
|
|
import { writeStage, readSnapshot } from './verdict-surface-store.mjs';
|
|
|
|
describe('verdict-surface-store снимок стадий (видимость «всё в лоб»)', () => {
|
|
it('writeStage pending → читается через readSnapshot по hash', () => {
|
|
const d = dir();
|
|
writeStage('s1', { stage: 'mentor:plan', hash: 'h1', status: 'pending', ts: 1 }, d);
|
|
expect(readSnapshot('s1', 'h1', d)['mentor:plan'].status).toBe('pending');
|
|
});
|
|
it('завершение перезаписывает pending на исход (та же стадия+hash)', () => {
|
|
const d = dir();
|
|
writeStage('s1', { stage: 'mentor:plan', hash: 'h1', status: 'pending', ts: 1 }, d);
|
|
writeStage('s1', { stage: 'mentor:plan', hash: 'h1', status: 'NO-GO', reason: 'переделай', ts: 2 }, d);
|
|
const snap = readSnapshot('s1', 'h1', d);
|
|
expect(snap['mentor:plan'].status).toBe('NO-GO');
|
|
expect(snap['mentor:plan'].reason).toBe('переделай');
|
|
});
|
|
it('разные стадии одного hash сосуществуют', () => {
|
|
const d = dir();
|
|
writeStage('s1', { stage: 'router', hash: 'h1', status: 'recommend', ts: 1 }, d);
|
|
writeStage('s1', { stage: 'judge:plan', hash: 'h1', status: 'GO', ts: 2 }, d);
|
|
const snap = readSnapshot('s1', 'h1', d);
|
|
expect(snap.router.status).toBe('recommend');
|
|
expect(snap['judge:plan'].status).toBe('GO');
|
|
});
|
|
it('неизвестный hash → пустой объект', () => {
|
|
const d = dir();
|
|
expect(readSnapshot('s1', 'nope', d)).toEqual({});
|
|
});
|
|
it('fail-quiet: битый baseDir не кидает (снимок)', () => {
|
|
expect(() => writeStage('s1', { stage: 'router', hash: 'h', status: 'GO' }, '\0bad')).not.toThrow();
|
|
expect(readSnapshot('s1', 'h', '\0bad')).toEqual({});
|
|
});
|
|
});
|