longevity: la prova sul profilo con una sola area misurata

Task 6 - la prova sul caso reale documentato dal cliente (dati sintetici,
nessuna persona reale). Il test smontava un difetto nel task 5: RisultatoSessione
esponeva fitnessAge come Punteggio invece che number|null (come lo produce
calcolaFitnessAge in cascata.ts), quindi un fitnessAge insufficiente non era mai
`null`. Corretto in motore/index.ts, con fitnessAgeCopertura a fianco per non
perdere l'informazione che salvaScore scrive in `score`.
This commit is contained in:
2026-08-22 10:07:28 +02:00
parent ac8b377f84
commit d22f88ad71
2 changed files with 61 additions and 7 deletions
+15 -7
View File
@@ -65,11 +65,19 @@ export function applicaCurva(voce: VoceRegistro, valore: number): number | null
}
}
/** Esito del calcolo di una sessione: i sette assi, i tre macro-score, la Fitness Age. */
/**
* Esito del calcolo di una sessione: i sette assi, i tre macro-score, la Fitness Age.
* `fitnessAge` è `number | null` — non `Punteggio` come gli assi e i macro-score — perché
* e' cosi' che la produce `calcolaFitnessAge` in `cascata.ts` (l'oracolo del cliente la
* tratta come un numero singolo, non come un contenitore stato/valore): `null` quando il
* composito e' insufficiente. `fitnessAgeCopertura` porta la copertura del composito, che
* altrimenti andrebbe persa: serve a `salvaScore` per scrivere la riga 'fitness_age'.
*/
export type RisultatoSessione = {
assi: Record<string, Punteggio>;
macro: Record<string, Punteggio>;
fitnessAge: Punteggio;
fitnessAge: number | null;
fitnessAgeCopertura: number;
modelVersion: string;
questVersion: string | null;
};
@@ -152,12 +160,9 @@ export function calcolaSessione(db: Database.Database, sessioneId: number): Risu
pesiDi(db, MODEL_VERSION, 'fitness_age', 'fitness_age'),
vociFitnessAge
);
const fitnessAgePunteggio: Punteggio = composito.stato === 'insufficiente'
? { stato: 'insufficiente', copertura: composito.copertura }
: { stato: 'ok', valore: fitnessAge as number, copertura: composito.copertura };
return {
assi, macro, fitnessAge: fitnessAgePunteggio,
assi, macro, fitnessAge, fitnessAgeCopertura: composito.copertura,
modelVersion: MODEL_VERSION, questVersion: sessione.quest_version,
};
}
@@ -189,7 +194,10 @@ export function salvaScore(db: Database.Database, sessioneId: number, risultato:
const tx = db.transaction(() => {
for (const [nome, p] of Object.entries(risultato.assi)) riga('asse', nome, p);
for (const [nome, p] of Object.entries(risultato.macro)) riga('macro', nome, p);
riga('fitness_age', 'fitness_age', risultato.fitnessAge);
const fitnessAgePunteggio: Punteggio = risultato.fitnessAge === null
? { stato: 'insufficiente', copertura: risultato.fitnessAgeCopertura }
: { stato: 'ok', valore: risultato.fitnessAge, copertura: risultato.fitnessAgeCopertura };
riga('fitness_age', 'fitness_age', fitnessAgePunteggio);
});
tx();
}