import test from 'node:test'; import assert from 'node:assert/strict'; import vm from 'node:vm'; import {readFileSync} from 'node:fs'; // Execute the shipped history review code against a minimal DOM, not a duplicate implementation. const source=readFileSync(new URL('../apps/server/public/admin.js',import.meta.url),'utf8'); const history=source.slice(source.indexOf('let historyNextBefore='),source.indexOf("$('history-load').onclick=")); function fixture(comparison,permissions=['policy.write']){ const fields=new Map(),element=()=>({children:[],hidden:false,checked:false,disabled:false,textContent:'',append(...items){this.children.push(...items);},replaceChildren(...items){this.children=items;},scrollIntoView(){}}),get=id=>{if(!fields.has(id))fields.set(id,element());return fields.get(id);}; const context=vm.createContext({$:get,document:{createElement:element},publishedSnapshot:{version:5},operatorPermissions:new Set(permissions),independentApprovalRequired:false,api:async path=>path.startsWith('/api/admin/policy/diff')?comparison:{currentVersion:5},cell(){},task:fn=>fn}); vm.runInContext(history,context); return {context,get,review:()=>context.reviewHistory(3)}; } const empty=()=>({changes:[],truncated:false,groupDiff:{changes:[],truncated:false},profileDiff:{changes:[],truncated:false}}); test('profile-only restore displays assignments, grants and categories even when baseline/groups unchanged',async()=>{ const comparison=empty();comparison.profileDiff.changes=[{path:'/profiles/0/subjects',kind:'changed',before:['verified-admin'],after:['verified-user']},{path:'/profiles/0/deviceIds',kind:'changed',before:['one'],after:['two']},{path:'/profiles/0/features/devTools',kind:'changed',before:'allow',after:'deny'},{path:'/categories/0/hosts',kind:'changed',before:['*.company.example'],after:['wiki.company.example']}]; const f=fixture(comparison);await f.review();const rows=f.get('history-diff-list').children; assert.equal(rows.length,4);assert.match(rows[0].children[0].textContent,/^\/userProfiles\/profiles\/0\/subjects/);assert.match(rows[0].children[1].textContent,/Selected version:.*verified-admin/);assert.match(rows[2].children[1].textContent,/Selected version: "allow"/);assert.match(f.get('history-review-summary').textContent,/0 baseline paths, 0 group paths and 4 user-profile/);assert.match(f.get('history-review-summary').textContent,/developer-tool grants/);assert.equal(f.get('history-review').hidden,false);assert.equal(f.get('history-approve').disabled,false);assert.equal(f.get('history-restore').disabled,true); }); for(const part of ['baseline','group','profile'])test(`truncated ${part} diff never arms restoration`,async()=>{ const comparison=empty();if(part==='baseline')comparison.truncated=true;else comparison[`${part}Diff`].truncated=true;const f=fixture(comparison);await assert.rejects(f.review(),/supported review size/);assert.equal(f.get('history-review').hidden,true);assert.equal(f.get('history-restore').disabled,true);assert.equal(vm.runInContext('historyReview',f.context),null); }); test('missing user-profile diff fails closed rather than treating it as unchanged',async()=>{const comparison=empty();delete comparison.profileDiff;const f=fixture(comparison);await assert.rejects(f.review(),/Complete baseline, group and user-profile/);assert.equal(f.get('history-review').hidden,true);assert.equal(vm.runInContext('historyReview',f.context),null);}); test('profile values are rendered as literal text and read-only identities cannot approve',async()=>{const comparison=empty();comparison.profileDiff.changes=[{path:'/profiles/0/name',kind:'changed',before:'',after:'Current'}];const f=fixture(comparison,['policy.read']);await f.review();assert.match(f.get('history-diff-list').children[0].children[1].textContent,//);assert.equal(f.get('history-approve').disabled,true);assert.equal(f.get('history-restore').disabled,true);});