import assert from 'node:assert/strict'; import { randomUUID } from 'node:crypto'; import test from 'node:test'; import { adminLogin, requireAdmin, AdminError } from '../db/admin-access'; import { readAdminOperations } from '../db/admin-operations'; import { adminStore } from './admin-access-fixture'; const now = 10_000; void test('owner operations sample counts saved states and backup provenance without leaking contents or changing rooms', async () => { const store = adminStore(); try { const owner = store.provision('owner'); const login = await adminLogin(store.database, owner.key, now); const identity = await requireAdmin(store.database, login.token, ['owner'], now); store.sqlite.prepare('INSERT INTO rooms(code,state,version,updated_at) VALUES(?,?,?,?)').run('OPERAABC', '{"private":"sealed card"}', 12, 9000); store.sqlite.prepare('INSERT INTO rooms(code,state,version,updated_at) VALUES(?,?,?,?)').run('OPERAABD', '{broken private state', 3, 9200); store.sqlite.prepare('INSERT INTO seats(token_hash,room_code,player_id,revoked) VALUES(?,?,?,0)').run('private-session', 'OPERAABC', 'one'); store.sqlite.prepare('INSERT INTO seats(token_hash,room_code,player_id,revoked) VALUES(?,?,?,1)').run('revoked-session', 'OPERAABC', 'two'); store.sqlite.prepare('INSERT INTO room_removals(room_code,removed,revision,removed_at,updated_at) VALUES(?,1,1,?,?)').run('OPERAABD', now, now); store.sqlite.prepare('INSERT INTO room_archives(room_code,archived,revision,archived_at,updated_at) VALUES(?,1,1,?,?)').run('OPERAABD', now, now); store.sqlite.prepare('INSERT INTO room_controls(room_code,paused,join_locked,revision,updated_at) VALUES(?,1,0,1,?)').run('OPERAABC', now); store.sqlite.prepare('INSERT INTO room_closures(room_code,closed,revision,closed_at,updated_at) VALUES(?,1,1,?,?)').run('OPERAABD', now, now); const backupId = randomUUID(); store.sqlite.prepare(`INSERT INTO admin_room_backups (operation_id,actor_admin_id,room_code,request_hash,room_version,created_at,size_bytes,digest,payload,reason) VALUES(?,?,?,'request',12,?,14,'digest','secret payload','private reason')`).run(backupId, owner.id, 'OPERAABC', now); store.sqlite.prepare('INSERT INTO admin_room_backup_downloads(download_id,operation_id,actor_admin_id,room_code,created_at) VALUES(?,?,?,?,?)').run(randomUUID(), backupId, owner.id, 'OPERAABC', now); const before = store.sqlite.prepare('SELECT code,state,version FROM rooms ORDER BY code').all(); const result = await readAdminOperations(store.database, identity, now); assert.deepEqual(result, { observedAt: now, rooms: 2, removedRooms: 1, archivedRooms: 1, pausedRooms: 1, closedRooms: 1, activeSeats: 1, unreadableRooms: 1, lastRoomChange: 9200, backupSnapshots: 1, backupBytes: 14, backupDownloads: 1, }); assert.equal(JSON.stringify(result).includes('secret'), false); assert.deepEqual(store.sqlite.prepare('SELECT code,state,version FROM rooms ORDER BY code').all(), before); } finally { store.sqlite.close(); } }); void test('stale owner identity, demotion and revoked session cannot sample operational counts', async () => { const store = adminStore(); try { const owner = store.provision('owner'); store.provision('owner', 'Other owner'); const login = await adminLogin(store.database, owner.key, now); const identity = await requireAdmin(store.database, login.token, ['owner'], now); const denied = (error: unknown) => error instanceof AdminError && error.status === 403; store.sqlite.prepare("UPDATE admin_accounts SET role='operator' WHERE id=?").run(owner.id); await assert.rejects(readAdminOperations(store.database, identity, now), denied); store.sqlite.prepare("UPDATE admin_accounts SET role='owner' WHERE id=?").run(owner.id); store.sqlite.prepare('UPDATE admin_sessions SET revoked_at=? WHERE token_hash=?').run(now, identity.sessionHash); await assert.rejects(readAdminOperations(store.database, identity, now), denied); } finally { store.sqlite.close(); } });