mirror of
https://github.com/UNITRONIX/BetterDesk.git
synced 2026-10-26 14:30:19 +00:00
abc00b8804
Bind guest WebSocket sessions to a single target peer, enforce grant lifecycle and atomic usage, add the guest issuer scope, and document the password-less security boundary.\n\nRefs #440\n\nThanks: INSOLVE (Honorary); Marco Jakobs (@jacotec); MyNameisStitch (@MyNameisStitch); Redspin (@playerumpknow)
112 lines
3.4 KiB
JavaScript
112 lines
3.4 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('node:assert/strict');
|
|
const test = require('node:test');
|
|
|
|
const {
|
|
CDAPBridge,
|
|
Widget,
|
|
Action,
|
|
Severity,
|
|
WidgetType,
|
|
button,
|
|
createMessage,
|
|
gauge,
|
|
parseMessage,
|
|
toggle,
|
|
} = require('../src');
|
|
const {
|
|
authApiKey,
|
|
authDeviceToken,
|
|
authUserPassword,
|
|
} = require('../src/protocol');
|
|
|
|
test('protocol messages round-trip with strings and buffers', () => {
|
|
const raw = createMessage('telemetry', { temperature: 21 }, 'message-1');
|
|
const parsed = parseMessage(raw);
|
|
assert.equal(parsed.type, 'telemetry');
|
|
assert.deepEqual(parsed.payload, { temperature: 21 });
|
|
assert.equal(parsed.id, 'message-1');
|
|
assert.match(parsed.timestamp, /^\d{4}-\d{2}-\d{2}T/);
|
|
|
|
const fromBuffer = parseMessage(Buffer.from(raw));
|
|
assert.deepEqual(fromBuffer, parsed);
|
|
});
|
|
|
|
test('protocol constants and authentication payloads are stable', () => {
|
|
assert.equal(Action.SET, 'set');
|
|
assert.equal(Severity.WARNING, 'warning');
|
|
assert.equal(WidgetType.GAUGE, 'gauge');
|
|
assert.deepEqual(authApiKey('key', 'device', '2.0.0'), {
|
|
method: 'api_key',
|
|
key: 'key',
|
|
device_id: 'device',
|
|
client_version: '2.0.0',
|
|
});
|
|
assert.deepEqual(authDeviceToken('token'), {
|
|
method: 'device_token',
|
|
token: 'token',
|
|
device_id: '',
|
|
client_version: '1.0.0',
|
|
});
|
|
assert.deepEqual(authUserPassword('admin', 'password'), {
|
|
method: 'user_password',
|
|
username: 'admin',
|
|
password: 'password',
|
|
device_id: '',
|
|
client_version: '1.0.0',
|
|
});
|
|
});
|
|
|
|
test('widget factories produce CDAP-compatible manifests', () => {
|
|
const temperature = gauge('temperature', 'Temperature', { unit: 'C', max: 50 });
|
|
const heater = toggle('heater', 'Heater');
|
|
const restart = button('restart', 'Restart', { confirm: true });
|
|
|
|
assert.ok(temperature instanceof Widget);
|
|
assert.deepEqual(temperature.toJSON(), {
|
|
type: 'gauge',
|
|
id: 'temperature',
|
|
label: 'Temperature',
|
|
unit: 'C',
|
|
max: 50,
|
|
precision: 1,
|
|
permissions: { read: 'viewer' },
|
|
});
|
|
assert.deepEqual(heater.toJSON(), {
|
|
type: 'toggle',
|
|
id: 'heater',
|
|
label: 'Heater',
|
|
permissions: { read: 'viewer', control: 'operator' },
|
|
});
|
|
assert.equal(restart.toJSON().confirm, true);
|
|
});
|
|
|
|
test('bridge registration and command handling are testable without a network', async () => {
|
|
const bridge = new CDAPBridge({
|
|
server: 'ws://127.0.0.1:1/cdap',
|
|
apiKey: 'test-only-key',
|
|
deviceId: 'device-1',
|
|
heartbeatSec: 5,
|
|
});
|
|
const sent = [];
|
|
bridge._send = (type, payload) => sent.push({ type, payload });
|
|
|
|
bridge.addWidget(toggle('heater', 'Heater'));
|
|
bridge.onCommand('heater', ({ value }) => Boolean(value));
|
|
bridge._register();
|
|
|
|
assert.equal(sent[0].type, 'register');
|
|
assert.equal(sent[0].payload.widgets[0].id, 'heater');
|
|
assert.deepEqual(sent[0].payload.capabilities, ['telemetry', 'commands', 'alerts', 'logs']);
|
|
|
|
await bridge._handleCommand({
|
|
id: 'command-1',
|
|
payload: { widget_id: 'heater', action: Action.SET, value: true },
|
|
});
|
|
const response = sent.at(-1);
|
|
assert.equal(response.type, 'command_response');
|
|
assert.equal(response.payload.success, true);
|
|
assert.equal(response.payload.value, true);
|
|
});
|