Passa al contenuto principale

ADR-0006: Test Strategy — Pyramid, Golden-Set e CI Gating

Status

Accepted — 13 maggio 2026. Strategia di test per il progetto Akira, da implementare in W4 (settimana di setup pre-Fase 1) e validare prima del primo merge su main.

Context

Akira non e un CRUD generico: opera su tre domini ad alto rischio operativo e contrattuale, ognuno dei quali deve essere coperto da test con criteri diversi.

  1. Rating engine — calcola denaro reale. Un bug di arrotondamento o billing_increment errato significa fatturazione sbagliata, recupero credito impossibile o cliente in default. Il modulo gestisce ~2-5 milioni di CDR/mese e deve produrre output bit-exact rispetto al sistema legacy Stealth durante la fase di parallel run.
  2. Signaling (Kamailio + RTPengine + FreeSWITCH) — vincolato da SLA contrattuali (ASR/PDD/MOS). Una regressione in routing o LCR puo causare drop di chiamate, fallimento failover, perdita di traffico premium. Test richiedono SIPp scenari realistici, non solo unit test.
  3. Fraud detection — esposizione finanziaria diretta (IRSF, Wangiri, SIM Box). False positives bloccano clienti legittimi (MSRN ranges, viaggiatori); false negatives generano perdite cash su numeri premium internazionali. Serve un dataset golden con casi noti e regressione settimanale.

Vincoli del team:

  • Team unipersonale (Massimo) + agenti AI. Non c'e bandwidth per QA manuale ricorrente: tutto cio che non e automatizzato non viene testato.
  • Stack omogeneo Python 3.12 (backend, sipp-orch, packages) + TypeScript 5 (frontend, e2e). Questo abilita tooling uniforme e ridotto context-switch.
  • CI/CD su GitHub Actions con deploy Ansible verso staging/prod (vedi ADR-0001).
  • Budget Anthropic API limitato: i test che esercitano AgentCore devono usare mock respx/pytest-httpx di default; le chiamate reali sono gated a job nightly opt-in.

Senza una strategia di test esplicita rischiamo: (a) regressioni rating in produzione, (b) deploy che rompono Kamailio dialplan senza accorgersene, (c) drift OpenAPI tra backend e frontend, (d) prompt injection non scoperte fino al primo incidente reale.

Decision

Test pyramid a 4 livelli

LivelloConteggio targetWall-clock targetTooling principale
Unit~2000 test< 60 spytest, vitest
Integration~300 test< 5 mintestcontainers
Contract+SIPp~30 + 6 scenari< 8 minschemathesis, SIPp
E2E~50 flussi< 15 minPlaywright

La pyramid e bilanciata: la base massimizza feedback rapido per il dev, il top copre flussi business-critical.

Tooling

Backend (Python):

  • pytest + pytest-asyncio (modalita auto) + pytest-xdist (-n auto).
  • testcontainers per integration (Postgres+Timescale, Redis, Qdrant, NATS).
  • polyfactory per factory di Pydantic/SQLAlchemy models.
  • hypothesis per property-based su rating e LCR.
  • respx per mocking httpx (Anthropic, Revolut, IMAP REST).
  • schemathesis per contract test su OpenAPI.

Frontend (TypeScript):

  • vitest + @testing-library/react per unit/component.
  • msw (Mock Service Worker) per mock fetch in jsdom.
  • Playwright per E2E cross-browser (Chromium + Firefox a campione).
  • axe-core integrato in Playwright per accessibility smoke (WCAG AA su 5 route critiche).

Cross-protocol:

  • SIPp XML scenari (vedi apps/sipp-orch/scenarios/qa/) per testare INVITE/REGISTER/BYE contro Kamailio in staging.
  • k6 per load test API REST (sustained 200 RPS su /cdr, /balance).

Coverage targets per modulo

Vincolanti come gate in pr.yml (overall) e in nightly.yml (per modulo). Riferimento: docs/conventions/definition-of-done.md.

