Files
roadwave/features/api/premium/multi-devices-dernier-priorite.feature
jpgiannetti f6a5b9afce test(gherkin): ajouter tests BDD pour toutes clarifications règles métier
Ajoute/modifie tests Gherkin pour couvrir les 7 sections clarifiées :

1. Algorithme recommandation (scoring intérêts nuls) :
   - Ajout scénarios scoring-recommandation.feature
   - Cas contenu géo-ancré proche avec intérêts nuls = recommandable
   - Comparaison scores géo vs intérêts

2. Audio-guides mode voiture (système double clic) :
   - Nouveau fichier systeme-double-clic-sortie.feature
   - Premier clic : passage mode manuel + séquence suivante
   - Deuxième clic <10s : sortie audio-guide
   - Détection hors itinéraire + reprise

3. Monétisation créateurs (soldes dormants + DAS2) :
   - Nouveau fichier soldes-dormants-inactifs.feature
   - Conservation indéfinie si actif
   - Emails 12/18 mois + versement forcé 18 mois + 30j
   - Exception soldes <10€ avec proposition don
   - Modification obligations-fiscales.feature
   - DAS2 systématique tous montants (même <1200€)

4. Skip et abonnement (neutralisation pénalités) :
   - Nouveau fichier skip-abonnes-neutralisation.feature
   - Skip <10s non-abonné : -0.5%
   - Skip <10s abonné : 0% (neutre)
   - Métriques engagement : abonnés ne pénalisent pas
   - Anti-raid naturel (sources non pertinentes)

5. Premium multi-devices (KISS) :
   - Nouveau fichier multi-devices-dernier-priorite.feature
   - Règle simple : dernier device prend toujours priorité
   - Offline connecté vs déconnecté
   - Détection abus post-MVP (pas automatique)

6. Mode offline (contenus supprimés) :
   - Nouveau fichier contenus-supprimes-pendant-offline.feature
   - Suppression immédiate à reconnexion
   - Modal si contenu en cours d'écoute
   - Popup récapitulative si 2+ contenus supprimés

7. Publicités (ciblage horaire + fuseaux horaires) :
   - Nouveau fichier ciblage-horaire-fuseaux-horaires.feature
   - Ciblage horaire = heure locale utilisateur
   - France entière = Métropole + DOM
   - Détection fuseau GPS/device/IP
   - Cas d'usage restaurant Guadeloupe, assureur national

Couverture complète de toutes les règles métier clarifiées.
2026-02-07 11:14:17 +01:00

