Files
rotatingMachine/test/edge/error-paths.edge.test.js
znetsixe 1a9f533b1e P11.6 wiki regen + Phase 10 private-test rewrites where applicable
For all 11 nodes with auto-gen markers: wiki/Home.md sections 5 (topic
contract) and 9 (data model) regenerated via npm run wiki:all. New
Unit column shows '<measure> (default <unit>)' for declared topics,
'—' otherwise. Effect column now uses descriptor.description (P11.2
field) overriding the generic per-prefix fallback.

For rotatingMachine + reactor: Phase 10 test rewrites — 3 + 8 files
moved off private nodeClass internals (_attachInputHandler, _commands,
_pendingExtras, _registerChild, _tick, etc.) to the public
BaseNodeAdapter surface (node.handlers.input, node.source.*).
+6 / +7 net new tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 19:44:11 +02:00

103 lines
3.6 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const Machine = require('../../src/specificClass');
const NodeClass = require('../../src/nodeClass');
const { makeMachineConfig, makeStateConfig, makeNodeStub, makeREDStub } = require('../helpers/factories');
function makeUiConfig(overrides = {}) {
return {
unit: 'm3/h', enableLog: false, logLevel: 'error',
supplier: 'hidrostal', category: 'machine', assetType: 'pump',
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 park a periodic status-poll timer. Drive the BaseNodeAdapter
// close handler after each test to stop it — the public teardown path
// used by Node-RED itself on flow shutdown.
const _adapters = [];
function buildAdapter(ui = makeUiConfig()) {
const node = makeNodeStub();
const inst = new NodeClass(ui, makeREDStub(), node, 'rotatingMachine');
_adapters.push(node);
return { inst, node };
}
test.afterEach(() => {
while (_adapters.length) {
const node = _adapters.pop();
try { node._handlers.close?.(() => {}); } catch (_) { /* best effort */ }
}
});
test('setpoint rejects negative inputs without throwing', async () => {
const machine = new Machine(makeMachineConfig(), makeStateConfig({ state: { current: 'operational' } }));
await assert.doesNotReject(async () => {
await machine.setpoint(-1);
});
});
test('setpoint is constrained to safe movement/curve bounds', async () => {
const machine = new Machine(makeMachineConfig(), makeStateConfig({ state: { current: 'operational' } }));
const requested = [];
machine.state.moveTo = async (target) => {
requested.push(target);
};
const stateMin = machine.state.movementManager.minPosition;
const stateMax = machine.state.movementManager.maxPosition;
const curveMin = machine.predictFlow.currentFxyXMin;
const curveMax = machine.predictFlow.currentFxyXMax;
const min = Math.max(stateMin, curveMin);
const max = Math.min(stateMax, curveMax);
await machine.setpoint(min - 100);
await machine.setpoint(max + 100);
assert.equal(requested.length, 2);
assert.equal(requested[0], min);
assert.equal(requested[1], max);
});
test('source.getStatusBadge returns error status on internal failure', () => {
// Build the full adapter, then force the source's state.getCurrentState
// to throw — the public getStatusBadge() must catch and return an
// error badge without propagating.
const { inst } = buildAdapter();
const errors = [];
inst.source.logger.error = (m) => errors.push(m);
inst.source.state.getCurrentState = () => { throw new Error('boom'); };
const status = inst.source.getStatusBadge();
assert.match(status.text, /Status Error/);
assert.equal(status.fill, 'red');
assert.equal(errors.length, 1);
});
test('measurement handlers reject incompatible units', () => {
const machine = new Machine(makeMachineConfig(), makeStateConfig({ state: { current: 'operational' } }));
assert.equal(machine.isUnitValidForType('flow', 'm3/h'), true);
assert.equal(machine.isUnitValidForType('flow', 'mbar'), false);
machine.updateMeasuredFlow(100, 'downstream', {
timestamp: Date.now(),
unit: 'mbar',
childName: 'bad-ft',
});
const measuredFlow = machine.measurements
.type('flow')
.variant('measured')
.position('downstream')
.getCurrentValue();
assert.equal(measuredFlow, null);
});