Files
brain/tools/escape-grant-floor-escape.test.mjs
T

59 lines
3.3 KiB
JavaScript

// tools/escape-grant-floor-escape.test.mjs
import { describe, it, expect } from 'vitest';
import { join } from 'node:path';
import { loadFloorEscapes } from './escape-grant.mjs';
import { signFloorEscapeRecord } from './askuser-answer-parser.mjs';
function memFs(seed = {}) {
const norm = (p) => String(p).replace(/\\/g, '/');
const s = new Map(Object.entries(seed).map(([k, v]) => [norm(k), v]));
return { s,
existsSync: (p) => s.has(norm(p)),
readFileSync: (p) => { const n = norm(p); if (!s.has(n)) { const e = new Error('ENOENT'); e.code = 'ENOENT'; throw e; } return s.get(n); } };
}
const DIR = '/rt'; const KEY = 'test-receipt-key'; const NOW = 1000;
const file = (recs) => ({ [join(DIR, 'askuser-decisions-s1.jsonl')]: recs.map((r) => JSON.stringify(r)).join('\n') + '\n' });
const signed = (action) => signFloorEscapeRecord({ type: 'floor_escape', action, ts: NOW }, KEY);
const unsigned = (action) => ({ type: 'floor_escape', action, ts: NOW });
describe('loadFloorEscapes — key-gated подпись', () => {
it('ключ есть → подписанный принят, неподписанный/битый отброшены', () => {
const fs = memFs(file([signed('bash:real'), unsigned('bash:forged'), { ...signed('bash:tampered'), action: 'bash:evil' }]));
const g = loadFloorEscapes('s1', NOW, { keyImpl: () => KEY, fsImpl: fs, runtimeDir: DIR });
expect(g.map((x) => x.action)).toEqual(['bash:real']);
});
it('ключ null → все приняты (backward-compat, content-floor backstop)', () => {
const fs = memFs(file([unsigned('bash:a'), signed('bash:b')]));
const g = loadFloorEscapes('s1', NOW, { keyImpl: () => null, fsImpl: fs, runtimeDir: DIR });
expect(g.map((x) => x.action).sort()).toEqual(['bash:a', 'bash:b']);
});
it("ключ '' (falsy) → трактуется как нет ключа → все приняты", () => {
const fs = memFs(file([unsigned('bash:a')]));
const g = loadFloorEscapes('s1', NOW, { keyImpl: () => '', fsImpl: fs, runtimeDir: DIR });
expect(g.map((x) => x.action)).toEqual(['bash:a']);
});
it('окно 5 мин и форма {action,ts} сохранены', () => {
const old = signFloorEscapeRecord({ type: 'floor_escape', action: 'bash:old', ts: NOW - 6 * 60 * 1000 }, KEY);
const fs = memFs(file([signed('bash:fresh'), old]));
const g = loadFloorEscapes('s1', NOW, { keyImpl: () => KEY, fsImpl: fs, runtimeDir: DIR });
expect(g).toEqual([{ action: 'bash:fresh', ts: NOW }]);
});
});
describe('loadFloorEscapes — быстрый путь (keychain только при наличии пропусков)', () => {
it('нет floor_escape-записей (только чужие) → [] без резолва ключа', () => {
let called = 0;
const fs = memFs(file([{ type: 'approve_git_operation', command: 'git push', ts: NOW }]));
const g = loadFloorEscapes('s1', NOW, { keyImpl: () => { called++; return KEY; }, fsImpl: fs, runtimeDir: DIR });
expect(g).toEqual([]);
expect(called).toBe(0);
});
it('файл отсутствует → [] без резолва ключа', () => {
let called = 0;
const fs = memFs({});
const g = loadFloorEscapes('s1', NOW, { keyImpl: () => { called++; return KEY; }, fsImpl: fs, runtimeDir: DIR });
expect(g).toEqual([]);
expect(called).toBe(0);
});
});