const test = require('node:test'); const assert = require('node:assert/strict'); const NodeClass = require('../../src/nodeClass'); const { makeNodeStub } = require('../helpers/factories'); test('_tick emits source effluent on process output', () => { const inst = Object.create(NodeClass.prototype); const node = makeNodeStub(); inst.node = node; inst._output = { formatMsg() { return null; } }; inst.source = { get getEffluent() { return { topic: 'Fluent', payload: { inlet: 0, F: 1, C: [] }, timestamp: 1 }; }, }; inst._tick(); assert.equal(node._sent.length, 1); assert.equal(node._sent[0][0].topic, 'Fluent'); assert.equal(node._sent[0][1], null); assert.equal(node._sent[0][2], null); }); test('_tick emits reactor telemetry on influx output', () => { const inst = Object.create(NodeClass.prototype); const node = makeNodeStub(); let captured = null; inst.node = node; inst.config = { functionality: { softwareType: 'reactor' }, general: { id: 'reactor-node-1' } }; inst._output = { formatMsg(output, config, format) { captured = { output, config, format }; return { topic: 'reactor_reactor-node-1', payload: { measurement: 'reactor_reactor-node-1', fields: output } }; } }; inst.source = { temperature: 19.5, get getGridProfile() { return null; }, get getEffluent() { return { topic: 'Fluent', payload: { inlet: 0, F: 42, C: [2.1, 30, 100, 16, 0, 1, 8, 25, 75, 1500, 0, 15, 2500] }, timestamp: 1 }; }, }; inst._tick(); assert.equal(node._sent.length, 1); assert.equal(node._sent[0][0].topic, 'Fluent'); assert.equal(node._sent[0][1].topic, 'reactor_reactor-node-1'); assert.equal(captured.format, 'influxdb'); assert.equal(captured.output.flow_total, 42); assert.equal(captured.output.temperature, 19.5); assert.equal(captured.output.S_O, 2.1); assert.equal(captured.output.S_NH, 16); assert.equal(captured.output.X_TS, 2500); }); test('_startTickLoop schedules periodic tick after startup delay', () => { const inst = Object.create(NodeClass.prototype); const delays = []; const intervals = []; let tickCount = 0; inst._tick = () => { tickCount += 1; }; const originalSetTimeout = global.setTimeout; const originalSetInterval = global.setInterval; global.setTimeout = (fn, ms) => { delays.push(ms); fn(); return 10; }; global.setInterval = (fn, ms) => { intervals.push(ms); fn(); return 22; }; try { inst._startTickLoop(); } finally { global.setTimeout = originalSetTimeout; global.setInterval = originalSetInterval; } assert.deepEqual(delays, [1000]); assert.deepEqual(intervals, [1000]); assert.equal(inst._tickInterval, 22); assert.equal(tickCount, 1); }); test('_attachCloseHandler clears tick interval and calls done callback', () => { const inst = Object.create(NodeClass.prototype); const node = makeNodeStub(); inst.node = node; inst._tickInterval = 55; const cleared = []; const originalClearInterval = global.clearInterval; global.clearInterval = (id) => { cleared.push(id); }; let doneCalled = 0; try { inst._attachCloseHandler(); node._handlers.close(() => { doneCalled += 1; }); } finally { global.clearInterval = originalClearInterval; } assert.deepEqual(cleared, [55]); assert.equal(doneCalled, 1); });