import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import test from 'node:test'; import { parseNeighborSnapshot, readNeighborSnapshot } from '../../src/server/adapters/neighbors.js'; import { createDeviceInventoryService, createVendorLookup, } from '../../src/server/services/deviceInventoryService.js'; import { createJsonStore } from '../../src/server/services/stateStore.js'; test('device inventory discovers, merges, persists metadata and expires anonymous devices', async (t) => { const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'harbor-devices-')); t.after(() => fs.rmSync(directory, { recursive: true, force: true })); const store = createJsonStore({ filePath: path.join(directory, 'devices.json'), defaultValue: {} }); const ouiPath = path.join(directory, 'oui.txt'); fs.writeFileSync(ouiPath, '00-11-22 (hex)\t\tExample Devices\n'); const vendor = createVendorLookup(ouiPath); assert.equal(vendor('00:11:22:33:44:55'), 'Example Devices'); assert.equal(vendor('02:11:22:33:44:55'), null); let current = new Date('2026-07-01T10:00:00.000Z'); let observation = { observedAt: current.toISOString(), observations: parseNeighborSnapshot([ { dst: '192.168.50.10', dev: 'br0', lladdr: '00:11:22:33:44:55', state: ['REACHABLE'] }, { dst: '192.168.50.10', dev: 'br0', lladdr: '00:11:22:33:44:55', state: ['STALE'] }, { dst: '192.168.50.11', dev: 'br0', state: ['INCOMPLETE'] }, ], current.toISOString()), error: null, }; let observeCalls = 0; const service = createDeviceInventoryService({ store, observe: async () => { observeCalls += 1; return observation; }, vendor, now: () => current, }); const [firstRefresh, sharedRefresh] = await Promise.all([service.refresh(), service.refresh()]); let snapshot = firstRefresh; assert.strictEqual(sharedRefresh, firstRefresh); assert.equal(observeCalls, 1); assert.equal(snapshot.devices.length, 1); assert.equal(snapshot.devices[0].manufacturer, 'Example Devices'); assert.equal(snapshot.devices[0].status, 'online'); assert.equal(snapshot.devices[0].confidence, 'high'); current = new Date('2026-07-01T10:05:00.000Z'); observation = { observedAt: current.toISOString(), observations: parseNeighborSnapshot([ { dst: '192.168.50.10', dev: 'br0', lladdr: '00:11:22:33:44:55', state: ['STALE'] }, ], current.toISOString()), error: null, }; const firstSeen = snapshot.devices[0].lastSeenAt; snapshot = await service.refresh(); assert.equal(snapshot.devices[0].lastSeenAt, firstSeen); assert.equal(snapshot.devices[0].status, 'recent'); snapshot = service.update(snapshot.devices[0].id, { alias: 'Телевизор', pinned: true }, snapshot.revision); const restarted = createDeviceInventoryService({ store, observe: async () => observation, vendor, now: () => current }); assert.deepEqual(restarted.snapshot().devices[0], snapshot.devices[0]); assert.throws( () => restarted.update(snapshot.devices[0].id, { pinned: false }, snapshot.revision - 1), (error) => error.code === 'STATE_CONFLICT', ); observation = { observedAt: current.toISOString(), observations: [], error: 'source unavailable' }; snapshot = await restarted.refresh(); assert.equal(snapshot.devices.length, 1); assert.equal(snapshot.source.error, 'source unavailable'); snapshot = restarted.update(snapshot.devices[0].id, { alias: '', pinned: false }, snapshot.revision); current = new Date('2026-08-02T10:00:00.000Z'); observation = { observedAt: current.toISOString(), observations: [], error: null }; snapshot = await restarted.refresh(); assert.equal(snapshot.devices.length, 0); const failed = readNeighborSnapshot(() => ({ status: 1, stderr: 'not available' }), () => current); assert.deepEqual(failed.observations, []); assert.match(failed.error, /not available/); });