109 lines
3.8 KiB
JavaScript
109 lines
3.8 KiB
JavaScript
'use strict';
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { EventEmitter } = require('node:events');
|
|
const {
|
|
altaKeyFromName,
|
|
cameraFromService,
|
|
isLocalAddress,
|
|
normalizeResults,
|
|
scanCameras,
|
|
} = require('../src/discovery');
|
|
|
|
const validService = (overrides = {}) => ({
|
|
name: 'ABH7-C62U-GC3E',
|
|
type: 'vaioncam',
|
|
protocol: 'tcp',
|
|
host: 'avacam-abh7-c62u-gc3e.local.',
|
|
addresses: ['fe80::1', '10.0.0.223'],
|
|
port: 443,
|
|
...overrides,
|
|
});
|
|
|
|
test('extracts an Alta Key from service names', () => {
|
|
assert.equal(altaKeyFromName('abh7-c62u-gc3e'), 'ABH7-C62U-GC3E');
|
|
assert.equal(altaKeyFromName('ABH7-C62U-GC3E._vaioncam._tcp.local'), 'ABH7-C62U-GC3E');
|
|
assert.equal(altaKeyFromName('not-a-key'), null);
|
|
});
|
|
|
|
test('projects a correlated local Bonjour service into a safe camera row', () => {
|
|
assert.deepEqual(cameraFromService(validService()), {
|
|
alta_key: 'ABH7-C62U-GC3E',
|
|
hostname: 'avacam-abh7-c62u-gc3e.local',
|
|
web_ui_url: 'https://avacam-abh7-c62u-gc3e.local',
|
|
ip_address: '10.0.0.223',
|
|
port: 443,
|
|
service: '_vaioncam._tcp.local',
|
|
});
|
|
});
|
|
|
|
test('rejects uncorrelated or non-local service announcements', () => {
|
|
assert.equal(cameraFromService(validService({ type: undefined })), null);
|
|
assert.equal(cameraFromService(validService({ protocol: undefined })), null);
|
|
assert.equal(cameraFromService(validService({ host: 'avacam-attacker.local.' })), null);
|
|
assert.equal(cameraFromService(validService({ port: 8443 })), null);
|
|
assert.equal(cameraFromService(validService({ addresses: ['203.0.113.10'] })), null);
|
|
assert.equal(cameraFromService(validService({ addresses: [] })), null);
|
|
});
|
|
|
|
test('recognizes supported private and link-local IP scopes', () => {
|
|
for (const address of ['10.0.0.1', '172.16.0.1', '172.31.255.254', '192.168.1.1', '169.254.1.1', 'fe80::1', 'fc00::1', 'fd12::1']) {
|
|
assert.equal(isLocalAddress(address), true, address);
|
|
}
|
|
for (const address of ['8.8.8.8', '172.32.0.1', '2001:4860:4860::8888', 'not-an-ip']) {
|
|
assert.equal(isLocalAddress(address), false, address);
|
|
}
|
|
});
|
|
|
|
test('deduplicates and sorts cameras', () => {
|
|
const a = { alta_key: 'AHG2-Z4ND-FVRN', ip_address: '10.0.0.192' };
|
|
const b = { alta_key: 'ABH7-C62U-GC3E', ip_address: '10.0.0.223' };
|
|
assert.deepEqual(normalizeResults([a, b, a]), [b, a]);
|
|
});
|
|
|
|
test('scans through the Bonjour adapter and closes resources', async () => {
|
|
let browserStopped = false;
|
|
let bonjourDestroyed = false;
|
|
class FakeBonjour {
|
|
find(query) {
|
|
assert.deepEqual(query, { type: 'vaioncam', protocol: 'tcp' });
|
|
const browser = new EventEmitter();
|
|
browser.stop = () => { browserStopped = true; };
|
|
process.nextTick(() => browser.emit('up', validService()));
|
|
return browser;
|
|
}
|
|
destroy() { bonjourDestroyed = true; }
|
|
}
|
|
const rows = await scanCameras({ durationMs: 5, BonjourClass: FakeBonjour });
|
|
assert.equal(rows.length, 1);
|
|
assert.equal(rows[0].alta_key, 'ABH7-C62U-GC3E');
|
|
assert.equal(browserStopped, true);
|
|
assert.equal(bonjourDestroyed, true);
|
|
});
|
|
|
|
test('caps scan results and closes discovery immediately at the cap', async () => {
|
|
let browserStopped = false;
|
|
class FakeBonjour {
|
|
find() {
|
|
const browser = new EventEmitter();
|
|
browser.stop = () => { browserStopped = true; };
|
|
process.nextTick(() => {
|
|
for (let index = 0; index < 10; index += 1) {
|
|
const key = `AAAA-BBBB-${String(index).padStart(4, '0')}`;
|
|
browser.emit('up', validService({
|
|
name: key,
|
|
host: `avacam-${key.toLowerCase()}.local.`,
|
|
addresses: [`10.0.0.${index + 1}`],
|
|
}));
|
|
}
|
|
});
|
|
return browser;
|
|
}
|
|
destroy() {}
|
|
}
|
|
const rows = await scanCameras({ durationMs: 1000, maxResults: 3, BonjourClass: FakeBonjour });
|
|
assert.equal(rows.length, 3);
|
|
assert.equal(browserStopped, true);
|
|
});
|