/** * Tests des ajouts v1.12.0 à la page Doses (miroir de DosesExtrasTest.kt + * ForecastDosesGuardTest.kt) : * - FIX #70 : un traitement INACTIF ne projette PLUS de créneaux * (generateForecastDoses) — l'historique reste simulé (§6.bis) ; * - routeGlyph : un émoji PAR forme de prise (IM/SC partagent la seringue * par design — différenciées par le texte de la ligne côté web) ; * - labMarkersForDose : marqueurs « prise de sang le même jour » — * avant/après la dose, jour calendaire LOCAL, frontière minuit. * * Déterministes (instants construits via Date locale, hors changement * d'heure — même discipline que misc.test.js). */ import { test, describe } from 'node:test'; import assert from 'node:assert/strict'; import { generateForecastDoses } from '../js/pk/pk-engine.js'; import { routeGlyph, treatmentsForDisplay } from '../js/data/models.js'; import { labMarkersForDose } from '../js/util/format.js'; import { makeTreatment, makeDose, DAY_MS, HOUR_MS } from './helpers.js'; const NOW = 1_790_000_000_000; describe('forecast guard for inactive treatments (fix #70, v1.12.0)', () => { test('un inactif à Posologie + historique ne projette plus rien', () => { const archived = makeTreatment({ id: 1, isActive: false, forecastIntervalDays: 7 }); const doses = [ makeDose(NOW - 8 * DAY_MS, 5, { treatmentId: 1 }), makeDose(NOW - DAY_MS, 5, { treatmentId: 1 }), ]; assert.equal(generateForecastDoses(archived, doses, NOW + 30 * DAY_MS, NOW).length, 0); }); test('un actif jumeau projette toujours (comportement inchangé)', () => { const active = makeTreatment({ id: 2, isActive: true, forecastIntervalDays: 7 }); const doses = [ makeDose(NOW - 8 * DAY_MS, 5, { treatmentId: 2 }), makeDose(NOW - DAY_MS, 5, { treatmentId: 2 }), ]; const slots = generateForecastDoses(active, doses, NOW + 30 * DAY_MS, NOW); assert.ok(slots.length > 0, 'un actif à Posologie projette'); assert.equal(slots[0].timestamp, NOW - DAY_MS + 7 * DAY_MS); }); test('archiver un traitement actif arrête ses créneaux futurs', () => { const treatment = makeTreatment({ id: 3, isActive: true, forecastIntervalDays: 7 }); const doses = [makeDose(NOW - DAY_MS, 5, { treatmentId: 3 })]; const before = generateForecastDoses(treatment, doses, NOW + 30 * DAY_MS, NOW); const after = generateForecastDoses( { ...treatment, isActive: false }, doses, NOW + 30 * DAY_MS, NOW, ); assert.ok(before.length > 0); assert.equal(after.length, 0); }); }); describe('route glyphs (v1.12.0)', () => { test('un glyphe par forme (IM/SC partagent la seringue par design)', () => { const oral = routeGlyph('ORAL'); const im = routeGlyph('INJECTION_IM'); const sc = routeGlyph('INJECTION_SUBCUT'); const gel = routeGlyph('TRANSDERMAL_GEL'); const patch = routeGlyph('TRANSDERMAL_PATCH'); const other = routeGlyph('OTHER'); assert.notEqual(oral, im); assert.notEqual(oral, gel); assert.notEqual(im, gel); assert.notEqual(gel, patch); assert.notEqual(patch, other); assert.equal(im, sc, 'IM/SC : même seringue (texte de ligne distinct côté web)'); }); test('route inconnue/null → glyphe OTHER', () => { assert.equal(routeGlyph(null), routeGlyph('OTHER')); assert.equal(routeGlyph(undefined), routeGlyph('OTHER')); }); }); describe('lab markers per dose (v1.12.0)', () => { // Jour calendaire LOCAL de node (hors changement d'heure) const noon = new Date(2026, 8, 27, 12, 0).getTime(); // 27 sept 2026 12:00 local const lab = (atMs) => ({ marker: 'E2', value: 100, unit: 'pg/mL', timestamp: atMs }); test('labs avant ET après la dose le même jour → les deux marqueurs', () => { const m = labMarkersForDose([lab(noon - HOUR_MS), lab(noon + HOUR_MS)], noon); assert.equal(m.before, true); assert.equal(m.after, true); }); test('lab d\'un autre jour ne marque jamais', () => { const m = labMarkersForDose([lab(noon - 24 * HOUR_MS)], noon); assert.equal(m.before, false); assert.equal(m.after, false); }); test('frontière minuit : 23:50 la veille ≠ jour suivant', () => { const lateEvening = new Date(2026, 8, 26, 23, 50).getTime(); const earlyMorning = new Date(2026, 8, 27, 0, 10).getTime(); const m = labMarkersForDose([lab(lateEvening)], earlyMorning); assert.equal(m.before, false); assert.equal(m.after, false); }); test('lab exactement à l\'heure de la dose compte comme APRÈS (convention)', () => { const m = labMarkersForDose([lab(noon)], noon); assert.equal(m.before, false); assert.equal(m.after, true); }); test('aucun lab → aucun marqueur', () => { const m = labMarkersForDose([], noon); assert.equal(m.before, false); assert.equal(m.after, false); }); });