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.
234 lines
10 KiB
Gherkin
234 lines
10 KiB
Gherkin
# 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 |
|