Aller au contenu

PD-172 — Scénarios de tests contractuels

1. Références

  • Spécification : PD-172-specification.md
  • Epic : EPIC-XX (référence produit : PD-186 — Backend Core sécurisé et modulaire)

2. Matrice de couverture

ID Invariant ID Critère ID Test Couverture Commentaire
INV-172-01 CA-172-01 TC-NOM-01 Oui Cohérence multi-instances sur tuple normalisé identique.
INV-172-02 CA-172-02 TC-NOM-02 Partiel 429 + headers testable; valeur métier exacte des seuils non spécifiée.
INV-172-04 CA-172-03 TC-NOM-03 Partiel Différenciation sensible/générique testable; seuils exacts manquants.
INV-172-01 CA-172-04 TC-NOM-04 Oui Variation unitaire de chaque dimension V1.
INV-172-01 CA-172-05 TC-NOM-05, TC-NOM-06 Partiel Mécanisme burst+sustained testable; bornes contractuelles manquantes.
INV-172-03 CA-172-06 TC-NOM-07 Oui Refus tracé en log structuré + métrique.
INV-172-03 CA-172-07 TC-NOM-08 Partiel Exploitabilité testable, granularité/labels max à clarifier.
INV-172-05 CA-172-08 TC-NOM-09, TC-NOM-10, TC-NOM-11 Partiel Matrice fail-open/fail-closed testable; code HTTP fail-closed exact ambigu.
N/A CA-172-09 TC-NOM-12 Partiel Mesure P99 testable en env cible instrumenté uniquement.
INV-172-08 CA-172-10 TC-NOM-13 Oui Stabilité config au boot et absence de reload live.
INV-172-07 N/A TC-NOM-14 Oui Aucune whitelist/bypass active en V1.
INV-172-06 N/A TC-NOM-15, TC-NOM-16 Oui Transitions autorisées uniquement, terminaux stricts.
INV-172-09 N/A TC-INV-09 Partiel Vérification documentaire/contrat, pas purement runtime.
INV-172-10 N/A TC-NOM-17 Oui Non-applicabilité crypto démontrée fonctionnellement.

3. Scénarios de test – Flux nominaux

Préconditions communes (P0) pour TC-NOM-01 à TC-NOM-17 :

  • Environnement de test figé avec 2 instances backend (A,B) version identique.
  • Redis dédié, DB index conforme, initialisé à vide avant chaque test.
  • Snapshot de configuration boot C_boot archivé (familles de routes, limites, fenêtres).
  • Horloges synchronisées; campagne exécutée sans bascule de fenêtre non maîtrisée.
  • Sondes d’observabilité accessibles (logs structurés + métriques) avec corrélation request_id.
  • Sonde de logique métier disponible pour prouver exécution/non-exécution (compteur d’effet de bord).
TEST-ID: TC-NOM-01
Référence spec: INV-172-01, CA-172-01

GIVEN
  - P0
  - Tuple normalisé T = (route, ip, user_id, tenant) identique pour toutes les requêtes
WHEN
  - Le trafic saturant T est réparti entre A et B jusqu’au dépassement
THEN
  - La décision de refus est identique quelle que soit l’instance ciblée
AND
  - Le compteur restant observé est cohérent inter-instances pour T
TEST-ID: TC-NOM-02
Référence spec: INV-172-02, CA-172-02

GIVEN
  - P0
  - Une route protégée avec quota atteignable dans la fenêtre active
WHEN
  - Une requête supplémentaire est émise après dépassement du quota
THEN
  - Réponse HTTP = 429
  - Headers présents et conformes regex: X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After
AND
  - Aucun effet métier n’est exécuté (compteur inchangé)
TEST-ID: TC-NOM-03
Référence spec: INV-172-04, CA-172-03

GIVEN
  - P0
  - Une route sensible et une route générique avec profils chargés depuis C_boot
WHEN
  - Deux campagnes strictement identiques sont exécutées (même tuple, même cadence)
THEN
  - La route sensible atteint le refus plus tôt sur au moins une fenêtre ou dimension
AND
  - La différenciation est observable en réponse et métriques
TEST-ID: TC-NOM-04
Référence spec: INV-172-01, CA-172-04

GIVEN
  - P0
  - Un tuple de base saturé proche de la limite
WHEN
  - Quatre essais sont exécutés en ne modifiant qu’une dimension à la fois: ip puis user_id puis tenant puis route
THEN
  - Chaque variation produit une décision recalculée sur une clé distincte
AND
  - Aucune autre dimension n’explique le changement de décision
TEST-ID: TC-NOM-05
Référence spec: INV-172-01, CA-172-05

GIVEN
  - P0
  - Paramètres burst/sustained lus depuis C_boot
