P3 wave 2: convert measurement to BaseDomain + Channel-based analog
specificClass.js: 716 → 244 lines.
Measurement extends BaseDomain. Analog mode now routes through one
Channel (key=null) — eliminates ~400 lines of inline pipeline that
duplicated what Channel.update() already did.
Public surface preserved for tests:
- tick() runs the simulator (when enabled) — Simulator owns the
random walk, orchestrator just writes the output back.
- inputValue setter routes through analogChannel.update.
- calibrate() / evaluateRepeatability() delegate to Calibrator.
- toggleSimulation / toggleOutlierDetection unchanged.
- 'mAbs' emitter event re-emitted from the analog channel's
MeasurementContainer event — backwards compat (deprecated;
tracked in OPEN_QUESTIONS.md for removal in Phase 7/8.5).
nodeClass.js: 230 → 42 lines.
Extends BaseNodeAdapter. tickInterval=1000 (only meaningful when
simulator enabled; tick is a no-op otherwise — toggling simulation
shouldn't require a redeploy). buildDomainConfig parses channels
JSON + mode and shapes scaling/smoothing/simulation slices.
96 / 96 tests pass (basic 77 + integration 17 + edge 2).
Two routing tests adjusted to seed the new commandRegistry path
(legacy private wiring removed); domain-tier tests unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,29 +2,40 @@ const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const NodeClass = require('../../src/nodeClass');
|
||||
const commands = require('../../src/commands');
|
||||
const { createRegistry } = require('generalFunctions');
|
||||
const { makeNodeStub, makeREDStub } = require('../helpers/factories');
|
||||
|
||||
test('_attachInputHandler routes known topics to source methods', () => {
|
||||
// These tests pinned the old private methods (_attachInputHandler /
|
||||
// _registerChild) on the pre-refactor nodeClass. After the BaseNodeAdapter
|
||||
// migration the same wiring is provided by the base class, but we still
|
||||
// exercise it from a prototype-derived instance to keep the surface covered
|
||||
// without booting a full Node-RED runtime.
|
||||
|
||||
test('input handler dispatches known topics to source methods', async () => {
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
const node = makeNodeStub();
|
||||
const calls = [];
|
||||
|
||||
inst.node = node;
|
||||
inst.RED = makeREDStub();
|
||||
inst.source = {
|
||||
const source = {
|
||||
mode: 'analog',
|
||||
logger: { warn: () => {}, info: () => {}, debug: () => {}, error: () => {} },
|
||||
toggleSimulation() { calls.push('simulator'); },
|
||||
toggleOutlierDetection() { calls.push('outlierDetection'); },
|
||||
calibrate() { calls.push('calibrate'); },
|
||||
set inputValue(v) { calls.push(['measurement', v]); },
|
||||
};
|
||||
|
||||
inst.node = node;
|
||||
inst.RED = makeREDStub();
|
||||
inst.source = source;
|
||||
inst._commands = createRegistry(commands, { logger: source.logger });
|
||||
inst._attachInputHandler();
|
||||
const onInput = node._handlers.input;
|
||||
|
||||
onInput({ topic: 'simulator' }, () => {}, () => {});
|
||||
onInput({ topic: 'outlierDetection' }, () => {}, () => {});
|
||||
onInput({ topic: 'calibrate' }, () => {}, () => {});
|
||||
onInput({ topic: 'measurement', payload: 12.3 }, () => {}, () => {});
|
||||
const onInput = node._handlers.input;
|
||||
await onInput({ topic: 'simulator' }, () => {}, () => {});
|
||||
await onInput({ topic: 'outlierDetection' }, () => {}, () => {});
|
||||
await onInput({ topic: 'calibrate' }, () => {}, () => {});
|
||||
await onInput({ topic: 'measurement', payload: 12.3 }, () => {}, () => {});
|
||||
|
||||
assert.deepEqual(calls[0], 'simulator');
|
||||
assert.deepEqual(calls[1], 'outlierDetection');
|
||||
@@ -32,7 +43,7 @@ test('_attachInputHandler routes known topics to source methods', () => {
|
||||
assert.deepEqual(calls[3], ['measurement', 12.3]);
|
||||
});
|
||||
|
||||
test('_registerChild emits delayed registerChild message on output 2', () => {
|
||||
test('registration emits delayed child.register message on output 2', () => {
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
const node = makeNodeStub();
|
||||
|
||||
@@ -42,13 +53,13 @@ test('_registerChild emits delayed registerChild message on output 2', () => {
|
||||
const originalSetTimeout = global.setTimeout;
|
||||
global.setTimeout = (fn) => { fn(); return 1; };
|
||||
try {
|
||||
inst._registerChild();
|
||||
inst._scheduleRegistration();
|
||||
} finally {
|
||||
global.setTimeout = originalSetTimeout;
|
||||
}
|
||||
|
||||
assert.equal(node._sent.length, 1);
|
||||
assert.equal(node._sent[0][2].topic, 'registerChild');
|
||||
assert.equal(node._sent[0][2].topic, 'child.register');
|
||||
assert.equal(node._sent[0][2].positionVsParent, 'upstream');
|
||||
assert.equal(node._sent[0][2].distance, 5);
|
||||
});
|
||||
|
||||
@@ -2,27 +2,32 @@ const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const NodeClass = require('../../src/nodeClass');
|
||||
const commands = require('../../src/commands');
|
||||
const { createRegistry } = require('generalFunctions');
|
||||
const { makeNodeStub, makeREDStub } = require('../helpers/factories');
|
||||
|
||||
test('measurement topic accepts numeric strings and ignores non-numeric objects', () => {
|
||||
test('measurement topic accepts numeric strings and ignores non-numeric objects', async () => {
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
const node = makeNodeStub();
|
||||
const calls = [];
|
||||
|
||||
inst.node = node;
|
||||
inst.RED = makeREDStub();
|
||||
inst.source = {
|
||||
const source = {
|
||||
mode: 'analog',
|
||||
logger: { warn: () => {}, info: () => {}, debug: () => {}, error: () => {} },
|
||||
set inputValue(v) { calls.push(v); },
|
||||
toggleSimulation() {},
|
||||
toggleOutlierDetection() {},
|
||||
calibrate() {},
|
||||
};
|
||||
|
||||
inst.node = node;
|
||||
inst.RED = makeREDStub();
|
||||
inst.source = source;
|
||||
inst._commands = createRegistry(commands, { logger: source.logger });
|
||||
inst._attachInputHandler();
|
||||
const onInput = node._handlers.input;
|
||||
|
||||
onInput({ topic: 'measurement', payload: '42' }, () => {}, () => {});
|
||||
onInput({ topic: 'measurement', payload: { value: 42 } }, () => {}, () => {});
|
||||
const onInput = node._handlers.input;
|
||||
await onInput({ topic: 'measurement', payload: '42' }, () => {}, () => {});
|
||||
await onInput({ topic: 'measurement', payload: { value: 42 } }, () => {}, () => {});
|
||||
|
||||
assert.deepEqual(calls, [42]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user