longevity: revisione finale - codice cliente non riusabile, ruoli allineati, middleware verificato

- creaCliente: il codice si deduce dal massimo fra identity E longevity (non piu' solo
  identity), cosi' un client_code cancellato dall'anagrafica non torna mai disponibile
  e non si attribuiscono le misure di un vecchio cliente a uno nuovo. Compensazione se
  la scrittura su longevity fallisce dopo quella su identity.
- ROLES unica fonte in auth.ts (Role e isRole derivati); users.astro e users/new.astro
  usano quella lista invece di array scritti a mano che dimenticavano cliente/trainer.
- Commento falso su cosa protegge il middleware, riscritto: rimanda a isProtectedPath.
- vitest.config.ts: alias per astro:middleware (stesso bersaglio della pipeline vite di
  Astro), cosi' src/middleware.ts e' finalmente importabile e testabile in isolamento -
  prima nessun test lo caricava davvero.
- export.test.ts: asserzione posizionale per colonna, non piu' solo intestazioni scritte
  a mano; intercetta un riordino della SELECT che disallinea etichette e valori.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-21 20:30:23 +02:00
parent e6c2ac9148
commit 763e5611e6
10 changed files with 260 additions and 17 deletions
+42
View File
@@ -46,6 +46,48 @@ describe('anagrafica pseudonimizzata', () => {
expect(etaAllaData('1988-12-31', '2026-08-21')).toBe(37); // compleanno non ancora passato
});
it('un codice cancellato dall anagrafica non torna mai disponibile: le misure del vecchio non si attribuiscono al nuovo', () => {
const id = createIdentityDb(':memory:');
const lg = createLongevityDb(':memory:');
const a = creaCliente(id, lg, { nome: 'A', cognome: 'A', sesso: 'F' });
const b = creaCliente(id, lg, { nome: 'B', cognome: 'B', sesso: 'M' });
// Il secondo cliente viene cancellato dall'anagrafica (disdetta, richiesta di
// cancellazione, errore): le sue misure restano in longevity, pseudonimizzate.
id.prepare(`DELETE FROM clienti WHERE client_code = ?`).run(b);
lg.prepare(`INSERT INTO sessioni (client_code, data, tipo) VALUES (?, '2026-08-01', 'checkup')`).run(b);
const c = creaCliente(id, lg, { nome: 'C', cognome: 'C', sesso: 'F' });
// Il terzo cliente riceve un codice nuovo, mai il codice riciclato del secondo.
expect(c).not.toBe(b);
expect(c).not.toBe(a);
// Le sessioni del vecchio cliente (b) restano leggibili sotto il suo codice...
const sessioniDiB = lg.prepare(`SELECT * FROM sessioni WHERE client_code = ?`).all(b);
expect(sessioniDiB.length).toBe(1);
// ...e non risultano in nessun modo attribuite al nuovo cliente (c).
const sessioniDiC = lg.prepare(`SELECT * FROM sessioni WHERE client_code = ?`).all(c);
expect(sessioniDiC.length).toBe(0);
});
it('se la scrittura su longevity fallisce, quella gia fatta su identity viene annullata', () => {
const id = createIdentityDb(':memory:');
const lg = createLongevityDb(':memory:');
const primaDelTentativo = (id.prepare(`SELECT COUNT(*) AS n FROM clienti`).get() as { n: number }).n;
// 'Z' non è un sesso valido (CHECK IN ('M','F') su longevity.soggetti): la scrittura
// su longevity fallisce per costruzione, dopo che quella su identity è già avvenuta.
expect(() =>
creaCliente(id, lg, { nome: 'Errato', cognome: 'Errato', sesso: 'Z' as unknown as 'M' | 'F' })
).toThrow();
const dopoIlTentativo = (id.prepare(`SELECT COUNT(*) AS n FROM clienti`).get() as { n: number }).n;
expect(dopoIlTentativo).toBe(primaDelTentativo); // nessun cliente orfano rimasto in identity
expect(id.prepare(`SELECT * FROM clienti WHERE nome = 'Errato'`).get()).toBeUndefined();
});
it('nessun file in src/ (escluso anagrafica.ts e db.ts) apre entrambe le connessioni', () => {
const srcDir = join(process.cwd(), 'src');
const colpevoli: string[] = [];