WHEN
  - burst est dépassé sans dépasser sustained
THEN
  - Décision = THROTTLED immédiate
AND
  - La cause de refus est attribuée à la fenêtre burst
TEST-ID: TC-NOM-06
Référence spec: INV-172-01, CA-172-05

GIVEN
  - P0
  - Paramètres burst/sustained lus depuis C_boot
WHEN
  - sustained est dépassé avec trafic cadencé pour rester sous burst à chaque sous-fenêtre
THEN
  - Décision = THROTTLED
AND
  - La cause de refus est attribuée à la fenêtre sustained
TEST-ID: TC-NOM-07
Référence spec: INV-172-03, CA-172-06

GIVEN
  - P0
  - Au moins un refus THROTTLED et un refus DENIED_DEGRADED générés
WHEN
  - Les observables sont interrogés par request_id
THEN
  - Chaque refus possède un log structuré corrélé
AND
  - Chaque refus incrémente une métrique dédiée
TEST-ID: TC-NOM-08
Référence spec: INV-172-03, CA-172-07

GIVEN
  - P0
  - Trafic mixte contrôlé (allowed/refused, familles auth/read/costly)
WHEN
  - Les métriques d’exploitation sont extraites sur intervalle fixe
THEN
  - Les comptes sont disponibles par route, famille, cause, type de refus
AND
  - La somme des métriques est réconciliable avec le volume injecté
TEST-ID: TC-NOM-09
Référence spec: INV-172-05, CA-172-08

GIVEN
  - P0
  - Redis indisponible
  - Route de famille read
WHEN
  - Une requête valide est envoyée
THEN
  - Décision = BYPASS_DEGRADED (fail-open contrôlé)
AND
  - La logique métier est exécutée
TEST-ID: TC-NOM-10
Référence spec: INV-172-05, INV-172-02, CA-172-08

GIVEN
  - P0
  - Redis indisponible
  - Route de famille auth
WHEN
  - Une requête valide est envoyée
THEN
  - Décision = DENIED_DEGRADED (fail-closed)
AND
  - La logique métier n’est pas exécutée
TEST-ID: TC-NOM-11
Référence spec: INV-172-05, INV-172-02, CA-172-08

GIVEN
  - P0
  - Redis indisponible
  - Route de famille costly
WHEN
  - Une requête valide est envoyée
THEN
  - Décision = DENIED_DEGRADED (fail-closed)
AND
  - La logique métier n’est pas exécutée
TEST-ID: TC-NOM-12
Référence spec: CA-172-09

GIVEN
  - P0
  - Protocole de charge reproductible (durée, QPS, concurrence, mix routes) figé
WHEN
  - La campagne est exécutée et la latence du check rate-limit est mesurée
THEN
  - P99(latency_rate_limit_check) <= 5 ms
AND
  - Le check effectue au plus 1 RTT Redis par requête
TEST-ID: TC-NOM-13
Référence spec: INV-172-08, CA-172-10

GIVEN
  - P0
  - Système démarré avec configuration C1
WHEN
  - La source de config est modifiée vers C2 sans redémarrage, puis avec redémarrage contrôlé
THEN
  - Sans redémarrage: comportement inchangé (C1)
AND
  - Après redémarrage: comportement conforme à C2
TEST-ID: TC-NOM-14
Référence spec: INV-172-07

GIVEN
  - P0
  - Tentatives explicites de bypass (headers internes, flags, pseudo-liste d’exemption)
WHEN
  - Le trafic saturant est exécuté
THEN
  - Aucune exemption n’est appliquée
AND
  - Les décisions restent strictement celles du rate-limit normal
TEST-ID: TC-NOM-15
Référence spec: INV-172-06

GIVEN
  - P0
  - Cinq contextes contrôlés couvrant toutes transitions autorisées depuis RECEIVED
WHEN
  - Une requête est exécutée par contexte
THEN
  - Transition observée = RECEIVED vers un unique état terminal autorisé
AND
  - Aucun état hors enum contractuel n’apparaît
TEST-ID: TC-NOM-16
Référence spec: INV-172-06

GIVEN
  - P0
  - Traces d’état intra-requête activées
WHEN
  - Une requête atteint un état terminal
THEN
  - Aucune transition sortante n’est observée depuis cet état terminal
AND
  - Une seule décision finale est émise par request_id
TEST-ID: TC-NOM-17
Référence spec: INV-172-10

GIVEN
  - P0
  - Deux requêtes identiques sur dimensions V1, l’une avec métadonnées crypto, l’autre sans
WHEN
  - Les deux requêtes sont exécutées dans le même état de quota
THEN
  - La décision rate-limit est identique
