import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import test from 'node:test'; import { parseDeviceSnapshot } from '../../.test-dist/src/web/features/devices/deviceSnapshot.js'; const root = path.resolve(import.meta.dirname, '../..'); const page = fs.readFileSync(path.join(root, 'src/web/components/ClientOverviewPage.tsx'), 'utf8'); const feature = fs.readFileSync(path.join(root, 'src/web/features/devices/DevicesFeature.tsx'), 'utf8'); const panel = fs.readFileSync(path.join(root, 'src/web/features/devices/DevicesPanel.tsx'), 'utf8'); const model = fs.readFileSync(path.join(root, 'src/web/features/devices/deviceSnapshot.ts'), 'utf8'); const boundary = fs.readFileSync(path.join(root, 'src/web/features/devices/index.ts'), 'utf8'); test('devices feature is the sole public owner and legacy component paths are gone', () => { assert.match(boundary, /DevicesPanel[\s\S]*DevicesToggle,[\s\S]*GatewayTrafficSummary,[\s\S]*useDevicesFeature/); assert.equal(fs.existsSync(path.join(root, 'src/web/components/DevicesPanel.jsx')), false); assert.equal(fs.existsSync(path.join(root, 'src/web/components/TrafficChart.jsx')), false); assert.equal((page.match(/useDevicesFeature\(/g) || []).length, 1); assert.equal((page.match(//); assert.doesNotMatch(page, /DEVICE_AUTO_REFRESH_MS|deviceSnapshot|deviceStatus|deviceError|devicesRefreshing|deviceRefreshCycle|devicesPanelRef|devicesToggleRef|devicesCloseRef|function loadDevices|client-devices-toggle|className="client-gateway-summary"/); assert.doesNotMatch([feature, panel].join('\n'), /from ['"][^'"]*\/api\/|ConnectionPanel|SubscriptionFeature|RoutingFeature|DiagnosticsPanel/); assert.doesNotMatch(panel, /listDevices|requestDeviceUpdate|setDevicePolicy|STATE_CONFLICT|DEVICE_POLICY_APPLY_FAILED|parseDeviceSnapshot/); }); test('device controller preserves Gateway-only polling, monotonic publication and drawer lifecycle', () => { assert.match(feature, /if \(!isGateway\) return undefined;[\s\S]*load\(\)/); assert.match(feature, /discover \? refreshDevices\(\) : listDevices\(\)/); assert.match(feature, /setTimeout\(\(\) => load\(true\), DEVICE_AUTO_REFRESH_MS\)/); assert.match(feature, /setSnapshot\(\(current\) => !current \|\| next\.revision > current\.revision \? next : current\)/); assert.match(feature, /finally \{[\s\S]*setRefreshing\(false\)[\s\S]*setRefreshCycle/); assert.match(feature, /closeRef\.current\?\.focus\(\)[\s\S]*panelRef\.current\?\.contains[\s\S]*toggleRef\.current\?\.focus\(\)/); assert.match(feature, /resetOpen[\s\S]*if \(resetOpen\) return/); assert.match(feature, /resetDeviceTraffic\(snapshot\.revision\)[\s\S]*STATE_CONFLICT[\s\S]*resetDeviceTraffic\(latest\.revision\)/); assert.match(panel, / switchDrawer\('devices'\)\}/); assert.match(page, / { const observedAt = '2026-08-08T12:34:56.000Z'; const valid = { revision: 3, devices: [{ id: 'dev_0123456789abcdef', alias: null, hostname: null, mac: '00:11:22:33:44:55', ip: null, firstSeenAt: observedAt, lastSeenAt: null, status: 'online', pinned: true, deprioritized: false, downloadBytes: '12', uploadBytes: '30', proxyDownloadBytes: '0', proxyUploadBytes: '0', policyStatus: 'applied', policyError: null, desiredPolicy: 'vpn', appliedPolicy: 'vpn', confidence: 'high', trafficHistory: [{ observedAt, gatewayBytes: '42', proxyBytes: '0' }], outboundTraffic: { observedAt, singboxObservedAt: observedAt, directIpv4ObservedAt: observedAt, vpnBytes: '30', directTrackedBytes: '8', directIpv4Bytes: '4', unknownBytes: '0', }, outboundTrafficHistory: [{ observedAt, vpnBytes: '30', directTrackedBytes: '8', directIpv4Bytes: '4', unknownBytes: '0', }], }], trafficHistoryCapacity: 120, traffic: { gatewayBytes: '42', proxyBytes: '0', totalBytes: '42', gatewayObservedAt: observedAt, proxyObservedAt: null, observedAt: observedAt, history: [{ observedAt, gatewayBytes: '42', proxyBytes: '0' }], }, source: { kind: 'neighbor', lastObservedAt: observedAt, error: null, traffic: { lastObservedAt: observedAt, error: null, proxy: { lastObservedAt: null, error: null }, }, policy: { lastAppliedAt: null, error: null }, }, extra: { retained: true }, }; const oldBackend = parseDeviceSnapshot(valid); assert.equal(oldBackend.taggingSupported, false); assert.deepEqual(oldBackend.tags, []); assert.deepEqual(oldBackend.devices[0].tagIds, []); assert.deepEqual(oldBackend.extra, valid.extra); const supported = { ...valid, tags: [{ id: 'tag_0123456789abcdef', name: 'Умный дом' }], devices: [{ ...valid.devices[0], tagIds: ['tag_0123456789abcdef'] }], }; const parsedSupported = parseDeviceSnapshot(supported); assert.equal(parsedSupported.taggingSupported, true); assert.deepEqual(parsedSupported.tags, supported.tags); assert.deepEqual(parsedSupported.devices[0].tagIds, ['tag_0123456789abcdef']); for (const invalid of [ null, {}, { ...valid, revision: -1 }, { ...valid, revision: 1.5 }, { ...valid, devices: undefined }, { ...valid, trafficHistoryCapacity: 0 }, { ...valid, traffic: undefined }, { ...valid, source: undefined }, { ...valid, devices: [{ ...valid.devices[0], id: 'not-a-device' }] }, { ...valid, devices: [{ ...valid.devices[0], mac: 'not-a-mac' }] }, { ...valid, devices: [{ ...valid.devices[0], downloadBytes: 'not-bytes' }] }, { ...valid, devices: [{ ...valid.devices[0], uploadBytes: '-1' }] }, { ...valid, devices: [{ ...valid.devices[0], proxyUploadBytes: 1 }] }, { ...valid, devices: [{ ...valid.devices[0], status: 'connected' }] }, { ...valid, devices: [{ ...valid.devices[0], deprioritized: 'yes' }] }, { ...valid, devices: [{ ...valid.devices[0], pinned: true, deprioritized: true }] }, { ...valid, devices: [{ ...valid.devices[0], desiredPolicy: 'automatic' }] }, { ...valid, devices: [{ ...valid.devices[0], policyStatus: 'queued' }] }, { ...valid, devices: [{ ...valid.devices[0], confidence: 'unknown' }] }, { ...valid, devices: [{ ...valid.devices[0], firstSeenAt: undefined }] }, { ...valid, devices: [{ ...valid.devices[0], firstSeenAt: 'not-a-date' }] }, { ...valid, devices: [{ ...valid.devices[0], lastSeenAt: 'not-a-date' }] }, { ...valid, devices: [{ ...valid.devices[0], trafficHistory: [{ observedAt, gatewayBytes: '1' }] }] }, { ...valid, devices: [{ ...valid.devices[0], outboundTraffic: { observedAt, vpnBytes: '1' } }] }, { ...valid, devices: [{ ...valid.devices[0], outboundTraffic: { ...valid.devices[0].outboundTraffic, directIpv4ObservedAt: 'not-a-date' } }] }, { ...valid, devices: [{ ...valid.devices[0], outboundTrafficHistory: [{ observedAt, vpnBytes: '1' }] }] }, { ...supported, tags: [{ id: 'bad', name: 'Дом' }] }, { ...supported, tags: [{ ...supported.tags[0], name: ' '.repeat(2) }] }, { ...supported, devices: [{ ...valid.devices[0] }] }, { ...supported, devices: [{ ...supported.devices[0], tagIds: ['tag_ffffffffffffffff'] }] }, { ...valid, traffic: { ...valid.traffic, totalBytes: -4 } }, { ...valid, traffic: { ...valid.traffic, observedAt: 'not-a-date' } }, { ...valid, source: { ...valid.source, kind: 'arp' } }, { ...valid, source: { ...valid.source, traffic: { proxy: [] } } }, ]) assert.throws(() => parseDeviceSnapshot(invalid), TypeError); assert.match(feature, /publish\(await \(discover \? refreshDevices\(\) : listDevices\(\)\)\)/); assert.ok((feature.match(/parseDeviceSnapshot\(await/g) || []).length >= 6); assert.match(feature, /DEVICE_POLICY_APPLY_FAILED[\s\S]*publish\(parseDeviceSnapshot\(await listDevices\(\)\)\)/); assert.doesNotMatch(model, /\sas\s(?:DeviceSnapshot|Record)/); });