'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const { createPayloadRuntimeService, normalizeInstalled, normalizeRepository, payloadIdentity } = require('../../src/services/payload-runtime-service'); function fixture() { const calls = []; const responses = { '/version': '0.3.1', '/list_payloads': { payloads: ['/data/pldmgr/payloads/websrv/websrv_v0.32.elf', '/data/pldmgr/payloads/etaHEN/etaHEN-2.6B.bin'], meta: { 'websrv_v0.32.elf': { display_name: 'websrv', version: 'v0.32', checksum: 'a'.repeat(64) }, 'etaHEN-2.6B.bin': { display_name: 'etaHEN-2.6B.bin' }, }, }, '/repository_payloads': { sources: [{ id: 'default', name: 'Official', payloads: [ { name: 'websrv', filename: 'websrv_v0.33.elf', version: 'v0.33', source_id: 'default', url: 'https://example.test/websrv.elf' }, { name: 'etaHEN', filename: 'etaHEN_2.5B.bin', version: '2.5B', source_id: 'default', url: 'https://example.test/etahen.bin' }, { name: 'klogsrv', filename: 'klogsrv_v0.11.elf', version: 'v0.11', source_id: 'default', url: 'https://example.test/klogsrv.elf' }, ] }] }, '/autoload_status': { list: 'websrv_v0.32.elf' }, '/sources_list': { sources: [{ id: 'default', name: 'Official', url: 'https://example.test/payloads.json' }] }, }; const payloadManager = { request: async route => { calls.push(route); if (route === '/repository_refresh') return { success: true }; if (route.startsWith('/repository_install?')) return { success: true }; if (!(route in responses)) throw new Error(`Unexpected ${route}`); return responses[route]; } }; const settings = { get: (key, fallback) => key === 'ftp.host' ? '127.0.0.1' : fallback }; return { service: createPayloadRuntimeService({ settings, payloadManager, timeoutMs: 10 }), calls }; } test('normalizers preserve trusted manager metadata and stable identities', () => { const installed = normalizeInstalled({ payloads: ['/data/x/websrv_v0.32.elf'], meta: { 'websrv_v0.32.elf': { display_name: 'websrv', version: 'v0.32', checksum: 'b'.repeat(64) } } }); assert.equal(installed[0].identity, 'websrv'); assert.equal(installed[0].checksumVerified, true); assert.equal(payloadIdentity('etaHEN-2.6B.bin'), 'etahen'); const repository = normalizeRepository({ sources: [{ id: 'official', payloads: [{ name: 'websrv', filename: 'websrv_v0.33.elf', url: 'http://unsafe.test/file' }] }] }); assert.equal(repository[0].url, ''); }); test('runtime update plan allows upgrades and blocks downgrades', async () => { const f = fixture(); const plan = await f.service.updatePlan(); assert.equal(plan.stats.updates, 1); assert.equal(plan.candidates.find(item => item.installed.identity === 'websrv').status, 'update-available'); assert.equal(plan.candidates.find(item => item.installed.identity === 'etahen').status, 'installed-newer'); assert.equal(plan.recommendations.find(item => item.identity === 'klogsrv').automaticInstall, false); assert.equal(plan.safety.automaticInstall, false); }); test('manager reads coalesce briefly and repository refresh forces a fresh inventory', async () => { const f = fixture(); await Promise.all([f.service.managerState(), f.service.managerState(), f.service.updatePlan()]); assert.equal(f.calls.filter(route => route === '/version').length, 1); await f.service.refreshRepositories(); assert.equal(f.calls.filter(route => route === '/repository_refresh').length, 1); assert.equal(f.calls.filter(route => route === '/version').length, 2); assert.ok(!f.calls.some(route => route.startsWith('/repository_install?'))); }); test('installation revalidates the plan, requires exact filename confirmation and never launches', async () => { const f = fixture(); await assert.rejects(() => f.service.installUpdate({ filename: 'websrv_v0.33.elf', sourceId: 'default', confirmation: 'yes' }), /Typ UPDATE/); const result = await f.service.installUpdate({ filename: 'websrv_v0.33.elf', sourceId: 'default', confirmation: 'UPDATE websrv_v0.33.elf' }); assert.equal(result.ok, true); assert.equal(result.launched, false); assert.ok(f.calls.some(route => route === '/repository_install?filename=websrv_v0.33.elf&source_id=default')); assert.ok(!f.calls.some(route => route.startsWith('/loadpayload:'))); });