Skip to main content

Qualità, sicurezza e criteri di accettazione

Strategia di test

LivelloCopertura minima
UnitIntervalli temporali, tariffe, capacità, ferie, limiti, normalizzazione città, compatibilità sedi, strategie employee/freelancer
ContractAPI, eventi e ogni adapter import/export/versione
IntegrationPostgreSQL/datahub, transazioni, vincoli tenant e outbox
End-to-endDue utenti stesso tenant, due tenant isolati, HR/progetto/forecast/actual/delivery/report
Golden datasetCosti reali/standard, forecast fattibile/impossibile, correzioni retroattive
Non funzionaliLoad, concorrenza, retry/idempotenza, backup/restore, security/IDOR

La perturbazione, e i modi in cui un test passa per la ragione sbagliata

Ogni invariante va perturbato: si rompe di proposito il comportamento che protegge e si verifica che il test cada. Un test che resta verde con la funzione disattivata non prova niente, e costa come uno che prova qualcosa.

Fra il 17 e il 22 agosto 2026 la perturbazione ha smascherato undici test scritti male. I modi sono diversi abbastanza da valere come promemoria:

  1. L'asserzione troppo larga. Il test sul consuntivo per sede verificava che esistessero righe da qualche parte, non che fossero quelle giuste. Passava con la regola spenta.

  2. La fixture che non discrimina. Il test su «vale l'ultima esecuzione del mese» aveva la più recente già in fondo alla lista: prendere «l'ultima letta» invece della «più recente» dava lo stesso risultato. Bastava invertire l'ordine.

  3. Il vincolo imposto due volte. Il test sulla capacità mensile del ricalcolo per progetto non cadeva, perché a tenere il numero giusto bastava già la presenza residua giornaliera. Ha dovuto guardare anche gli avvisi.

  4. Il caso che non copre il caso. I test sulla riconciliazione anagrafica verificavano l'ordine di precedenza su una coppia che non lo esercitava: perturbando l'ordine dei livelli e il segno di «già abbinata», nessuno cadeva. Servivano due casi diversi che si contendono la stessa persona.

  5. La scorciatoia scambiata per regola. Un controllo sulla lunghezza dei cognomi non faceva cadere niente perché il conteggio successivo scartava comunque quei casi: era un'ottimizzazione ridondante. Lasciarla senza dirlo avrebbe fatto credere a chi la legge di poter allargare la tolleranza cambiando quel numero.

  6. L'helper che conta la cosa sbagliata. Un test verificava «nessuna creazione» con una funzione che contava anche gli aggiornamenti: passava mentre l'anagrafica veniva riscritta.

  7. Il totale che nasconde la riga. Il test su «un import con righe escluse non si dichiara riuscito» guardava failed > 0: ma escludendo un dipendente cadono anche le sue ore, quindi era vero comunque, e passava anche togliendo il conteggio dell'esclusione. Un totale non attribuisce: ora cerca quella riga per chiave naturale.

  8. Il caso che non può collidere. «Due fasce dello stesso tariffario non collidono fra loro» passava anche togliendo l'aggiornamento dell'indice in memoria: fasce diverse hanno chiavi diverse e non collidono comunque. Il caso che avrebbe discriminato — lo stesso tariffario due volte — non arriva mai alla scrittura, perché lo staging rifiuta i codici duplicati. Il test giusto fissa quella garanzia, non un caso che non esiste.

  9. La fixture collassata dalla regola sbagliata. La prova che il raggruppamento dei motivi toglie i nomi propri usava nomi finti P1, P2, P3: si collassano già per la regola sui numeri, quindi il test passava anche togliendo quella sui nomi. Servivano nomi che differiscono per lettere.

  10. Il test che guarda accanto al bersaglio. Il contratto verso data-service confrontava le radici dei percorsi chiesti con quelle esposte. Ma tutto il CRUD generico passa da /tables/:tabella: una radice sola, sempre presente. Caricando i moduli di rendicontazione il verticale chiedeva due tabelle che data-service non aveva in elenco — 400 «tabella non consentita», con la rotta che esiste e il servizio che risponde — e il test restava verde. Ora legge anche i nomi delle tabelle e l'elenco che data-service consente, e la mappa delle capability: una tabella dichiarata senza capability ricade su admin, che non produce un errore ma un permesso più stretto del dovuto.

  11. Il test scritto a valle della guardia. «Le righe di totale non entrano fra i dati» guardava il modello finito, dove una riga TOTALE PROGETTI verrebbe scartata comunque perché non ha un dipendente riconoscibile: togliendo la guardia nel lettore il test restava verde. Con un nome di persona accanto — un trascinamento, e nel file vero succede — quella riga entrerebbe eccome. Il test va scritto dove sta la guardia, non dove il caso si risolve da solo per un'altra ragione.

E una regola che ne discende, dal caso del congelamento: un test di conservazione deve cambiare l'input. Verificare che una riga «conservata» sia identica dopo un ricalcolo non prova niente se ricalcolarla darebbe comunque lo stesso risultato — si sta misurando il determinismo, non la conservazione.

