const {test} = require('node:test'); const assert = require('node:assert/strict'); const fs = require('node:fs/promises'); const path = require('node:path'); const os = require('node:os'); const vm = require('node:vm'); const model = require('../src/gui_common/js/configuracion-modelo.js'); const {guardarConfiguracion} = require('../src/core/configuracion-store.js'); const {registrarConfiguracion} = require('../src/gui_web/configuracion-api.js'); const express = require('../src/node_modules/express'); const fixture = require('./fixtures/configuracion.cjs'); test('el formulario conserva propiedades ajenas y no modifica el objeto original', () => { const old = fixture(), fields = model.read(old); fields.NombreEstacion = 'Nuevo'; fields.inicio_auto = 'Activado'; const next = model.build(old, fields); assert.equal(old.ConfGeneral.NombreEstacion, 'Estación prueba'); assert.equal(next.ConfGeneral.inicio_automatico, true); assert.deepEqual(next.Redes, old.Redes); assert.equal(next.ConfGeneral.conservar, old.ConfGeneral.conservar); assert.deepEqual(model.validate(fields), {}); }); test('validación condicional de visor, IP y SMTP', () => { const fields = model.read(fixture()); fields.Modo = 'Visor'; assert(model.validate(fields).Llave_R); fields.Llave_R = 'llave'; assert.deepEqual(model.validate(fields), {}); fields.Modo = 'Recolector_IP'; fields.r_puerto = '65536'; fields.r_path = 'datos'; assert(model.validate(fields).r_puerto); assert(model.validate(fields).r_path); fields.r_puerto = '65535'; fields.r_path = '/datos'; assert.deepEqual(model.validate(fields), {}); fields.Tipo_SMTP = 'Manual'; fields.Usuario_SMTP = 'invalido'; const errors = model.validate(fields); assert(errors.Usuario_SMTP && errors.Contrasenia_SMTP && errors.Server_SMTP && errors.Email_receptor); }); test('guardar y volver a leer, rechazo sin alterar el archivo y limpieza de temporales', async t => { const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'carpincho-config-')); t.after(() => fs.rm(dir, {recursive: true, force: true})); const file = path.join(dir, 'ConfiguracionGeneral.js'); const config = fixture(); config.ConfGeneral.NombreEstacion = 'Prueba " especial'; await guardarConfiguracion(config, file); const previous = await fs.readFile(file, 'utf8'); const sandbox = {module: {exports: {}}}; vm.runInNewContext(previous, sandbox); assert.deepEqual(JSON.parse(JSON.stringify(sandbox.module.exports)), config); config.ConfGeneral.debugTamanio = -1; await assert.rejects(guardarConfiguracion(config, file)); assert.equal(await fs.readFile(file, 'utf8'), previous); await assert.rejects(guardarConfiguracion(fixture(), dir)); assert.deepEqual(await fs.readdir(dir), ['ConfiguracionGeneral.js']); }); test('HTTP espera la escritura, informa rechazos y permite reintentar', async t => { const app = express(); app.use(express.json()); let release, fail = false, reload = 0; registrarConfiguracion(app, {guardar: async config => { assert(config.ConfGeneral); if (fail) throw new Error('disco no disponible'); await new Promise(resolve => {release = resolve;}); }, reconfigurar: () => {reload++;}}); const server = await new Promise(resolve => {const s = app.listen(0,'127.0.0.1',()=>resolve(s));}); t.after(() => server.close()); const url = `http://127.0.0.1:${server.address().port}/GuardarConf`; const request = () => fetch(url,{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({configuracion:fixture()})}); let replied = false; const pending = request().then(response => {replied = true; return response.json();}); while (!release) await new Promise(resolve => setTimeout(resolve, 5)); assert.equal(replied, false); assert.equal(reload, 0); release(); assert.deepEqual(await pending,{ok:true}); assert.equal(reload,1); fail = true; const failed = await request(); assert.equal(failed.status,500); assert.equal((await failed.json()).ok,false); fail = false; release = null; const retry = request(); while (!release) await new Promise(resolve => setTimeout(resolve,5)); release(); assert.equal((await (await retry).json()).ok,true); }); test('la reconfiguración actualiza el objeto compartido y los parámetros del recolector', async () => { const source = await fs.readFile(path.join(__dirname,'../src/core/ema.js'),'utf8'); const start = source.indexOf(' Reconfigurar: function'); const end = source.indexOf(' Iniciar: function',start); const current = fixture(); const context = {configGeneralEMA:current, APP:{Configuracion:{},RegistrarLog() {}},recolector:{conf:{}},Correo:{EnviarEmail(){throw new Error('Unexpected email');}}}; vm.runInNewContext('handler = ({'+source.slice(start,end)+'}).Reconfigurar;',context); const next = fixture(); next.ConfGeneral.Tiempo_de_Recoleccion=30;next.ConfGeneral.UTC=2;next.ConfGeneral.Modo='Recolector_IP'; context.handler(next); assert.equal(current.ConfGeneral.Tiempo_de_Recoleccion,30); assert.equal(context.APP.Configuracion.TiempoContinuo,30); assert.equal(context.APP.Configuracion.Modo,'Recolector_IP'); assert.equal(context.APP.Configuracion.utc,2); });