// tools/enforce-askuser-answer-parser-floor-escape.test.mjs import { describe, it, expect } from 'vitest'; import { join } from 'node:path'; import { processEvent } from './enforce-askuser-answer-parser.mjs'; import { verifyFloorEscapeRecord } from './askuser-answer-parser.mjs'; function memFs() { const s = new Map(); const norm = (p) => String(p).replace(/\\/g, '/'); return { s, appendFileSync: (p, d) => { const n = norm(p); s.set(n, (s.get(n) || '') + d); }, mkdirSync: () => {} }; } const DIR = '/rt'; const KEY = 'test-receipt-key'; const ev = (action) => ({ session_id: 's1', tool_input: { questions: [{ question: 'Q?' }] }, tool_response: { answers: { 'Q?': `да, разрешаю. FLOOR-ESCAPE: ${action}` } }, }); function readLines(fs) { const raw = fs.s.get(join(DIR, 'askuser-decisions-s1.jsonl').replace(/\\/g, '/')) || ''; return raw.trim().split('\n').filter(Boolean).map((l) => JSON.parse(l)); } describe('processEvent — key-gated подпись floor_escape', () => { it('ключ есть → floor_escape несёт валидную подпись', () => { const fs = memFs(); processEvent(ev('bash:git push --force'), { runtimeDir: DIR, nowMs: 5, keyImpl: () => KEY, fsImpl: fs }); const esc = readLines(fs).find((r) => r.type === 'floor_escape'); expect(esc).toBeTruthy(); expect(verifyFloorEscapeRecord(esc, KEY)).toBe(true); }); it('ключ null → floor_escape без подписи (как сегодня)', () => { const fs = memFs(); processEvent(ev('bash:git push --force'), { runtimeDir: DIR, nowMs: 5, keyImpl: () => null, fsImpl: fs }); const esc = readLines(fs).find((r) => r.type === 'floor_escape'); expect(esc.sig).toBeUndefined(); }); });