Il catalogo che si controlla da solo

test/catalogo.test.js legge i codici di segnalazione dal sorgente dei moduli e verifica che ognuno abbia una voce in modules/qualita/catalogo.js. Un elenco scritto a mano avrebbe avuto lo stesso difetto che il test chiude: si aggiunge un avviso, ci si dimentica dell'elenco, e nessuno se ne accorge.

Ha già funzionato sei volte contro chi l'ha scritto: i quattro codici IMPORT_*, quelli del foglio progetti, quelli dei tariffari, quelli dell'anagrafica personale, quelli della riconciliazione e quelli della lettura dei moduli di rendicontazione. Ogni volta bocciando codici aggiunti lo stesso giorno, da chi li aveva appena scritti.

Invarianti da automatizzare

  • Nessun record tenant-owned senza organizzazione.
  • Nessuna relazione collega entità di organizzazioni diverse.
  • Nessun intervallo costo/tariffa/ruolo si sovrappone quando la regola richiede unicità.
  • Nessuna assegnazione sede di un dipendente si sovrappone temporalmente a un'altra.
  • Ogni associazione a un progetto con sede è compatibile, per l'intero intervallo, con la città della sede della risorsa — regola dura nel forecast, avviso nel consuntivo, dove le ore sono già state lavorate e toglierle farebbe sparire un costo reale. Il consuntivo confronta con la sede storica del giorno, non con quella corrente.
  • Nessun risultato forecast supera capacità o limite progetto-risorsa.
  • La somma allocata è coerente tra dettaglio e aggregati.
  • Un periodo consegnato non cambia, inclusi import, job e chiamate dirette. Coperto in quattro punti: import presenze, annullamento import, modifica delle associazioni, ricalcolo del consuntivo (globale e per progetto). Il ricalcolo globale di un mese passato conserva i progetti consegnati invece di rifiutare il mese, altrimenti un solo progetto che consegna bloccherebbe l'organizzazione.
  • Lo stesso evento/import ripetuto non duplica dati o calcoli. Coperto su: dipendenti, cataloghi (sedi, aree, società, orari), enti, tariffari, intervalli di costo e legami ente-tariffario.
  • Ciò che un import crea si può cancellare e modificare senza smontare l'anagrafica: i cataloghi nati da un import non usano ON DELETE RESTRICT.
  • Nessun abbinamento fra anagrafiche diverse avviene in silenzio quando non è certo: un abbinamento sbagliato attribuisce ore e costi a un'altra persona, e i totali tornano lo stesso.
  • Un run è riproducibile dagli input/versione registrati.

Definition of Done per feature

  • Regole e acceptance criteria approvati.
  • Migrazione forward e rollback/mitigazione documentati; schema clean-install aggiornato.
  • API e UI autorizzano server-side e rispettano il tenant.
  • Audit ed eventi includono correlation e organization ID.
  • Test unit/integration/E2E rilevanti verdi.
  • Errori utente spiegabili, metriche e log operativi presenti.
  • Documentazione utente/tecnica e checklist aggiornate.
  • Nessun dato personale o segreto nei log/fixture.

KPI del prodotto

  • percentuale domanda progetto coperta;
  • numero/ore di risorse over-allocate (target: 0 nei piani validi);
  • ore scoperte e motivazioni;
  • tempo medio/p95 di ricalcolo;
  • tasso errori import e righe da correggere;
  • modifiche retroattive per periodo e tempo di propagazione;
  • percentuale mesi consegnati entro scadenza;
  • incidenti cross-tenant (target: 0).

UAT MVP avanzato

Il committente deve completare almeno questi scenari:

  1. Due operatori della stessa organizzazione lavorano sullo stesso progetto.
  2. HR crea una risorsa, assegna società, sede con validità temporale, ruolo, livello, manager, costo e ferie.
  3. Un dipendente accede e inserisce il proprio piano ferie.
  4. Si configurano le sedi, un ente con cambio tariffa e un progetto a costi standard associato a una sede.
  5. Si verifica che una risorsa della stessa città sia associabile e una di città diversa venga rifiutata con errore esplicito.
  6. Si importa un nuovo formato employee/project e si approva il diff.
  7. Si genera un forecast senza over-allocation, con evidenza di eventuali ore scoperte.
  8. Si importa il consuntivo, si corregge un mese aperto e si verifica il ricalcolo futuro.
  9. Si consegna un mese e si prova che ogni modifica precedente sia bloccata.
  10. Si esportano report coerenti per dipendente e progetto.
  11. Si verifica che un utente di un'altra organizzazione non possa accedere agli stessi ID.

Requisiti Release 1

  • Vulnerabilità critiche/alte risolte o formalmente accettate con mitigazione.
  • SLO e alert concordati; runbook di recovery provato.
  • Restore completo testato su ambiente isolato.
  • Migrazione di onboarding ripetibile e riconciliata con conteggi/checksum.
  • Prestazioni validate sui volumi target con margine concordato.
  • Audit esportabile per azioni sensibili e retention definita.
  • Manuali e processo supporto pronti; ownership operativa assegnata.