'use strict'; const assert = require('node:assert/strict'); const http = require('node:http'); const test = require('node:test'); const { createPs5PayloadManagerClient, validPort, normalizeAutoloadStatus, normalizeProcesses, normalizeLogs, } = require('../../src/services/ps5-payload-manager-client'); test('PS5 Payload Manager client probes version and autoload state', async t => { const server = http.createServer((req, res) => { res.setHeader('Content-Type', 'application/json'); if (req.url === '/version') return res.end(JSON.stringify('0.3.1')); if (req.url === '/autoload_status') return res.end(JSON.stringify({ total: 2, done: 1, remaining: 0, current: 'websrv_v0.33.elf', list: 'elfldr_v0.23.elf,websrv_v0.33.elf', SCAN_USB_PAYLOADS: true })); if (req.url === '/processes_list') return res.end(JSON.stringify({ processes: [ { pid: 42, name: 'websrv.elf', memory: 12.34, is_daemon: true }, { pid: 43, name: 'SceShellCore', memory: 99.9, is_daemon: false }, ] })); if (req.url === '/log') return res.end(JSON.stringify({ logs: ['manager ready', 'payload started'] })); if (req.url === '/list_payloads') return res.end(JSON.stringify({ payloads: ['/data/pldmgr/payloads/elfldr/elfldr_v0.23.elf'], meta: { 'elfldr_v0.23.elf': { display_name: 'ELF Loader', version: 'v0.23', checksum: 'a'.repeat(64) } }, })); if (req.url === '/sources_list') return res.end(JSON.stringify({ sources: [{ id: 'default', name: 'Official Repository', url: 'https://example.test/payloads.json', removable: false }] })); if (req.url === '/repository_payloads') return res.end(JSON.stringify({ sources: [{ id: 'default', payloads: [{ filename: 'elfldr_v0.23.elf' }, { filename: 'websrv_v0.33.elf' }] }] })); res.statusCode = 404; res.end(JSON.stringify({ error: 'not found' })); }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); t.after(() => new Promise(resolve => server.close(resolve))); const port = server.address().port; const values = new Map([['ftp.host', '127.0.0.1'], ['payloadManager.port', port]]); const client = createPs5PayloadManagerClient({ settings: { get: (key, fallback) => values.has(key) ? values.get(key) : fallback } }); const result = await client.probe(); assert.equal(result.ok, true); assert.equal(result.version, '0.3.1'); assert.equal(result.autoload.state, 'running'); assert.deepEqual(result.autoload.list, ['elfldr_v0.23.elf', 'websrv_v0.33.elf']); assert.equal(result.inventory.total, 1); assert.equal(result.inventory.payloads[0].version, 'v0.23'); assert.equal(result.inventory.payloads[0].checksumVerified, true); assert.equal(result.sources.total, 1); assert.equal(result.repository.total, 2); assert.equal(result.url, `http://127.0.0.1:${port}`); const live = await client.observability(); assert.equal(live.autoload.current, 'websrv_v0.33.elf'); assert.equal(live.autoload.scanUsbPayloads, true); assert.equal(live.processes.total, 2); assert.equal(live.processes.daemonCount, 1); assert.equal(live.processes.items[0].memoryMiB, 12.3); assert.deepEqual(live.logs.lines, ['manager ready', 'payload started']); const preflight = await client.launchPreflight('/data/pldmgr/payloads/websrv_v0.33.elf'); assert.equal(preflight.conflict, true); assert.equal(preflight.active[0].pid, 42); }); test('PS5 Payload Manager probe fails when its liveness endpoint is unhealthy', async t => { const server = http.createServer((_req, res) => { res.statusCode = 503; res.end('offline'); }); await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)); t.after(() => new Promise(resolve => server.close(resolve))); const client = createPs5PayloadManagerClient({ settings: { get: (key, fallback) => key === 'ftp.host' ? '127.0.0.1' : key === 'payloadManager.port' ? server.address().port : fallback }, }); await assert.rejects(() => client.probe(), /HTTP 503/); }); test('PS5 Payload Manager client validates ports and routes', () => { assert.equal(validPort(8084), 8084); assert.throws(() => validPort(70000), /Ongeldige/); const client = createPs5PayloadManagerClient({ settings: { get: key => key === 'ftp.host' ? '192.168.1.5' : 8084 } }); assert.throws(() => client.request('http://example.test'), /route/); }); test('Payload Manager observability normalizers bound and sanitize untrusted console data', () => { const processes = normalizeProcesses({ processes: [ { pid: 7, name: 'daemon\0.elf\nspoof', memory: 4.26, is_daemon: true }, { pid: -1, name: 'invalid.elf', memory: 1, is_daemon: true }, ] }); assert.equal(processes.total, 1); assert.equal(processes.items[0].name, 'daemon.elf spoof'); assert.equal(processes.items[0].memoryMiB, 4.3); const logs = normalizeLogs({ logs: [...Array.from({ length: 260 }, (_, index) => `line ${index}`), 'bad\0\nline'] }); assert.equal(logs.total, 250); assert.equal(logs.truncated, true); assert.equal(logs.lines.at(-1), 'bad line'); const autoload = normalizeAutoloadStatus({ remaining: 5, total: 1, done: 0, current: 'demo.elf', list: 'demo.elf' }); assert.equal(autoload.state, 'countdown'); assert.equal(autoload.remainingMs, 5_000); });