234 lines
10 KiB
Gherkin
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# language: fr
Fonctionnalité: Multi-devices Premium - Dernier device prend priorité (KISS)
En tant que système Premium
Je veux appliquer une règle simple: le dernier device à démarrer prend toujours la priorité
Afin d'éviter la complexité des exceptions temporelles et géographiques
Contexte:
Étant donné qu'un utilisateur "UserA" a un abonnement Premium actif
Et que l'utilisateur possède 2 devices:
| device | type |
| iPhone_123 | iOS |
| iPad_456 | iOS |
# Règle KISS : Dernier device = Priorité
Scénario: Transition normale - Device 2 démarre après Device 1
Étant donné que "UserA" écoute un contenu sur iPhone_123
Et que le heartbeat iPhone_123 est actif (toutes les 30s)
Quand "UserA" démarre la lecture sur iPad_456 (5 secondes après)
Alors le serveur détecte une session active (iPhone_123)
Et envoie un WebSocket close à iPhone_123
Et iPhone_123 affiche le message:
"""
Lecture interrompue
Votre compte est utilisé sur un autre appareil.
Un seul appareil peut écouter à la fois avec votre compte Premium.
Le partage de compte viole nos CGU et peut entraîner une suspension.
[Reprendre ici] [Sécuriser mon compte]
"""
Et Redis met à jour active_streams:UserA = {device_id: "iPad_456", started_at: timestamp}
Et la lecture démarre sur iPad_456
# Offline connecté internet
Scénario: Device 1 offline MAIS connecté WiFi - Device 2 coupe Device 1
Étant donné que "UserA" écoute un contenu téléchargé en offline sur iPhone_123
Mais que iPhone_123 est connecté au WiFi
Et que le heartbeat iPhone_123 est envoyé toutes les 30s
Quand "UserA" démarre la lecture online sur iPad_456
Alors le serveur détecte une session active (iPhone_123, mode offline)
Et envoie un WebSocket close à iPhone_123
Et iPhone_123 affiche le même message d'interruption
Et iPad_456 démarre normalement
# Offline déconnecté internet (exception technique)
Scénario: Device 1 vraiment offline (mode avion) - Pas de détection possible
Étant donné que "UserA" écoute un contenu offline sur iPhone_123 en mode avion
Et qu'aucun heartbeat n'est envoyé (pas de connexion internet)
Quand "UserA" démarre la lecture online sur iPad_456
Alors le serveur ne détecte AUCUNE session active
Car aucun heartbeat depuis iPhone_123
Et iPad_456 démarre normalement
Et iPhone_123 continue en offline (pas de coupure possible)
Et c'est une exception technique acceptable ("Tant pis")
Scénario: Device 1 offline reconnecte - Heartbeat reprend
Étant donné que "UserA" écoutait offline sur iPhone_123 en mode avion
Quand iPhone_123 sort du mode avion et se connecte au WiFi
Alors le heartbeat reprend immédiatement
Et Redis crée/met à jour active_streams:UserA = {device_id: "iPhone_123", started_at: timestamp}
Et si iPad_456 démarre, iPhone_123 sera coupé normalement
# Heartbeat et TTL Redis
Scénario: Heartbeat toutes les 30s maintient la session active
Étant donné que "UserA" écoute sur iPhone_123
Quand iPhone_123 envoie un heartbeat toutes les 30 secondes
Alors Redis met à jour active_streams:UserA avec TTL 5 minutes
Et la session reste active tant que les heartbeats arrivent
Scénario: Pas de heartbeat pendant 5 min - Session expirée
Étant donné que "UserA" écoutait sur iPhone_123
Mais que iPhone_123 a été éteint brutalement (pas de heartbeat final)
Quand 5 minutes se sont écoulées sans heartbeat
Alors Redis expire automatiquement l'entrée active_streams:UserA (TTL)
Et iPad_456 peut démarrer sans détecter de conflit
# Boutons message coupure
Scénario: Bouton "Reprendre ici" sur device coupé
Étant donné que iPhone_123 a été coupé par iPad_456
Et que le message d'interruption est affiché sur iPhone_123
Quand "UserA" clique sur [Reprendre ici] sur iPhone_123
Alors le serveur détecte que iPad_456 est actif
Et envoie un WebSocket close à iPad_456
Et iPhone_123 reprend la lecture
Et iPad_456 affiche le même message d'interruption
Scénario: Bouton "Sécuriser mon compte" sur device coupé
Étant donné que iPhone_123 a été coupé par iPad_456
Et que le message d'interruption est affiché sur iPhone_123
Quand "UserA" clique sur [Sécuriser mon compte]
Alors l'utilisateur est redirigé vers:
- Page changement mot de passe
- Option "Déconnecter tous les appareils"
Et un email de sécurité est envoyé
Et un log de sécurité est créé (suspicion piratage)
# Limite offline 30 jours
Scénario: Contenus offline expirés >30j - Force reconnexion
Étant donné que "UserA" a téléchargé 50 contenus il y a 32 jours
Et qu'il n'a pas reconnecté son device depuis 32 jours
Quand "UserA" essaie d'écouter un contenu offline
Alors un message s'affiche:
"""
Contenus expirés (>30 jours)
Reconnectez-vous au WiFi pour renouveler vos contenus.
"""
Et la lecture est bloquée
Et cela force la reconnexion (détection multi-devices redevient possible)
Scénario: Notification J-3 avant expiration contenus offline
Étant donné que "UserA" a des contenus offline qui expirent dans 3 jours
Quand le système détecte l'approche de l'expiration
Alors une notification push est envoyée:
"""
10 contenus offline expirent dans 3 jours
Reconnectez-vous au WiFi pour renouveler automatiquement.
"""
# Détection abus post-MVP
Scénario: Monitoring patterns suspects - Flag modération manuelle
Étant donné que le système monitore les patterns d'utilisation
Quand "UserA" change de device >10 fois par jour pendant 7 jours
Alors un flag de suspicion est créé dans le dashboard modération
Et un email d'investigation est envoyé à "UserA":
"""
Activité suspecte détectée sur votre compte
Nous avons détecté des changements fréquents d'appareil.
Si ce n'est pas vous, sécurisez votre compte immédiatement.
[Sécuriser mon compte] [Contacter le support]
"""
Mais aucune action automatique n'est prise (évite faux positifs)
Scénario: Connexions alternées villes éloignées - Flag modération
Étant donné que le système monitore les localisations approximatives
Quand "UserA" se connecte à Paris à 10h puis Marseille à 10h30 (même jour)
Et que ce pattern se répète 5 fois en 1 semaine
Alors un flag de suspicion est créé
Et un modérateur humain enquête manuellement
Mais aucun blocage automatique (évite faux positifs: TGV légitime)
Scénario: Partage de compte confirmé - Suspension 7 jours
Étant donné qu'un modérateur confirme manuellement un partage de compte
Et que les preuves sont claires (signalements users + patterns suspects)
Quand le modérateur applique la sanction
Alors le compte est suspendu 7 jours
Et un email de warning est envoyé:
"""
Suspension de compte (7 jours)
Le partage de compte Premium viole nos CGU.
Votre compte est suspendu jusqu'au [Date].
En cas de récidive, le compte sera définitivement fermé.
"""
Scénario: Récidive partage de compte - Ban définitif
Étant donné qu'un compte a déjà été suspendu 7 jours pour partage
Et que le partage continue après la réactivation
Quand le modérateur confirme la récidive
Alors le compte est définitivement fermé (ban)
Et un email de fermeture définitive est envoyé
Et le remboursement pro-rata de l'abonnement est effectué
# Cas d'usage réels
Scénario: User change de pièce (voiture → maison) rapidement
Étant donné que "UserA" écoute dans sa voiture sur iPhone_123
Quand il arrive chez lui et passe sur iPad_456 après 30 secondes
Alors iPhone_123 est coupé immédiatement
Et iPad_456 prend le relai
Et l'utilisateur comprend le message (device unique)
Scénario: User oublie son téléphone en lecture et part
Étant donné que "UserA" écoute sur iPhone_123
Et qu'il part en laissant iPhone_123 en lecture
Quand il démarre la lecture sur iPad_456 au travail
Alors iPhone_123 est coupé à distance (WebSocket close)
Et iPad_456 prend la priorité
Scénario: TGV Paris → Lyon (légitime) - Pas de faux positif
Étant donné que "UserA" prend le TGV Paris Lyon
Et qu'il écoute pendant tout le trajet sur iPhone_123
Quand le système détecte un changement de localisation (GPS)
Alors aucun flag de suspicion n'est créé
Car c'est un déplacement continu légitime
Et aucune action automatique n'est prise
# Implémentation technique Redis
Scénario: Stockage Redis - Structure active_streams
Étant donné que "UserA" démarre la lecture sur iPhone_123
Quand le heartbeat est envoyé au serveur
Alors Redis stocke:
| clé | valeur |
| active_streams:UserA | {"device_id": "iPhone_123", "started_at": 1707311400} |
Et un TTL de 5 minutes (300 secondes) est défini
Et la clé expire automatiquement si pas de heartbeat
Scénario: Mise à jour Redis à chaque heartbeat
Étant donné qu'une session active existe sur iPhone_123
Quand un heartbeat est envoyé toutes les 30 secondes
Alors Redis met à jour le TTL à 5 minutes
Et started_at reste inchangé (timestamp initial)
Et la session reste active indéfiniment tant que les heartbeats arrivent
# Justification KISS
Scénario: Comparaison avec règle complexe - Avantages KISS
Étant donné qu'on compare la règle KISS avec une règle complexe (exceptions temporelles, GPS, etc.)
Quand on évalue les avantages
Alors les avantages KISS sont:
| avantage | description |
| Simplicité technique | Pas de tracking GPS précis, pas de calcul distances |
| Pas de faux positifs | TGV légitime ne déclenche pas d'alerte automatique |
| Assume bonne foi | Majorité users honnêtes, gestion réactive suffit |
| Message dissuasif clair | Avertissement CGU dans message, option sécurité |
| Protection revenus créateurs | 1 abonnement = 1 personne = 1 écoute active |
| UX claire | User comprend immédiatement le comportement |