import test from 'node:test'; import assert from 'node:assert/strict'; import { randomUUID } from 'node:crypto'; import { DatabaseSync } from 'node:sqlite'; import { adminStore } from './admin-access-fixture'; import { adminLogin, requireAdmin, AdminError, type AdminRole } from '../db/admin-access'; import { createAdminRoom } from '../db/admin-room-creation'; import { readAdminLobby, configureAdminLobby } from '../db/admin-lobby-configuration'; import { readAdminAudit } from '../db/admin-audit'; import { adminAuditResponse, AUDIT_CATEGORIES } from '../lib/admin-audit'; const now = Date.parse('2026-09-26T12:00:00Z'), room = 'AUDITQAA'; const privateValue = 'PRIVATE_GAME_OR_CREDENTIAL_SENTINEL', reason = 'Operational reason sentinel'; async function fixture(role: AdminRole = 'operator') { const f = adminStore(); assert.ok(f.sqlite instanceof DatabaseSync); const account = f.provision(role, 'QA _operator'), target = f.provision('viewer','Target account'); const login = await adminLogin(f.database,account.key,now); const identity = await requireAdmin(f.database,login.token,undefined,now); f.sqlite.exec('DELETE FROM admin_audit'); const seat = randomUUID(), operation = randomUUID(); function insert(table: string, row: Record) { const keys=Object.keys(row); f.sqlite.prepare(`INSERT INTO ${table}(${keys.join(',')}) VALUES(${keys.map(()=>'?').join(',')})`).run(...Object.values(row)); } const core = { operation_id:operation,actor_admin_id:account.id,room_code:room,reason,created_at:now-1000 }; const snapshot = JSON.stringify({version:7,revision:2,paused:true,joinLocked:false,removed:false,closed:false,archived:false,private:privateValue}); const config = JSON.stringify({host:seat,advanced:false,techTokens:false,strongholdCards:false,players:[{id:seat,name:'Public player',faction:'atreides',bot:null}],private:privateValue}); insert('admin_audit',{id:'a'.repeat(32),actor_admin_id:null,target_admin_id:target.id,action:'provision',created_at:now-1000,detail:JSON.stringify({role:'viewer',sessionId:privateValue,private:privateValue})}); insert('admin_room_audit',{...core,action:'room_control',expected_revision:1,before_paused:0,before_join_locked:0,after_paused:1,after_join_locked:0}); insert('admin_room_creations',{...core,host_id:seat,request_hash:privateValue,session_hash:privateValue,configuration:JSON.stringify({name:'Public player',faction:'atreides',advanced:false,techTokens:false,strongholdCards:false,bots:[],private:privateValue})}); insert('admin_lobby_operations',{...core,request_hash:privateValue,expected_version:6,applied_version:7,action:JSON.stringify({type:'assignHost',target:seat,private:privateValue}),before_configuration:config,after_configuration:config}); for (const [table,flag] of [['admin_room_removals','removed'],['admin_room_closures','closed'],['admin_room_archives','archived']]) insert(table,{...core,request_hash:privateValue,expected_version:6,expected_revision:1,applied_version:7,applied_revision:2,[flag]:0,before_metadata:snapshot,after_metadata:snapshot}); insert('admin_seat_ai_operations',{...core,request_hash:privateValue,expected_version:6,expected_control_revision:1,applied_version:7,target:seat,difficulty:'Hard'}); insert('admin_discussion_operations',{...core,request_hash:privateValue,expected_version:7,expected_revision:1,applied_revision:2,target:seat,muted:1}); const read = (search='') => readAdminAudit(f.database,identity,new URLSearchParams(search),now); return {...f,account,target,identity,seat,operation,insert,core,read}; } void test('unified audit covers all nine sources without reading saved games or exposing private JSON, sessions or hashes',async () => { const f = await fixture(); try { const page = await f.read(); adminAuditResponse(page); assert.equal(page.total,9); assert.equal(page.events.length,9); assert.deepEqual(page.events.map(e=>e.category).sort(),Object.keys(AUDIT_CATEGORIES).sort()); assert.equal(new Set(page.events.map(e=>e.id)).size,9,'same operation UUID in different tables must remain distinct'); assert.doesNotMatch(JSON.stringify(page),new RegExp(privateValue+'|sessionId|request_hash|session_hash|key_hash|before_json|after_json')); assert.equal(page.reasonsVisible,true); assert.equal(page.events.filter(e=>e.reason===reason).length,8); const external=page.events.find(e=>e.category==='account')!; assert.equal(external.actorId,null); assert.equal(external.action,'provision'); assert.equal(external.targetAdminId,f.target.id); assert.deepEqual(external.changes,[{field:'Role',before:null,after:'viewer'}]); const lobby=page.events.find(e=>e.category==='lobby')!; assert.equal(lobby.action,'assignHost'); assert.equal(lobby.targetSeatId,f.seat); assert.ok(lobby.changes.some(c=>c.field==='Host seat' && c.after===f.seat)); assert.equal(f.sqlite.prepare('SELECT COUNT(*) n FROM rooms').get()!.n,0,'historical events exist independently of rooms'); } finally {f.sqlite.close();} }); void test('nine-source audit executes within workerd D1 compound-query limits',async () => { const { Miniflare } = await import('miniflare'); const f = await fixture(); const worker = new Miniflare({ modules:true,script:'export default {fetch(){return new Response("ok")}}',compatibilityDate:'2026-05-15',d1Databases:['DB'] }); try { const db = await worker.getD1Database('DB'); // Copy the isolated fixture, never the running server's database. Install // its triggers after copying rows so provisioning does not add new receipts. const schema = f.sqlite.prepare("SELECT type,name,sql FROM sqlite_master WHERE sql IS NOT NULL AND name NOT LIKE 'sqlite_%' ORDER BY CASE type WHEN 'table' THEN 0 WHEN 'index' THEN 1 ELSE 2 END").all() as {type:string;name:string;sql:string}[]; for (const entry of schema.filter(e=>e.type!=='trigger')) await db.prepare(entry.sql).run(); for (const entry of schema.filter(e=>e.type==='table')) { for (const row of f.sqlite.prepare(`SELECT * FROM "${entry.name}"`).all()) { const columns=Object.keys(row); await db.prepare(`INSERT INTO "${entry.name}" (${columns.map(c=>'"'+c+'"').join(',')}) VALUES (${columns.map(()=>'?').join(',')})`).bind(...Object.values(row)).run(); } } for (const entry of schema.filter(e=>e.type==='trigger')) await db.prepare(entry.sql).run(); const read = (query='') => readAdminAudit(db as unknown as D1Database,f.identity,new URLSearchParams(query),now); const result=await read(); adminAuditResponse(result); assert.equal(result.total,9); assert.deepEqual(result,await f.read()); assert.equal((await read('q='+room+'&category=discussion')).total,1); await db.prepare("UPDATE admin_accounts SET role='viewer' WHERE id=?").bind(f.account.id).run(); const viewer=await read(); assert.equal(viewer.reasonsVisible,false); assert.ok(viewer.events.every(e=>e.reason===null)); } finally { await worker.dispose(); f.sqlite.close(); } }); void test('viewer hides reasons and reasons never influence searches or counts for any role',async () => { for (const role of ['viewer','operator'] as const) { const f = await fixture(role); try { const result=await f.read();adminAuditResponse(result); assert.equal(result.reasonsVisible,role==='operator'); if(role==='viewer')assert.ok(result.events.every(e=>e.reason===null)); for (const q of [reason,privateValue,'%','\\']) assert.equal((await f.read('q='+encodeURIComponent(q))).total,0); assert.equal((await f.read('q='+encodeURIComponent('QA _operator'))).total,8); assert.equal((await f.read('q='+f.seat)).total,4); // creation, host assignment, AI and discussion } finally {f.sqlite.close();} } }); void test('live demotion hides reasons even when the prior identity claims operator; revocation/expiry fails closed',async () => { const f=await fixture(); try { f.hooks.beforeBatch=async()=>{delete f.hooks.beforeBatch;f.sqlite.prepare("UPDATE admin_accounts SET role='viewer' WHERE id=?").run(f.account.id);}; const result=await f.read();assert.equal(result.reasonsVisible,false);assert.ok(result.events.every(e=>e.reason===null)); for(const sql of ['UPDATE admin_sessions SET revoked_at=1','UPDATE admin_sessions SET revoked_at=NULL,expires_at=1','UPDATE admin_sessions SET expires_at=9999999999999,generation=generation+1']) { f.sqlite.exec(sql);await assert.rejects(f.read,(error:unknown)=>error instanceof AdminError && error.status===401); } } finally {f.sqlite.close();} }); void test('history survives account deletion and malformed metadata without revealing raw contents',async () => { const f=await fixture(); try { f.sqlite.prepare('DELETE FROM admin_accounts WHERE id=?').run(f.target.id); f.sqlite.prepare('UPDATE admin_lobby_operations SET action=?,before_configuration=?,after_configuration=?').run('{broken',JSON.stringify({private:privateValue}),JSON.stringify([privateValue])); f.sqlite.prepare('UPDATE admin_room_creations SET configuration=?').run('"'+privateValue+'"'); f.sqlite.prepare('UPDATE admin_audit SET action=?,detail=?').run(privateValue,JSON.stringify({role:privateValue,sessionId:privateValue})); const page=await f.read();adminAuditResponse(page);assert.equal(page.total,9); assert.doesNotMatch(JSON.stringify(page),new RegExp(privateValue)); assert.equal(page.events.find(e=>e.category==='account')!.action,'unknown'); assert.equal(page.events.find(e=>e.category==='lobby')!.action,'unknown'); } finally {f.sqlite.close();} }); void test('equal-time events page deterministically and filters apply before pagination',async () => { const f=await fixture(); try { for(let i=0;i<58;i++)f.insert('admin_discussion_operations',{...f.core,operation_id:randomUUID(),request_hash:privateValue,expected_version:7,expected_revision:2,applied_revision:3,target:f.seat,muted:0}); const ids=[]; for(let page=1;page<=3;page++) { const result=await f.read('category=discussion&page='+page);assert.equal(result.total,59); ids.push(...result.events.map(e=>e.id)); assert.deepEqual(await f.read('category=discussion&page='+page),result); } assert.equal(ids.length,59);assert.equal(new Set(ids).size,59); assert.equal((await f.read('category=discussion&from=2026-09-26&to=2026-09-26')).total,59); assert.equal((await f.read('from=2026-09-27')).total,0); assert.equal((await f.read('to=2026-09-25')).total,0); for(const bad of ['q=x&q=y','category=private','page=0','page=1e3','page=9999999','from=2026-02-30','from=2026-09-27&to=2026-09-26','secret=x']) await assert.rejects(()=>f.read(bad),(error:unknown)=>error instanceof AdminError && error.status===400); } finally {f.sqlite.close();} }); void test('client rejects private or malformed audit fields rather than rendering unknown server data',async () => { const f=await fixture(); try { const page=await f.read(); for(const bad of [{...page,state:{}},{...page,reasonsVisible:false},{...page,events:[{...page.events[0],sessionHash:privateValue}]}, {...page,events:[{...page.events[0],changes:[{field:'Private hand',before:null,after:privateValue}]}]}, {...page,events:[{...page.events[0],createdAt:Infinity}]}])assert.throws(()=>adminAuditResponse(bad)); } finally {f.sqlite.close();} }); void test('malformed roles and rejected target metadata cannot crash history or become a viewer search oracle',async () => { const f=await fixture('viewer'); try { for(const invalid of [{toString:null},['owner'],{nested:privateValue}]) { f.sqlite.prepare('UPDATE admin_audit SET detail=?').run(JSON.stringify({role:invalid,previousRole:invalid})); const page=await f.read();adminAuditResponse(page);assert.deepEqual(page.events.find(e=>e.category==='account')!.changes,[]); } for(const target of [{private:privateValue},[privateValue],privateValue,7]) { f.sqlite.prepare('UPDATE admin_lobby_operations SET action=?').run(JSON.stringify({type:'configureBot',target})); const page=await f.read('category=lobby');assert.equal(page.events[0].targetSeatId,null); assert.equal((await f.read('q='+encodeURIComponent(privateValue))).total,0); } f.sqlite.prepare('UPDATE admin_room_audit SET room_code=?,actor_admin_id=?').run(privateValue,privateValue); assert.equal((await f.read('q='+encodeURIComponent(privateValue))).total,0); f.sqlite.prepare('UPDATE admin_room_audit SET room_code=?,actor_admin_id=?').run('SECRETAA\0hidden',f.account.id+'\0hidden'); assert.equal((await f.read('q=SECRETAA')).total,0); assert.equal((await f.read('category=control&q='+f.account.id)).total,0); f.sqlite.prepare('UPDATE admin_accounts SET name=? WHERE id=?').run(privateValue+'\n',f.account.id); assert.equal((await f.read('q='+encodeURIComponent(privateValue))).total,0); } finally {f.sqlite.close();} }); void test('external account operations match affected account names and position-only AI changes show the actual circles',async () => { const f=await fixture(); try { const found=await f.read('q='+encodeURIComponent('Target account')); assert.equal(found.total,1);assert.equal(found.events[0].actorId,null);assert.equal(found.events[0].targetAdminName,'Target account'); const config=(position:number)=>JSON.stringify({advanced:false,techTokens:false,strongholdCards:false,host:f.seat,players:[{id:f.seat,faction:'atreides',bot:'Hard',position}]}); f.sqlite.prepare('UPDATE admin_lobby_operations SET action=?,before_configuration=?,after_configuration=?').run(JSON.stringify({type:'configureBot',target:f.seat}),config(1),config(6)); const page=await f.read('category=lobby');adminAuditResponse(page); assert.deepEqual(page.events[0].changes.find(c=>c.field==='Player circle'),{field:'Player circle',before:'1',after:'6'}); } finally {f.sqlite.close();} }); void test('real room creation and position-only configuration receipts display the authoritative player circles',async () => { const f=await fixture(); try { const created=await createAdminRoom(f.database,f.identity,{operationId:randomUUID(),sessionToken:'b'.repeat(64),name:'Audit host',faction:'atreides',advanced:false,techTokens:false,strongholdCards:false,bots:[{faction:'emperor',difficulty:'Hard'}],reason:'Audit genuine operation'},now); const lobby=await readAdminLobby(f.database,f.identity,created.code,now), bot=lobby.players.find(p=>p.bot)!; assert.equal(bot.position,2); await configureAdminLobby(f.database,f.identity,created.code,{operationId:randomUUID(),expectedVersion:lobby.version,action:{type:'configureBot',target:bot.id,faction:'emperor',difficulty:'Hard',position:6},reason:'Move the player circle'},now); const page=await f.read('category=lobby&q='+created.code);adminAuditResponse(page); assert.equal(page.events.length,1); assert.deepEqual(page.events[0].changes.find(c=>c.field==='Player circle'),{field:'Player circle',before:'2',after:'6'}); } finally {f.sqlite.close();} });