const test = require('node:test'); const assert = require('node:assert/strict'); const { UnitPolicy } = require('generalFunctions'); const { normalizeMachineCurve, normalizeCurveSection, convertUnitValue, } = require('../../src/curves/curveNormalizer'); function makePolicy() { return UnitPolicy.declare({ canonical: { pressure: 'Pa', flow: 'm3/s', power: 'W', temperature: 'K' }, output: { pressure: 'mbar', flow: 'm3/h', power: 'kW', temperature: 'C' }, curve: { pressure: 'mbar', flow: 'm3/h', power: 'kW', control: '%' }, }); } function captureLogger() { const warns = []; return { warn: (m) => warns.push(m), warns, }; } test('normalizeMachineCurve: rejects raw without nq/np', () => { const policy = makePolicy(); assert.throws(() => normalizeMachineCurve(null, policy), /missing required nq\/np/); assert.throws(() => normalizeMachineCurve({ nq: { 700: { x: [0], y: [0] } } }, policy), /missing required nq\/np/); assert.throws(() => normalizeMachineCurve({ np: { 700: { x: [0], y: [0] } } }, policy), /missing required nq\/np/); }); test('normalizeMachineCurve: converts pressure mbar -> Pa and flow m3/h -> m3/s', () => { const policy = makePolicy(); const raw = { nq: { 1000: { x: [0, 100], y: [0, 3600] }, // 3600 m3/h = 1 m3/s }, np: { 1000: { x: [0, 100], y: [0, 1] }, // 1 kW = 1000 W }, }; const out = normalizeMachineCurve(raw, policy); // 1000 mbar = 100000 Pa const pressureKey = Object.keys(out.nq)[0]; assert.equal(Number(pressureKey), 100000); assert.ok(Math.abs(out.nq[pressureKey].y[1] - 1) < 1e-9, `expected 1 m3/s got ${out.nq[pressureKey].y[1]}`); assert.ok(Math.abs(out.np[pressureKey].y[1] - 1000) < 1e-6, `expected 1000 W got ${out.np[pressureKey].y[1]}`); }); test('normalizeCurveSection: warns on cross-pressure median > 3x jump', () => { const logger = captureLogger(); const section = { 1000: { x: [0, 50, 100], y: [0, 5, 10] }, // median 5 1100: { x: [0, 50, 100], y: [0, 50, 100] }, // median 50 (10x jump) }; normalizeCurveSection(section, 'm3/h', 'm3/h', 'mbar', 'mbar', 'nq', logger); const hit = logger.warns.find((w) => /Curve anomaly/.test(w)); assert.ok(hit, `expected a Curve anomaly warning, got: ${JSON.stringify(logger.warns)}`); assert.match(hit, /pressure 1100/); }); test('normalizeCurveSection: does not warn on smooth progressions', () => { const logger = captureLogger(); const section = { 1000: { x: [0, 50, 100], y: [0, 5, 10] }, 1100: { x: [0, 50, 100], y: [0, 6, 11] }, }; normalizeCurveSection(section, 'm3/h', 'm3/h', 'mbar', 'mbar', 'nq', logger); assert.equal(logger.warns.filter((w) => /Curve anomaly/.test(w)).length, 0); }); test('normalizeCurveSection: throws when x/y length mismatch', () => { assert.throws( () => normalizeCurveSection({ 1000: { x: [0, 50], y: [0, 5, 10] } }, 'm3/h', 'm3/s', 'mbar', 'Pa', 'nq', null), /Invalid nq section/ ); }); test('convertUnitValue: identity when units match or missing', () => { assert.equal(convertUnitValue(42, 'm3/h', 'm3/h'), 42); assert.equal(convertUnitValue(42, null, null), 42); }); test('convertUnitValue: throws on non-finite input', () => { assert.throws(() => convertUnitValue('not-a-number', 'm3/h', 'm3/s', 'test'), /not finite/); });