feat: publish Alta Local Camera Discovery
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
'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);
|
||||
});
|
||||
Reference in New Issue
Block a user