import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { sourceFingerprint } from './guard-evidence.mjs'; import { reviewActions } from './guard-requirements.mjs'; function fixture(source, fn) { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'hm-action-review-')); try { fs.writeFileSync(path.join(dir, 'App.tsx'), source); const guard = { status: 'NOT VERIFIED', scope: 'FULL_SCREEN', target: dir, contractSource: dir, fingerprintFiles: ['App.tsx'], contractFiles: ['App.tsx'], checkedFiles: ['App.tsx'], issues: [{ code: 'RENDER-002' }] }; guard.sourceFingerprint = guard.contractFingerprint = sourceFingerprint(dir, ['App.tsx']); fn(guard, { schemaVersion: 1, actionPolicy: { allowedLabels: ['조회', '초기화'] } }); } finally { fs.rmSync(dir, { recursive: true, force: true }); } } test('explicit authorized actions pass without changing raw Guard', () => fixture('import {Button as Action} from "@hm-design/ui"; const App=()=> <>조회내보내기;', (g, r) => { const result = reviewActions(g, r); assert.equal(result.status, 'FAIL'); assert.equal(result.findings.filter(f => f.code === 'REQ-003').length, 2); })); test('dynamic action names and spread props remain unverified', () => fixture('const App=()=> <>;', (g, r) => assert.equal(reviewActions(g, r).status, 'NOT VERIFIED'))); test('stale source, absent policy and partial coverage cannot pass', () => fixture('const App=()=> ;', (g, r) => { assert.equal(reviewActions(g, {}).status, 'NOT VERIFIED'); assert.equal(reviewActions({ ...g, scope: 'PARTIAL' }, r).status, 'NOT VERIFIED'); fs.appendFileSync(path.join(g.target, 'App.tsx'), '\n// changed'); assert.equal(reviewActions(g, r).status, 'NOT VERIFIED'); })); test('layout HTML is permitted; no observed actions is not proof of compliance', () => fixture('const App=()=>

CSV guide

;', (g, r) => assert.equal(reviewActions(g, r).status, 'NOT VERIFIED')));