'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const EventEmitter = require('events'); const Settler = require('../../src/specificClass'); const NUM_SPECIES = 13; function makeSettler() { return new Settler({ general: { name: 'TestSettler', id: 'settler-test-1', logging: { enabled: false, logLevel: 'error' } }, functionality: { softwareType: 'settler', positionVsParent: 'downstream' }, }); } test('constructor sets default state', () => { const s = makeSettler(); assert.equal(s.F_in, 0); assert.deepEqual(s.Cs_in, new Array(NUM_SPECIES).fill(0)); assert.equal(s.C_TS, 2500); assert.equal(s.upstreamReactor, null); assert.equal(s.returnPump, null); }); test('getEffluent conserves total flow (mass balance)', () => { const s = makeSettler(); s.F_in = 200; s.C_TS = 3000; const C = new Array(NUM_SPECIES).fill(5); C[12] = 2000; s.Cs_in = C; const [eff, sur, ret] = s.getEffluent; assert.equal(eff.topic, 'Fluent'); assert.ok(Math.abs(eff.payload.F + sur.payload.F + ret.payload.F - 200) < 1e-6); for (let i = 7; i <= 12; i++) assert.equal(eff.payload.C[i], 0); }); test('getEffluent clamps F_s to F_in when X_TS exceeds C_TS', () => { const s = makeSettler(); s.F_in = 100; s.C_TS = 1000; s.Cs_in = new Array(NUM_SPECIES).fill(10); s.Cs_in[12] = 5000; const [eff] = s.getEffluent; assert.equal(eff.payload.F, 0); }); test('reactor stateChange pulls effluent (preserves _connectReactor integration)', () => { const s = makeSettler(); let outputChanges = 0; s.emitter.on('output-changed', () => outputChanges++); const reactor = { config: { general: { name: 'r', id: 'r-1' }, functionality: { positionVsParent: 'upstream' } }, emitter: new EventEmitter(), measurements: { emitter: new EventEmitter() }, // Mirror the array shape the reactor produces in production. get getEffluent() { const C = new Array(NUM_SPECIES).fill(2); C[12] = 3500; return [{ topic: 'Fluent', payload: { inlet: 0, F: 150, C } }]; }, }; s.router.dispatchRegister(reactor, 'reactor'); reactor.emitter.emit('stateChange'); assert.equal(s.upstreamReactor, reactor); assert.equal(s.F_in, 150); assert.equal(s.Cs_in[12], 3500); assert.ok(outputChanges >= 1, 'reactor stateChange should trigger output-changed'); }); test('reactor stateChange handles single-envelope getEffluent (not array)', () => { const s = makeSettler(); const reactor = { config: { general: { name: 'r', id: 'r-1' }, functionality: { positionVsParent: 'upstream' } }, emitter: new EventEmitter(), measurements: { emitter: new EventEmitter() }, get getEffluent() { const C = new Array(NUM_SPECIES).fill(1); C[12] = 800; return { topic: 'Fluent', payload: { inlet: 0, F: 42, C } }; }, }; s.router.dispatchRegister(reactor, 'reactor'); reactor.emitter.emit('stateChange'); assert.equal(s.F_in, 42); assert.equal(s.Cs_in[12], 800); }); test('TSS measurement updates C_TS via _updateMeasurement', () => { const s = makeSettler(); s._updateMeasurement('quantity (tss)', 7000); assert.equal(s.C_TS, 7000); }); test('downstream machine becomes returnPump', () => { const s = makeSettler(); const pump = { config: { general: { name: 'pump', id: 'p-1' }, functionality: { positionVsParent: 'downstream' } }, measurements: { emitter: new EventEmitter() }, }; s.router.dispatchRegister(pump, 'machine'); assert.equal(s.returnPump, pump); assert.equal(pump.upstreamSource, s); }); test('getStatusBadge returns idle when F_in=0, green when flowing', () => { const s = makeSettler(); const idle = s.getStatusBadge(); assert.equal(idle.fill, 'blue'); s.F_in = 100; s.C_TS = 5000; const C = new Array(NUM_SPECIES).fill(10); C[12] = 3000; s.Cs_in = C; const active = s.getStatusBadge(); assert.equal(active.fill, 'green'); assert.ok(active.text.includes('F_in')); });