'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const { getOutput } = require('../../src/io/output.js'); const MachineGroup = require('../../src/specificClass.js'); // Real declared unit policy so the m³/s → m³/h conversion is the production one. const unitPolicy = MachineGroup.unitPolicy; // Minimal MGC stand-in exposing exactly the surface getOutput reads. The // measurement loop is short-circuited with an empty type list so the test // isolates the demand telemetry without needing curves / CoolProp. function mockMgc(overrides = {}) { return { measurements: { getTypes: () => [] }, unitPolicy, mode: 'optimalControl', scaling: 'absolute', absDistFromPeak: 0, relDistFromPeak: 0, dynamicTotals: { flow: { min: 0.05, max: 0.25 } }, // m³/s machines: {}, operatingPoint: {}, _lastDemand: null, ...overrides, }; } test('demandFlow + demandPct emitted once a demand is resolved', () => { // Demand resolved to 0.15 m³/s inside a 0.05..0.25 envelope → midpoint = 50%. const out = getOutput(mockMgc({ _lastDemand: { canonical: 0.15, clamped: 0.15 } })); // m³/s → m³/h is ×3600. 0.15 m³/s = 540 m³/h. assert.equal(out.demandFlow, 540); assert.ok(Math.abs(out.demandPct - 50) < 1e-9, `expected ~50%, got ${out.demandPct}`); }); test('demandPct reflects the clamped setpoint, not the raw request', () => { // Operator asked for 0.40 m³/s but the envelope caps at 0.25 → 100%. const out = getOutput(mockMgc({ _lastDemand: { canonical: 0.40, clamped: 0.25 } })); assert.equal(out.demandFlow, 900); // 0.25 m³/s = 900 m³/h assert.equal(out.demandPct, 100); }); test('demandPct is 0 (never NaN) when the capacity span is zero', () => { const out = getOutput(mockMgc({ dynamicTotals: { flow: { min: 0.1, max: 0.1 } }, _lastDemand: { canonical: 0.1, clamped: 0.1 }, })); assert.equal(out.demandPct, 0); assert.ok(Number.isFinite(out.demandFlow)); }); test('turnOff demand (0) emits a zero setpoint, not absent', () => { const out = getOutput(mockMgc({ _lastDemand: { canonical: 0, clamped: 0 } })); assert.equal(out.demandFlow, 0); assert.equal(out.demandPct, 0); }); test('demand telemetry is absent before the first demand (degraded state)', () => { const out = getOutput(mockMgc({ _lastDemand: null })); assert.ok(!('demandFlow' in out), 'demandFlow must be absent pre-first-demand'); assert.ok(!('demandPct' in out), 'demandPct must be absent pre-first-demand'); // The always-on capacity fields are still present, converted to the output // flow unit (m³/h): 0.05 m³/s → 180, 0.25 m³/s → 900. assert.equal(out.flowCapacityMin, 180); assert.equal(out.flowCapacityMax, 900); }); test('flow capacity is emitted in the output unit (m³/h), matching the flow series', () => { const out = getOutput(mockMgc({ dynamicTotals: { flow: { min: 0.1, max: 0.3 } } })); assert.equal(out.flowCapacityMin, 360); // 0.1 m³/s × 3600 assert.equal(out.flowCapacityMax, 1080); // 0.3 m³/s × 3600 }); test('flow capacity falls back to 0 when the envelope is unresolved (Infinity)', () => { // Pre-first-equalize: dynamicTotals seeds min=Infinity, max=0. const out = getOutput(mockMgc({ dynamicTotals: { flow: { min: Infinity, max: 0 } } })); assert.equal(out.flowCapacityMin, 0); assert.equal(out.flowCapacityMax, 0); });