import { test, expect } from 'bun:test'; import { Hono } from 'hono'; import { registerP2pAdminRoutes } from './p2p-admin.js'; test('P2P admin actions require auth, log mutations, and control online devices', async () => { const events = [], calls = []; const db = { execute: async query => { events.push(query.args); return { rowsAffected: 1 }; } }; const p2pDb = { listAdminDevices: async ({ limit }) => { expect(limit).toBe(101); return [{ device_id: 'dev_0123456789abcdef', fingerprint: 'fingerprint', profile: 'home', user_agent: 'iPhone', share: 1, banned_at: null, last_seen_at: 42, holdings: 2, holding_bytes: 80, bytes_up: 4, bytes_down: 3 }]; }, setDeviceBanned: async (id, banned) => { calls.push(['ban', id, banned]); return true; }, removeDeviceHolders: async id => { calls.push(['remove', id]); return 2; }, }; const hub = { isOnline: () => true, onlineCount: () => 1, send: (...args) => { calls.push(['send', ...args]); return true; }, disconnect: id => calls.push(['disconnect', id]) }; const app = new Hono(); registerP2pAdminRoutes(app, { db, p2pDb, hub, stats: async () => ({ content: 5 }), adminAuth: async (c, next) => { if (c.req.header('x-admin') !== 'yes') return c.json({ ok:false },401); await next(); } }); expect((await app.request('/api/admin/p2p/devices')).status).toBe(401); const list = await app.request('/api/admin/p2p/devices', { headers: { 'x-admin': 'yes' } }); expect(await list.json()).toMatchObject({ ok: true, network: { online: 1, content: 5 }, devices: [{ id: 'dev_0123456789abcdef', online: true, holdingBytes: 80 }] }); const headers = { 'x-admin': 'yes', 'content-type': 'application/json' }; expect((await app.request('/api/admin/p2p/devices/dev_0123456789abcdef/ban', { method:'POST',headers,body:'{"banned":true}' })).status).toBe(200); expect((await app.request('/api/admin/p2p/devices/dev_0123456789abcdef/reannounce', { method:'POST',headers,body:'{}' })).status).toBe(200); expect((await app.request('/api/admin/p2p/devices/dev_0123456789abcdef/holdings', { method:'DELETE',headers })).status).toBe(200); expect(calls.some(x => x[0] === 'disconnect')).toBe(true); expect(calls.some(x => x[0] === 'send' && x[2].type === 'force-sync')).toBe(true); expect(events).toHaveLength(3); });