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>
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
# 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).
|
||||
Reference in New Issue
Block a user