Revisión de lo entregado ayer sin dar nada por hecho. Aparecieron dos defectos reales y un vacío en las pruebas que los dejaba pasar. 1. DOBLE ANUNCIO (voz). En Chrome, cancel() dispara 'error' (interrupted) sobre la locución vieja, y ese handler volvía a lanzar el reintento: dos voces superpuestas diciendo lo mismo, intermitente — otra fuente del "entrecortado". Cada locución toma ahora un token de generación; si al dispararse un evento ya no es la vigente (la superó un reintento o el anuncio siguiente), sus handlers solo sueltan el anclaje y callan. De paso la bitácora deja de registrar 'end' falsos de locuciones canceladas. 2. FUGA ENTRE FICHAS (RIPS). La respuesta del sondeo puede llegar después de que la recepcionista cambió de paciente: los exámenes de uno se cargaban en la ficha del siguiente. Ahora se compara la cédula de la respuesta con la de quien está en pantalla y, si no coincide, se descarta y se detiene. 3. EL SIMULADOR MENTÍA POR OMISIÓN. No imitaba que cancel() interrumpe con 'error', por eso el defecto 1 pasó las pruebas. Ahora sí lo hace, y además modela los dos modos reales de fallo de Chrome: el speak descartado en silencio (se reintenta) y la voz eternamente en pending (se tolera: es indistinguible de una voz remota lenta, y cortarla fue el defecto del entrecortado original). Batería completa: 62 verificaciones en 4 suites, todas en verde. test_tv_voz.js 16 incluye: exactamente 1 reintento, sin fantasmas test_rips_sondeo.js 9 incluye: el guardián de ficha existe y corre antes test_rips_ventana.php 6 test_bsuid.php 31 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
119 lines
4.9 KiB
JavaScript
119 lines
4.9 KiB
JavaScript
/**
|
|
* scripts/test_rips_sondeo.js — node scripts/test_rips_sondeo.js
|
|
*
|
|
* Pruebas del sondeo RIPS contra el código real de recepcion.php.
|
|
*
|
|
* El defecto que esto vigila: la consulta a RIPS se hacía UNA vez al vincular
|
|
* al paciente, y en 7 días 547 de 549 registros llegaron después de esa única
|
|
* consulta. El sondeo reintenta cada 10 s hasta 10 minutos y debe detenerse
|
|
* al encontrar, al limpiar la ficha, o al agotar los intentos.
|
|
*/
|
|
const fs = require('fs');
|
|
let fallos = 0;
|
|
const ok = (d, c) => { console.log((c ? ' ok ' : ' FALLA ') + d); if (!c) fallos++; };
|
|
|
|
const src = fs.readFileSync(__dirname + '/../modules/turnero/views/recepcion.php', 'utf8')
|
|
.replace(/<\?(php|=)[\s\S]*?\?>/g, '"PHP"');
|
|
|
|
// Se extraen exactamente las tres piezas del control de sondeo
|
|
const m = src.match(/let _ripsSondeoId[\s\S]*?function detenerSondeoRips\(\) \{[\s\S]*?\n\}/);
|
|
if (!m) { console.log(' FALLA no se encontró el bloque de sondeo'); process.exit(1); }
|
|
|
|
function armar() {
|
|
let consultas = 0;
|
|
const intervalos = new Map();
|
|
let proximoId = 1;
|
|
const setIntervalFake = (fn, ms) => { const id = proximoId++; intervalos.set(id, { fn, ms }); return id; };
|
|
const clearIntervalFake = (id) => intervalos.delete(id);
|
|
|
|
const api = new Function(
|
|
'setInterval', 'clearInterval', 'INTERVALOS', 'CONTADOR',
|
|
'let _ripsData = null;' +
|
|
'const consultarExamenesRips = (c) => { CONTADOR.n++; };' +
|
|
m[0] + `;
|
|
return {
|
|
iniciar: iniciarSondeoRips,
|
|
detener: detenerSondeoRips,
|
|
tic() { for (const v of [...INTERVALOS.values()]) v.fn(); },
|
|
activo() { return _ripsSondeoId !== null; },
|
|
encontrado() { _ripsData = { encontrados: [1] }; },
|
|
};`
|
|
)(setIntervalFake, clearIntervalFake, intervalos, (globalThis.__c = { n: 0 }));
|
|
return { api, contador: globalThis.__c, intervalos };
|
|
}
|
|
|
|
// ── 1. Consulta de inmediato y reintenta ────────────────────────────────
|
|
{
|
|
const { api, contador } = armar();
|
|
api.iniciar('12345678');
|
|
ok('consulta de inmediato al vincular', contador.n === 1);
|
|
api.tic(); api.tic(); api.tic();
|
|
ok('reintenta en cada ciclo mientras no encuentra', contador.n === 4);
|
|
}
|
|
|
|
// ── 2. Al encontrar, deja de sondear ────────────────────────────────────
|
|
{
|
|
const { api, contador } = armar();
|
|
api.iniciar('12345678');
|
|
api.tic();
|
|
api.encontrado();
|
|
api.tic(); // este ciclo detecta _ripsData y se apaga
|
|
const n = contador.n;
|
|
api.tic(); api.tic();
|
|
ok('al encontrar los exámenes se apaga y no consulta más',
|
|
contador.n === n && !api.activo());
|
|
}
|
|
|
|
// ── 3. Limpiar la ficha lo detiene ──────────────────────────────────────
|
|
{
|
|
const { api, contador } = armar();
|
|
api.iniciar('12345678');
|
|
api.detener();
|
|
const n = contador.n;
|
|
api.tic(); api.tic();
|
|
ok('limpiar la ficha detiene el sondeo en seco', contador.n === n && !api.activo());
|
|
}
|
|
|
|
// ── 4. Tope de intentos: no sondea para siempre ─────────────────────────
|
|
{
|
|
const { api, contador } = armar();
|
|
api.iniciar('12345678');
|
|
for (let i = 0; i < 80; i++) api.tic();
|
|
ok('se rinde tras ~60 intentos (10 min), no queda sondeando eternamente',
|
|
contador.n <= 61 && !api.activo());
|
|
}
|
|
|
|
// ── 5. Vincular a otro paciente reinicia sin duplicar intervalos ────────
|
|
{
|
|
const { api, contador, intervalos } = armar();
|
|
api.iniciar('11111111');
|
|
api.iniciar('22222222');
|
|
ok('re-vincular no deja dos sondeos corriendo a la vez', intervalos.size === 1);
|
|
}
|
|
|
|
// ── 6. Sin cédula no arranca ────────────────────────────────────────────
|
|
{
|
|
const { api, contador } = armar();
|
|
api.iniciar('');
|
|
ok('sin cédula no consulta ni deja intervalos', contador.n === 0 && !api.activo());
|
|
}
|
|
|
|
// ── 7. Guardián de ficha: una respuesta tardía no aterriza en otro paciente ──
|
|
// La respuesta del fetch puede llegar DESPUÉS de que la recepcionista cambió
|
|
// de ficha. Este caso vigila que el código compare la cédula de la respuesta
|
|
// contra la de quien está en pantalla ANTES de cargar nada. Es estructural
|
|
// (el código real usa fetch y DOM), pero si alguien borra el guardián, falla.
|
|
{
|
|
const i = src.indexOf('get_examenes_rips.php?cedula=');
|
|
const j = src.indexOf('cedulaEnPantalla', i);
|
|
const k = src.indexOf("d.fuente === 'cache'", i);
|
|
ok('el guardián de cédula existe y corre ANTES de cargar los exámenes',
|
|
i > -1 && j > -1 && k > -1 && j < k);
|
|
ok('el guardián descarta y detiene el sondeo si la ficha cambió',
|
|
/cedulaEnPantalla !== cedula\) \{ detenerSondeoRips\(\); return; \}/.test(src));
|
|
}
|
|
|
|
console.log();
|
|
console.log(fallos === 0 ? 'Todo correcto.' : fallos + ' pruebas fallaron.');
|
|
process.exit(fallos === 0 ? 0 : 1);
|