import { describe, it, expect } from 'vitest'; import { createLongevityDb } from '../../src/lib/longevity/db'; import { seedRegistro } from '../../src/lib/longevity/registro'; import { salvaCompilazione } from '../../src/lib/longevity/questionario'; import { esportaMisure, esportaCsv } from '../../src/lib/longevity/export'; function dbConDati() { const db = createLongevityDb(':memory:'); seedRegistro(db); db.prepare(`INSERT INTO soggetti (client_code, sesso) VALUES ('ISL-0001', 'F')`).run(); salvaCompilazione(db, { client_code: 'ISL-0001', data: '2026-08-21', eta: 35, risposte: { q_ore_sonno: 7, q_riposato: 8 }, liberi: { farmaci: 'nessuno' }, }); return db; } describe('export per le statistiche', () => { it('esporta una riga per misura con codice, non con nome', () => { const db = dbConDati(); const out = esportaMisure(db); expect(out.intestazioni).toEqual( ['client_code', 'sesso', 'eta', 'data', 'tipo_sessione', 'quest_version', 'test_id', 'valore', 'unita', 'fonte', 'fuori_range'] ); expect(out.righe.length).toBe(2); expect(out.righe[0][0]).toBe('ISL-0001'); }); it('l export non contiene campi identificativi', () => { const db = dbConDati(); const testo = JSON.stringify(esportaMisure(db)); for (const vietato of ['nome', 'cognome', 'email', 'telefono', 'data_nascita']) { expect(testo).not.toContain(vietato); } }); it('i campi liberi restano fuori dall export statistico', () => { const db = dbConDati(); expect(JSON.stringify(esportaMisure(db))).not.toContain('nessuno'); }); it('produce un CSV con intestazione e una riga per misura', () => { const db = dbConDati(); const righe = esportaCsv(db).trim().split('\n'); expect(righe.length).toBe(3); // intestazione + 2 misure expect(righe[0]).toBe('client_code,sesso,eta,data,tipo_sessione,quest_version,test_id,valore,unita,fonte,fuori_range'); }); it('ogni valore sta sotto l intestazione giusta, colonna per colonna (asserzione posizionale)', () => { // Non un indice scritto a mano: l'indice si cerca per NOME nell'intestazione prodotta. // Se un domani la SELECT in export.ts viene riordinata senza riallineare INTESTAZIONI, // il valore letto a quell'indice smette di corrispondere e l'asserzione fallisce - // invece di restare verde con l'età etichettata come sesso. const db = dbConDati(); const { intestazioni, righe } = esportaMisure(db); const idx = (col: string) => { const i = intestazioni.indexOf(col); if (i < 0) throw new Error(`intestazione mancante: ${col}`); return i; }; const rigaSonno = righe.find((r) => r[idx('test_id')] === 'q_ore_sonno')!; expect(rigaSonno).toBeDefined(); expect(rigaSonno[idx('client_code')]).toBe('ISL-0001'); expect(rigaSonno[idx('sesso')]).toBe('F'); expect(rigaSonno[idx('eta')]).toBe(35); expect(rigaSonno[idx('data')]).toBe('2026-08-21'); expect(rigaSonno[idx('tipo_sessione')]).toBe('questionario'); expect(rigaSonno[idx('quest_version')]).toBe('v1.0'); expect(rigaSonno[idx('valore')]).toBe(7); // salvaCompilazione non passa 'unita' per le risposte del questionario: resta null. expect(rigaSonno[idx('unita')]).toBeNull(); expect(rigaSonno[idx('fonte')]).toBe('questionario'); expect(rigaSonno[idx('fuori_range')]).toBe(0); const rigaRiposato = righe.find((r) => r[idx('test_id')] === 'q_riposato')!; expect(rigaRiposato).toBeDefined(); expect(rigaRiposato[idx('client_code')]).toBe('ISL-0001'); expect(rigaRiposato[idx('sesso')]).toBe('F'); expect(rigaRiposato[idx('eta')]).toBe(35); expect(rigaRiposato[idx('valore')]).toBe(8); expect(rigaRiposato[idx('unita')]).toBeNull(); }); });