Aller au contenu

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 Mineur Résolu - Observable renforcé via TC-ZK-01 (aucun GetObject S3 backend)
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)