Files
brain/tools/enforce-askuser-answer-parser.test.mjs
T

110 lines
4.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect } from 'vitest';
import { mkdtempSync, writeFileSync, readFileSync, existsSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { processEvent } from './enforce-askuser-answer-parser.mjs';
function tmpRuntimeDir() {
return mkdtempSync(join(tmpdir(), 'askuser-decisions-test-'));
}
describe('enforce-askuser-answer-parser wrapper (Stream H Task 6)', () => {
it('appends approve_git_operation record for git-pattern answer', () => {
const dir = tmpRuntimeDir();
const event = {
session_id: 'sess-abc',
tool_input: { questions: [{ question: 'разрешить?' }] },
tool_response: { answers: { 'разрешить?': 'подтверди git push origin main' } },
};
processEvent(event, { runtimeDir: dir, nowMs: 1700000000000 });
const path = join(dir, 'askuser-decisions-sess-abc.jsonl');
expect(existsSync(path)).toBe(true);
const lines = readFileSync(path, 'utf-8').split(/\r?\n/).filter(Boolean);
expect(lines.length).toBe(1);
const rec = JSON.parse(lines[0]);
expect(rec).toMatchObject({ type: 'approve_git_operation', command: 'git push origin main', ts: 1700000000000 });
rmSync(dir, { recursive: true, force: true });
});
it('appends nothing for non-git answer', () => {
const dir = tmpRuntimeDir();
const event = {
session_id: 'sess-def',
tool_input: { questions: [{ question: 'continue?' }] },
tool_response: { answers: { 'continue?': 'yes' } },
};
processEvent(event, { runtimeDir: dir });
const path = join(dir, 'askuser-decisions-sess-def.jsonl');
expect(existsSync(path)).toBe(false);
rmSync(dir, { recursive: true, force: true });
});
it('appends multiple records across multiple answers', () => {
const dir = tmpRuntimeDir();
const event = {
session_id: 'sess-multi',
tool_input: { questions: [{ question: 'A?' }, { question: 'B?' }] },
tool_response: { answers: { 'A?': 'git push origin main', 'B?': 'git add tools/x.mjs' } },
};
processEvent(event, { runtimeDir: dir, nowMs: 1700000000000 });
const path = join(dir, 'askuser-decisions-sess-multi.jsonl');
const lines = readFileSync(path, 'utf-8').split(/\r?\n/).filter(Boolean);
expect(lines.length).toBe(2);
rmSync(dir, { recursive: true, force: true });
});
it('fail-open: missing tool_response does not throw', () => {
const dir = tmpRuntimeDir();
expect(() => processEvent({ session_id: 's' }, { runtimeDir: dir })).not.toThrow();
rmSync(dir, { recursive: true, force: true });
});
it('fail-open: missing answer key does not throw', () => {
const dir = tmpRuntimeDir();
expect(() => processEvent({
session_id: 's',
tool_input: { questions: [{ question: 'X?' }] },
tool_response: { answers: {} },
}, { runtimeDir: dir })).not.toThrow();
rmSync(dir, { recursive: true, force: true });
});
it('fail-open: missing session_id does not throw and does not write', () => {
const dir = tmpRuntimeDir();
expect(() => processEvent({
tool_input: { questions: [{ question: 'X?' }] },
tool_response: { answers: { 'X?': 'git push origin main' } },
}, { runtimeDir: dir })).not.toThrow();
rmSync(dir, { recursive: true, force: true });
});
});
describe('enforce-askuser-answer-parser floor_escape (M6)', () => {
it('ответ с FLOOR-ESCAPE пишет floor_escape-запись', () => {
const dir = tmpRuntimeDir();
const event = {
session_id: 's1',
tool_input: { questions: [{ question: 'q' }] },
tool_response: { answers: { q: 'да FLOOR-ESCAPE: bash:reset --hard' } },
};
processEvent(event, { runtimeDir: dir, nowMs: 7 });
const content = readFileSync(join(dir, 'askuser-decisions-s1.jsonl'), 'utf-8');
expect(content).toContain('"type":"floor_escape"');
expect(content).toContain('"action":"bash:reset --hard"');
rmSync(dir, { recursive: true, force: true });
});
it('git-ответ по-прежнему пишет approve_git_operation (без регрессии)', () => {
const dir = tmpRuntimeDir();
const event = {
session_id: 's2',
tool_input: { questions: [{ question: 'q' }] },
tool_response: { answers: { q: 'подтверди git push origin main' } },
};
processEvent(event, { runtimeDir: dir, nowMs: 9 });
const content = readFileSync(join(dir, 'askuser-decisions-s2.jsonl'), 'utf-8');
expect(content).toContain('"type":"approve_git_operation"');
expect(content).not.toContain('floor_escape');
rmSync(dir, { recursive: true, force: true });
});
});