// tools/observer-verdicts.test.mjs import { describe, it, expect } from 'vitest'; import { mkdtempSync, writeFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { readVerdictSnapshot, extractFourVerdicts } from './observer-verdicts.mjs'; describe('extractFourVerdicts (D1)', () => { it('снимок с четырьмя звеньями → все четыре с правильным status', () => { const snap = { router: { status: 'recommend', reason: 'r', ts: 1 }, 'mentor:plan': { status: 'GO', reason: 'm', ts: 2 }, 'judge:plan': { status: 'GO', reason: 'j', ts: 3 }, 'judge:gate3': { status: 'NO-GO', reason: 'g', ts: 4 }, }; const v = extractFourVerdicts(snap); expect(v.router.status).toBe('recommend'); expect(v.mentor.status).toBe('GO'); expect(v.judge.status).toBe('GO'); expect(v.gate3.status).toBe('NO-GO'); }); it('несколько записей звена → берётся последняя по ts', () => { const snap = { 'mentor:plan': { status: 'NO-GO', reason: 'old', ts: 1 }, 'mentor:spec': { status: 'GO', reason: 'new', ts: 5 }, }; expect(extractFourVerdicts(snap).mentor).toEqual({ status: 'GO', reason: 'new' }); }); it('отсутствующее звено → null', () => { expect(extractFourVerdicts({ router: { status: 'recommend', ts: 1 } }).mentor).toBeNull(); }); it('пустой снимок → все четыре null', () => { const v = extractFourVerdicts({}); expect(v).toEqual({ router: null, mentor: null, judge: null, gate3: null }); }); }); describe('readVerdictSnapshot (D1)', () => { it('читает файл снимка и агрегирует последнюю запись по стадии', () => { const baseDir = mkdtempSync(join(tmpdir(), 'verdict-snap-')); const obj = { h1: { router: { status: 'recommend', reason: 'r', ts: 1 }, 'mentor:plan': { status: 'NO-GO', reason: 'a', ts: 2 } }, h2: { 'mentor:plan': { status: 'GO', reason: 'b', ts: 9 }, 'judge:plan': { status: 'GO', reason: 'j', ts: 10 } }, }; writeFileSync(join(baseDir, 'verdict-snapshot-sX.json'), JSON.stringify(obj)); const snap = readVerdictSnapshot('sX', baseDir); expect(snap['router'].status).toBe('recommend'); expect(snap['mentor:plan']).toMatchObject({ status: 'GO', ts: 9 }); // последняя по ts expect(snap['judge:plan'].status).toBe('GO'); rmSync(baseDir, { recursive: true, force: true }); }); it('нет файла → {} (fail-safe)', () => { const baseDir = mkdtempSync(join(tmpdir(), 'verdict-snap-')); expect(readVerdictSnapshot('nope', baseDir)).toEqual({}); rmSync(baseDir, { recursive: true, force: true }); }); });