'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const net = require('net'); const { createPs5DebugReadonlyClient, bitPairSwap32, firmwareString, COMMANDS, PACKET_MAGIC, CMD_SUCCESS } = require('../../src/services/ps5debug-readonly-client'); test('ps5debug client sends only the five allowlisted read-only commands', async () => { const seen = []; const server = net.createServer(socket => { let pending = Buffer.alloc(0); socket.on('data', chunk => { pending = Buffer.concat([pending, chunk]); while (pending.length >= 12) { const header = pending.subarray(0, 12); pending = pending.subarray(12); assert.equal(header.readUInt32LE(0), PACKET_MAGIC >>> 0); assert.equal(header.readUInt32LE(8), 0); const command = header.readUInt32LE(4); seen.push(command); if ([COMMANDS.VERSION, COMMANDS.BRANDING].includes(command)) { const text = Buffer.from(command === COMMANDS.VERSION ? '1.3' : 'ps5debug-NG test'); const length = Buffer.alloc(4); length.writeUInt32LE(text.length, 0); socket.write(Buffer.concat([length, text])); } else if (command === COMMANDS.FW_VERSION) { const value = Buffer.alloc(2); value.writeUInt16LE(751, 0); socket.write(value); } else if (command === COMMANDS.PLATFORM_ID) { const value = Buffer.alloc(2); value.writeUInt16LE(5, 0); socket.write(value); } else if (command === COMMANDS.FOREGROUND_APP) { const status = Buffer.alloc(4); status.writeUInt32LE(bitPairSwap32(CMD_SUCCESS), 0); socket.write(status); const value = Buffer.alloc(140); value.writeUInt32LE(4242, 0); value.write('PPSA00001', 4); value.write('EP0000-PPSA00001_00-TEST', 20); value.write('eboot.bin', 84); value.write('01.23', 124); socket.write(value); } } }); }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); try { const port = server.address().port; const client = createPs5DebugReadonlyClient({ settings: { get: key => key === 'ftp.host' ? '127.0.0.1' : '' }, port, timeoutMs: 1_000, db: { getGame: id => id === 'PPSA00001' ? { title: 'Test Game', prospero_icon_url: '/icon.webp', version: '1.23' } : null }, }); const packet = await client.query(); assert.equal(packet.online, true); assert.equal(packet.protocol, '1.3'); assert.equal(packet.firmware, '7.51'); assert.equal(packet.platform, 5); assert.equal(packet.foreground.titleId, 'PPSA00001'); assert.equal(packet.foreground.game.title, 'Test Game'); assert.deepEqual(seen, [COMMANDS.VERSION, COMMANDS.BRANDING, COMMANDS.FW_VERSION, COMMANDS.PLATFORM_ID, COMMANDS.FOREGROUND_APP]); assert.equal(packet.policy.memoryRead, false); assert.equal(packet.policy.memoryWrite, false); assert.equal(packet.policy.kernelAccess, false); } finally { await new Promise(resolve => server.close(resolve)); } }); test('firmware and status bit transforms are deterministic', () => { assert.equal(firmwareString(505), '5.05'); assert.equal(firmwareString(960), '9.60'); assert.equal(bitPairSwap32(bitPairSwap32(CMD_SUCCESS)), CMD_SUCCESS); });