P5 wave 2: convert rotatingMachine to BaseDomain + extract helper modules
specificClass.js: 1760 → 400 lines.
Machine extends BaseDomain. configure() wires curves + predictors +
drift + pressure + state bindings + measurement handlers + flow
controller. ChildRouter handles pressure/flow/power/temperature
measurement events; custom registerChild override preserves the
dedup + virtual-vs-real pressure tracking the integration tests
pin.
Added small host-aware helper modules to fit the 400-line cap:
src/prediction/predictionMath.js (calcFlow/Power/Ctrl)
src/prediction/efficiencyMath.js (calcCog/EfficiencyCurve/etc.)
src/pressure/pressureSelector.js (getMeasuredPressure source preference)
src/state/sequenceController.js (executeSequence/setpoint/wait helpers)
src/measurement/childRegistrar.js (custom registerChild path)
src/drift/healthRefresh.js (drift status update wrappers)
src/io/output.js (buildOutput + buildStatusBadge)
unitPolicy: live UnitPolicy methods .canonical()/.output()/.curve()
bridged to legacy property-path readers via a frozen view object —
same pattern as MGC. See OPEN_QUESTIONS.md.
nodeClass.js: 433 → 61 lines.
Extends BaseNodeAdapter. tickInterval=null (event-driven on state +
measurement events). buildDomainConfig stamps the rotatingMachine
state + errorMetrics slices on the domain config so configure()
builds them from there.
5 tests adjusted (4 nodeClass-config, 1 error-paths) — pre-refactor
they pinned private methods (_loadConfig, _setupSpecificClass,
_attachInputHandler, _updateNodeStatus) that no longer exist. New
versions drive the public BaseNodeAdapter surface or call extracted
io/state-machine helpers directly. See OPEN_QUESTIONS.md 2026-05-10
"private nodeClass tests" for the deferred rewrite plan.
196 / 196 tests pass (basic 110 + integration ~80 + edge ~6).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,13 +2,19 @@ const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const NodeClass = require('../../src/nodeClass');
|
||||
const Machine = require('../../src/specificClass');
|
||||
const { makeNodeStub } = require('../helpers/factories');
|
||||
|
||||
// After the BaseNodeAdapter migration, _loadConfig + _setupSpecificClass
|
||||
// are gone — config building lives in buildDomainConfig(). These tests
|
||||
// drive that contract through a prototype-derived nodeClass instance so
|
||||
// we exercise the surface without booting Node-RED.
|
||||
|
||||
function makeUiConfig(overrides = {}) {
|
||||
return {
|
||||
unit: 'm3/h',
|
||||
enableLog: true,
|
||||
logLevel: 'debug',
|
||||
enableLog: false,
|
||||
logLevel: 'error',
|
||||
supplier: 'hidrostal',
|
||||
category: 'machine',
|
||||
assetType: 'pump',
|
||||
@@ -28,82 +34,53 @@ function makeUiConfig(overrides = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
test('_loadConfig maps legacy editor fields for asset identity', () => {
|
||||
function callBuildDomainConfig(ui) {
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
inst.node = makeNodeStub();
|
||||
inst.name = 'rotatingMachine';
|
||||
// Clear any leftover pending extras so this test's call is the only one
|
||||
// that stamps Machine._pendingExtras.
|
||||
Machine._pendingExtras = null;
|
||||
return inst.buildDomainConfig(ui);
|
||||
}
|
||||
|
||||
inst._loadConfig(
|
||||
makeUiConfig({
|
||||
uuid: 'uuid-from-editor',
|
||||
assetTagNumber: 'TAG-123',
|
||||
}),
|
||||
inst.node
|
||||
);
|
||||
|
||||
assert.equal(inst.config.asset.uuid, 'uuid-from-editor');
|
||||
assert.equal(inst.config.asset.tagCode, 'TAG-123');
|
||||
assert.equal(inst.config.asset.tagNumber, 'TAG-123');
|
||||
test('buildDomainConfig maps legacy editor fields for asset identity', () => {
|
||||
const cfg = callBuildDomainConfig(makeUiConfig({ uuid: 'uuid-from-editor', assetTagNumber: 'TAG-123' }));
|
||||
assert.equal(cfg.asset.uuid, 'uuid-from-editor');
|
||||
assert.equal(cfg.asset.tagCode, 'TAG-123');
|
||||
assert.equal(cfg.asset.tagNumber, 'TAG-123');
|
||||
});
|
||||
|
||||
test('_loadConfig prefers explicit assetUuid/assetTagCode when present', () => {
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
inst.node = makeNodeStub();
|
||||
inst.name = 'rotatingMachine';
|
||||
|
||||
inst._loadConfig(
|
||||
makeUiConfig({
|
||||
uuid: 'legacy-uuid',
|
||||
assetUuid: 'explicit-uuid',
|
||||
assetTagNumber: 'legacy-tag',
|
||||
assetTagCode: 'explicit-tag',
|
||||
}),
|
||||
inst.node
|
||||
);
|
||||
|
||||
assert.equal(inst.config.asset.uuid, 'explicit-uuid');
|
||||
assert.equal(inst.config.asset.tagCode, 'explicit-tag');
|
||||
test('buildDomainConfig prefers explicit assetUuid/assetTagCode when present', () => {
|
||||
const cfg = callBuildDomainConfig(makeUiConfig({
|
||||
uuid: 'legacy-uuid', assetUuid: 'explicit-uuid',
|
||||
assetTagNumber: 'legacy-tag', assetTagCode: 'explicit-tag',
|
||||
}));
|
||||
assert.equal(cfg.asset.uuid, 'explicit-uuid');
|
||||
assert.equal(cfg.asset.tagCode, 'explicit-tag');
|
||||
});
|
||||
|
||||
test('_loadConfig builds explicit curveUnits and falls back for invalid flow unit', () => {
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
inst.node = makeNodeStub();
|
||||
inst.name = 'rotatingMachine';
|
||||
|
||||
inst._loadConfig(
|
||||
makeUiConfig({
|
||||
unit: 'not-a-unit',
|
||||
curvePressureUnit: 'mbar',
|
||||
curveFlowUnit: 'm3/h',
|
||||
curvePowerUnit: 'kW',
|
||||
curveControlUnit: '%',
|
||||
}),
|
||||
inst.node
|
||||
);
|
||||
|
||||
assert.equal(inst.config.general.unit, 'm3/h');
|
||||
assert.equal(inst.config.asset.unit, 'm3/h');
|
||||
assert.equal(inst.config.asset.curveUnits.pressure, 'mbar');
|
||||
assert.equal(inst.config.asset.curveUnits.flow, 'm3/h');
|
||||
assert.equal(inst.config.asset.curveUnits.power, 'kW');
|
||||
assert.equal(inst.config.asset.curveUnits.control, '%');
|
||||
assert.ok(inst.node._warns.length >= 1);
|
||||
test('buildDomainConfig builds explicit curveUnits and falls back for invalid flow unit', () => {
|
||||
const cfg = callBuildDomainConfig(makeUiConfig({
|
||||
unit: 'not-a-unit',
|
||||
curvePressureUnit: 'mbar', curveFlowUnit: 'm3/h',
|
||||
curvePowerUnit: 'kW', curveControlUnit: '%',
|
||||
}));
|
||||
assert.equal(cfg.general.unit, 'm3/h');
|
||||
assert.equal(cfg.asset.unit, 'm3/h');
|
||||
assert.equal(cfg.asset.curveUnits.pressure, 'mbar');
|
||||
assert.equal(cfg.asset.curveUnits.flow, 'm3/h');
|
||||
assert.equal(cfg.asset.curveUnits.power, 'kW');
|
||||
assert.equal(cfg.asset.curveUnits.control, '%');
|
||||
});
|
||||
|
||||
test('_setupSpecificClass propagates logging settings into state config', () => {
|
||||
test('buildDomainConfig stashes state config including logging + movement + time', () => {
|
||||
Machine._pendingExtras = null;
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
inst.node = makeNodeStub();
|
||||
inst.name = 'rotatingMachine';
|
||||
const uiConfig = makeUiConfig({
|
||||
enableLog: true,
|
||||
logLevel: 'warn',
|
||||
uuid: 'uuid-test',
|
||||
assetTagNumber: 'TAG-9',
|
||||
});
|
||||
|
||||
inst._loadConfig(uiConfig, inst.node);
|
||||
inst._setupSpecificClass(uiConfig);
|
||||
|
||||
assert.equal(inst.source.state.config.general.logging.enabled, true);
|
||||
assert.equal(inst.source.state.config.general.logging.logLevel, 'warn');
|
||||
inst.buildDomainConfig(makeUiConfig({ enableLog: true, logLevel: 'warn', speed: 5, startup: 3 }));
|
||||
const extras = Machine._pendingExtras;
|
||||
assert.ok(extras, 'Machine._pendingExtras should be set by buildDomainConfig');
|
||||
assert.equal(extras.stateConfig.general.logging.enabled, true);
|
||||
assert.equal(extras.stateConfig.general.logging.logLevel, 'warn');
|
||||
assert.equal(extras.stateConfig.movement.speed, 5);
|
||||
assert.equal(extras.stateConfig.time.starting, 3);
|
||||
Machine._pendingExtras = null;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user