// Test B (#13): l'API checkup.ts scarta i campi di un'alternativa non scelta e aggiunge una // voce di giornale dalla nota operatore. File separato (stesso motivo di // correzione-checkup-api.test.ts): l'endpoint usa il singleton getLongevityDb(), e // LONGEVITY_DB_PATH va impostata PRIMA che lo apra — non nello stesso file di altri test che // lo inizializzano prima. A differenza di correzione-checkup-api.test.ts (un solo test) qui // ce ne sono diversi: il singleton si apre UNA volta per tutto il file (beforeAll/afterAll, // non chiuso e riaperto a ogni test — getLongevityDb() lo terrebbe chiuso dopo il primo // close()), e ogni test usa un client_code proprio per non leggere le voci di giornale di un // altro test. import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { mkdtempSync, rmSync } from 'node:fs'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; import { getLongevityDb } from '../../src/lib/longevity/db'; import { seedRegistro, seedPesi, MODEL_VERSION } from '../../src/lib/longevity/registro'; import { vociDelGiornale } from '../../src/lib/longevity/giornale'; import { salvaCheckup } from '../../src/lib/longevity/gestionale'; // La data d'oggi vera del sistema che esegue i test (non una finta): un checkup con questa // data non è "nel futuro" per la validazione dell'API. const OGGI = new Date().toISOString().slice(0, 10); let dir: string; let primaLg: string | undefined; let lg: ReturnType; beforeAll(() => { dir = mkdtempSync(join(tmpdir(), 'lg-api-alt-test-')); primaLg = process.env.LONGEVITY_DB_PATH; process.env.LONGEVITY_DB_PATH = join(dir, 'longevity.db'); lg = getLongevityDb(); seedRegistro(lg); seedPesi(lg, MODEL_VERSION); for (const code of ['ISL-0001', 'ISL-0002', 'ISL-0003', 'ISL-0004', 'ISL-0005']) { lg.prepare(`INSERT INTO soggetti (client_code, sesso) VALUES (?, 'F')`).run(code); } }); afterAll(() => { lg.close(); // libera l'handle sul file: senza, rmSync fallisce con EPERM su Windows if (primaLg === undefined) delete process.env.LONGEVITY_DB_PATH; else process.env.LONGEVITY_DB_PATH = primaLg; rmSync(dir, { recursive: true, force: true }); }); describe('API checkup.ts, sezioni ad alternative e nota operatore (#13)', () => { it('scarta i campi di un\'alternativa non scelta, anche se compilati', async () => { const { POST } = await import('../../src/pages/api/longevity/gestionale/checkup'); const form = new FormData(); form.set('client_code', 'ISL-0001'); form.set('data', OGGI); form.set('alt_spinta', 'pushup'); // scelta: push-up form.set('f_pushup_rip', '15'); // dell'alternativa scelta: deve restare form.set('f_panca_bil_carico_kg', '60'); // di un'altra alternativa (bilanciere): va scartato form.set('f_panca_bil_rip', '5'); const richiesta = new Request('http://localhost/api/longevity/gestionale/checkup', { method: 'POST', body: form }); const res = await POST({ request: richiesta, locals: { user: { username: 'nicola' } } } as any); expect(res.status).toBe(303); const loc = res.headers.get('Location')!; expect(loc).toMatch(/salvato=/); const sessioneId = Number(new URL(loc, 'http://localhost').searchParams.get('salvato')); const righe = lg.prepare(`SELECT test_id FROM misure WHERE sessione_id = ?`).all(sessioneId) as { test_id: string }[]; const ids = righe.map((r) => r.test_id); expect(ids).toContain('f_pushup_rip'); expect(ids).not.toContain('f_panca_bil_carico_kg'); expect(ids).not.toContain('f_panca_bil_rip'); }); it('senza radio inviata, vince la prima alternativa dell\'elenco: i campi delle altre si scartano lo stesso', async () => { const { POST } = await import('../../src/pages/api/longevity/gestionale/checkup'); const form = new FormData(); form.set('client_code', 'ISL-0002'); form.set('data', OGGI); // nessun alt_spinta: la prima alternativa di "Forza spinta" è 'manubri' form.set('f_panca_man_carico_kg', '20'); form.set('f_panca_man_rip', '5'); form.set('f_pushup_rip', '15'); // non è la prima alternativa: va scartato const richiesta = new Request('http://localhost/api/longevity/gestionale/checkup', { method: 'POST', body: form }); const res = await POST({ request: richiesta, locals: { user: { username: 'nicola' } } } as any); const sessioneId = Number(new URL(res.headers.get('Location')!, 'http://localhost').searchParams.get('salvato')); const ids = (lg.prepare(`SELECT test_id FROM misure WHERE sessione_id = ?`).all(sessioneId) as { test_id: string }[]) .map((r) => r.test_id); expect(ids).toContain('f_panca_man_carico_kg'); expect(ids).not.toContain('f_pushup_rip'); }); it('nota operatore non vuota: aggiunge una voce di giornale con la data del check-up e l\'operatore come autore', async () => { const { POST } = await import('../../src/pages/api/longevity/gestionale/checkup'); const form = new FormData(); form.set('client_code', 'ISL-0003'); form.set('data', OGGI); form.set('f_peso_kg', '70'); form.set('nota', 'Cliente motivato, riprende dopo la pausa estiva.'); const richiesta = new Request('http://localhost/api/longevity/gestionale/checkup', { method: 'POST', body: form }); const res = await POST({ request: richiesta, locals: { user: { username: 'nicola' } } } as any); expect(res.status).toBe(303); const voci = vociDelGiornale(lg, 'ISL-0003'); expect(voci.length).toBe(1); expect(voci[0].testo).toBe('Cliente motivato, riprende dopo la pausa estiva.'); expect(voci[0].data).toBe(OGGI); expect(voci[0].autore).toBe('nicola'); }); it('nota operatore vuota (o assente): nessuna voce di giornale', async () => { const { POST } = await import('../../src/pages/api/longevity/gestionale/checkup'); const form = new FormData(); form.set('client_code', 'ISL-0004'); form.set('data', OGGI); form.set('f_peso_kg', '70'); form.set('nota', ' '); // solo spazi: trim -> vuoto const richiesta = new Request('http://localhost/api/longevity/gestionale/checkup', { method: 'POST', body: form }); await POST({ request: richiesta, locals: { user: { username: 'nicola' } } } as any); expect(vociDelGiornale(lg, 'ISL-0004').length).toBe(0); }); it('una correzione (sessione_id nel form) non porta il campo nota: nessuna voce anche se la sessione ha una data valida', async () => { const esito = salvaCheckup(lg, { client_code: 'ISL-0005', data: OGGI, eta: 30, operatore: 'nicola', misure: [{ test_id: 'f_peso_kg', valore_num: 70 }], }); if (!esito.ok) throw new Error(esito.errore); const { POST } = await import('../../src/pages/api/longevity/gestionale/checkup'); const form = new FormData(); form.set('client_code', 'ISL-0005'); form.set('sessione_id', String(esito.sessioneId)); form.set('data', OGGI); form.set('f_peso_kg', '71'); // niente campo "nota": la pagina non lo mostra in correzione const richiesta = new Request('http://localhost/api/longevity/gestionale/checkup', { method: 'POST', body: form }); await POST({ request: richiesta, locals: { user: { username: 'nicola' } } } as any); expect(vociDelGiornale(lg, 'ISL-0005').length).toBe(0); }); });