PD-46 — Scénarios de tests contractuels¶
1. Références¶
- Spécification : PD-46-specification.md
- Epic : STORAGE (PD-198)
2. Matrice de couverture¶
| ID Invariant | ID Critère | ID Test | Couverture | Commentaire |
|---|---|---|---|---|
| INV-46-01 | CA-46-01, CA-46-02, CA-46-07 | TC-INV-01, TC-TTL-01 | Oui | Vérifie l'expiration du TTL et les droits d'accès |
| INV-46-02 | CA-46-03, CA-46-04 | TC-INV-02, TC-REV-01 | Oui | Révocation bloque nouvelles requêtes |
| INV-46-03 | CA-46-08 | TC-INV-03, TC-REV-02 | Oui | Téléchargement en cours non interrompu |
| INV-46-04 | CA-46-05, CA-46-06 | TC-INV-04, TC-LOG-01, TC-LOG-02 | Oui | Journalisation des événements |
| INV-46-05 | CA-46-05, CA-46-06 | TC-INV-05, TC-LOG-01 | Oui | Attribution acteur + personne morale |
| INV-46-06 | CA-46-09 | TC-ZK-01 | Oui | Zero-Knowledge (observable renforcé : aucun GetObject S3 backend) |
3. Scénarios de test – Flux nominaux¶
TEST-ID: TC-NOM-01
Référence spec: Flux 1, Critère CA-46-01
GIVEN
- Un utilisateur owner authentifié avec un JWT valide
- Un document existant dont il est propriétaire (owner_id = user_id)
WHEN
- Il demande une URL de téléchargement via GET /documents/:id/download
THEN
- Le backend retourne HTTP 200 avec une URL pre-signed S3
- L'URL contient les paramètres X-Amz-Algorithm, X-Amz-Credential, X-Amz-Expires=300
AND
- Le client peut télécharger le fichier depuis S3 avec cette URL
- L'événement DOWNLOAD_SUCCESS est journalisé avec user_id, document_id, timestamp
TEST-ID: TC-NOM-02
Référence spec: Flux 2, Critère CA-46-02
GIVEN
- Un utilisateur authentifié avec un JWT valide
- Un partage actif existe (is_active=true, expires_at > now, revoked_at IS NULL)
- Le partage accorde les droits de lecture
WHEN
- Il demande une URL de téléchargement via GET /documents/:id/download
THEN
- Le backend retourne HTTP 200 avec une URL pre-signed S3
- L'URL est valide pour 5 minutes
AND
- Le téléchargement depuis S3 réussit
- L'événement DOWNLOAD_SUCCESS est journalisé avec user_id du bénéficiaire
TEST-ID: TC-NOM-03
Référence spec: Flux 3, Critère CA-46-01 (B2B)
GIVEN
- Un partenaire authentifié via OAuth/OIDC
- Le token contient tenant_id correspondant au document
- Le token contient un rôle avec permission "document:download"
WHEN
- Il demande une URL de téléchargement via GET /documents/:id/download
THEN
- Le backend retourne HTTP 200 avec une URL pre-signed S3
AND
- L'événement DOWNLOAD_SUCCESS est journalisé avec user_id ET tenant_id
4. Scénarios de test – Cas d'erreur¶
TEST-ID: TC-ERR-01
Référence spec: Erreur HTTP 401
GIVEN
- Un utilisateur avec un token JWT invalide (signature incorrecte)
WHEN
- Il demande GET /documents/:id/download
THEN
- Le backend retourne HTTP 401
- Le body contient { "error": "UNAUTHORIZED" }
- Aucune URL n'est générée
- Aucun événement audit n'est créé
TEST-ID: TC-ERR-02
Référence spec: Erreur HTTP 403 (non owner, pas de partage)
GIVEN
- Un utilisateur authentifié valide
- Le document appartient à un autre utilisateur
- Aucun partage actif n'existe pour cet utilisateur
WHEN
- Il demande GET /documents/:id/download
THEN
- Le backend retourne HTTP 403
- Le body contient { "error": "FORBIDDEN" }
- L'événement DOWNLOAD_DENIED est journalisé
TEST-ID: TC-ERR-03
Référence spec: Erreur HTTP 403 (partage révoqué)
GIVEN
- Un utilisateur authentifié valide
- Un partage existait mais a été révoqué (revoked_at IS NOT NULL)
WHEN
- Il demande GET /documents/:id/download
THEN
- Le backend retourne HTTP 403
- Le body contient { "error": "SHARE_REVOKED" }
- L'événement DOWNLOAD_DENIED est journalisé avec raison "SHARE_REVOKED"
TEST-ID: TC-ERR-04
Référence spec: Erreur HTTP 403 (tenant incorrect B2B)
GIVEN
- Un partenaire B2B authentifié OIDC
- Le document appartient à un tenant différent du token
WHEN
- Il demande GET /documents/:id/download
THEN
- Le backend retourne HTTP 403
- Le body contient { "error": "TENANT_MISMATCH" }
TEST-ID: TC-ERR-05
Référence spec: Erreur HTTP 403 (rôle insuffisant B2B)
GIVEN
- Un partenaire B2B authentifié OIDC
- Le token ne contient pas le rôle "document:download"
WHEN
- Il demande GET /documents/:id/download
THEN
- Le backend retourne HTTP 403
- Le body contient { "error": "INSUFFICIENT_ROLE" }
TEST-ID: TC-ERR-06
Référence spec: Erreur HTTP 404
GIVEN
- Un utilisateur authentifié valide
- Le document_id n'existe pas en base
WHEN
- Il demande GET /documents/:id/download
THEN
- Le backend retourne HTTP 404
- Le body contient { "error": "NOT_FOUND" }
TEST-ID: TC-ERR-07
Référence spec: Erreur HTTP 410 (TTL expiré)
GIVEN
- Un utilisateur a obtenu une URL pre-signed
- 6 minutes se sont écoulées (TTL = 5 min dépassé)
WHEN
- Il tente de télécharger depuis S3 avec l'URL expirée
THEN
- S3 retourne HTTP 403 ou 410 (AccessDenied/ExpiredToken)
- Le téléchargement échoue
5. Tests d'invariants (non négociables)¶
| Invariant | Test(s) dédiés | Observable | Commentaire |
|---|---|---|---|
| INV-46-01 | TC-INV-01, TC-TTL-01 | URL invalide après 5min + droits vérifiés | TTL + droits |
| INV-46-02 | TC-INV-02, TC-REV-01 | HTTP 403 après révocation | Révocation bloquante |
| INV-46-03 | TC-INV-03, TC-REV-02 | Téléchargement continue | Non-interruption |
| INV-46-04 | TC-INV-04, TC-LOG-01/02 | Entrée dans registre WORM | Journalisation |
| INV-46-05 | TC-INV-05 | user_id + tenant_id dans événement | Attribution |
| INV-46-06 | TC-ZK-01 | Backend ne proxy pas le contenu | Zero-Knowledge |
TEST-ID: TC-INV-01
Référence spec: INV-46-01 (TTL + droits)
GIVEN
- Un utilisateur owner avec URL pre-signed obtenue à T0
- À T0+3min, ses droits sont révoqués
WHEN
- À T0+4min, il tente de re-demander GET /documents/:id/download
THEN
- Le backend retourne HTTP 403 (droits révoqués)
- L'ancienne URL reste potentiellement valide jusqu'à T0+5min (TTL S3)
TEST-ID: TC-INV-02
Référence spec: INV-46-02 (révocation bloque)
GIVEN
- Un utilisateur avec partage actif
- Le partage est révoqué via PUT /shares/:id (revoked_at = now)
WHEN
- Immédiatement après, il demande GET /documents/:id/download
THEN
- Le backend retourne HTTP 403 SHARE_REVOKED
- Pas de nouvelle URL générée
TEST-ID: TC-INV-03
Référence spec: INV-46-03 (téléchargement non interrompu)
GIVEN
- Un utilisateur owner initie un téléchargement de fichier volumineux (100MB)
- Le téléchargement est à 50% de progression
WHEN
- Ses droits sont révoqués pendant le transfert
THEN
- Le téléchargement en cours se termine normalement
- Le fichier est complet côté client
TEST-ID: TC-INV-04
Référence spec: INV-46-04 (journalisation)
GIVEN
- Un téléchargement réussi
WHEN
- On consulte le registre probatoire
THEN
- Une entrée DOWNLOAD_SUCCESS existe
- Contient: user_id, document_id, timestamp, result=SUCCESS
- Le registre est append-only (immutable)
TEST-ID: TC-INV-05
Référence spec: INV-46-05 (attribution)
GIVEN
- Un téléchargement B2B par un partenaire
WHEN
- On consulte l'événement audit
THEN
- L'événement contient user_id (personne physique)
- L'événement contient tenant_id (personne morale)
TEST-ID: TC-ZK-01
Référence spec: INV-46-06 (Zero-Knowledge)
GIVEN
- Un téléchargement via pre-signed URL
- Monitoring des appels S3 du backend activé (CloudTrail ou logs SDK)
WHEN
- Le client effectue un téléchargement complet
THEN
- Le backend ne voit jamais le contenu déchiffré
- Le backend ne fait pas de proxy/buffering du fichier
- Le client télécharge directement depuis S3
AND
- **Observable renforcé** : Aucune opération GetObject S3 n'est effectuée par le backend
- Seule l'opération de génération de pre-signed URL est visible (signature côté backend)
6. Tests de non-régression¶
| Test ID | Objet | Observable | Commentaire |
|---|---|---|---|
| TC-NR-01 | Téléchargement owner iOS | HTTP 200 + téléchargement OK | Flux nominal mobile |
| TC-NR-02 | Téléchargement partage PWA | HTTP 200 + téléchargement OK | Flux nominal web |
| TC-NR-03 | Téléchargement B2B API | HTTP 200 + téléchargement OK | Flux nominal partenaire |
| TC-NR-04 | Rejet token expiré | HTTP 401 | Sécurité auth |
| TC-NR-05 | Rejet accès non autorisé | HTTP 403 | Sécurité droits |
7. Tests négatifs et adversariaux¶
| Test ID | Entrée invalide / abus | Résultat attendu | Observable |
|---|---|---|---|
| TC-NEG-01 | Token JWT forgé (mauvaise signature) | HTTP 401 UNAUTHORIZED | Rejet immédiat |
| TC-NEG-02 | URL pre-signed modifiée (paramètres altérés) | HTTP 403 S3 AccessDenied | Signature invalide |
| TC-NEG-03 | Tentative de téléchargement après expiration TTL | HTTP 403/410 S3 | URL expirée |
| TC-NEG-04 | Injection SQL dans document_id | HTTP 400/404 | Paramètre invalide |
| TC-NEG-05 | Flood de requêtes de téléchargement | Rate limiting (429) | Protection anti-abuse |
| TC-NEG-06 | URL pre-signed réutilisée après révocation | Téléchargement OK si dans TTL | Fenêtre résiduelle documentée |
8. Observabilité requise pour les tests¶
- État système :
- Table
documents: owner_id, s3_key - Table
shares: is_active, expires_at, revoked_at - Tokens JWT : user_id, exp
-
Tokens OIDC : tenant_id, roles
-
Réponse API :
- Code HTTP (200, 401, 403, 404, 410, 500)
- Body JSON avec error code et message
-
URL pre-signed dans la réponse 200
-
Journal d'audit :
- Événements DOWNLOAD_SUCCESS / DOWNLOAD_DENIED
-
Champs: user_id, tenant_id, document_id, timestamp, result, reason
-
Métriques S3 :
- S3 Access Logs ou CloudTrail pour téléchargements effectifs
- Événements S3 via Lambda/SNS
9. Règles non testables¶
| Règle | Raison | Impact |
|---|---|---|
| INV-46-06 (Zero-Knowledge complet) | Le contenu déchiffré n'est visible que côté client | |
| Performance mobile 4G | Dépend de conditions réseau réelles | Hors scope - test de charge séparé |
10. Verdict QA¶
✅ Testable intégralement (avec réserves mineures sur INV-46-06)
- 6 invariants couverts
- 9 critères d'acceptation couverts
- 3 flux nominaux testés
- 7 cas d'erreur testés
- 6 tests adversariaux
- Observabilité définie
Généré par : ChatGPT (OpenCode) Date : 2026-02-11 Statut : En attente de review (Gate 3)