diff --git a/src/lib/longevity/motore/test-fisici.ts b/src/lib/longevity/motore/test-fisici.ts index 175ccf1..446c33c 100644 --- a/src/lib/longevity/motore/test-fisici.ts +++ b/src/lib/longevity/motore/test-fisici.ts @@ -14,6 +14,19 @@ * perché il cliente ha rimosso l'agilità dallo score il 13/08 (scale non * comparabili fra protocolli), la seconda perché non è mai chiamata da * nessun peso. + * + * ⚠️ NON COLLEGATE AL MOTORE. Queste venti funzioni sono corrette e verificate + * contro l'oracolo (`tests/longevity/motore-fisici.test.ts`), ma oggi sono + * irraggiungibili in produzione: `applicaCurva` (`motore/index.ts`) smista solo + * sulle sette curve generiche dichiarate nel vocabolario di `registro.ts` + * (`bell`, `lin_dec`, `inc_plateau`, `x10`, `x10_inv`, `decstep`, `incstep`) e + * non conosce nessun `test_id` fisico; e la sua firma prende `(voce, valore)`, + * senza modo di passare età e sesso, che quasi tutte queste funzioni + * richiedono. Agganciarle è lavoro del piano degli import, non di questa + * revisione, e richiederà di ESTENDERE il vocabolario delle curve e di far + * arrivare età/sesso fino ad `applicaCurva` — quindi non sarà "una modifica ai + * dati" nel senso in cui lo intende la spec (registro senza migrazioni): serve + * anche codice nuovo nel motore, non solo righe nuove in `registro_test`. */ import { clamp, lerp, arrotonda1 } from './curve'; diff --git a/tests/longevity/motore-caso-reale.test.ts b/tests/longevity/motore-caso-reale.test.ts index 1e00659..2e09e20 100644 --- a/tests/longevity/motore-caso-reale.test.ts +++ b/tests/longevity/motore-caso-reale.test.ts @@ -43,4 +43,39 @@ describe('un profilo con una sola area misurata', () => { } } }); + + /** + * Il caso misto: quello che la prima dashboard disegnera' davvero, non "tutto + * insufficiente". Realizza per intero la premessa del describe qui sopra — la + * composizione corporea davvero misurata dalla Wellness Tower — aggiungendo le + * due voci al registro (arrivano col piano degli import; qui bastano due righe, + * non serve aspettarle) e registrando le misure end-to-end. + */ + it('un asse valido (Composizione Corporea) e sei insufficienti, end-to-end', () => { + const db = createLongevityDb(':memory:'); + seedRegistro(db); + seedPesi(db, MODEL_VERSION); + db.prepare(`INSERT INTO soggetti (client_code, sesso) VALUES ('ISL-0003','M')`).run(); + + db.prepare( + `INSERT INTO registro_test (test_id, etichetta, tipo_valore, curva, params, asse, sotto_dominio, attivo_da) + VALUES ('wt_grasso', 'Grasso % (Wellness Tower)', 'num', 'lin_dec', ?, 'Composizione Corporea', 'grasso', '2026-01-01')` + ).run(JSON.stringify({ best: 10, worst: 40 })); + db.prepare( + `INSERT INTO registro_test (test_id, etichetta, tipo_valore, curva, params, asse, sotto_dominio, attivo_da) + VALUES ('wt_muscolo', 'Muscolo % (Wellness Tower)', 'num', 'lin_dec', ?, 'Composizione Corporea', 'muscolo', '2026-01-01')` + ).run(JSON.stringify({ best: 50, worst: 30 })); + + const s = apriSessione(db, { client_code: 'ISL-0003', data: '2026-08-22', tipo: 'checkup', eta_alla_data: 38 }); + registraMisure(db, s, 'wellness_tower', [ + { test_id: 'wt_grasso', valore_num: 15 }, + { test_id: 'wt_muscolo', valore_num: 45 }, + ]); + + const r = calcolaSessione(db, s); + expect(r.assi['Composizione Corporea'].stato).toBe('ok'); + const insufficienti = Object.entries(r.assi) + .filter(([nome, a]) => nome !== 'Composizione Corporea' && a.stato === 'insufficiente'); + expect(insufficienti.length).toBe(6); + }); }); diff --git a/tests/longevity/motore-fisici.test.ts b/tests/longevity/motore-fisici.test.ts index 5e885c9..29646eb 100644 --- a/tests/longevity/motore-fisici.test.ts +++ b/tests/longevity/motore-fisici.test.ts @@ -47,21 +47,38 @@ describe('curve dei test fisici, confrontate con l oracolo caso per caso', () => }); } - it('i sollevamenti sul peso corporeo combaciano su tutte e tre le tabelle', () => { + it('i sollevamenti sul peso corporeo combaciano su tutte e tre le tabelle DEFINITE NEL CODICE', () => { + // Prima versione di questo test: passava alla funzione le tabelle lette dagli + // argomenti del riferimento (c.args), non le costanti TIERS_* definite nel + // codice — le sei tabelle non erano confrontate contro l'oracolo da nessuno. + // Qui si usano SOLO le costanti del modulo: se una venisse alterata, nessun + // caso combacerebbe piu' con gli argomenti del riferimento e il test cade. const casi = RIF.casi.filter((c) => c.fn === 'score_bw_ratio_lift'); expect(casi.length).toBeGreaterThan(50); - const perTabella = (nome: string) => - ({ bench: [F.TIERS_BENCH_M, F.TIERS_BENCH_F], squat: [F.TIERS_SQUAT_M, F.TIERS_SQUAT_F], - row: [F.TIERS_ROW_M, F.TIERS_ROW_F] } as Record)[nome]; + + const TABELLE: [string, [number, number][], [number, number][]][] = [ + ['bench', F.TIERS_BENCH_M, F.TIERS_BENCH_F], + ['squat', F.TIERS_SQUAT_M, F.TIERS_SQUAT_F], + ['row', F.TIERS_ROW_M, F.TIERS_ROW_F], + ]; + const perTabella: Record = { bench: 0, squat: 0, row: 0 }; + for (const c of casi) { - const [carico, peso, rip, tiersM, tiersF, sesso] = c.args as [number, number, number, unknown, unknown, 'M' | 'F']; - const ottenuto = F.scoreSollevamentoSuPeso( - carico, peso, rip, - tiersM as [number, number][], tiersF as [number, number][], sesso - ); - expect(ottenuto, `carico ${carico} ${sesso}`).toBeCloseTo(c.atteso, 1); + const [carico, peso, rip, tiersMRif, , sesso] = c.args as [number, number, number, unknown, unknown, 'M' | 'F']; + // Identifica QUALE tabella del codice questo caso sta esercitando confrontando + // gli argomenti del riferimento con le costanti vere — non il contrario. + const trovata = TABELLE.find(([, tm]) => JSON.stringify(tm) === JSON.stringify(tiersMRif)); + expect(trovata, `nessuna tabella del codice combacia con gli argomenti del caso: carico ${carico} ${sesso}`).toBeDefined(); + const [nome, tiersM, tiersF] = trovata!; + perTabella[nome]++; + + const ottenuto = F.scoreSollevamentoSuPeso(carico, peso, rip, tiersM, tiersF, sesso); + expect(ottenuto, `${nome} carico ${carico} ${sesso}`).toBeCloseTo(c.atteso, 1); } - expect(perTabella('bench')).toBeTruthy(); + // Tutte e tre le tabelle sono state esercitate davvero, non solo dichiarate. + expect(perTabella.bench).toBeGreaterThan(0); + expect(perTabella.squat).toBeGreaterThan(0); + expect(perTabella.row).toBeGreaterThan(0); }); it('le due funzioni escluse dal porting non esistono nel modulo', () => {