Files
harbor-net/test/web/devices-feature-contract.test.js
dokril 34d8b681ad
Build and Deploy Gateway / build-and-push (push) Failing after 2s
Build and Deploy Gateway / deploy (push) Has been skipped
Refactor VPN proxy client implementation
2026-08-09 00:41:52 +03:00

117 lines
6.2 KiB
JavaScript

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(/<DevicesToggle/g) || []).length, 1);
assert.equal((page.match(/<GatewayTrafficSummary/g) || []).length, 1);
assert.equal((page.match(/<DevicesPanel/g) || []).length, 1);
assert.match(page, /useDevicesFeature\(\{[\s\S]*listDevices: actions\.listDevices[\s\S]*refreshDevices: actions\.refreshDevices[\s\S]*updateDevice: actions\.updateDevice[\s\S]*setDevicePolicy: actions\.setDevicePolicy[\s\S]*\}\)/);
assert.match(page, /<DevicesPanel feature=\{devicesFeature\} \/>/);
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(page, /<DevicesToggle[\s\S]*devicesFeature\.toggle\(\)/);
assert.match(page, /<GatewayTrafficSummary feature=\{devicesFeature\} now=\{now\}/);
});
test('all unknown inventory results pass one identity-preserving runtime parser', () => {
const observedAt = '2026-08-08T12:34:56.000Z';
const valid = {
revision: 3,
devices: [{
id: 'dev_0123456789abcdef',
alias: null,
hostname: null,
ip: null,
lastSeenAt: null,
status: 'online',
pinned: true,
downloadBytes: '12',
uploadBytes: '30',
proxyDownloadBytes: '0',
proxyUploadBytes: '0',
policyStatus: 'applied',
policyError: null,
desiredPolicy: 'vpn',
appliedPolicy: 'vpn',
confidence: 'high',
trafficHistory: [{ observedAt, gatewayBytes: '42', proxyBytes: '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 },
};
assert.equal(parseDeviceSnapshot(valid), valid);
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], 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], desiredPolicy: 'automatic' }] },
{ ...valid, devices: [{ ...valid.devices[0], policyStatus: 'queued' }] },
{ ...valid, devices: [{ ...valid.devices[0], confidence: 'unknown' }] },
{ ...valid, devices: [{ ...valid.devices[0], lastSeenAt: 'not-a-date' }] },
{ ...valid, devices: [{ ...valid.devices[0], trafficHistory: [{ observedAt, gatewayBytes: '1' }] }] },
{ ...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<string, unknown>)/);
});