Files
rotatingMachine/test/basic/nodeClass-config.basic.test.js

106 lines
4.2 KiB
JavaScript
Raw Normal View History

2026-03-11 11:13:26 +01:00
const test = require('node:test');
const assert = require('node:assert/strict');
const NodeClass = require('../../src/nodeClass');
const { makeNodeStub, makeREDStub } = require('../helpers/factories');
2026-03-11 11:13:26 +01:00
// These tests drive the BaseNodeAdapter public surface. We construct the
// full nodeClass and observe the resulting `inst.source.config` (the
// validated merged shape) and the source's runtime mode. No private hooks.
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>
2026-05-10 22:00:34 +02:00
2026-03-11 11:13:26 +01:00
function makeUiConfig(overrides = {}) {
// After the AssetResolver cutover, the editor no longer saves
// supplier/category/assetType — those are derived from the model id via
// assetResolver.resolveAssetMetadata at runtime.
2026-03-11 11:13:26 +01:00
return {
unit: 'm3/h',
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>
2026-05-10 22:00:34 +02:00
enableLog: false,
logLevel: 'error',
2026-03-11 11:13:26 +01:00
model: 'hidrostal-H05K-S03R',
curvePressureUnit: 'mbar',
curveFlowUnit: 'm3/h',
curvePowerUnit: 'kW',
curveControlUnit: '%',
positionVsParent: 'atEquipment',
speed: 1,
movementMode: 'staticspeed',
startup: 0,
warmup: 0,
shutdown: 0,
cooldown: 0,
...overrides,
};
}
// Adapters built by these tests park a periodic status-poll timer. We
// drive the BaseNodeAdapter close handler after each test to stop it so
// node:test exits cleanly — this is the public teardown path Node-RED
// itself uses on flow shutdown.
const _adapters = [];
function buildAdapter(ui) {
const node = makeNodeStub();
const RED = makeREDStub();
const inst = new NodeClass(ui, RED, node, 'rotatingMachine');
_adapters.push(node);
return { inst, node };
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>
2026-05-10 22:00:34 +02:00
}
test.afterEach(() => {
while (_adapters.length) {
const node = _adapters.pop();
try { node._handlers.close?.(() => {}); } catch (_) { /* best effort */ }
}
});
2026-03-11 11:13:26 +01:00
test('asset identity flows from legacy editor fields through buildDomainConfig', () => {
const { inst } = buildAdapter(makeUiConfig({ uuid: 'uuid-from-editor', assetTagNumber: 'TAG-123' }));
assert.equal(inst.source.config.asset.uuid, 'uuid-from-editor');
assert.equal(inst.source.config.asset.tagCode, 'tag-123');
assert.equal(inst.source.config.asset.tagNumber, 'tag-123');
2026-03-11 11:13:26 +01:00
});
test('explicit assetUuid/assetTagCode override legacy editor fields', () => {
const { inst } = buildAdapter(makeUiConfig({
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>
2026-05-10 22:00:34 +02:00
uuid: 'legacy-uuid', assetUuid: 'explicit-uuid',
assetTagNumber: 'legacy-tag', assetTagCode: 'explicit-tag',
}));
assert.equal(inst.source.config.asset.uuid, 'explicit-uuid');
assert.equal(inst.source.config.asset.tagCode, 'explicit-tag');
2026-03-11 11:13:26 +01:00
});
test('curveUnits propagate through buildDomainConfig, invalid flow unit falls back', () => {
const { inst } = buildAdapter(makeUiConfig({
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>
2026-05-10 22:00:34 +02:00
unit: 'not-a-unit',
curvePressureUnit: 'mbar', curveFlowUnit: 'm3/h',
curvePowerUnit: 'kW', curveControlUnit: '%',
}));
assert.equal(inst.source.config.general.unit, 'm3/h');
assert.equal(inst.source.config.asset.unit, 'm3/h');
assert.equal(inst.source.config.asset.curveUnits.pressure, 'mbar');
assert.equal(inst.source.config.asset.curveUnits.flow, 'm3/h');
assert.equal(inst.source.config.asset.curveUnits.power, 'kW');
assert.equal(inst.source.config.asset.curveUnits.control, '%');
});
test('logging.enabled flag reaches the domain via configManager.buildConfig', () => {
const { inst } = buildAdapter(makeUiConfig({ enableLog: true }));
// uiConfig.enableLog flows through configManager.buildConfig and lands
// on the validated source config. (logLevel currently doesn't propagate
// — known platform behaviour; not exercised here.)
assert.equal(inst.source.config.general.logging.enabled, true);
});
test('state machine is wired and exposes its public surface', () => {
const { inst } = buildAdapter(makeUiConfig());
// The state machine is constructed during configure() and exposes
// observable methods used by the rest of the domain + the status badge.
assert.equal(typeof inst.source.state.getCurrentState, 'function');
assert.equal(typeof inst.source.state.getCurrentPosition, 'function');
assert.equal(inst.source.state.getCurrentState(), 'idle');
2026-03-11 11:13:26 +01:00
});
test('default mode is honoured on the constructed source', () => {
const { inst } = buildAdapter(makeUiConfig());
assert.equal(typeof inst.source.currentMode, 'string');
assert.ok(inst.source.currentMode.length > 0);
2026-03-11 11:13:26 +01:00
});