'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const { MAX_PROJECTED_PAYLOAD_BYTES, projectDeviceHierarchy, } = require('../src/device-projection'); const { buildDeviceTree } = require('../device-tree'); function uuid(index) { return `00000000-0000-4000-8000-${index.toString(16).padStart(12, '0')}`; } function rawDevice(index, overrides = {}) { return { guid: uuid(index), name: `Camera ${index}`, type: 'camera', model: 'Synthetic', address: `10.0.${Math.floor(index / 255)}.${index % 255}`, server_group_id: 'server-group-1', device_group_id: 'group-1', capabilities: { localStorage: index % 2 === 0, secretCapability: 'drop-me' }, live: { display_status: 'online', private: 'drop-me' }, cookie: 'must-not-cross-ipc', ...overrides, }; } test('projects only allowlisted fields and honestly diagnoses ineligible devices', () => { const payload = projectDeviceHierarchy({ devices: [ rawDevice(1), rawDevice(2, { guid: 'not-a-uuid' }), rawDevice(4), ], sites: [{ id: 'site-1', name: 'HQ', pending_deletion_start: null, secret: 'drop-me' }], groups: [{ id: 'group-1', name: 'Lobby', parent_id: null, pending_deletion_start: null, secret: 'drop-me' }], }); assert.deepEqual(payload.devices, [{ id: uuid(1), name: 'Camera 1', type: 'camera', model: 'Synthetic', address: '10.0.0.1', siteId: null, deviceGroupId: 'group-1', displayStatus: 'online', localStorage: false, }]); assert.deepEqual(payload.sites, [{ id: 'site-1', name: 'HQ', pendingDeletionStart: null }]); assert.deepEqual(payload.groups, [{ id: 'group-1', name: 'Lobby', parentId: null, pendingDeletionStart: null }]); assert.deepEqual(payload.diagnostics.devices, { received: 3, eligible: 1, invalid: 1, cloudNativeExcluded: 1, }); assert.doesNotMatch(JSON.stringify(payload), /drop-me|cookie|secretCapability|private/); }); test('infers camera sites from device-group parents instead of server groups', () => { const payload = projectDeviceHierarchy({ devices: [rawDevice(3, { name: 'Front Door', server_group_id: 'cloud-connector-server-group', device_group_id: 'entrances', })], sites: [{ id: 'test-site', name: 'Test Site' }], groups: [{ id: 'entrances', name: 'Entrances', parent_id: 'test-site' }], }); const model = buildDeviceTree(payload); const camera = [...model.cameraByKey.values()][0]; assert.equal(payload.devices[0].siteId, null); assert.equal(camera.site.canonicalId, 'test-site'); assert.equal(camera.group.canonicalId, 'entrances'); assert.equal(camera.hierarchyStatus, 'inferred-site'); assert.deepEqual(model.diagnostics, []); }); test('excludes cloud-native cameras while retaining Cloud Connector and unknown-storage devices', () => { const payload = projectDeviceHierarchy({ devices: [ rawDevice(10, { name: 'Cloud Native', capabilities: { localStorage: true } }), rawDevice(11, { name: 'Cloud Connector', capabilities: { localStorage: false } }), rawDevice(12, { name: 'Unknown Storage', capabilities: {} }), ], }); assert.deepEqual(payload.devices.map((device) => device.name), ['Cloud Connector', 'Unknown Storage']); assert.deepEqual(payload.diagnostics.devices, { received: 3, eligible: 2, invalid: 0, cloudNativeExcluded: 1, }); }); test('projects synthetic 1,500 and 10,000 camera deployments below the 8 MiB IPC cap', () => { for (const count of [1_500, 10_000]) { const payload = projectDeviceHierarchy({ devices: Array.from({ length: count }, (_, index) => rawDevice(index, { capabilities: { localStorage: false }, })), sites: [], groups: [], }); assert.equal(payload.devices.length, count); assert.equal(payload.diagnostics.devices.received, count); assert.ok(Buffer.byteLength(JSON.stringify(payload)) <= MAX_PROJECTED_PAYLOAD_BYTES); } }); test('malformed hierarchy metadata does not hide valid cameras', () => { const payload = projectDeviceHierarchy({ devices: [rawDevice(7)], sites: [null, { id: {}, name: 'bad' }], groups: ['bad'], metadataErrors: { sites: 'Unavailable', groups: 'Unavailable' }, }); assert.equal(payload.devices.length, 1); assert.deepEqual(payload.sites, []); assert.deepEqual(payload.groups, []); assert.equal(payload.diagnostics.sites.invalid, 2); assert.equal(payload.diagnostics.groups.invalid, 1); assert.equal(payload.diagnostics.sites.error, 'Unavailable'); }); test('repairs lone UTF-16 surrogates without changing valid Unicode or hiding cameras', () => { const payload = projectDeviceHierarchy({ devices: [rawDevice(8, { name: 'Front \ud800 camera', model: 'Model \udc00', address: 'Hall \ud800\udc00 / \udc00', server_group_id: 'site-\ud800', device_group_id: 'group-\udc00', capabilities: { localStorage: false }, })], sites: [{ id: 'site-\ud800', name: 'Valid \ud83d\udcf7 \udc00', pending_deletion_start: '\ud800' }], groups: [{ id: 'group-\udc00', name: 'Group \ud83d\udcf7 \ud800', parent_id: 'site-\ud800' }], metadataErrors: { sites: 'warning \ud800', groups: 'warning \udc00' }, }); assert.equal(payload.devices.length, 1); assert.equal(payload.devices[0].name, 'Front \ufffd camera'); assert.equal(payload.devices[0].model, 'Model \ufffd'); assert.equal(payload.devices[0].address, 'Hall \ud800\udc00 / \ufffd'); assert.equal(payload.devices[0].siteId, null); assert.equal(payload.devices[0].deviceGroupId, 'group-\ufffd'); assert.deepEqual(payload.sites, [{ id: 'site-\ufffd', name: 'Valid \ud83d\udcf7 \ufffd', pendingDeletionStart: '\ufffd' }]); assert.deepEqual(payload.groups, [{ id: 'group-\ufffd', name: 'Group \ud83d\udcf7 \ufffd', parentId: 'site-\ufffd', pendingDeletionStart: null }]); assert.equal(payload.diagnostics.sites.error, 'warning \ufffd'); assert.doesNotMatch(JSON.stringify(payload), /[\ud800-\udfff]/u); }); test('rejects a projected hierarchy payload above 8 MiB', () => { const devices = Array.from({ length: 10_000 }, (_, index) => rawDevice(index, { name: `Camera ${index} ${'x'.repeat(900)}`, capabilities: { localStorage: false }, })); assert.throws( () => projectDeviceHierarchy({ devices, sites: [], groups: [] }), { code: 'PROJECTED_PAYLOAD_TOO_LARGE' }, ); });