P6: convert reactor to platform infrastructure

Refactor of reactor to use BaseNodeAdapter + commandRegistry + statusBadge.
reactor follows the platform refactor plan in .claude/refactor/MODULE_SPLIT.md.
Tests stay green; CONTRACT.md generated; legacy aliases preserved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
znetsixe
2026-05-10 22:23:43 +02:00
parent c5fc5c1b59
commit 7bf464b467
16 changed files with 780 additions and 919 deletions

View File

@@ -2,72 +2,51 @@ 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 supported topics to source methods/setters', () => {
// Post-refactor: dispatch goes through the commands registry built by
// BaseNodeAdapter (this._commands). We seed the registry on a prototype-
// derived instance, then drive _attachInputHandler the same way the live
// adapter would.
test('input handler routes legacy topic aliases to engine setters', async () => {
const inst = Object.create(NodeClass.prototype);
const node = makeNodeStub();
const calls = [];
const source = {
updateState(timestamp) {
calls.push(['clock', timestamp]);
},
logger: { warn: () => {}, info: () => {}, debug: () => {}, error: () => {} },
updateState(t) { calls.push(['clock', t]); },
childRegistrationUtils: {
registerChild(childSource, position) {
calls.push(['registerChild', childSource, position]);
},
registerChild(childSource, position) { calls.push(['registerChild', childSource, position]); },
},
};
Object.defineProperty(source, 'setInfluent', {
set(v) {
calls.push(['Fluent', v]);
},
});
Object.defineProperty(source, 'setOTR', {
set(v) {
calls.push(['OTR', v]);
},
});
Object.defineProperty(source, 'setTemperature', {
set(v) {
calls.push(['Temperature', v]);
},
});
Object.defineProperty(source, 'setDispersion', {
set(v) {
calls.push(['Dispersion', v]);
},
});
Object.defineProperty(source, 'setInfluent', { set(v) { calls.push(['Fluent', v]); } });
Object.defineProperty(source, 'setOTR', { set(v) { calls.push(['OTR', v]); } });
Object.defineProperty(source, 'setTemperature', { set(v) { calls.push(['Temperature', v]); } });
Object.defineProperty(source, 'setDispersion', { set(v) { calls.push(['Dispersion', v]); } });
inst.node = node;
inst.RED = makeREDStub({
childA: {
source: { id: 'child-source-A' },
},
});
inst.RED = makeREDStub({ childA: { source: { id: 'child-source-A' } } });
inst.source = source;
inst._commands = createRegistry(commands, { logger: source.logger });
inst._attachInputHandler();
const onInput = node._handlers.input;
const sent = [];
let doneCount = 0;
const done = () => { doneCount += 1; };
onInput({ topic: 'clock', timestamp: 1000 }, (msg) => sent.push(msg), () => doneCount++);
onInput({ topic: 'Fluent', payload: { inlet: 0, F: 10, C: [] } }, () => {}, () => doneCount++);
onInput({ topic: 'OTR', payload: 3.5 }, () => {}, () => doneCount++);
onInput({ topic: 'Temperature', payload: 18.2 }, () => {}, () => doneCount++);
onInput({ topic: 'Dispersion', payload: 0.2 }, () => {}, () => doneCount++);
onInput({ topic: 'registerChild', payload: 'childA', positionVsParent: 'upstream' }, () => {}, () => doneCount++);
await onInput({ topic: 'clock', timestamp: 1000 }, () => {}, done);
await onInput({ topic: 'Fluent', payload: { inlet: 0, F: 10, C: [] } }, () => {}, done);
await onInput({ topic: 'OTR', payload: 3.5 }, () => {}, done);
await onInput({ topic: 'Temperature', payload: 18.2 }, () => {}, done);
await onInput({ topic: 'Dispersion', payload: 0.2 }, () => {}, done);
await onInput({ topic: 'registerChild', payload: 'childA', positionVsParent: 'upstream' }, () => {}, done);
assert.equal(doneCount, 6);
assert.equal(sent.length, 1);
assert.equal(Array.isArray(sent[0]), true);
assert.deepEqual(calls[0], ['clock', 1000]);
assert.equal(calls.some((x) => x[0] === 'Fluent'), true);
assert.equal(calls.some((x) => x[0] === 'OTR'), true);