import { describe, it, expect } from 'vitest'; import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import * as F from '../../src/lib/longevity/motore/test-fisici'; const RIF = JSON.parse( readFileSync(join(process.cwd(), 'tests/longevity/riferimento/riferimento.json'), 'utf8') ) as { casi: { fn: string; args: unknown[]; atteso: number }[] }; /** Ogni funzione dell'oracolo con la sua gemella in TypeScript. */ const COPPIE: [string, (...a: never[]) => number][] = [ ['score_fat_percent', F.scoreGrassoPercento as never], ['score_muscle_percent', F.scoreMuscoloPercento as never], ['score_whr', F.scoreWhr as never], ['score_vo2max', F.scoreVo2max as never], ['score_spo2', F.scoreSpo2 as never], ['score_blood_pressure', F.scorePressione as never], ['score_hrr', F.scoreRecuperoCardiaco as never], ['score_handgrip', F.scoreHandgrip as never], ['score_pushup', F.scorePushup as never], ['score_flexed_arm_hang', F.scoreTrazioneIsometrica as never], ['score_sit_to_stand_1min', F.scoreSitToStand as never], ['score_plank', F.scorePlank as never], ['score_back_scratch', F.scoreBackScratch as never], ['score_shoulder_mobility_wt', F.scoreMobilitaSpalla as never], ['score_flamingo', F.scoreFlamingo as never], ['score_sit_and_reach', F.scoreSitAndReach as never], ['vo2max_from_step_test', F.vo2maxDaStepTest as never], ['vo2max_from_2km_walk', F.vo2maxDa2kmWalk as never], ['vo2max_from_mutt', F.vo2maxDaMutt as never], ['vo2max_from_milfit', F.vo2maxDaMilfit as never], ]; describe('curve dei test fisici, confrontate con l oracolo caso per caso', () => { for (const [nomePython, fnTs] of COPPIE) { it(`${nomePython} combacia su tutti i casi del riferimento`, () => { const casi = RIF.casi.filter((c) => c.fn === nomePython); expect(casi.length, `nessun caso per ${nomePython}: la griglia non lo copre`).toBeGreaterThan(0); const divergenti: string[] = []; for (const c of casi) { const ottenuto = (fnTs as (...a: unknown[]) => number)(...c.args); if (Math.abs(ottenuto - c.atteso) > 0.05) { divergenti.push(`${nomePython}(${c.args.join(', ')}): atteso ${c.atteso}, ottenuto ${ottenuto}`); } } expect(divergenti.slice(0, 5).join('\n')).toBe(''); }); } 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 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, 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); } // 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', () => { // Agilita: il cliente l'ha esclusa dallo score il 13/08 (scale non comparabili fra protocolli). expect((F as Record).scoreAgilita).toBeUndefined(); expect((F as Record).scoreAgilityMs).toBeUndefined(); // score_generic_range_local: definita in fondo all'oracolo ma non chiamata da nessun peso. expect((F as Record).scoreGenericRangeLocal).toBeUndefined(); }); });