ModuloLineeBranch
apps/backend/services/rating≥ 95%≥ 90%
apps/backend/services/routing≥ 90%≥ 85%
apps/backend/services/fraud≥ 90%≥ 85%
apps/backend/services/billing≥ 90%≥ 85%
Altri servizi backend≥ 80%≥ 75%
packages/*≥ 85%≥ 80%
Frontend componenti business≥ 75%-

Golden-set rating

Export anonimizzato di 10.000 CDR Stealth (numeri E.164 hashati SHA-256, importi preservati, billing_increment originale) versionato in tests/fixtures/golden/. Ogni run del rating Akira deve produrre output bit-exact rispetto al golden. Il test tests/golden/test_rating_golden.py e gating in nightly.yml e in pr.yml quando il path services/rating/** e modificato.

Dataset QA dedicati

  • Fraud sintetico (fraud_dataset_builder.py): 6080 CDR seedati a 42 con casi noti (Wangiri, IRSF, SIM Box, MSRN legittimi, Ping, Robocalling). Output golden_fraud_dataset.parquet. Target: precision ≥ 0.95, recall ≥ 0.90.
  • Prompt injection regression (prompt_injection.jsonl): 30 payload classificati per categoria (jailbreak, sql_injection, impersonation, prompt_structure_injection, credential_extraction, legitimate, destructive_legitimate, vague_destructive). Gating: 100% delle entry expected_action=refused devono restituire rifiuto.
  • Email corpus eterogeneo: 50 EML (plain, HTML, multipart, allegati PDF/XLSX, headers Gmail/M365/Postfix/Exchange) per testare l'ingest IMAP+OAuth2 (ADR-0009).

CI workflows

WorkflowTriggerJob principali
pr.ymlpull_request -> mainlint, unit (BE+FE), contract OpenAPI diff
main.ymlpush mainintegration testcontainers, schemathesis, build+deploy staging, E2E
security.ymlpr + pushTrivy, Bandit, Semgrep, Gitleaks
nightly.ymlcron 02:00 UTCregression full + mutation testing rating + notify Telegram
weekly.ymlcron lun 03:00 UTCperformance SIPp 1h + k6 sustained

Branch protection: pr.yml + security.yml obbligatori per merge su main.

Consequences

Positive

  • Catch precoce di regressioni rating/routing/fraud prima che raggiungano staging.
  • Golden-set abilita parallel run con Stealth senza paura di drift.
  • Pyramid bilanciata mantiene loop di sviluppo < 60 s sul caso comune (unit only).
  • CI gating rende impossibile il merge di codice che rompe coverage minimo o contract OpenAPI.
  • Dataset versionati permettono benchmark longitudinali (precision/recall fraud nel tempo).

Negative / Trade-offs

  • Setup tooling settimana W4 (pre-dev) bloccante: nessun PR Fase 1 puo essere aperto prima che pr.yml sia green su un commit smoke.
  • Golden-set Stealth richiede export anonimato firmato Massimo (BLOCCANTE prima Fase 1). Senza questo, il modulo rating va a produzione cieco.
  • testcontainers boot time: Postgres+Timescale+Redis+Qdrant devono bootare < 30 s su runner GitHub ubuntu-latest. Da validare prima del primo PR; se > 30 s, fallback a services: containerizzati nativi GHA.
  • CI/CD overhead +5 min per PR (lint+unit+contract). Accettabile in rapporto al ROI (catch precoce ~10x cheaper di hotfix in prod).
  • Mutation testing rating (mutmut o cosmic-ray) e costoso (~20 min). Solo nightly, mai su PR.

Neutral

  • Skill set richiesto al team: Python+TS gia presenti; SIPp e nuovo ma documentato e bash-friendly.
  • Dipendenza GitHub Actions: in caso di outage GH, fallback locale via act accettato.

Implementation hints

  1. W4 day 1: creare tests/ skeleton + pyproject.toml con [tool.pytest.ini_options] e [tool.coverage]. Validare uv run pytest tests/unit -n auto < 60 s su skeleton vuoto.
  2. W4 day 2: validare testcontainers boot time. Script scripts/bench_testcontainers.py deve loggare wall-clock di ogni container.
  3. W4 day 3: importare golden-set Stealth (richiede sign-off Massimo su anonimizzazione). Salvare in tests/fixtures/golden/cdr_stealth_v1.parquet.gz + VERSION file.
  4. W4 day 4: scrivere primi 5 test rating parametrici + fixture testcontainers; aprire PR smoke per validare pr.yml end-to-end.
  5. W4 day 5: setup pre-commit + dependabot. Validare che il pre-commit non rompa l'editing AI-assisted.
  6. Continuous: ogni nuovo modulo MVP deve includere il proprio set di unit + almeno 1 integration test nel PR introduttivo. DoD bloccante.

Alternatives considered

A. Test-light + heavy manual QA

Rejected: team unipersonale, non sostenibile. Manual QA non scala oltre 2-3 release/mese.

B. Full E2E senza unit/integration

Rejected: feedback loop > 15 min per ogni cambio, debug impossibile, costo CI runner alto.

C. Solo unit + smoke E2E (no integration)

Rejected: il rischio principale di Akira e l'interazione DB+Redis+Kamailio (race condition rating, lock contention balance). Integration con testcontainers e non negoziabile.

D. Pact (consumer-driven contract) invece di Schemathesis

Rejected per ora: Pact richiede broker dedicato e disciplina su entrambi i lati. Con team unipersonale, schemathesis su OpenAPI generato da FastAPI da copertura sufficiente. Rivalutare in v2 quando ci sara un consumer esterno (es. portale cliente).

E. Cypress invece di Playwright

Rejected: Playwright ha primitivi auto-wait, parallelizzazione nativa, e supporto WebKit/Firefox out-of-the-box, tutti rilevanti per il mockup Akira.

References

  • ADR-0001 — Stack Python/FastAPI + Next.js
  • ADR-0007 — CDR pipeline NATS JetStream (impatto su test integration)
  • docs/conventions/definition-of-done.md — coverage targets canonici
  • tests/README.md — guida operativa per dev
  • tests/fixtures/golden/README.md — procedura import golden-set Stealth