const {test} = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs'); const vm = require('node:vm'); const stats = require('../src/gui_common/js/estadisticas.js'); const row = (time, values = {}) => ({fecha: `29/09/2026 ${time}`, ...values}); const metric = (result, key = 'temperatura_externa') => result.series.find(s => s.key === key); test('empty history and malformed input', () => { assert.equal(stats.analyze('[]').dated, 0); assert.throws(() => stats.analyze('{}')); assert.throws(() => stats.analyze('invalid')); }); test('one unchanged record is enough', () => { const result = stats.analyze([row('12:00:00', {temperatura_externa: 0, cambio: false})]); assert.equal(result.changed, 0); assert.equal(metric(result).mean, 0); assert.equal(metric(result).count, 1); }); test('missing values do not become zero and latest remains missing', () => { const result = stats.analyze([row('12:00:00', {temperatura_externa: '-4'}), row('12:01:00', {temperatura_externa: 0}), row('12:02:00', {temperatura_externa: ''}), row('12:03:00', {temperatura_externa: null})]); assert.equal(metric(result).mean, -2); assert.equal(metric(result).count, 2); assert.equal(metric(result).last, null); assert.deepEqual(metric(result).points.map(p => p.value), [-4, 0, null, null]); }); test('orders dates and excludes malformed rows without mutating input', () => { const input = [row('13:00:00', {temperatura_externa: 20}), null, {}, row('12:00:00', {temperatura_externa: 10, cambio: true})]; const before = JSON.stringify(input); const result = stats.analyze(input); assert.equal(result.discarded, 2); assert.equal(result.changed, 1); assert.equal(metric(result).last, 20); assert.equal(result.first, '29/09/2026 12:00:00'); assert.equal(JSON.stringify(input), before); }); test('imperial temperature and wind conversion preserves absent measurements', () => { const result = stats.analyze([row('12:00:00', {temperatura_externa: 0, viento_velocidad: 100})], 'SM'); assert.equal(metric(result).mean, 32); assert.equal(metric(result).unit, '°F'); assert.equal(metric(result, 'viento_velocidad').unit, 'mph'); assert.ok(Math.abs(metric(result, 'viento_velocidad').mean - 62.1371192) < 0.000001); assert.equal(metric(result, 'temperatura_interna').mean, null); }); test('rain accumulators are not summed', () => { const result = stats.analyze([row('12:00:00', {lluvia_acu_diaria: 10}), row('13:00:00', {lluvia_acu_diaria: 11})]); assert.equal(metric(result, 'lluvia_acu_diaria').last, 11); assert.equal(metric(result, 'lluvia_acu_diaria').max, 11); }); test('both production adapters load the proper data source', async () => { for (const mode of ['gui', 'gui_web']) { let request; const sandbox = { configGeneralEMA: {ConfGeneral: {Modo: 'Recolector_IP', SistemaMetrico: 'SI'}}, document: {getElementById: id => id}, EstadisticasUI: {mount: async (root, options) => {assert.equal(root, 'estadisticas'); assert.equal(options.mode, 'Recolector_IP'); return options.load();}}, fsconf: {readFile: (file, encoding, cb) => {request = file; cb(null, '[]');}}, $: {ajax: options => {request = options.url; return Promise.resolve('[]');}} }; const script = fs.readFileSync(`src/${mode}/controlador/funcionesgenerales.js`, 'utf8').split('async function graficoEstadisticas()')[1]; vm.runInNewContext('async function graficoEstadisticas()' + script, sandbox); assert.equal(await sandbox.graficoEstadisticas(), '[]'); assert.equal(request, mode === 'gui' ? './logs/registro_diario.log' : '/VerHistorico'); } });