AND
  - Aucune dépendance envelope encryption n’est requise

4. Scénarios de test – Cas d’erreur

TEST-ID: TC-ERR-01
Référence spec: ERR-172-01

GIVEN
  - Quota burst ou sustained déjà dépassé
WHEN
  - Une requête additionnelle est envoyée
THEN
  - 429 + headers standards obligatoires
  - decision_state = THROTTLED
AND
  - Absence d’exécution métier
TEST-ID: TC-ERR-02
Référence spec: ERR-172-02

GIVEN
  - Redis indisponible
  - Route fail-closed (auth ou costly)
WHEN
  - Une requête valide est envoyée
THEN
  - Refus fail-closed + decision_state = DENIED_DEGRADED
AND
  - Absence d’exécution métier
  - Vérification du code HTTP exact = NON TESTABLE tant que Q-172-03 n’est pas tranché
TEST-ID: TC-ERR-03
Référence spec: ERR-172-03

GIVEN
  - Contexte dimensions invalide ou incomplet (ip/user/tenant/route)
WHEN
  - La requête est traitée
THEN
  - 400 + decision_state = REJECTED_INVALID_CONTEXT
AND
  - Absence d’exécution métier
TEST-ID: TC-ERR-04
Référence spec: ERR-172-04

GIVEN
  - Capacité d’injection de faute test activée pour produire une redis_key hors regex contractuelle
WHEN
  - Une requête au contexte valide est traitée
THEN
  - 500 + code ERR-172-04
AND
  - Absence d’exécution métier
TEST-ID: TC-ERR-05
Référence spec: ERR-172-05

GIVEN
  - Configuration de boot invalide (dimension manquante, fenêtre invalide, enum invalide)
WHEN
  - Le service démarre
THEN
  - Démarrage refusé
AND
  - Aucun endpoint ne sert de trafic
TEST-ID: TC-ERR-06
Référence spec: ERR-172-06

GIVEN
  - Timeout du backend de quota simulé
WHEN
  - Requêtes envoyées sur read puis auth/costly
THEN
  - read => BYPASS_DEGRADED
  - auth/costly => DENIED_DEGRADED
AND
  - Observabilité porte explicitement la cause timeout
TEST-ID: TC-ERR-07
Référence spec: ERR-172-07

GIVEN
  - Chaîne proxy client ambiguë/non fiable empêchant résolution IP sûre
WHEN
  - Une requête est envoyée
THEN
  - 400 + code ERR-172-07
AND
  - Absence d’exécution métier
TEST-ID: TC-ERR-08
Référence spec: ERR-172-08

GIVEN
  - Campagne adversariale de cardinalité anormale des clés
WHEN
  - Le seuil opérationnel de saturation est franchi
THEN
  - Dégradation contrôlée activée + alerte exploitation émise
AND
  - Service reste stable (pas de crash)

5. Tests d’invariants (non négociables)

Invariant Test(s) dédiés Observable Commentaire
INV-172-01 TC-NOM-01, TC-NOM-04, TC-NOM-05, TC-NOM-06 Décision identique pour même tuple/fenêtres; recalcul si dimension modifiée Cohérence distribuée + multi-dimensions.
INV-172-02 TC-NOM-02, TC-NOM-10, TC-NOM-11, TC-ERR-03 Aucun effet métier sur refus Atomicité quota-métier.
INV-172-03 TC-NOM-07, TC-ERR-01, TC-ERR-02, TC-ERR-06 Log structuré + métrique par refus Exploitabilité refus obligatoire.
INV-172-04 TC-NOM-03 Profil sensible plus restrictif Hiérarchie de protection.
INV-172-05 TC-NOM-09, TC-NOM-10, TC-NOM-11, TC-ERR-06 read fail-open; auth/costly fail-closed Matrice Redis-down respectée.
INV-172-06 TC-NOM-15, TC-NOM-16 Uniquement transitions autorisées; terminaux sans sortie Machine d’états normative.
INV-172-07 TC-NOM-14 Aucune exemption active Whitelist/bypass interdit en V1.
INV-172-08 TC-NOM-13, TC-ERR-05 Configuration appliquée uniquement au boot Stabilité opérationnelle.
INV-172-09 TC-INV-09 Formats runtime alignés sur §5.1 sans divergence Vérification documentaire + schémas.
INV-172-10 TC-NOM-17 Décision RL indépendante des métadonnées crypto Non-applicabilité crypto confirmée.
TEST-ID: TC-INV-09
Référence spec: INV-172-09

GIVEN
  - Canon contractuel (§5.1) figé
  - Artefacts de validation exposés (schema config, validation API, contrats d’erreurs)
WHEN
  - Une vérification de cohérence compare tous formats/enums/regex utilisés
