PD-284 — Scénarios de tests contractuels (v3)
1. Références
- Spécification :
PD-284-specification.md (v3) - Epic :
EPIC-XX (identifiant réel à confirmer, cf. Q-284-01)
2. Matrice de couverture
| ID Invariant | ID Critère | ID Test | Couverture | Commentaire |
| INV-284-01 | CA-284-02 | TC-NOM-01 | Oui | Bouton urgent visible pour account_type != minor + quota affiché |
| INV-284-02 | CA-284-01 | TC-NOM-02 | Oui | Compte minor : aucun bouton urgent affiché |
| INV-284-03 | CA-284-03 | TC-NOM-03 | Oui | Bouton disabled quand quota=0 (tooltip "Quota épuisé") |
| INV-284-03 | CA-284-04 | TC-NOM-15 | Oui | Bouton disabled quand has_active_urgent_seal=true |
| INV-284-04 | CA-284-03 | TC-NOM-03 | Oui | Tooltip "Quota épuisé ce mois" quand quota=0 |
| INV-284-04 | CA-284-04 | TC-NOM-15 | Oui | Tooltip "Scellement urgent déjà en cours" quand urgent actif |
| INV-284-05 | CA-284-11 | TC-NOM-10 | Oui | Badge dégradation reflète flags serveur uniquement |
| INV-284-06 | CA-284-05 | TC-NOM-05 | Oui | Transitions autorisées selon §5.7 |
| INV-284-06 | CA-284-05 | TC-ERR-04 | Oui | État inconnu / transition interdite → erreur contrôlée |
| INV-284-07 | CA-284-05 | TC-NOM-05 | Oui | Table des sorties par état appliquée strictement |
| INV-284-08 | CA-284-08 | TC-NOM-06 | Oui | SEALED terminal + notification succès |
| INV-284-08 | CA-284-09 | TC-NOM-07 | Oui | FAILED_TIMEOUT terminal + notification échec |
| INV-284-08 | CA-284-05 | TC-ERR-06 | Oui | Toute transition sortante depuis terminal interdite |
| INV-284-09 | CA-284-06 | TC-NOM-14 | Oui | Perte réseau → badge Hors ligne immédiat |
| INV-284-09 | CA-284-06 | TC-NOM-08 | Oui | Échec SSE sans perte réseau → bascule polling silencieuse (pas de badge) |
| INV-284-10 | N/A | TC-INV-09 | Oui | Artefacts sensibles (tsa_token_ref, tokens) protégés iOS Keychain/SecureStore |
| INV-284-11 | N/A | TC-INV-11 | Oui | Artefacts publics (hash, merkle, tx) non classés sensibles |
| INV-284-12 | CA-284-08 | TC-NOM-06 | Oui | Deep-link succès vers seal_id |
| INV-284-12 | CA-284-09 | TC-NOM-07 | Oui | Deep-link échec vers seal_id |
| INV-284-13 | N/A | TC-INV-10 | Partielle | Vérification périmètre backend via traçage réseau |
| N/A | CA-284-05 | TC-NOM-04 | Oui | Après POST, GET /seals/{id}/status AVANT ouverture SSE |
| N/A | CA-284-07 | TC-NOM-16 | Oui | Déduplication client par event_id (cache N=100) |
| N/A | CA-284-08 | TC-NOM-17 | Oui | Tri par sequence_number + resync si gap |
| N/A | CA-284-10 | TC-NOM-13 | Oui | Latence rendu événement→UI P95 ≤ 100 ms |
| N/A | CA-284-11 | TC-NOM-11 | Oui | Panneau expert absent si mode_expert=false |
| N/A | CA-284-12 | TC-NOM-12 | Oui | Champs expert progressifs selon état et payload §5.12 |
| N/A | §5.12 | TC-NOM-18 | Oui | Payload SSE contractualisé par état (champs requis) |
3. Scénarios de test – Flux nominaux
TEST-ID: TC-NOM-01
Référence spec: INV-284-01, CA-284-02
GIVEN
- Utilisateur authentifié account_type=standard
- document_id valide
- Donnée serveur: urgent_quota_remaining=2
WHEN
- L'utilisateur ouvre l'écran détail document
THEN
- Le bouton "Scellement urgent" est visible
- Le bouton est actif
- Le quota restant affiché vaut "2" (strictement cohérent payload)
AND
- Aucun message d'inéligibilité n'est affiché
TEST-ID: TC-NOM-02
Référence spec: INV-284-02, CA-284-01
GIVEN
- Utilisateur authentifié account_type=minor
- document_id valide
WHEN
- L'utilisateur ouvre l'écran détail document
THEN
- Aucune occurrence du bouton "Scellement urgent" n'est rendue
AND
- Aucune action UI ne permet de déclencher un urgent explicite
TEST-ID: TC-NOM-03
Référence spec: INV-284-01, CA-284-03 (v2)
GIVEN
- Utilisateur account_type=premium
- urgent_quota_remaining=0
- has_active_urgent_seal=false
- document_id valide
WHEN
- L'utilisateur ouvre l'écran détail document
THEN
- Le bouton urgent est visible et disabled (jamais absent)
- La raison explicite "quota épuisé" est affichée
AND
- Aucune requête de déclenchement urgent n'est émise sur interaction
TEST-ID: TC-NOM-04
Référence spec: Flux A §5.1, CA-284-05, §5.12 (v3)
GIVEN
- Bouton urgent visible et actif
- Mock API POST urgent répond succès avec seal_id valide
- Mock API GET /seals/{id}/status disponible
WHEN
- L'utilisateur appuie sur "Scellement urgent"
THEN
- Un feedback immédiat de chargement est visible
- Le client appelle GET /seals/{id}/status avant toute ouverture SSE
- La carte de progression du scellement s'affiche avec l'état retourné par GET
AND
- L'abonnement SSE est initialisé uniquement après succès du GET status
TEST-ID: TC-NOM-05
Référence spec: INV-284-04, INV-284-05, CA-284-05, §5.7
GIVEN
- Carte active pour seal_id S
- Flux SSE fournit la séquence:
RECEIVED -> QUEUED_PRIORITY -> TSA_PENDING -> TSA_SEALED -> ANCHOR_PENDING -> SEALED
WHEN
- Chaque événement valide est reçu dans l'ordre
THEN
- L'UI reflète exactement la séquence sans saut interdit
- L'étape visuelle "Scellé" n'apparaît qu'à l'état SEALED
AND
- Aucun retour arrière d'étape n'est affiché
TEST-ID: TC-NOM-06
Référence spec: INV-284-06, INV-284-08, CA-284-08, Flux E
GIVEN
- Scellement en ANCHOR_PENDING pour seal_id S
- Canal notification mobile disponible
WHEN
- Événement terminal SEALED reçu
THEN
- L'UI passe en état terminal succès
- Une notification succès est émise
- Le deep-link ouvre l'écran détail du scellement S
AND
- Aucun polling/SSE supplémentaire n'est requis pour transition ultérieure
TEST-ID: TC-NOM-07
Référence spec: INV-284-06, INV-284-08, CA-284-09, Flux E
GIVEN
- Scellement en cours pour seal_id S
- Canal notification mobile disponible
WHEN
- Événement terminal FAILED_TIMEOUT reçu
THEN
- L'UI passe en état terminal échec
- Le message de contact support est visible
- Une notification échec est émise
- Le deep-link ouvre l'écran détail du scellement S en terminal
AND
- Aucune transition sortante n'est ensuite affichable
TEST-ID: TC-NOM-08
Référence spec: INV-284-07, CA-284-06, Flux B (v2)
GIVEN
- Suivi actif via SSE pour seal_id S
- Connectivité réseau disponible (pas de coupure réseau)
- Paramètre sse_reconnect_base_delay=1s
WHEN
- La connexion SSE échoue 3 fois consécutivement
THEN
- Les tentatives de reconnexion respectent un backoff exponentiel: 1s, 2s, 4s
- Le badge "Hors ligne" n'est pas affiché (pas de perte réseau)
- Le client bascule silencieusement en polling périodique toutes les 5 secondes
AND
- Le journal client trace explicitement la séquence SSE retry puis bascule SSE->polling
TEST-ID: TC-NOM-09
Référence spec: Flux B §5.2
GIVEN
- Le client est en mode polling après failover SSE
WHEN
- Le canal SSE redevient disponible et connexion réussie
THEN
- Le client revient en mode SSE
- Le polling est arrêté
AND
- L'état affiché reste cohérent avec le dernier événement valide reçu
TEST-ID: TC-NOM-10
Référence spec: INV-284-05, CA-284-11, Flux C (v3)
GIVEN
- Scellement en cours
- Backend envoie successivement degradation_flag: none, delayed, critical (enum §5.3 v3)
WHEN
- Les événements sont reçus
THEN
- Badge absent pour none, "En retard" pour delayed, "Critique" pour critical
AND
- Aucun badge n'est inféré à partir d'un calcul local de durée si flag absent
TEST-ID: TC-NOM-11
Référence spec: Flux D, CA-284-11
GIVEN
- Préférence utilisateur mode_expert=false
WHEN
- L'utilisateur consulte un scellement à n'importe quel état
THEN
- Le panneau "Mode expert" est absent
AND
- Aucun champ expert n'est rendu
TEST-ID: TC-NOM-12
Référence spec: Flux D, CA-284-12, §5.12 (v2)
GIVEN
- Préférence utilisateur mode_expert=true
- seal_id valide
WHEN
- Les états progressent: RECEIVED -> TSA_SEALED -> ANCHOR_PENDING -> SEALED
THEN
- hash_document apparaît dès réception valide
- tsa_token_ref apparaît au plus tôt à TSA_SEALED
- merkle_root apparaît quand backend le fournit
- blockchain_tx_hash + proof package apparaissent uniquement à SEALED
AND
- Les champs non disponibles restent masqués sans rupture de carte
TEST-ID: TC-NOM-13
Référence spec: Paramètre render_latency_after_valid_event, CA-284-10
GIVEN
- Campagne de 500 événements SSE valides sur appareil de référence (iPhone 12+)
- Horodatage t_event (réception) et t_render (UI stable) instrumenté
WHEN
- La progression complète est rejouée
THEN
- La distribution de latence respecte P95 <= 100 ms
AND
- Tout résultat > 100 ms au P95 est classé non conforme contractuel
TEST-ID: TC-NOM-14
Référence spec: INV-284-07, Cas d'erreur "Coupure réseau" (v2)
GIVEN
- Dernier état valide affiché = TSA_SEALED
- Réseau indisponible
WHEN
- La perte réseau est détectée
THEN
- L'UI conserve TSA_SEALED
- Le badge "Hors ligne" est affiché immédiatement
AND
- La reprise automatique est tentée jusqu'au retour connectivité
TEST-ID: TC-NOM-15
Référence spec: CA-284-04 (v3), champ has_active_urgent_seal
GIVEN
- Utilisateur éligible urgent
- urgent_quota_remaining > 0
- has_active_urgent_seal=true
WHEN
- L'utilisateur ouvre l'écran détail document
THEN
- Le bouton urgent est visible et disabled
- La raison explicite "scellement urgent déjà en cours" est affichée
AND
- Aucune requête POST urgent n'est émise sur interaction
TEST-ID: TC-NOM-16
Référence spec: §5.12 (v2), déduplication event_id
GIVEN
- Cache de déduplication client configuré à N=100 event_id
- État courant TSA_PENDING
WHEN
- Le client reçoit plusieurs événements identiques avec le même event_id
THEN
- Seul le premier événement est appliqué
- Les doublons sont ignorés silencieusement sans effet visuel secondaire
AND
- En mode debug uniquement, une trace telemetry de déduplication est émise (pas de toast, pas de telemetry en production)
TEST-ID: TC-NOM-17
Référence spec: §5.12 (v2), sequence_number + resync gap
GIVEN
- Flux SSE pour seal_id S avec sequence_number croissants attendus
WHEN
- Un gap est détecté (ex: réception 41 puis 43, manque 42)
THEN
- Le client déclenche une resynchronisation via GET /seals/{id}/status
- L'UI n'applique pas d'état incohérent pendant la fenêtre de resync
AND
- Après resync, l'ordre des états est rétabli selon sequence_number
TEST-ID: TC-NOM-18
Référence spec: §5.12 (v2), payload SSE contractualisé par état
GIVEN
- Jeux d'événements SSE valides pour chaque état contractuel
WHEN
- Chaque événement est injecté
THEN
- Les champs requis par état sont présents et exploitables
- Les champs progressifs n'apparaissent qu'aux états autorisés
AND
- Tout payload conforme est rendu sans erreur contrôlée
4. Scénarios de test – Cas d’erreur
TEST-ID: TC-ERR-01
Référence spec: Cas d'erreur "POST urgent rejeté (4xx/5xx)"
GIVEN
- Bouton urgent actif
- API POST urgent répond 4xx métier
WHEN
- L'utilisateur déclenche urgent
THEN
- Message d'échec explicite affiché
- Aucune carte de progression active n'est ouverte
- Aucun abonnement SSE n'est démarré
AND
- L'état document initial reste inchangé
TEST-ID: TC-ERR-02
Référence spec: Cas d'erreur "POST urgent rejeté (4xx/5xx)"
GIVEN
- Bouton urgent actif
- API POST urgent répond 5xx
WHEN
- L'utilisateur déclenche urgent
THEN
- Message d'échec technique affiché
- Aucune progression active n'est affichée
AND
- Aucun effet secondaire de scellement n'est visualisé
TEST-ID: TC-ERR-03
Référence spec: Cas d'erreur "Événement SSE invalide (format)", "erreur contrôlée" (v2)
GIVEN
- Carte active sur seal_id S
WHEN
- Un événement SSE mal formé est reçu (champ obligatoire manquant/invalide)
THEN
- L'événement est ignoré
- Un toast non bloquant est affiché pendant 5 secondes
- Un log telemetry d'erreur contrôlée est émis
AND
- L'UI conserve le dernier état valide sans interruption
TEST-ID: TC-ERR-04
Référence spec: INV-284-04, Cas d'erreur "état inconnu", "erreur contrôlée" (v2)
GIVEN
- Dernier état valide = QUEUED_PRIORITY
WHEN
- Un événement SSE propose transition interdite vers RECEIVED ou état non listé
THEN
- Transition refusée côté affichage
- Un toast non bloquant est affiché pendant 5 secondes
- Un log telemetry d'anomalie de transition est émis
AND
- Continuité de suivi maintenue sur dernier état valide
TEST-ID: TC-ERR-05
Référence spec: Cas d'erreur "seal_id incohérent"
GIVEN
- Carte active sur seal_id S1
WHEN
- Un événement SSE reçu porte seal_id S2 != S1
THEN
- Événement ignoré
- Aucune mutation d'état de la carte S1
AND
- Journal client trace l'incohérence de corrélation
TEST-ID: TC-ERR-06
Référence spec: INV-284-06
GIVEN
- État courant SEALED (puis scénario répété avec FAILED_TIMEOUT)
WHEN
- Un événement ultérieur tente une transition sortante quelconque
THEN
- L'état affiché reste terminal inchangé
- L'événement est rejeté/ignoré comme invalide
AND
- Un log telemetry d'anomalie contrôlée est produit
TEST-ID: TC-ERR-07
Référence spec: Cas d'erreur "Donnée expert invalide"
GIVEN
- mode_expert=true
- État compatible avec affichage d'artefact expert
WHEN
- hash_document / merkle_root / blockchain_tx_hash ne respectent pas leur format contractuel
THEN
- Le(s) champ(s) invalide(s) est/sont masqué(s)
- Un toast non bloquant est affiché pendant 5 secondes
- Un log telemetry de validation de payload est émis
AND
- La carte de progression reste fonctionnelle
TEST-ID: TC-ERR-08
Référence spec: Flux E, Cas d'erreur "Push impossible : fallback email"
GIVEN
- Terminal atteint (SEALED ou FAILED_TIMEOUT)
- Envoi push indisponible simulé
WHEN
- La clôture est traitée
THEN
- L'absence de push est constatée côté client
- Le signal de fallback email backend est observable (événement/trace contractuelle)
AND
- L'utilisateur peut toujours accéder au détail via l'application
TEST-ID: TC-ERR-09
Référence spec: §5.12 (v2), resync gap sequence_number
GIVEN
- Flux SSE actif avec sequence_number attendu=120
WHEN
- Le client reçoit sequence_number=124 (gap multiple)
THEN
- Le client ne rejoue pas localement des transitions spéculatives
- Le client déclenche immédiatement un GET /seals/{id}/status de resync
AND
- L'état UI est recalé sur la réponse de resync
5. Tests d’invariants (non négociables)
| Invariant | Test(s) dédiés | Observable | Commentaire |
| INV-284-01 | TC-NOM-01 | Bouton visible pour account_type != minor | Visibilité déterministe |
| INV-284-02 | TC-NOM-02 | Absence stricte du bouton pour minor | Non ambigu |
| INV-284-03 | TC-NOM-03, TC-NOM-15 | Bouton disabled quand quota=0 ou urgent actif | Conditions d’activation v2 |
| INV-284-04 | TC-NOM-03, TC-NOM-15 | Tooltips contractuels exacts | Texte déterministe |
| INV-284-05 | TC-NOM-10 | Badge aligné flag backend, pas de calcul local | Source de vérité serveur |
| INV-284-06 | TC-NOM-05, TC-ERR-04 | Transitions autorisées/interdites + erreur contrôlée | Conformité machine d’états |
| INV-284-07 | TC-NOM-05 | Sorties autorisées/interdites vérifiées par état | Exigence explicite |
| INV-284-08 | TC-NOM-06, TC-NOM-07, TC-ERR-06 | États terminaux sans transition sortante | Fermeture stricte |
| INV-284-09 | TC-NOM-14, TC-NOM-08 | Badge Hors ligne (perte réseau) vs bascule silencieuse (échec SSE) | Distinction v2 |
| INV-284-10 | TC-INV-09 | Artefacts sensibles en stockage sécurisé (Keychain/SecureStore) | Sécurité client v2 |
| INV-284-11 | TC-INV-11 | Hash/merkle/tx non classés sensibles | Clarification NT-01 |
| INV-284-12 | TC-NOM-06, TC-NOM-07 | Deep-link ouvrable sur bon seal_id | Traçabilité utilisateur |
| INV-284-13 | TC-INV-10 | Pas de nouvelle contrainte inter-modules backend | Couverture partielle (périmètre client) |
TEST-ID: TC-INV-09
Référence spec: INV-284-09 (v2)
GIVEN
- Parcours complet jusqu'à SEALED avec mode_expert=true
WHEN
- Inspection du stockage durable client (clé/valeur, fichiers, caches applicatifs)
THEN
- Aucun tsa_token_ref en clair hors stockage sécurisé
- Aucun token auth/session en clair hors stockage sécurisé
- Les hashes (hash_document, blockchain_tx_hash) peuvent être persistés car non sensibles
AND
- Toute persistance sensible constatée utilise iOS Keychain / SecureStore
TEST-ID: TC-INV-10
Référence spec: INV-284-10, §5.10
GIVEN
- Suite de tests couvrant tous flux UI nominaux et erreurs
WHEN
- Traçage réseau applicatif complet est collecté
THEN
- Aucun nouvel appel inter-module backend hors contrats existants PD-80/PD-284 n'est observé
AND
- Aucune exigence backend additionnelle n'est déduite depuis le client
6. Tests de non-régression
| Test ID | Objet | Observable | Commentaire |
| TC-NR-01 | Régression visibilité bouton selon account_type/quota | Bouton présent et disabled quand quota=0 | Préserve INV-284-01/02 |
| TC-NR-02 | Régression machine d’états monotone | Aucun retour arrière ni saut interdit en replay | Préserve INV-284-04/05 |
| TC-NR-03 | Régression failover SSE | Backoff 1s/2s/4s puis polling 5s, arrêt polling au retour SSE | Préserve INV-284-07 |
| TC-NR-04 | Régression statut initial post-POST | GET status systématique avant SSE | Préserve Flux A v2 |
| TC-NR-05 | Régression notifications terminales | Notifications succès/échec + deep-link correct | Préserve INV-284-08 |
| TC-NR-06 | Régression mode expert progressif | Ordre d’apparition des artefacts inchangé | Préserve CA-284-12 |
| TC-NR-07 | Régression performance rendu | P95 événement→UI <= 100 ms | Préserve CA-284-10 |
| TC-NR-08 | Régression déduplication/event ordering | Cache event_id N=100 + resync sur gap sequence | Préserve §5.12 |
| TC-NR-09 | Régression sécurité stockage | Aucun artefact sensible en clair non protégé | Préserve INV-284-09 |
7. Tests négatifs et adversariaux
| Test ID | Entrée invalide / abus | Résultat attendu | Observable |
| TC-NEG-01 | seal_id non UUID dans événement SSE | Événement ignoré + erreur contrôlée (toast 5s + telemetry) | Aucun changement d’état UI |
| TC-NEG-02 | document_id invalide lors déclenchement urgent | Rejet requête (4xx) + message échec | Pas de carte progression |
| TC-NEG-03 | Injection d’état backend inconnu (state=FOO) | Erreur contrôlée non bloquante | Dernier état valide conservé |
| TC-NEG-04 | Transition inverse forcée (TSA_SEALED -> TSA_PENDING) | Rejet de transition + telemetry | Historique sans rollback |
| TC-NEG-05 | hash_document non conforme regex | Champ expert masqué non bloquant | Carte intacte + toast 5s |
| TC-NEG-06 | blockchain_tx_hash sans préfixe 0x | Lien explorateur masqué | Pas de crash UI |
| TC-NEG-07 | Tempête de doublons même event_id (>100) | Idempotence + éviction FIFO conforme cache N=100 | Étape stable, logs dédup cohérents |
| TC-NEG-08 | Réception désordonnée sequence_number | Tri correct si possible, resync si gap | Suivi cohérent sans état impossible |
| TC-NEG-09 | Échec SSE sans perte réseau | Bascule silencieuse en polling, pas de badge hors ligne | UX non bloquante |
| TC-NEG-10 | Tentative interaction urgente avec has_active_urgent_seal=true | Action refusée/désactivée avec raison | Aucun POST additionnel |
8. Observabilité requise pour les tests
- État système : état UI courant, étape visuelle, badge dégradation, badge
Hors ligne, mode transport actif (SSE/polling). - Réponse API : payloads
POST urgent, GET /seals/{id}/status, codes HTTP, messages métier, corrélation document_id/seal_id. - Journal d’audit client : transitions, erreurs contrôlées, bascules SSE/polling, retries backoff (délais exacts), validation de deep-link.
- Telemetry obligatoire : émission d’événement sur erreur contrôlée, déduplication
event_id, détection gap sequence_number, resync déclenché. - Mesure performance : horodatage réception (
t_event) et rendu stable (t_render) pour P95. - Export probatoire : capture notifications reçues, preuve d’ouverture deep-link, dump traçage réseau, rapport inspection stockage local sécurisé.
9. Règles non testables
| Règle | Raison | Impact |
Validation formelle exhaustive tsa_token_ref | Résolu v3 : format ASCII + regex ^[A-Za-z0-9:_-]{1,256}$ formalisés | Majeur → testable |
| Politique iOS hors foreground (locale vs push distante) | Décision produit non fixée (Q-284-06) | Majeur |
| Vérification quota max par plan | Valeurs non fournies (Q-284-08) | Mineur |
| Référence Epic finalisée dans l’artefact | Identifiant non fourni (Q-284-01) | Mineur |
10. Verdict QA
- ✅ Testable quasi complètement (avec réserves résiduelles listées)
Motif : les corrections v2 rendent testables les points auparavant ambigus (bouton urgent déterministe, transport SSE/reconnexion, erreur contrôlée, payload SSE contractualisé, déduplication event_id, sequence_number, statut initial via GET avant SSE, classification hors-ligne). Les réserves restantes portent principalement sur des décisions produit et quelques formalismes de format non pleinement normés.