longevity: test unità B — struttura modulo, alternative, nota su giornale (#13)
gestionale-modulo-checkup.test.ts: ordine sezioni, pressione/SpO2 esclusi, dismesso in «Altre misure» su data vecchia, dettagli MUTT, preselezione alternativa (prima/più recente/ultimo check-up). checkup-alternative-api.test.ts: API scarta i campi dell'alternativa non scelta (radio inviata o assente), nota operatore aggiunge voce di giornale (data check-up, autore operatore), nota vuota o assente (correzione) non aggiunge nulla. Suite completa: 313 verdi, 4 rossi di piattaforma (baseline, invariati). tsc --noEmit e npm run build puliti. Refs #13 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
// Test B (#13): la struttura del modulo di check-up (strutturaCheckup) e la preselezione
|
||||
// delle alternative (alternativaSelezionata) — tutto in memoria, nessun singleton. I test
|
||||
// dell'API (campi scartati, nota->giornale) sono in checkup-alternative-api.test.ts: stesso
|
||||
// motivo di correzione-checkup-api.test.ts, l'endpoint usa il singleton getLongevityDb().
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { createLongevityDb } from '../../src/lib/longevity/db';
|
||||
import { seedRegistro, seedPesi, MODEL_VERSION } from '../../src/lib/longevity/registro';
|
||||
import {
|
||||
strutturaCheckup, alternativaSelezionata, valoriUltimoCheckup, salvaCheckup,
|
||||
type SezioneCheckup,
|
||||
} from '../../src/lib/longevity/gestionale';
|
||||
|
||||
function dbPronto(clientCode = 'ISL-0001', sesso: 'M' | 'F' = 'M') {
|
||||
const db = createLongevityDb(':memory:');
|
||||
seedRegistro(db);
|
||||
seedPesi(db, MODEL_VERSION);
|
||||
db.prepare(`INSERT INTO soggetti (client_code, sesso) VALUES (?, ?)`).run(clientCode, sesso);
|
||||
return db;
|
||||
}
|
||||
|
||||
describe('strutturaCheckup: ordine e contenuto delle sezioni', () => {
|
||||
it('le sezioni compaiono nell\'ordine del manuale: Pre-test, Mobilità, Equilibrio, Presa, Core, Forza spinta, Forza trazione, Arti inferiori, Aerobico, Strumenti', () => {
|
||||
const db = dbPronto();
|
||||
const modulo = strutturaCheckup(db, '2026-09-28');
|
||||
expect(modulo.sezioni.map((s) => s.titolo)).toEqual([
|
||||
'Pre-test', 'Mobilità', 'Equilibrio', 'Presa', 'Core', 'Forza spinta',
|
||||
'Forza trazione', 'Arti inferiori', 'Aerobico', 'Strumenti (Wellness Tower, Stress Index)',
|
||||
]);
|
||||
});
|
||||
|
||||
it('pressione e SpO2 non compaiono in nessuna sezione né in Altre misure', () => {
|
||||
const db = dbPronto();
|
||||
const modulo = strutturaCheckup(db, '2026-09-28');
|
||||
const tuttiGliId = modulo.sezioni.flatMap((s) =>
|
||||
s.tipo === 'campi' ? s.campi.map((c) => c.id)
|
||||
: s.alternative.flatMap((a) => [...a.campi.map((c) => c.id), ...(a.dettagli?.map((c) => c.id) ?? [])])
|
||||
);
|
||||
for (const id of ['f_pa_sistolica', 'f_pa_diastolica', 'f_spo2_pct']) {
|
||||
expect(tuttiGliId).not.toContain(id);
|
||||
expect(modulo.altreMisure.map((c) => c.id)).not.toContain(id);
|
||||
}
|
||||
});
|
||||
|
||||
it('su una data vecchia (prima del 28/09) un test dismesso finisce in Altre misure, mai perso', () => {
|
||||
const db = dbPronto();
|
||||
// 2026-09-27: i vecchi test_id storici sono ancora attivi (attivo_a: 2026-09-28),
|
||||
// e non compaiono in nessuna sezione di STRUTTURA_RAW (che usa solo i nuovi id).
|
||||
const modulo = strutturaCheckup(db, '2026-09-27');
|
||||
const altreIds = modulo.altreMisure.map((c) => c.id);
|
||||
expect(altreIds).toContain('f_handgrip_kg');
|
||||
expect(altreIds).toContain('f_back_scratch_cm');
|
||||
expect(altreIds).toContain('f_flamingo_cadute');
|
||||
expect(altreIds).toContain('f_hrr_1min_bpm');
|
||||
});
|
||||
|
||||
it('su una data recente (dopo il 28/09) i test dismessi non sono più attivi: spariscono anche da Altre misure', () => {
|
||||
const db = dbPronto();
|
||||
const modulo = strutturaCheckup(db, '2026-09-28');
|
||||
const altreIds = modulo.altreMisure.map((c) => c.id);
|
||||
expect(altreIds).not.toContain('f_handgrip_kg');
|
||||
});
|
||||
|
||||
it('il MUTT porta i suoi otto step FC come dettagli, non come campi normali', () => {
|
||||
const db = dbPronto();
|
||||
const modulo = strutturaCheckup(db, '2026-09-28');
|
||||
const aerobico = modulo.sezioni.find((s) => s.titolo === 'Aerobico');
|
||||
expect(aerobico?.tipo).toBe('alternative');
|
||||
if (aerobico?.tipo !== 'alternative') throw new Error('atteso alternative');
|
||||
const mutt = aerobico.alternative.find((a) => a.chiave === 'mutt')!;
|
||||
expect(mutt.dettagli?.length).toBe(8);
|
||||
expect(mutt.dettagli?.map((c) => c.id)).toContain('f_mutt_fc_7_5');
|
||||
expect(mutt.campi.map((c) => c.id)).not.toContain('f_mutt_fc_7_5');
|
||||
});
|
||||
});
|
||||
|
||||
describe('alternativaSelezionata: quale alternativa preselezionare', () => {
|
||||
it('senza corrispondenza in valori, vince la prima alternativa elencata (neofita)', () => {
|
||||
const db = dbPronto();
|
||||
const modulo = strutturaCheckup(db, '2026-09-28');
|
||||
const spinta = modulo.sezioni.find((s) => s.titolo === 'Forza spinta') as Extract<SezioneCheckup, { tipo: 'alternative' }>;
|
||||
expect(alternativaSelezionata(spinta, new Map())).toBe('manubri');
|
||||
});
|
||||
|
||||
it('con un valore di un\'alternativa più avanti nell\'elenco, quella vince', () => {
|
||||
const db = dbPronto();
|
||||
const modulo = strutturaCheckup(db, '2026-09-28');
|
||||
const spinta = modulo.sezioni.find((s) => s.titolo === 'Forza spinta') as Extract<SezioneCheckup, { tipo: 'alternative' }>;
|
||||
const valori = new Map([['f_panca_bil_carico_kg', 60]]);
|
||||
expect(alternativaSelezionata(spinta, valori)).toBe('bilanciere');
|
||||
});
|
||||
|
||||
it('valoriUltimoCheckup: vuota se il cliente non ha ancora un check-up, altrimenti le misure dell\'ultimo', () => {
|
||||
const db = dbPronto();
|
||||
expect(valoriUltimoCheckup(db, 'ISL-0001').size).toBe(0);
|
||||
|
||||
salvaCheckup(db, {
|
||||
client_code: 'ISL-0001', data: '2026-01-10', eta: 40, operatore: 'nicola',
|
||||
misure: [{ test_id: 'f_panca_bil_carico_kg', valore_num: 50 }, { test_id: 'f_panca_bil_rip', valore_num: 5 }],
|
||||
});
|
||||
const ultima = valoriUltimoCheckup(db, 'ISL-0001');
|
||||
expect(ultima.get('f_panca_bil_carico_kg')).toBe(50);
|
||||
|
||||
// un secondo check-up più recente sovrascrive quale sia "l'ultimo"
|
||||
salvaCheckup(db, {
|
||||
client_code: 'ISL-0001', data: '2026-03-10', eta: 40, operatore: 'nicola',
|
||||
misure: [{ test_id: 'f_pushup_rip', valore_num: 12 }],
|
||||
});
|
||||
const piuRecente = valoriUltimoCheckup(db, 'ISL-0001');
|
||||
expect(piuRecente.get('f_pushup_rip')).toBe(12);
|
||||
expect(piuRecente.has('f_panca_bil_carico_kg')).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user