import { describe, it, expect } from 'vitest'; import { createLongevityDb } from '../../src/lib/longevity/db'; import { seedRegistro } from '../../src/lib/longevity/registro'; import { salvaCompilazione, QUEST_VERSION } from '../../src/lib/longevity/questionario'; function dbPronto() { const db = createLongevityDb(':memory:'); seedRegistro(db); db.prepare(`INSERT INTO soggetti (client_code, sesso) VALUES ('ISL-0001', 'F')`).run(); return db; } describe('compilazione del questionario', () => { it('salva le risposte punteggiate come misure', () => { const db = dbPronto(); const s = salvaCompilazione(db, { client_code: 'ISL-0001', data: '2026-08-21', risposte: { q_ore_sonno: 7.5, q_riposato: 8, q_alcol_life: 3 }, }); const n = db.prepare(`SELECT COUNT(*) n FROM misure WHERE sessione_id = ? AND fonte = 'questionario'`) .get(s) as { n: number }; expect(n.n).toBe(3); }); it('marchia la compilazione con la versione del questionario', () => { const db = dbPronto(); const s = salvaCompilazione(db, { client_code: 'ISL-0001', data: '2026-08-21', risposte: { q_riposato: 6 } }); const row = db.prepare(`SELECT tipo, quest_version FROM sessioni WHERE id = ?`).get(s) as { tipo: string; quest_version: string }; expect(row.tipo).toBe('questionario'); expect(row.quest_version).toBe(QUEST_VERSION); }); it('i campi liberi vanno in profilo_note, non fra le misure', () => { const db = dbPronto(); const s = salvaCompilazione(db, { client_code: 'ISL-0001', data: '2026-08-21', risposte: { q_riposato: 6 }, liberi: { farmaci: 'nessuno', obiettivi: 'dormire meglio' }, }); const note = db.prepare(`SELECT campo_id, testo FROM profilo_note WHERE sessione_id = ? ORDER BY campo_id`) .all(s) as { campo_id: string; testo: string }[]; expect(note.map((n) => n.campo_id)).toEqual(['farmaci', 'obiettivi']); const misure = db.prepare(`SELECT test_id FROM misure WHERE sessione_id = ?`).all(s) as { test_id: string }[]; expect(misure.map((m) => m.test_id)).toEqual(['q_riposato']); }); it('ogni compilazione e un record nuovo, non un aggiornamento', () => { const db = dbPronto(); salvaCompilazione(db, { client_code: 'ISL-0001', data: '2026-08-21', risposte: { q_riposato: 6 } }); salvaCompilazione(db, { client_code: 'ISL-0001', data: '2026-09-21', risposte: { q_riposato: 9 } }); const n = db.prepare(`SELECT COUNT(*) n FROM sessioni WHERE tipo = 'questionario'`).get() as { n: number }; expect(n.n).toBe(2); }); it('una risposta con id sconosciuto fa fallire la compilazione intera', () => { const db = dbPronto(); expect(() => salvaCompilazione(db, { client_code: 'ISL-0001', data: '2026-08-21', risposte: { q_riposato: 6, q_domanda_nuova: 3 }, }) ).toThrow(/q_domanda_nuova/); const n = db.prepare(`SELECT COUNT(*) n FROM sessioni`).get() as { n: number }; expect(n.n).toBe(0); }); it('fallimento a metà non lascia traccia: sessione, misure e note sono atomiche', () => { const db = dbPronto(); // Passa due campi liberi: il primo valido (farmaci), il secondo con null (obiettivi). // Il primo insert su profilo_note avviene (per farmaci: 'nessuno'), poi fallisce su // obiettivi quando tenta di eseguire testo.trim() su un null, lanciando un TypeError. // Il rollback della transazione deve annullare anche il primo insert già avvenuto. expect(() => salvaCompilazione(db, { client_code: 'ISL-0001', data: '2026-08-21', risposte: { q_riposato: 6 }, liberi: { farmaci: 'nessuno', obiettivi: null as any }, // null causa TypeError su .trim() }) ).toThrow(); // Verifica che la transazione sia stata rollback: niente sessione, niente misure, niente note. // Se il rollback non funzionasse, almeno il record di farmaci resterebbe in profilo_note. const sessioni = db.prepare(`SELECT COUNT(*) n FROM sessioni`).get() as { n: number }; const misure = db.prepare(`SELECT COUNT(*) n FROM misure`).get() as { n: number }; const note = db.prepare(`SELECT COUNT(*) n FROM profilo_note`).get() as { n: number }; expect(sessioni.n).toBe(0); expect(misure.n).toBe(0); expect(note.n).toBe(0); }); });