// Unit tests for services/dectDiscovery.js. Pure — no network, no fs. // The interesting cases are all around the 10.x guardrail and the // dedup/priority logic when Meraki and Webex report different IPs // for the same base. import test from 'node:test'; import assert from 'node:assert/strict'; import { discoverDectBases, isTenDotIp, normalizeMac, } from '../services/dectDiscovery.js'; // ─── Helpers ──────────────────────────────────────────────────────── const fixture = (overrides = {}) => ({ dectBasestations: [], ...overrides, }); const base = (attrs = {}) => ({ mac: '6c:ab:05:f6:28:19', name: 'Basestation A', ipAddress: '—', meraki: {}, ...attrs, }); // ─── isTenDotIp ───────────────────────────────────────────────────── test('isTenDotIp: accepts 10.x/8 addresses', () => { assert.equal(isTenDotIp('10.0.0.1'), true); assert.equal(isTenDotIp('10.255.255.254'), true); assert.equal(isTenDotIp('10.4.11.87'), true); }); test('isTenDotIp: rejects non-10.x addresses', () => { assert.equal(isTenDotIp('192.168.1.164'), false); assert.equal(isTenDotIp('172.16.0.1'), false); assert.equal(isTenDotIp('11.0.0.1'), false); assert.equal(isTenDotIp('100.0.0.1'), false); }); test('isTenDotIp: rejects malformed input', () => { assert.equal(isTenDotIp(null), false); assert.equal(isTenDotIp(''), false); assert.equal(isTenDotIp('10.'), false); assert.equal(isTenDotIp('10.0.0'), false); assert.equal(isTenDotIp('10.0.0.256'), false); assert.equal(isTenDotIp('10.0.0.1.5'), false); assert.equal(isTenDotIp('not-an-ip'), false); assert.equal(isTenDotIp(10), false); }); // ─── normalizeMac ─────────────────────────────────────────────────── test('normalizeMac: handles various input formats', () => { assert.equal(normalizeMac('6cab05f62819'), '6c:ab:05:f6:28:19'); assert.equal(normalizeMac('6C:AB:05:F6:28:19'), '6c:ab:05:f6:28:19'); assert.equal(normalizeMac('6c-ab-05-f6-28-19'), '6c:ab:05:f6:28:19'); assert.equal(normalizeMac('6cab.05f6.2819'), '6c:ab:05:f6:28:19'); }); test('normalizeMac: rejects bad input', () => { assert.equal(normalizeMac(null), null); assert.equal(normalizeMac(''), null); assert.equal(normalizeMac('not-a-mac'), null); assert.equal(normalizeMac('6cab05f62819aa'), null); // 14 hex chars }); // ─── discoverDectBases ────────────────────────────────────────────── test('discoverDectBases: empty input returns empty result (never throws)', () => { assert.deepEqual(discoverDectBases({}), { bases: [], warnings: [] }); assert.deepEqual(discoverDectBases(null), { bases: [], warnings: [] }); assert.deepEqual(discoverDectBases({ dectBasestations: null }), { bases: [], warnings: [] }); }); test('discoverDectBases: happy path — one Meraki-enriched base on 10.x', () => { const result = discoverDectBases(fixture({ dectBasestations: [ base({ meraki: { ip: '10.4.11.87' } }), ], })); assert.equal(result.bases.length, 1); assert.equal(result.warnings.length, 0); assert.deepEqual(result.bases[0], { mac: '6c:ab:05:f6:28:19', ip: '10.4.11.87', name: 'Basestation A', source: 'meraki', }); }); test('discoverDectBases: prefers Meraki IP over Webex IP', () => { const result = discoverDectBases(fixture({ dectBasestations: [ base({ ipAddress: '10.4.11.100', // Webex-reported (potentially stale) meraki: { ip: '10.4.11.87' }, // Meraki-reported (live) }), ], })); assert.equal(result.bases[0].ip, '10.4.11.87'); assert.equal(result.bases[0].source, 'meraki'); }); test('discoverDectBases: falls back to Webex IP when no Meraki data', () => { const result = discoverDectBases(fixture({ dectBasestations: [ base({ ipAddress: '10.4.11.87', meraki: {} }), ], })); assert.equal(result.bases[0].ip, '10.4.11.87'); assert.equal(result.bases[0].source, 'webex'); }); test('discoverDectBases: 10.x guardrail rejects non-corporate IPs with a warning', () => { const result = discoverDectBases(fixture({ dectBasestations: [ base({ mac: 'aa:bb:cc:dd:ee:01', meraki: { ip: '192.168.1.164' } }), base({ mac: 'aa:bb:cc:dd:ee:02', meraki: { ip: '172.16.0.100' } }), base({ mac: 'aa:bb:cc:dd:ee:03', meraki: { ip: '10.4.11.87' } }), ], })); assert.equal(result.bases.length, 1); assert.equal(result.bases[0].ip, '10.4.11.87'); assert.equal(result.warnings.length, 2); // Both warnings should reference the offending IPs and the 10.x rule. assert.match(result.warnings[0].reason, /192\.168\.1\.164/); assert.match(result.warnings[0].reason, /10\.0\.0\.0\/8/); assert.match(result.warnings[1].reason, /172\.16\.0\.100/); }); test('discoverDectBases: rejects bases with no MAC (inventory bug)', () => { const result = discoverDectBases(fixture({ dectBasestations: [ base({ mac: null, meraki: { ip: '10.4.11.87' } }), ], })); assert.equal(result.bases.length, 0); assert.equal(result.warnings.length, 1); assert.match(result.warnings[0].reason, /no MAC/i); }); test('discoverDectBases: rejects bases with no IP anywhere', () => { const result = discoverDectBases(fixture({ dectBasestations: [ base({ ipAddress: '—', meraki: {} }), ], })); assert.equal(result.bases.length, 0); assert.equal(result.warnings.length, 1); assert.match(result.warnings[0].reason, /no IP address/i); }); test('discoverDectBases: dedups by IP and MAC (keeps first)', () => { const result = discoverDectBases(fixture({ dectBasestations: [ base({ mac: 'aa:bb:cc:dd:ee:01', meraki: { ip: '10.4.11.87' }, name: 'first' }), base({ mac: 'aa:bb:cc:dd:ee:02', meraki: { ip: '10.4.11.87' }, name: 'duplicate-ip' }), base({ mac: 'aa:bb:cc:dd:ee:01', meraki: { ip: '10.4.11.88' }, name: 'duplicate-mac' }), ], })); assert.equal(result.bases.length, 1); assert.equal(result.bases[0].name, 'first'); assert.equal(result.warnings.length, 2); });