Files
InsanityLab-web/.superpowers/sdd/2026-08-22-longevity-interfaccia/review-finale-report.md
T
Adriano b913a2bf90 registri dei piani in git (AI-OS#309)
Refs gitea:Adriano/AI-OS#310

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-06 10:36:26 +02:00

242 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Review finale interfaccia Longevity — report
Repo: `/data/Sorgenti/Clienti/InsanityLab/WebSite/insanitylab-website`
Branch: `feat/longevity` (nessun checkout, nessun push)
Procedura: TDD per ogni correzione (test rosso → codice → verde), e per ogni test nuovo
un tentativo di romperlo per confermare che discrimina davvero.
## Stato di partenza (verificato prima di toccare codice)
```
$ npm test
...
Test Files 1 failed | 39 passed (40)
Tests 1 failed | 327 passed (328)
$ npx tsc --noEmit
(nessun output)
```
Il fallito era `tests/modifiche-agosto.test.ts` (fuori perimetro), atteso.
## 1. Il blocco — nessuno chiama il motore
**Test scritto per primo** (`tests/longevity/salvataggio-e2e.test.ts`): parte da
`salvaDalForm` (la funzione che chiama l'endpoint reale) con venti risposte, arriva a
`refertoDi`, non chiama mai `calcolaSessione`/`salvaScore` a mano.
Rosso prima del fix:
```
FAIL tests/longevity/salvataggio-e2e.test.ts > ... > venti risposte compilate producono un referto ...
AssertionError: expected 0 to be greater than 0
❯ tests/longevity/salvataggio-e2e.test.ts:46:26
| expect(righeScore.n).toBeGreaterThan(0);
```
**Fix**: `src/lib/longevity/vista.ts`, `salvaDalForm` — dentro un'unica `db.transaction`,
dopo `salvaCompilazione`: `salvaScore(db, sessioneId, calcolaSessione(db, sessioneId))`.
`better-sqlite3` annida automaticamente con savepoint le transazioni interne di
`salvaCompilazione` e `salvaScore`.
Verde dopo il fix, suite longevity completa: 148→ poi 162 test (vedi sotto), tutti verdi.
## 2. Il codice cliente può ancora arrivare dal browser
**Verificato il difetto del vecchio test** (regex su `body.client_code`/`corpo.client_code`,
ma il file usa la variabile `data`):
```
# iniettata la vulnerabilità vera in src/pages/api/longevity/questionario.ts:
# if (typeof data.client_code === 'string') codice = data.client_code;
$ npx vitest run tests/longevity/questionario-pagina.test.ts
Test Files 1 passed (1)
Tests 7 passed (7) ← suite tutta verde CON la vulnerabilità presente
```
**Fix strutturale**: `src/pages/api/longevity/questionario.ts` — nuovo tipo
`CorpoQuestionario` (`risposte`, `liberi`, `consensoSanitario`, `eta`; **niente**
`client_code`) e `corpoDaJson()` che estrae solo quei campi. Verificato che il tipo
blocca davvero l'errore a compile-time:
```
# aggiunta `console.log(corpo.client_code)` dopo l'estrazione:
$ npx tsc --noEmit
src/pages/api/longevity/questionario.ts(73,21): error TS2339: Property 'client_code'
does not exist on type 'CorpoQuestionario'.
```
**Nuovo test comportamentale** (sostituisce quello vacuo, in
`tests/longevity/questionario-pagina.test.ts`): chiama l'endpoint `POST` vero
(dbs file-backed via `IDENTITY_DB_PATH`/`LONGEVITY_DB_PATH`, due clienti veri) con un
`client_code` estraneo nel corpo, verifica che la sessione finisca sotto il cliente
della sessione, non quello iniettato.
Rosso con la vulnerabilità reintrodotta:
```
AssertionError: expected 'ISL-0002' to be 'ISL-0001'
```
Verde con il fix.
## 3. Il consenso: tre difetti
### (a) Consenso dato non testato
Verificato: cancellando `registraConsenso(...)` dal ramo affermativo di `salvaDalForm`,
`npx vitest run tests/longevity` restava **148/148 verdi** (nessun rosso).
**Test aggiunto** in `questionario-pagina.test.ts`: verifica 1 riga in `profilo_note`
con `campo_id='consenso_sanitario'` e `sessione_id` corretto dopo un salvataggio
affermativo. Rosso con la riga rimossa, verde col fix presente.
### (b) Consenso fuori transazione
**Test aggiunto**: `tests/longevity/consenso-atomico.test.ts`, mocka `calcolaSessione`
(via `vi.mock`) per farlo lanciare DOPO che sessione e misure sono già scritte, verifica
che sessione/misure/score/consenso tornino TUTTI a zero.
Rosso contro il codice originale (pre-fix, dove `salvaDalForm` non chiamava nemmeno il
motore):
```
AssertionError: expected [Function] to throw an error
```
(il mock di `calcolaSessione` non veniva mai invocato — prova indiretta che anche il
punto 1 era assente). Verde con il fix (transazione unica in `salvaDalForm`).
### (c) Versione sbagliata sul consenso
Aggiunta `CONSENT_VERSION` in `src/lib/longevity/questionario.ts`, accanto a
`QUEST_VERSION`. `registraConsenso` (`vista.ts`) ora scrive `consenso ${CONSENT_VERSION}`
invece di `questionario ${QUEST_VERSION}`.
**Test aggiunto**, rotto e ripristinato:
```
# tornato a QUEST_VERSION nella nota:
AssertionError: expected 'Consenso al trattamento dei dati sani…' to contain 'consenso v1.0'
Received: "...— questionario v1.0, registrato il 2026-08-22"
```
## 4. La prima apertura non comunica
Nuovo file `tests/longevity/prima-apertura.test.ts` (7 test, basati su lettura del
sorgente — non esiste un harness di rendering Astro/React in questo repo e non erano
ammesse dipendenze nuove), tutti scritti PRIMA del fix e verificati rossi:
```
$ npx vitest run tests/longevity/prima-apertura.test.ts (prima del fix)
Tests 7 failed (7)
```
Fix:
- `src/pages/longevity/io.astro` — carta Fitness Age: bordo tratteggiato +
"Copertura dati: X%" anche quando manca (nuovo campo `fitnessAgeCopertura` esposto da
`refertoDi`, `vista.ts`); numero formattato con `formattaNumero`.
- `src/components/longevity/Radar.tsx` — paragrafo di spiegazione spostato PRIMA del
grafico; aggiunta frase "Questi test si fanno in sala, con il trainer: prenota la
prossima sessione..." sopra l'elenco di ciò che manca.
- `src/components/longevity/MacroScore.astro` — riga "Copertura dati: X%" mostrata
SEMPRE (non solo quando il valore manca); valore formattato con `formattaNumero`.
- `src/lib/longevity/formato.ts` (nuovo) — `formattaNumero(n)` = `n.toLocaleString('it-IT')`,
virgola decimale. Le etichette PERFORMANCE/ENERGY/RECOVERY non sono state tradotte
(nomi di prodotto).
Ogni singola assunzione (7) è stata rotta manualmente per verificare che il test
corrispondente diventasse rosso, poi ripristinata:
```
$ npx vitest run tests/longevity/prima-apertura.test.ts (con MacroScore.astro riportato al vecchio comportamento)
Tests 2 failed | 5 passed (7)
$ npx vitest run tests/longevity/prima-apertura.test.ts (con io.astro riportato al vecchio comportamento)
Tests 3 failed | 4 passed (7)
$ npx vitest run tests/longevity/prima-apertura.test.ts (con Radar.tsx riportato al vecchio comportamento)
Tests 2 failed | 5 passed (7)
```
Tutti e 7 i sotto-controlli discriminano davvero. Ripristinato il codice fissato dopo
ogni prova (diff a zero contro la versione corretta, verificato con `diff`).
Anche `refertoDi` (dato, non solo template) ha un test dedicato in `referto.test.ts`:
`fitnessAgeCopertura` deve restare leggibile anche quando lo stato è insufficiente —
rosso prima (`undefined`), verde dopo (`0`).
## 5. Chi ha compilato non può più ricompilare
Collegamento aggiunto in fondo al referto (`io.astro`, dentro `{referto && (...)}`):
link a `/longevity/questionario`. Coperto dallo stesso file `prima-apertura.test.ts`
(ultimo describe), rosso prima del fix, verde dopo, verificato rompendolo e
ripristinandolo (vedi sopra, terzo blocco "3 failed").
## 6. Due controlli che elencano invece di guardare
### (a) Query nelle pagine
`tests/longevity/referto.test.ts`, test "nessuna pagina interroga il database da sola":
prima elencava due file a mano. Provato il buco: creata `prova-query.astro` con
`db.prepare('SELECT 1')` dentro `src/pages/longevity/`, la suite restava verde. Riscritto
per scandire **tutte** le `.astro` sotto `src/pages/longevity/` con `readdirSync`
ricorsivo. Riprovato lo stesso file spurio: ora il test va rosso; rimosso il file, torna
verde.
### (b) Giunzione identity/longevity via `createIdentityDb`
Come richiesto, **non** ho provato a chiudere il buco con un test più furbo (già tentato
e aggirato due volte, secondo il commento esistente). Ho verificato empiricamente il buco
reale: un file che chiama `createIdentityDb()` (esportata, a differenza del singleton
privato `getIdentityDb`) e apre `longevity.db` con una connessione propria — mai col nome
`getLongevityDb`/`createLongevityDb`, quindi senza mai scrivere "Longevity" con
l'iniziale maiuscola — passa indisturbato:
```
$ npx vitest run tests/longevity/anagrafica.test.ts (con il file-sonda in src/lib/longevity/)
Test Files 1 passed (1)
Tests 7 passed (7)
```
Corretto **solo il commento** in `tests/longevity/anagrafica.test.ts` per dichiarare
onestamente questo buco specifico (createIdentityDb esportata per `creaCliente`, quindi
non chiudibile come si è chiuso `getIdentityDb` senza cambiare quella funzione). Nessuna
modifica alla logica del test.
## Verifica finale
```
$ npm test
Test Files 1 failed | 43 passed (44)
Tests 1 failed | 343 passed (344)
$ npx tsc --noEmit
(nessun output — pulito)
```
Il solo fallito resta `tests/modifiche-agosto.test.ts` (fuori perimetro, atteso). 16 test
nuovi rispetto al punto di partenza (328 → 344), nessuno regredito.
## File toccati
```
M src/components/longevity/MacroScore.astro
M src/components/longevity/Radar.tsx
M src/lib/longevity/questionario.ts (+ CONSENT_VERSION)
M src/lib/longevity/vista.ts (+ transazione unica, fitnessAgeCopertura)
M src/pages/api/longevity/questionario.ts (+ CorpoQuestionario tipato)
M src/pages/longevity/io.astro
M tests/longevity/anagrafica.test.ts (solo commento)
M tests/longevity/questionario-pagina.test.ts
M tests/longevity/referto.test.ts
?? src/lib/longevity/formato.ts
?? tests/longevity/consenso-atomico.test.ts
?? tests/longevity/formato.test.ts
?? tests/longevity/prima-apertura.test.ts
?? tests/longevity/salvataggio-e2e.test.ts
```
## Dieci prove di rottura (richieste dal brief) — riepilogo
1. Vecchio test §2 (regex `body.client_code`): vulnerabilità vera iniettata → suite
verde (confermato vacuo).
2. Nuovo test §2 (comportamentale): stessa vulnerabilità iniettata → rosso.
3. Tipo `CorpoQuestionario`: `corpo.client_code` aggiunto al codice → `tsc --noEmit`
rifiuta (TS2339).
4. §1: `registraConsenso`/motore rimossi dal ramo affermativo → nuovo test rosso
(`consenso-atomico.test.ts` non lancia neppure, prova indiretta del blocco originale).
5. §3a: `registraConsenso` rimossa dal ramo affermativo → nuovo test rosso; **verificato
anche che la suite INTERA restasse verde senza il nuovo test** (vacuità originale).
6. §3c: `CONSENT_VERSION` sostituita con `QUEST_VERSION` nella nota → nuovo test rosso.
7. §4 MacroScore: copertura-sempre e formattaNumero rimossi → 2 test rossi.
8. §4 io.astro: nota Fitness Age, formattaNumero, link ricompilazione rimossi → 3 test
rossi.
9. §4/§5 Radar: spiegazione riportata sotto, invito trainer rimosso → 2 test rossi.
10. §6a: pagina spuria con `SELECT` in `src/pages/longevity/` → test riscritto la
intercetta (prima non la vedeva).
Tutte le 10 rotture sono state ripristinate immediatamente dopo la verifica; lo stato
finale del repo corrisponde al codice corretto (confermato con `diff` contro le copie
salvate prima di ogni prova, e con la suite completa verde alla fine).