THEN
  - Chaque donnée de §5.1 a une unique définition effective
AND
  - Toute divergence de format est classée non-conformité

6. Tests de non-régression

Test ID Objet Observable Commentaire
TC-NR-01 Quota global inter-instances Résultat TC-NOM-01 inchangé après montée de version Protège CA-172-01.
TC-NR-02 Contrat 429 + headers Résultat TC-NOM-02 inchangé Protège CA-172-02.
TC-NR-03 Matrice fail-open/fail-closed Résultats TC-NOM-09/10/11 inchangés Protège CA-172-08.
TC-NR-04 Blocage exécution métier sur refus Sonde métier inchangée sur refus Protège INV-172-02.
TC-NR-05 Machine d’états terminale Résultats TC-NOM-15/16 inchangés Protège INV-172-06.
TC-NR-06 Config figée au boot Résultat TC-NOM-13 inchangé Protège INV-172-08/CA-172-10.
TC-NR-07 Observabilité des refus Résultat TC-NOM-07 inchangé Protège INV-172-03/CA-172-06.

7. Tests négatifs et adversariaux

Test ID Entrée invalide / abus Résultat attendu Observable
TC-NEG-01 route invalide (regex/longueur) 400 ERR-172-03 Réponse + log invalid_context
TC-NEG-02 dimension hors enum (IP, api_key) 400 ERR-172-03 Réponse + log invalid_context
TC-NEG-03 identifier invalide (caractère interdit / >128) 400 ERR-172-03 Réponse + log invalid_context
TC-NEG-04 Contexte incomplet (tenant ou user absent) 400 ERR-172-03 Réponse + REJECTED_INVALID_CONTEXT
TC-NEG-05 Chaîne proxy forgée / ambiguë 400 ERR-172-07 Réponse + trace résolution IP
TC-NEG-06 Bruteforce distribué sur A/B même tuple Refus global cohérent Réponses + métriques cross-instance
TC-NEG-07 Injection headers de bypass (X-Internal-*) Aucun bypass, quota normal Réponses + absence d’état bypass whitelist
TC-NEG-08 Tempête de timeouts Redis Dégradation par famille conforme BYPASS_DEGRADED sur read, DENIED_DEGRADED sinon
TC-NEG-09 Spray haute cardinalité clés Dégradation contrôlée + alerte Alarme ops + stabilité service

8. Observabilité requise pour les tests

  • État système : disponibilité Redis, instance backend, snapshot C_boot, état décisionnel final par requête.
  • Réponse API : code HTTP, X-RateLimit-Limit, X-RateLimit-Remaining, Retry-After, code d’erreur ERR-172-*.
  • Journal d’audit : log structuré corrélable (request_id, route, route_family, decision_state, cause, instance_id, horodatage UTC).
  • Événement signé / horodaté : horodatage obligatoire pour rejouabilité; signature cryptographique non spécifiée contractuellement.
  • Export probatoire : jeu d’entrées de test, captures réponses, requêtes métriques, extraits logs, snapshot config, rapport de campagne perf.

9. Règles non testables

Règle Raison Impact
Valeurs exactes burst.max_requests Non fournies au contrat (§5.2) Bloquant
Valeurs exactes burst.window_seconds Non fournies au contrat (§5.2) Bloquant
Valeurs exactes sustained.max_requests Non fournies au contrat (§5.2) Bloquant
Valeurs exactes sustained.window_seconds Non fournies au contrat (§5.2) Bloquant
burst_window_ttl / sustained_window_ttl SLA numériques absents (§5.3) Majeur
degraded_clearing_cycles Valeur N absente (§5.3, §5.7) Majeur
Paramètres de réconciliation (cron, seuil orphelin) Données absentes (§5.7) Majeur
Code HTTP exact fail-closed Q-172-03 non tranchée (503 ?), §6 ambigu Majeur
Liste exhaustive routes read fail-open Non fournie (§10.2 Q-172-04) Majeur
Format final identifier (brut vs pseudonymisé) Contradiction RG/clé Redis non arbitrée (§5.1, §10.2 Q-172-05) Majeur
Politique santé/monitoring vs no-whitelist Contradiction potentielle non résolue (Q-172-06) Majeur
Granularité labels métriques et cardinalité max Non définies (Q-172-09) Majeur
Convention publique stable ERR-172-* Non figée (Q-172-10) Mineur

10. Verdict QA

  • ⚠️ Testable partiellement (avec réserves listées)

Motif : les mécanismes principaux (cohérence distribuée, machine d’états, observabilité, stratégie Redis-down, non-régression) sont testables de manière déterministe; la conformité complète reste bloquée par les paramètres contractuels manquants/ambigus listés en section 9.