PD-286 — Scénarios de tests contractuels
1. Références
- Spécification :
PD-286-specification.md - Epic :
EPIC-286 — Export probatoire et restitution
2. Matrice de couverture
| ID Invariant | ID Critère | ID Test | Couverture | Commentaire |
| INV-286-01 | CA-286-01 | TC-NOM-02 | Oui | Vérifie 0 < estimatedBytes <= 805306368 pour tous volumes |
| INV-286-02 | CA-286-01 | TC-ERR-01 | Oui | Dépassement 10GB -> 413 EXPORT_TOTAL_LIMIT_EXCEEDED |
| INV-286-03 | CA-286-08 | TC-INV-03 | Oui | Union des preuves = ensemble validé, sans omission/doublon |
| INV-286-04 | CA-286-03 | TC-NOM-06 | Oui | Preuve 900MB -> volume dédié exceptionnel (non scindée) |
| INV-286-05 | CA-286-02 | TC-NR-01 | Oui | Contrat legacy single-volume inchangé |
| INV-286-06 | CA-286-01 | TC-ERR-06 | Oui | volumeIndex continu, trous/doublons interdits |
| INV-286-07 | CA-286-04 | TC-NOM-04 | Oui | Recalcul + comparaison hash avant assemblage |
| INV-286-07 | CA-286-05 | TC-ERR-04 | Oui | Hash KO -> FAILED global immédiat |
| INV-286-08 | CA-286-03 | TC-NOM-03 | Oui | .pvproof unique + volumes_count + assembled_from[] |
| INV-286-09 | CA-286-06 | TC-NOM-05 | Oui | Audit WORM fail-closed contient champs requis |
| INV-286-10 | CA-286-08 | TC-INV-10 | Oui | Machine à états : toute transition non listée rejetée |
| INV-286-11 | CA-286-08 | TC-INV-11 | Oui | États terminaux sans transition sortante |
| N/A | CA-286-10 | TC-ERR-09 | Oui | Preuve atomique >10GB rejetée 413 PROOF_TOO_LARGE |
| N/A | CA-286-09 | TC-ERR-05 | Oui | Expiration TTL -> EXPIRED, reprise interdite |
3. Scénarios de test – Flux nominaux
TEST-ID: TC-NOM-01
Référence spec: INV-286-05, CA-286-02
GIVEN
- Dossier validé de taille déterministe 500MB
- ExportId UUID v4 valide
WHEN
- L’utilisateur déclenche un export
THEN
- L’API retourne un contrat single-volume exploitable sans dépendance à volumes[]
AND
- L’app produit un seul fichier .pvproof et statut final COMPLETED
TEST-ID: TC-NOM-02
Référence spec: INV-286-01, INV-286-06, CA-286-01
GIVEN
- Dossier validé de taille déterministe 2GB
- Ensemble de preuves atomiques dont chacune <= 768MB
WHEN
- L’utilisateur déclenche un export
THEN
- L’API retourne totalVolumes >= 2 et volumes[] non vide
AND
- Pour chaque volume: 0 < estimatedBytes <= 805306368 et volumeIndex couvre [0..totalVolumes-1] sans trou
TEST-ID: TC-NOM-03
Référence spec: INV-286-08, CA-286-03
GIVEN
- Réponse multi-volumes valide et téléchargements de tous volumes réussis
WHEN
- L’app termine l’assemblage
THEN
- Un unique .pvproof final est généré
AND
- Le .pvproof contient volumes_count et assembled_from[] de cardinalité = totalVolumes
TEST-ID: TC-NOM-04
Référence spec: INV-286-07, CA-286-04
GIVEN
- Export multi-volumes avec integrityHash valide par volume
WHEN
- L’app traite chaque volume séquentiellement
THEN
- L’app recalcule SHA3-256 du manifest partiel canonicalisé et compare au integrityHash attendu
AND
- Aucun volume n’est assemblé sans comparaison explicite hash attendu/hash recalculé
TEST-ID: TC-NOM-05
Référence spec: INV-286-09, CA-286-06
GIVEN
- Export multi-volumes terminé en succès
WHEN
- Les événements d’audit sont persistés
THEN
- Le journal WORM contient exportId, volumes_count, integrityHash[] et statut final COMPLETED
AND
- Les entrées sont corrélables à un seul exportId sans ambiguïté
4. Scénarios de test – Cas d’erreur
TEST-ID: TC-ERR-01
Référence spec: ERR-286-01, INV-286-02
GIVEN
- Dossier validé de taille strictement > 10_737_418_240 bytes
WHEN
- Une demande d’export est soumise
THEN
- Réponse 413 EXPORT_TOTAL_LIMIT_EXCEEDED
AND
- Aucun volume n’est émis, aucun assemblage démarré
TEST-ID: TC-NOM-06
Référence spec: INV-286-01, INV-286-04, CA-286-03
GIVEN
- Une preuve atomique unique de 900MB (> VOLUME_MAX_BYTES mais < MAX_TOTAL)
WHEN
- Le backend planifie les volumes
THEN
- La preuve est placée dans un volume dédié exceptionnel (1 seule preuve par volume)
AND
- Aucune scission de preuve n’est réalisée, le volume dépasse VOLUME_MAX_BYTES mais est accepté
TEST-ID: TC-ERR-03
Référence spec: ERR-286-03
GIVEN
- Réponse backend contenant integrityHash non conforme regex /^[0-9a-f]{64}$/
WHEN
- L’app valide le contrat reçu
THEN
- Le contrat est rejeté et l’export passe FAILED
AND
- Audit fail-closed d’échec émis
TEST-ID: TC-ERR-04
Référence spec: ERR-286-04, INV-286-07, CA-286-05
GIVEN
- Un volume téléchargé altéré en transit
WHEN
- Hash recalculé != integrityHash attendu
THEN
- Arrêt immédiat du flux et statut export FAILED
AND
- Aucun succès partiel ni fichier final COMPLETED
TEST-ID: TC-ERR-05
Référence spec: ERR-286-05, CA-286-09
GIVEN
- Export en cours avec SIGNED_URL_TTL ou EXPORT_SESSION_TTL expiré
WHEN
- L’app tente le volume suivant ou la reprise
THEN
- Transition vers EXPIRED
AND
- Reprise interdite sur le même exportId (nouvelle requête obligatoire)
TEST-ID: TC-ERR-06
Référence spec: ERR-286-06, INV-286-06
GIVEN
- volumes[] contenant trou d’index ou doublon
WHEN
- L’app valide la structure de plan d’export
THEN
- Assemblage refusé, statut FAILED
AND
- Aucun .pvproof final n’est produit
TEST-ID: TC-ERR-07
Référence spec: ERR-286-07, CA-286-05
GIVEN
- Échec réseau déterministe sur téléchargement d’un volume
WHEN
- Le volume concerné est requis
THEN
- Export global FAILED
AND
- Aucun statut de succès partiel n’est exposé
TEST-ID: TC-ERR-08
Référence spec: ERR-286-08
GIVEN
- Donnée d’entrée mal formée (exportId invalide ou signedUrl non HTTPS/format invalide)
WHEN
- Validation contractuelle exécutée
THEN
- Code 400/422 selon champ
AND
- Aucune tentative d’assemblage n’est lancée
TEST-ID: TC-ERR-09
Référence spec: ERR-286-02, CA-286-10
GIVEN
- Une preuve atomique unique de 11GB (> MAX_TOTAL_EXPORT_BYTES)
WHEN
- Le backend planifie les volumes
THEN
- Réponse 413 PROOF_TOO_LARGE
AND
- Aucun volume émis, aucun assemblage engagé
4bis. Scénarios de test — Finalisation (POST /exports/:exportId/finalize)
TEST-ID: TC-FIN-01
Référence spec: INV-286-09, §5.6
GIVEN
- Export multi-volumes en état ASSEMBLING, ownership vérifié
WHEN
- L’app appelle POST /exports/{exportId}/finalize avec {finalStatus: "COMPLETED"}
THEN
- Session transite vers COMPLETED
AND
- Audit FINAL émis via ExportAuditService.appendFinal() avec integrityHashes[] et volumes_count
AND
- Réponse 204 No Content
TEST-ID: TC-FIN-02
Référence spec: INV-286-09, §5.6
GIVEN
- Export en état DOWNLOADING, hash mismatch détecté par l’app
WHEN
- L’app appelle POST /exports/{exportId}/finalize avec {finalStatus: "FAILED", reasonCode: "HASH_MISMATCH"}
THEN
- Session transite vers FAILED
AND
- Audit FINAL émis avec status FAILED et reasonCode
AND
- Réponse 204 No Content
TEST-ID: TC-FIN-03
Référence spec: §5.6, anti-énumération
GIVEN
- exportId inconnu ou non détenu par l’utilisateur authentifié
WHEN
- L’app appelle POST /exports/{exportId}/finalize
THEN
- Réponse 404 EXPORT_NOT_FOUND (message uniforme, anti-énumération)
AND
- Aucune transition ni audit émis
TEST-ID: TC-FIN-04
Référence spec: INV-286-11, §5.6
GIVEN
- Export déjà en état terminal (COMPLETED, FAILED ou EXPIRED)
WHEN
- L’app appelle POST /exports/{exportId}/finalize
THEN
- Transition refusée par la state machine (ForbiddenStateTransitionException)
AND
- Réponse 500 ou 409
5. Tests d’invariants (non négociables)
| Invariant | Test(s) dédiés | Observable | Commentaire |
| INV-286-01 | TC-NOM-02, TC-INV-01 | estimatedBytes borné par volume | Inclut cas bordes 1 byte et 805306368 bytes |
| INV-286-02 | TC-ERR-01, TC-INV-02 | 413 au-delà 10GB | Cas limite exact 10GB inclus à tester |
| INV-286-03 | TC-INV-03 | Couverture des IDs de preuves sur tous volumes | Vérifie égalité d’ensemble + cardinalité |
| INV-286-04 | TC-ERR-02, TC-INV-04 | Aucune preuve répartie sur >1 volume | Contrôle par identifiants de preuve |
| INV-286-05 | TC-NR-01, TC-NOM-01 | Compatibilité contrat legacy | Régression API/client legacy |
| INV-286-06 | TC-ERR-06, TC-INV-06 | Continuité index [0..N-1] | Rejet trous, doublons, index hors bornes |
| INV-286-07 | TC-NOM-04, TC-ERR-04 | Recalcul hash effectif + arrêt sur mismatch | Preuve de comparaison obligatoire |
| INV-286-08 | TC-NOM-03, TC-INV-08 | .pvproof unique + métadonnées multi-volumes | assembled_from[] complet et cohérent |
| INV-286-09 | TC-NOM-05, TC-INV-09 | Écritures WORM fail-closed | Champs requis et statut final présents |
| INV-286-10 | TC-INV-10 | Transitions autorisées uniquement | Matrice d’états exhaustive |
| INV-286-11 | TC-INV-11 | États terminaux sans sortie | Toute sortie depuis terminal = rejet |
6. Tests de non-régression
| Test ID | Objet | Observable | Commentaire |
| TC-NR-01 | Contrat single-volume legacy inchangé | Clients legacy consomment manifest/signedUrls sans volumes[] obligatoire | Couvre INV-286-05 |
| TC-NR-02 | Format final .pvproof inchangé | Ouverture/validation des consommateurs existants | Ajout champs multi-volumes sans rupture |
| TC-NR-03 | Guards auth/premium inchangés | Même matrice d’accès avant/après | Hors périmètre confirmé |
| TC-NR-04 | Rate limiting inchangé | Seuils/comportements identiques avant/après | Hors périmètre confirmé |
| TC-NR-05 | Validation des preuves inchangée | Même verdict pipeline pour même entrée | Hors périmètre confirmé |
7. Tests négatifs et adversariaux
| Test ID | Entrée invalide / abus | Résultat attendu | Observable |
| TC-NEG-01 | exportId non UUIDv4 | 400 INVALID_EXPORT_ID | Code HTTP + message |
| TC-NEG-02 | integrityHash en majuscules ou longueur != 64 | 422 VOLUME_INTEGRITY_HASH_INVALID / rejet contrat | Validation regex stricte |
| TC-NEG-03 | signedUrl non HTTPS ou >4096 chars | 422 SIGNED_URL_INVALID | Validation URL |
| TC-NEG-04 | totalVolumes=0 | 500 INVALID_TOTAL_VOLUMES | Rejet backend |
| TC-NEG-05 | volumeIndex <0 | 500 INVALID_VOLUME_INDEX_RANGE | Rejet backend |
| TC-NEG-06 | Tentative de transition FAILED -> REQUESTED même exportId | Transition interdite | Erreur contractuelle + état inchangé |
| TC-NEG-07 | Tentative de transition depuis COMPLETED vers état non terminal | Transition interdite | Erreur contractuelle |
| TC-NEG-08 | Réutilisation exportId EXPIRED pour reprise | Reprise refusée | Nouvelle requête exigée |
8. Observabilité requise pour les tests
- État système : état courant export (
REQUESTED/PLANNED_*/DOWNLOADING/ASSEMBLING/COMPLETED/FAILED/EXPIRED) avec horodatage de transition. - Réponse API : payload complet (
exportId, totalVolumes, volumes[], estimatedBytes, integrityHash, codes erreur contractuels). - Journal d’audit : événements WORM corrélés (
exportId, volumeIndex, hash_ok/hash_mismatch, volumes_count, statut final). - Événement signé / horodaté : preuve d’écriture append-only et d’ordre temporel des événements.
- Export probatoire : fichier final
.pvproof, présence/absence de volumes_count et assembled_from[] selon totalVolumes.
9. Règles non testables
| Règle | Raison | Impact |
| Canonicalisation JSON exacte (Q-286-01) | RFC 8785 (JCS) référencée mais implémentation cross-platform à valider | Mineur |
| Taxonomie finale des codes d’erreur (Q-286-02) | Nomenclature non figée contractuellement | Majeur |
Durée chiffrée de rétention post FAILED/EXPIRED (Q-286-03) | Politique citée mais valeur normative absente | Mineur |
| Valeurs TTL de production finales (Q-286-04) | Bornes définies, valeurs déployées non confirmées | Mineur |
10. Verdict QA
- ⚠️ Testable partiellement (avec réserves listées)
En l’état, la conformité est testable de manière robuste sur flux, erreurs, invariants et non-régression, sous réserve de lever les ambiguïtés Q-286-01 et Q-286-02 pour éviter des verdicts divergents inter-environnements/outils.