PD-56 — Scénarios de tests contractuels (v3)
1. Références
- Spécification : PD-56-specification.md (v3).
- Epic : PD-187.
2. Matrice de couverture
| ID Invariant | ID Critère | ID Test | Couverture | Commentaire |
| INV-56-01-root-source-of-truth | CA-56-01 | TC-NOM-01 | Oui | merkleRoot retourné strictement égal à la valeur persistée. |
| INV-56-01-root-source-of-truth | CA-56-02 | TC-NOM-04 | Oui | Recalcul off-chain cohérent avec racine persistée. |
| INV-56-02-determinisme | CA-56-01 | TC-NOM-03 | Oui | Même snapshot transactionnel + même eventId => même résultat. |
| INV-56-03-minimal-disclosure | CA-56-01 | TC-NOM-08 | Oui | Vérification stricte des champs exposés. |
| INV-56-04-algo-explicit | CA-56-01 | TC-NOM-01 | Oui | hashAlgorithm et hashAlgorithmVersion obligatoires en available. |
| INV-56-05-auto-verification | CA-56-07 | TC-NOM-05 | Oui | Vérification exécutée avant tout retour available. |
| INV-56-05-auto-verification | CA-56-11 | TC-ERR-08 | Oui | Mismatch racine => transition persistée + ERR-56-04. |
| INV-56-06-transitions | CA-56-08 | TC-INV-06 | Oui | Transitions autorisées validées et tracées. |
| INV-56-06-transitions | CA-56-08 | TC-ERR-04 | Oui | Corruption structurelle => transition vers CORRUPTED vérifiée. |
| INV-56-06-transitions | CA-56-08 | TC-ERR-09 | Oui | AVAILABLE->PENDING refusée. |
| INV-56-06-transitions | CA-56-12 | TC-ERR-10 | Oui | CORRUPTED terminal, aucun sortant. |
| INV-56-06-transitions | CA-56-11 | TC-ERR-11 | Oui | AVAILABLE->CORRUPTED sur mismatch. |
| INV-56-07-format-single-source | CA-56-09 | TC-INV-07 | Oui | Campagne exhaustive format/bornes §5.1/§5.2, inclut treeSize=1/merklePath=[]. |
| INV-56-08-no-secret-cleartext | N/A | TC-INV-08 | Oui (partielle) | Vérifiable côté flux PD-56, périmètre global hors scope. |
| INV-56-09-security-alert-on-first-corruption-detection | CA-56-14 | TC-NOM-11 | Oui | Alerte émise à la première détection uniquement. |
| INV-56-09-security-alert-on-first-corruption-detection | CA-56-14 | TC-ERR-04 | Oui | Corruption structurelle initiale -> alerte critique. |
| INV-56-09-security-alert-on-first-corruption-detection | CA-56-14 | TC-ERR-08 | Oui | Corruption cryptographique initiale -> alerte critique. |
| INV-56-09-security-alert-on-first-corruption-detection | CA-56-15 | TC-ERR-10 | Oui | État déjà CORRUPTED -> aucune alerte additionnelle. |
| N/A | CA-56-03 | TC-NOM-04 | Oui | Vérification par script externe sans API/BDD. |
| N/A | CA-56-04 | TC-NOM-09 | Oui | Mesure P95 taille JSON < 10 KB. |
| N/A | CA-56-05 | TC-NOM-10 | Oui (conditionnelle) | Opposabilité liée à benchmark officiel figé. |
| N/A | CA-56-06 | TC-NOM-02 | Oui | pending + ETA UTC conforme. |
| N/A | CA-56-10 | TC-NOM-06 | Oui | Finality gate finalized_at obligatoire pour available. |
| N/A | CA-56-09 | TC-NOM-12 | Oui | Cas limite valide : treeSize=1 et merklePath=[]. |
| N/A | CA-56-13 | TC-ERR-03 | Oui | ETA non calculable bornée => ERR-56-05. |
| N/A | CA-56-09 | TC-ERR-01 | Oui | eventId invalide => ERR-56-01. |
| N/A | CA-56-09 | TC-ERR-05 | Oui | Hash/format invalide => ERR-56-03. |
| N/A | CA-56-09 | TC-ERR-06 | Oui | Bornes numériques invalides => ERR-56-03. |
| N/A | CA-56-09 | TC-ERR-07 | Oui | Algo/version invalides => ERR-56-03. |
3. Scénarios de test – Flux nominaux
TEST-ID: TC-NOM-01
Référence spec: INV-56-01, INV-56-04, CA-56-01
GIVEN
- Snapshot S-56-AVAIL figé
- eventId E1 valide et connu
- Preuve persistée valide, arbre finalisé (finalized_at IS NOT NULL)
WHEN
- getMerkleProof(E1) est appelé
THEN
- status='available'
- Champs présents et conformes: eventHash, merkleRoot, merklePath, treeId, treeSize, leafIndex, hashAlgorithm='SHA-256', hashAlgorithmVersion='1.0'
- merkleRoot retourné = merkleRoot persisté
AND
- Aucun champ hors contrat n’est exposé
TEST-ID: TC-NOM-02
Référence spec: F-02, CA-56-06, ERR-56-02
GIVEN
- Snapshot S-56-PENDING figé
- eventId E2 valide et connu
- finalized_at IS NULL
- window_end disponible et calculable
WHEN
- getMerkleProof(E2) est appelé
THEN
- status='pending'
- estimatedAvailableAt est présent et conforme RFC3339 UTC (suffixe Z)
AND
- Aucune donnée de preuve available n’est retournée
TEST-ID: TC-NOM-03
Référence spec: INV-56-02
GIVEN
- Snapshot transactionnel S-56-AVAIL immuable
- eventId E1 constant
WHEN
- Deux appels successifs puis deux appels concurrents sont exécutés
THEN
- Les résultats sont strictement identiques (égalité structurelle champ-à-champ)
AND
- Aucun écart de statut, de valeurs, ni de code
TEST-ID: TC-NOM-04
Référence spec: F-03, CA-56-02, CA-56-03
GIVEN
- Une réponse available valide exportée en JSON
- Outil externe indépendant (JS ou Python), sans accès API/BDD
WHEN
- Le tiers recalcule computedRoot = fold(hashPair(sorted(a,b))) en SHA-256
THEN
- computedRoot == merkleRoot
AND
- L’exécution du script est réussie sans dépendance backend
TEST-ID: TC-NOM-05
Référence spec: INV-56-05, CA-56-07
GIVEN
- Snapshot S-56-AVAIL avec observabilité activée (trace corrélée)
WHEN
- getMerkleProof(E1) est appelé
THEN
- La vérification cryptographique est exécutée avant l’émission de la réponse available
AND
- La trace contient la décision de vérification (succès/échec)
TEST-ID: TC-NOM-06
Référence spec: §5.4 (conditions PENDING->AVAILABLE), F-01, CA-56-10
GIVEN
- eventId E3 avec preuve trouvée et auto-vérification OK
- finalized_at IS NULL
- window_end calculable
WHEN
- getMerkleProof(E3) est appelé
THEN
- Le service ne retourne pas available
- Le service retourne pending avec ETA UTC
AND
- Aucune transition PENDING->AVAILABLE n’est validée sans finality
TEST-ID: TC-INV-06
Référence spec: INV-56-06, CA-56-08
GIVEN
- Jeux de données forçant les états PENDING, AVAILABLE, CORRUPTED
WHEN
- Les appels sont rejoués dans des conditions menant aux transitions autorisées
THEN
- Transitions acceptées: PENDING->PENDING, PENDING->AVAILABLE, PENDING->CORRUPTED, AVAILABLE->AVAILABLE, AVAILABLE->CORRUPTED
AND
- Aucune transition sortante depuis CORRUPTED
TEST-ID: TC-INV-07
Référence spec: INV-56-07, §5.1, §5.2, CA-56-09
GIVEN
- Une campagne de conformité FORMAT-56 regroupant:
* Cas valides frontières (UUID v4 valide, hashes lowercase hex, bornes min/max, treeSize=1 avec merklePath=[])
* Cas invalides par champ (regex, charset, casse, taille)
* Cas invalides de cohérence (leafIndex hors plage, merklePath.length hors borne dynamique, merklePath=[] avec treeSize>1)
WHEN
- Chaque jeu de données est injecté via getMerkleProof(eventId) ou via validations internes équivalentes
THEN
- Chaque entrée invalide retourne le code contractuel attendu (ERR-56-01 / ERR-56-03 / ERR-56-05 selon la règle)
- Chaque entrée valide frontière est acceptée
AND
- Un rapport trace pour chaque cas: {caseId, attendu, observé, verdict}
TEST-ID: TC-INV-08
Référence spec: INV-56-08
GIVEN
- Traces de persistance PD-56 et payloads de sortie sur scénarios nominaux/erreurs
WHEN
- Les écritures DB et les réponses sont inspectées
THEN
- Aucune donnée secrète en clair n’est persistée ni exposée
AND
- Seuls hashes et métadonnées publiques contractuelles sont manipulés
TEST-ID: TC-NOM-07
Référence spec: §5.3 (expiration ETA)
GIVEN
- Événement en pending avec ETA W1 expirée
- Preuve toujours absente
- Fenêtre suivante connue W2
WHEN
- getMerkleProof(eventId) est appelé après W1
THEN
- Le statut reste pending
- estimatedAvailableAt est recalculé sur W2 en UTC
AND
- Aucune promotion implicite vers available
TEST-ID: TC-NOM-08
Référence spec: INV-56-03
GIVEN
- Une réponse available
WHEN
- Le payload est inspecté
THEN
- Seuls les champs contractuels de preuve et métadonnées sont présents
AND
- Aucun payload métier brut, aucune donnée sensible, aucun secret en clair
TEST-ID: TC-NOM-09
Référence spec: CA-56-04
GIVEN
- Campagne de référence PERF-56-SIZE (N défini, dataset figé)
WHEN
- Chaque réponse available est sérialisée JSON UTF-8
THEN
- Le P95 de taille est strictement < 10 KB
AND
- Le rapport percentile est archivé comme preuve
TEST-ID: TC-NOM-10
Référence spec: CA-56-05
GIVEN
- Campagne de référence PERF-56-LAT (N défini, dataset figé)
- Environnement benchmark officiel préalablement figé
WHEN
- La latence de génération est mesurée
THEN
- P95 <= 10 ms
AND
- Rapport horodaté avec distribution complète (P50/P95/P99)
TEST-ID: TC-NOM-11
Référence spec: INV-56-09, CA-56-14, CA-56-15
GIVEN
- Un scénario de première corruption structurelle (S1) et un scénario de première corruption cryptographique (S2)
- Puis des appels répétés sur les mêmes eventId déjà passés en CORRUPTED
WHEN
- Le service traite S1, S2, puis les relectures
THEN
- Exactement un SECURITY_ALERT CRITICAL est émis par eventId lors de la transition initiale vers CORRUPTED
AND
- Aucun SECURITY_ALERT supplémentaire n’est émis lors des appels répétés en état CORRUPTED
TEST-ID: TC-NOM-12
Référence spec: §5.1 (règle treeSize=1), CA-56-09
GIVEN
- eventId E4 avec preuve valide
- treeSize=1, leafIndex=0, merklePath=[]
- computedRoot == merkleRoot, finalized_at IS NOT NULL
WHEN
- getMerkleProof(E4) est appelé
THEN
- Le service retourne status='available'
AND
- Aucun ERR-56-03 n’est déclenché sur merklePath de longueur 0
4. Scénarios de test – Cas d’erreur
TEST-ID: TC-ERR-01
Référence spec: ERR-56-01, §5.1 (eventId)
GIVEN
- eventId invalide (regex UUID v4 non respectée)
WHEN
- getMerkleProof(eventId) est appelé
THEN
- Rejet explicite ERR-56-01
AND
- Aucune preuve retournée
TEST-ID: TC-ERR-02
Référence spec: ERR-56-01
GIVEN
- eventId valide syntaxiquement mais inexistant
WHEN
- Appel du service
THEN
- Rejet explicite ERR-56-01
AND
- Aucun artefact de preuve n’est exposé
TEST-ID: TC-ERR-03
Référence spec: ERR-56-05, F-05, §5.1 (estimatedAvailableAt)
GIVEN
- Cas pending où un batch candidat existe
- finalized_at IS NULL
- window_end IS NULL (ETA impossible)
WHEN
- Appel du service
THEN
- Rejet explicite ERR-56-05
AND
- Aucun pending sans ETA UTC conforme n’est retourné
TEST-ID: TC-ERR-04
Référence spec: F-06, §5.4, INV-56-06, INV-56-09, CA-56-08, CA-56-14, CA-56-15
GIVEN
- État courant non CORRUPTED (PENDING ou AVAILABLE)
- Donnée Merkle structurellement invalide, par exemple:
* merklePath absent ou null
* merklePath=[] alors treeSize>1
* merklePath[i] non conforme format hex
WHEN
- getMerkleProof(eventId) est appelé
THEN
- Transition persistée vers CORRUPTED
- Rejet explicite ERR-56-03
- SECURITY_ALERT severity=CRITICAL émis (première détection)
AND
- Un appel immédiat suivant sur le même eventId retourne ERR-56-03 sans nouvelle transition ni nouvelle alerte
TEST-ID: TC-ERR-05
Référence spec: ERR-56-03, §5.1 (eventHash/merkleRoot/merklePath[i])
GIVEN
- Au moins un hash non conforme (taille, charset, case)
WHEN
- Appel du service
THEN
- Rejet explicite ERR-56-03
AND
- Le motif de format invalide est traçable
TEST-ID: TC-ERR-06
Référence spec: ERR-56-03, §5.2
GIVEN
- treeSize hors [1..10000] ou leafIndex hors [0..treeSize-1] ou merklePath.length hors borne
WHEN
- Appel du service
THEN
- Rejet explicite ERR-56-03
AND
- Aucune preuve exploitable n’est retournée
TEST-ID: TC-ERR-07
Référence spec: ERR-56-03, INV-56-04
GIVEN
- Réponse candidate available avec hashAlgorithm ou hashAlgorithmVersion non conformes
WHEN
- Validation contractuelle du résultat
THEN
- Rejet explicite ERR-56-03
AND
- Le statut available est refusé
TEST-ID: TC-ERR-08
Référence spec: ERR-56-04, INV-56-05, CA-56-11, CA-56-14
GIVEN
- État courant PENDING
- eventHash + merklePath produisant computedRoot != merkleRoot
WHEN
- Appel du service
THEN
- Transition PENDING->CORRUPTED persistée en base
- Rejet explicite ERR-56-04
AND
- SECURITY_ALERT severity=CRITICAL émis (première détection)
TEST-ID: TC-ERR-09
Référence spec: §5.4, INV-56-06
GIVEN
- État courant AVAILABLE
WHEN
- Une transition vers PENDING est évaluée
THEN
- Transition refusée contractuellement
AND
- État conservé, rejet tracé
TEST-ID: TC-ERR-10
Référence spec: §5.4, INV-56-06, INV-56-09, CA-56-12, CA-56-15
GIVEN
- État courant CORRUPTED déjà persisté
- Une alerte a déjà été émise lors de la première détection
WHEN
- getMerkleProof(eventId) est appelé (répétitions incluses)
THEN
- Retour explicite ERR-56-03
- Aucune transition d’état n’est effectuée
AND
- Aucun SECURITY_ALERT additionnel n’est émis
TEST-ID: TC-ERR-11
Référence spec: §5.4, F-04, ERR-56-04, CA-56-11, CA-56-12
GIVEN
- État courant AVAILABLE
- Une auto-vérification ultérieure détecte computedRoot != merkleRoot
WHEN
- getMerkleProof(eventId) est appelé
THEN
- Transition AVAILABLE->CORRUPTED persistée en base
- Rejet explicite ERR-56-04
AND
- Appel suivant sur le même eventId retourne ERR-56-03 (état terminal)
5. Tests d’invariants (non négociables)
| Invariant | Test(s) dédiés | Observable | Commentaire |
| INV-56-01-root-source-of-truth | TC-NOM-01, TC-NOM-04 | Égalité stricte racine retournée vs persistée | Vérifie absence de substitution/recalcul de sortie. |
| INV-56-02-determinisme | TC-NOM-03, TC-NR-04 | Résultats identiques à snapshot transactionnel constant | Reproductibilité audit. |
| INV-56-03-minimal-disclosure | TC-NOM-08, TC-NEG-10 | Liste de champs strictement contractuelle | Confidentialité préservée. |
| INV-56-04-algo-explicit | TC-NOM-01, TC-ERR-07 | Valeurs exactes SHA-256 / 1.0 | Vérification indépendante possible. |
| INV-56-05-auto-verification | TC-NOM-05, TC-ERR-08, TC-ERR-11 | Vérification exécutée avant available | Détection de corruption active. |
| INV-56-06-transitions | TC-INV-06, TC-ERR-04, TC-ERR-09, TC-ERR-10, TC-ERR-11 | Respect machine d’état §5.4 | Inclut interdictions et terminalité CORRUPTED. |
| INV-56-07-format-single-source | TC-INV-07, TC-NOM-12 | Couverture exhaustive format/bornes §5.1/§5.2 | Inclut le cas limite valide treeSize=1. |
| INV-56-08-no-secret-cleartext | TC-INV-08 | Aucune persistance/émission de secret en clair | Couverture partielle sans preuve globale inter-modules. |
| INV-56-09-security-alert-on-first-corruption-detection | TC-NOM-11, TC-ERR-04, TC-ERR-08, TC-ERR-10 | Émission unique sur première détection + absence de flood | Traçabilité sécurité opposable. |
6. Tests de non-régression
| Test ID | Objet | Observable | Commentaire |
| TC-NR-01 | Stabilité contrat available | Même schéma de sortie et mêmes règles de validation entre baseline et candidate | Non-régression CA-56-01/INV-56-03/INV-56-04. |
| TC-NR-02 | Stabilité contrat pending | status='pending' + ETA UTC conforme inchangés | Non-régression CA-56-06. |
| TC-NR-03 | Stabilité mapping codes | Même entrée invalide => même code ERR-56-0x attendu | Non-régression CA-56-09/CA-56-13. |
| TC-NR-04 | Stabilité déterminisme | Même snapshot transactionnel => même résultat sur versions successives | Non-régression INV-56-02. |
| TC-NR-05 | Stabilité performance | P95 taille/latence ne dépassent pas les budgets contractuels | Non-régression CA-56-04/CA-56-05. |
| TC-NR-06 | Effets de bord contrôlés | Seules écritures autorisées: transition vers CORRUPTED et audit SECURITY_ALERT de première détection | Non-régression §5.7/INV-56-09. |
| TC-NR-07 | Anti-flood audit | Sur état déjà CORRUPTED, répétitions n’ajoutent pas d’alertes | Non-régression CA-56-15. |
7. Tests négatifs et adversariaux
| Test ID | Entrée invalide / abus | Résultat attendu | Observable |
| TC-NEG-01 | eventId injection (' OR 1=1 --) | ERR-56-01 | Rejet explicite, aucun effet secondaire |
| TC-NEG-02 | UUID non-v4 mais format UUID | ERR-56-01 | Rejet regex v4 |
| TC-NEG-03 | eventHash uppercase hex | ERR-56-03 | Rejet case-sensitive |
| TC-NEG-04 | merklePath[i] avec caractère non-hex | ERR-56-03 | Rejet format élément |
| TC-NEG-05 | merklePath.length = 21 | ERR-56-03 | Rejet borne max |
| TC-NEG-06 | merklePath.length > ceil(log2(treeSize)) | ERR-56-03 | Rejet borne dynamique |
| TC-NEG-07 | treeSize = 0 ou 10001 | ERR-56-03 | Rejet bornes numériques |
| TC-NEG-08 | leafIndex = treeSize ou -1 | ERR-56-03 | Rejet contrainte d’index |
| TC-NEG-09 | Métadonnée ETA source non UTC (ex: +02:00) | Le service doit produire une ETA UTC (Z) dans la réponse pending; si conversion impossible, ERR-56-05 | Vérification stricte sortie UTC |
| TC-NEG-10 | Payload sortant contient eventPayload ou donnée métier brute | Non-conformité INV-56-03 | Contrôle de schéma sortant échoue |
| TC-NEG-11 | hashAlgorithm='sha-256' (mauvaise casse) | ERR-56-03 | Rejet littéral exact |
| TC-NEG-12 | merklePath permuté (ordre non contractuel) | ERR-56-04 puis CORRUPTED | Mismatch computedRoot + transition persistée |
| TC-NEG-13 | treeSize=1 avec merklePath non vide | ERR-56-03 | Rejet cohérence arbre unitaire |
| TC-NEG-14 | Flood volontaire: rafale d’appels sur eventId déjà CORRUPTED | ERR-56-03 à chaque appel, 0 alerte additionnelle | Vérification anti-flood CA-56-15 |
8. Observabilité requise pour les tests
- État système : snapshots corrélés des résolutions
eventId->eventHash, leaf/tree Merkle, finalité batch, état disponibilité. - Réponse service : payload
MerkleProofResult ou code contractuel (ERR-56-01..05) avec horodatage UTC. - Journal d’audit :
requestId, eventId, décision validation format, décision auto-vérification, transition d’état, code, timestamp UTC. - Événement sécurité :
SECURITY_ALERT avec severity=CRITICAL, eventId, errorCode (ERR-56-03/ERR-56-04), requestId, émis uniquement lors de la transition initiale vers CORRUPTED. - Contrôle anti-flood : preuve corrélée que les appels ultérieurs sur état
CORRUPTED n’ajoutent pas d’événement SECURITY_ALERT. - Export probatoire : bundle versionné (jeux de tests, réponses, logs corrélés, rapport off-chain SHA-256, rapport perf P95).
9. Règles non testables
| Règle | Raison | Impact |
| Opposabilité CA-56-05 (P95 10 ms) | Environnement benchmark officiel peut rester non figé. | Majeur |
Couverture universelle eventId -> eventHash tous types d’événements | Source canonique exacte non explicitée pour tous types. | Majeur |
| Preuve absolue INV-56-08 “aucun secret en clair persisté” | Vérifiable partiellement sur PD-56; preuve globale nécessite périmètre système complet. | Majeur |
10. Verdict QA
- Testable intégralement sur cœur contractuel (formats, statuts, transitions, codes erreurs, auto-vérification, anti-flood audit sécurité).
- Testable partiellement sur les points dépendants d’un cadre externe (benchmark officiel, couverture inter-modules complète).
Verdict retenu : Testable partiellement (avec réserves externes non bloquantes pour le contrat PD-56 v3).