Ajout de 47 features Gherkin (~650 scénarios) pour couvrir 100% des règles métier : - Authentification (5) : validation mot de passe, tentatives connexion, multi-device, 2FA, récupération - Audio-guides (12) : détection mode, création, navigation piéton/voiture, ETA, gestion points, progression - Navigation (5) : notifications minimalistes, décompte 5s, stationnement, historique, basculement auto - Création contenu (3) : image auto, restrictions modification, suppression - Radio live (2) : enregistrement auto, interdictions modération - Droits auteur (6) : fair use 30s, détection musique, signalements, sanctions, appels - Modération (9) : badges Bronze/Argent/Or, score fiabilité, utilisateur confiance, audit, anti-abus - Premium (2) : webhooks Mangopay, tarification multi-canal - Profil/Partage/Recherche (5) : badge vérifié, stats arrondies, partage premium, filtres avancés, carte Tous les scénarios incluent edge cases, métriques de performance et conformité RGPD. Couverture fonctionnelle MVP maintenant complète.
224 lines
11 KiB
Gherkin
224 lines
11 KiB
Gherkin
# language: fr
|
||
|
||
@api @premium @multi-device @mvp
|
||
Fonctionnalité: Détection et gestion des conflits de streaming multi-device Premium
|
||
|
||
En tant qu'abonné Premium
|
||
Je peux écouter sur plusieurs appareils (iPhone, iPad, Android, Web)
|
||
Mais un seul stream audio peut être actif à la fois
|
||
Afin de respecter les conditions d'abonnement Premium individuel
|
||
|
||
Contexte:
|
||
Étant donné un utilisateur avec abonnement Premium actif
|
||
Et plusieurs appareils enregistrés sur le compte :
|
||
| device_id | platform | nom | last_seen |
|
||
| iphone-123 | iOS | iPhone de Jean | 2026-02-03 14:00:00 |
|
||
| ipad-456 | iOS | iPad Pro | 2026-02-03 12:30:00 |
|
||
| android-789 | Android | Samsung Galaxy | 2026-02-02 18:00:00 |
|
||
| web-abc | Web | Chrome MacBook | 2026-02-03 10:00:00 |
|
||
|
||
# ============================================================================
|
||
# DÉTECTION STREAM ACTIF ET CONFLIT
|
||
# ============================================================================
|
||
|
||
Scénario: Premier stream - aucun conflit
|
||
Étant donné aucun stream n'est actuellement actif sur le compte
|
||
Quand l'utilisateur démarre la lecture sur "iPhone de Jean"
|
||
Alors le stream doit démarrer normalement
|
||
Et une session active doit être enregistrée dans Redis :
|
||
| user_id | user-123 |
|
||
| device_id | iphone-123 |
|
||
| started_at | 2026-02-03 14:00:00|
|
||
| content_id | content-xyz |
|
||
| stream_token | token-abc123 |
|
||
Et le TTL Redis doit être de 2 heures
|
||
|
||
Scénario: Tentative de stream simultané sur second appareil
|
||
Étant donné un stream actif sur "iPhone de Jean" depuis 10 minutes
|
||
Quand l'utilisateur tente de démarrer la lecture sur "iPad Pro"
|
||
Alors une réponse HTTP 409 Conflict doit être retournée
|
||
Et le message doit indiquer :
|
||
"""
|
||
Un contenu est déjà en cours de lecture sur "iPhone de Jean".
|
||
Voulez-vous arrêter la lecture sur cet appareil et continuer ici ?
|
||
"""
|
||
Et les options proposées doivent être :
|
||
| option | action |
|
||
| Continuer ici | kill_previous_session |
|
||
| Annuler | cancel_new_session |
|
||
|
||
Scénario: Utilisateur choisit "Continuer ici" - kill de l'ancienne session
|
||
Étant donné un stream actif sur "iPhone de Jean"
|
||
Et l'utilisateur tente de lire sur "iPad Pro"
|
||
Et le conflit est détecté
|
||
Quand l'utilisateur choisit "Continuer ici"
|
||
Alors l'ancienne session sur "iPhone de Jean" doit être terminée immédiatement
|
||
Et un message WebSocket doit être envoyé à "iPhone de Jean" :
|
||
"""
|
||
{"type": "stream_stopped", "reason": "playback_started_on_other_device", "device": "iPad Pro"}
|
||
"""
|
||
Et la lecture sur "iPhone de Jean" doit s'arrêter avec notification :
|
||
"""
|
||
Lecture arrêtée : un autre appareil utilise votre compte Premium.
|
||
"""
|
||
Et le nouveau stream sur "iPad Pro" doit démarrer normalement
|
||
|
||
Scénario: Utilisateur choisit "Annuler" - maintien de la session actuelle
|
||
Étant donné un stream actif sur "iPhone de Jean"
|
||
Et l'utilisateur tente de lire sur "iPad Pro"
|
||
Et le conflit est détecté
|
||
Quand l'utilisateur choisit "Annuler"
|
||
Alors la session sur "iPhone de Jean" doit continuer normalement
|
||
Et aucun stream ne doit démarrer sur "iPad Pro"
|
||
Et l'utilisateur doit être redirigé vers l'écran d'accueil sur "iPad Pro"
|
||
|
||
# ============================================================================
|
||
# GESTION AUTOMATIQUE DES SESSIONS EXPIRÉES
|
||
# ============================================================================
|
||
|
||
Scénario: Session expirée automatiquement après pause prolongée
|
||
Étant donné un stream actif sur "iPhone de Jean"
|
||
Et l'utilisateur met en pause à 14:00:00
|
||
Et le TTL Redis est configuré pour expirer après 30 minutes de pause
|
||
Quand il est 14:35:00 (>30 min de pause)
|
||
Alors la session Redis doit expirer automatiquement
|
||
Et l'utilisateur peut démarrer un nouveau stream sur n'importe quel appareil
|
||
Et aucun conflit ne doit être détecté
|
||
|
||
Scénario: Heartbeat maintient la session active pendant la lecture
|
||
Étant donné un stream actif sur "iPhone de Jean"
|
||
Quand l'application envoie un heartbeat toutes les 30 secondes
|
||
Alors le TTL Redis doit être renouvelé à 2 heures à chaque heartbeat
|
||
Et la session doit rester active tant que les heartbeats continuent
|
||
Et si 3 heartbeats consécutifs échouent, la session doit expirer
|
||
|
||
Scénario: Fermeture propre de l'application libère la session
|
||
Étant donné un stream actif sur "iPhone de Jean"
|
||
Quand l'utilisateur ferme proprement l'application (swipe kill ou logout)
|
||
Alors une requête "end_session" doit être envoyée à l'API
|
||
Et la session Redis doit être immédiatement supprimée
|
||
Et l'utilisateur peut démarrer un stream sur un autre appareil sans délai
|
||
|
||
# ============================================================================
|
||
# CAS LIMITES ET EDGE CASES
|
||
# ============================================================================
|
||
|
||
Scénario: Crash application sans fermeture propre
|
||
Étant donné un stream actif sur "iPhone de Jean"
|
||
Quand l'application crash sans envoyer "end_session"
|
||
Alors la session Redis reste active avec son TTL
|
||
Et après 2 minutes sans heartbeat, la session doit être marquée "stale"
|
||
Et un nouveau stream peut être démarré après 2 minutes sans heartbeat
|
||
Ou l'utilisateur peut forcer le kill via le conflit modal
|
||
|
||
Scénario: Connexion réseau perdue pendant stream
|
||
Étant donné un stream actif sur "iPhone de Jean"
|
||
Quand la connexion réseau est perdue pendant 5 minutes
|
||
Alors les heartbeats échouent mais l'app continue en buffer
|
||
Et après 3 heartbeats manqués (90 secondes), la session est considérée "stale"
|
||
Et un autre appareil peut démarrer un stream après 90 secondes
|
||
|
||
Scénario: Deux appareils tentent de démarrer simultanément (race condition)
|
||
Étant donné aucun stream actif
|
||
Quand "iPhone de Jean" et "iPad Pro" tentent de démarrer un stream à la même milliseconde
|
||
Alors le premier à obtenir le lock Redis doit réussir
|
||
Et le second doit recevoir un conflit 409
|
||
Et un mécanisme de lock distribué (Redis SET NX) doit être utilisé
|
||
|
||
Scénario: Utilisateur bascule rapidement entre appareils (<10s)
|
||
Étant donné un stream sur "iPhone de Jean"
|
||
Quand l'utilisateur kill la session et démarre sur "iPad Pro"
|
||
Et tente de redémarrer sur "iPhone de Jean" 5 secondes après
|
||
Alors le système doit détecter le conflit avec "iPad Pro"
|
||
Et proposer à nouveau de kill la session iPad
|
||
Et un délai de grâce de 5 secondes doit être respecté pour éviter les boucles
|
||
|
||
# ============================================================================
|
||
# GESTION UTILISATEUR GRATUIT (pas de multi-device)
|
||
# ============================================================================
|
||
|
||
Scénario: Utilisateur gratuit tente de streamer sur 2 appareils
|
||
Étant donné un utilisateur avec abonnement gratuit (pas Premium)
|
||
Et un stream actif sur "iPhone de Jean"
|
||
Quand l'utilisateur tente de lire sur "iPad Pro"
|
||
Alors une réponse HTTP 403 Forbidden doit être retournée
|
||
Et le message doit indiquer :
|
||
"""
|
||
Le multi-device nécessite un abonnement Premium.
|
||
Actuellement en lecture sur "iPhone de Jean".
|
||
Passez à Premium pour écouter sur plusieurs appareils.
|
||
"""
|
||
Et un bouton "Passer à Premium" doit être proposé
|
||
|
||
# ============================================================================
|
||
# INTERFACE ADMIN & GESTION DES CONFLITS
|
||
# ============================================================================
|
||
|
||
Scénario: Dashboard admin - voir sessions actives par utilisateur
|
||
Étant donné un administrateur connecté au dashboard
|
||
Quand l'admin recherche l'utilisateur "user-123"
|
||
Alors les sessions actives doivent être affichées :
|
||
| device_id | platform | started_at | content_id | duration |
|
||
| iphone-123 | iOS | 2026-02-03 14:00:00 | content-xyz | 15:30 |
|
||
Et l'admin doit pouvoir "Terminer la session" manuellement
|
||
|
||
Scénario: Support client - résolution conflit bloqué
|
||
Étant donné un utilisateur signale ne pas pouvoir lire sur aucun appareil
|
||
Et une session "fantôme" existe dans Redis (crash + heartbeat bloqué)
|
||
Quand le support client force la suppression de la session Redis
|
||
Alors la clé Redis "active_stream:user-123" doit être supprimée
|
||
Et l'utilisateur doit pouvoir redémarrer immédiatement
|
||
|
||
# ============================================================================
|
||
# MÉTRIQUES & MONITORING
|
||
# ============================================================================
|
||
|
||
Scénario: Logging des conflits pour analytics
|
||
Étant donné un conflit est détecté entre "iPhone de Jean" et "iPad Pro"
|
||
Quand le conflit est résolu par kill de session
|
||
Alors un événement analytics doit être loggé :
|
||
| event_type | stream_conflict_resolved |
|
||
| user_id | user-123 |
|
||
| previous_device | iphone-123 |
|
||
| new_device | ipad-456 |
|
||
| resolution | kill_previous |
|
||
| previous_session_duration | 900 |
|
||
| timestamp | 2026-02-03 14:15:00 |
|
||
Et ces métriques doivent être disponibles dans le dashboard
|
||
|
||
Scénario: Alerte monitoring - taux de conflits élevé
|
||
Étant donné le système monitore les conflits de stream
|
||
Quand le taux de conflits dépasse 15% des nouvelles sessions sur 1 heure
|
||
Alors une alerte Slack doit être envoyée à l'équipe technique
|
||
Et le message doit indiquer :
|
||
"""
|
||
⚠️ Taux de conflits stream élevé : 18% (seuil : 15%)
|
||
Sessions impactées : 234 sur 1300
|
||
Action recommandée : vérifier expiration Redis et heartbeats
|
||
"""
|
||
|
||
# ============================================================================
|
||
# COMPATIBILITÉ AVEC D'AUTRES FEATURES
|
||
# ============================================================================
|
||
|
||
Scénario: Radio live avec conflit de stream
|
||
Étant donné un utilisateur écoute une radio live sur "iPhone de Jean"
|
||
Quand l'utilisateur démarre un stream sur "iPad Pro" et kill la session iPhone
|
||
Alors la radio live doit s'arrêter sur iPhone
|
||
Et le nouveau stream sur iPad peut être une radio live ou contenu normal
|
||
Et la progression audio-guide (si applicable) doit être sauvegardée
|
||
|
||
Scénario: Mode offline ne déclenche PAS de conflit
|
||
Étant donné un stream actif en ligne sur "iPhone de Jean"
|
||
Quand l'utilisateur écoute un contenu téléchargé en mode offline sur "iPad Pro"
|
||
Alors aucun conflit ne doit être détecté
|
||
Car le mode offline ne consomme pas de stream en ligne
|
||
Et les deux lectures peuvent coexister
|
||
|
||
Scénario: Multi-device avec partage familial (post-MVP)
|
||
Étant donné la fonctionnalité de partage familial est activée (post-MVP)
|
||
Et le compte principal a 3 profils famille
|
||
Quand chaque profil démarre un stream sur son appareil
|
||
Alors jusqu'à 3 streams simultanés doivent être autorisés
|
||
Et la détection de conflit doit s'appliquer par profil (1 stream/profil)
|