feat(bdd): réorganiser features en catégories api/ui/e2e et créer ADR-024
Résolution des incohérences #10, #11, et #12 de l'analyse d'architecture. ## Phase 1 : Réorganisation Features BDD (Point #10 - RÉSOLU) - Créer structure features/{api,ui,e2e} - Déplacer 83 features en 3 catégories via git mv (historique préservé) - features/api/ : 53 features (tests API backend) - features/ui/ : 22 features (tests UI mobile) - features/e2e/ : 8 features (tests end-to-end) Domaines déplacés : - API : authentication, recommendation, rgpd-compliance, content-creation, moderation, monetisation, premium, radio-live, publicites - UI : audio-guides, navigation, interest-gauges, mode-offline, partage, profil, recherche - E2E : abonnements, error-handling ## Phase 2 : Mise à jour Documentation ### ADR-007 - Tests BDD - Ajouter section "Convention de Catégorisation des Features" - Documenter règles api/ui/e2e avec exemples concrets - Spécifier step definitions (backend Go, mobile Dart) ### ADR-024 - Stratégie CI/CD Monorepo (NOUVEAU) - Créer ADR dédié pour stratégie CI/CD avec path filters - Architecture workflows séparés (backend.yml, mobile.yml, shared.yml) - Configuration path filters détaillée avec exemples YAML - Matrice de déclenchement et optimisations (~70% gain temps CI) - Plan d'implémentation (~2h, reporté jusqu'au développement) ### ADR-016 - Organisation Monorepo - Simplifier en retirant section CI/CD détaillée - Ajouter référence vers ADR-024 pour stratégie CI/CD ### INCONSISTENCIES-ANALYSIS.md - Point #10 (Tests BDD synchronisés) : ✅ RÉSOLU - Catégorisation features implémentée - ADR-007 mis à jour avec convention complète - Point #11 (70/30 Split paiements) : ✅ ANNULÉ (faux problème) - ADR-009 et Règle 18 parfaitement cohérents - Documentation exhaustive existante (formule, SQL, comparaisons) - Point #12 (Monorepo path filters) : ⏸️ DOCUMENTÉ - Architecture CI/CD complète dans ADR-024 - Implémentation reportée (projet en phase documentation) - Métriques mises à jour : - MODERATE : 6/9 traités (4 résolus + 1 annulé + 1 documenté) - ADR à jour : 100% (19/19 avec ADR-024) ## Phase 3 : Validation - Structure features validée (api/ui/e2e, aucun répertoire restant) - Historique Git préservé (git mv, renommages détectés) - 83 features total (API: 53, UI: 22, E2E: 8) Closes: Point #10 (résolu), Point #11 (annulé), Point #12 (documenté) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
425
features/ui/mode-offline/synchronisation-actions.feature
Normal file
425
features/ui/mode-offline/synchronisation-actions.feature
Normal file
@@ -0,0 +1,425 @@
|
||||
# language: fr
|
||||
Fonctionnalité: Synchronisation actions offline
|
||||
En tant qu'utilisateur
|
||||
Je veux que mes actions offline soient synchronisées quand je me reconnecte
|
||||
Afin de ne perdre aucune interaction même sans connexion
|
||||
|
||||
Contexte:
|
||||
Étant donné que j'utilise l'application RoadWave
|
||||
|
||||
# ===== ACTIONS STOCKÉES LOCALEMENT =====
|
||||
|
||||
Scénario: Like d'un contenu en mode offline
|
||||
Étant donné que je n'ai aucune connexion Internet
|
||||
Quand je like un contenu téléchargé
|
||||
Alors l'action est enregistrée localement dans SQLite:
|
||||
```sql
|
||||
INSERT INTO pending_actions (type, content_id, created_at)
|
||||
VALUES ('like', 'abc123', '2025-06-15 14:30:00');
|
||||
```
|
||||
Et l'UI affiche immédiatement le like (optimistic update)
|
||||
|
||||
Scénario: Unlike d'un contenu en mode offline
|
||||
Étant donné que je n'ai aucune connexion Internet
|
||||
Et que j'avais liké un contenu
|
||||
Quand je retire mon like
|
||||
Alors l'action est enregistrée localement:
|
||||
```sql
|
||||
INSERT INTO pending_actions (type, content_id, created_at)
|
||||
VALUES ('unlike', 'abc123', '2025-06-15 14:35:00');
|
||||
```
|
||||
Et l'UI retire immédiatement le like
|
||||
|
||||
Scénario: Abonnement à un créateur en mode offline
|
||||
Étant donné que je n'ai aucune connexion Internet
|
||||
Quand je m'abonne à un créateur
|
||||
Alors l'action est enregistrée localement:
|
||||
```sql
|
||||
INSERT INTO pending_actions (type, creator_id, created_at)
|
||||
VALUES ('subscribe', 'creator456', '2025-06-15 14:40:00');
|
||||
```
|
||||
Et l'UI affiche immédiatement "Abonné ✓"
|
||||
|
||||
Scénario: Désabonnement d'un créateur en mode offline
|
||||
Étant donné que je n'ai aucune connexion Internet
|
||||
Et que j'étais abonné à un créateur
|
||||
Quand je me désabonne
|
||||
Alors l'action est enregistrée localement:
|
||||
```sql
|
||||
INSERT INTO pending_actions (type, creator_id, created_at)
|
||||
VALUES ('unsubscribe', 'creator456', '2025-06-15 14:45:00');
|
||||
```
|
||||
Et l'UI affiche "S'abonner"
|
||||
|
||||
Scénario: Signalement d'un contenu en mode offline
|
||||
Étant donné que je n'ai aucune connexion Internet
|
||||
Quand je signale un contenu pour "Contenu inapproprié"
|
||||
Alors l'action est enregistrée localement:
|
||||
```sql
|
||||
INSERT INTO pending_actions (type, content_id, reason, created_at)
|
||||
VALUES ('report', 'abc123', 'Contenu inapproprié', '2025-06-15 14:50:00');
|
||||
```
|
||||
Et je vois "Signalement enregistré. Sera envoyé à la reconnexion."
|
||||
|
||||
Scénario: Progression audio-guide en mode offline
|
||||
Étant donné que je n'ai aucune connexion Internet
|
||||
Et que j'écoute un audio-guide multi-séquences
|
||||
Quand je termine la séquence 3/10
|
||||
Alors la progression est enregistrée localement:
|
||||
```sql
|
||||
INSERT INTO pending_actions (type, guide_id, sequence_id, created_at)
|
||||
VALUES ('guide_progress', 'guide789', 'seq003', '2025-06-15 15:00:00');
|
||||
```
|
||||
Et ma progression est sauvegardée
|
||||
|
||||
Scénario: Multiple actions offline stockées en queue
|
||||
Étant donné que je n'ai aucune connexion Internet pendant 2 jours
|
||||
Quand j'effectue plusieurs actions:
|
||||
| action | cible |
|
||||
| like | contenu A |
|
||||
| like | contenu B |
|
||||
| subscribe | créateur X |
|
||||
| unlike | contenu C |
|
||||
| report | contenu D |
|
||||
Alors les 5 actions sont stockées dans pending_actions
|
||||
Et elles seront synchronisées dans l'ordre à la reconnexion
|
||||
|
||||
# ===== SYNCHRONISATION AUTOMATIQUE =====
|
||||
|
||||
Scénario: Détection reconnexion Internet
|
||||
Étant donné que j'étais en mode offline
|
||||
Quand l'app détecte une reconnexion Internet
|
||||
Alors le processus de synchronisation démarre automatiquement
|
||||
Et je vois une notification "Synchronisation en cours..."
|
||||
|
||||
Scénario: Récupération queue locale pendant sync
|
||||
Étant donné que la synchronisation démarre
|
||||
Quand l'app récupère les actions en attente
|
||||
Alors une requête SQL est exécutée:
|
||||
```sql
|
||||
SELECT * FROM pending_actions ORDER BY created_at ASC;
|
||||
```
|
||||
Et toutes les actions sont récupérées dans l'ordre chronologique
|
||||
|
||||
Scénario: Envoi batch API des actions
|
||||
Étant donné que 15 actions sont en attente
|
||||
Quand le batch est envoyé au backend
|
||||
Alors une requête POST /sync/actions est faite:
|
||||
```json
|
||||
{
|
||||
"actions": [
|
||||
{"type": "like", "content_id": "abc123", "timestamp": "2025-06-15T14:30:00Z"},
|
||||
{"type": "subscribe", "creator_id": "creator456", "timestamp": "2025-06-15T14:40:00Z"},
|
||||
{"type": "unlike", "content_id": "def789", "timestamp": "2025-06-15T14:50:00Z"},
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
Et toutes les actions sont groupées en une seule requête
|
||||
|
||||
Scénario: Backend traite chaque action
|
||||
Étant donné que le backend reçoit le batch d'actions
|
||||
Quand il traite chaque action
|
||||
Alors pour chaque action:
|
||||
| étape | détail |
|
||||
| Validation | Vérifier user_id, content_id valides |
|
||||
| Vérification existence | Contenu/créateur existe toujours ? |
|
||||
| Application action | INSERT/UPDATE/DELETE en base |
|
||||
| Mise à jour compteurs | Likes, abonnés, etc. |
|
||||
| Impact sur algorithme | Mise à jour jauges si nécessaire |
|
||||
|
||||
Scénario: Confirmation réception et suppression queue locale
|
||||
Étant donné que le backend a traité toutes les actions avec succès
|
||||
Quand la confirmation est reçue par l'app
|
||||
Alors les actions sont supprimées de la queue locale:
|
||||
```sql
|
||||
DELETE FROM pending_actions WHERE id IN (1, 2, 3, ..., 15);
|
||||
```
|
||||
Et la table pending_actions est vidée
|
||||
|
||||
Scénario: Toast confirmation synchronisation
|
||||
Étant donné que 15 actions ont été synchronisées
|
||||
Quand la synchronisation se termine
|
||||
Alors je vois un toast:
|
||||
"""
|
||||
✅ Synchronisation réussie
|
||||
|
||||
3 likes, 1 abonnement et 1 signalement synchronisés.
|
||||
"""
|
||||
|
||||
Scénario: Synchronisation silencieuse si peu d'actions
|
||||
Étant donné que j'ai seulement 2 actions en attente
|
||||
Quand la synchronisation se termine
|
||||
Alors aucun toast n'est affiché (sync silencieuse)
|
||||
Et l'expérience reste fluide
|
||||
Mais je peux voir le détail dans l'historique des syncs
|
||||
|
||||
# ===== GESTION ERREURS SYNC =====
|
||||
|
||||
Scénario: Échec synchronisation - Retry automatique
|
||||
Étant donné que la synchronisation échoue (erreur réseau)
|
||||
Quand l'échec est détecté
|
||||
Alors un retry automatique est programmé dans 30 secondes
|
||||
Et les actions restent dans pending_actions
|
||||
|
||||
Scénario: 3 tentatives échouées - Notification utilisateur
|
||||
Étant donné que 3 tentatives de synchronisation ont échoué
|
||||
Quand la 3ème tentative échoue
|
||||
Alors je reçois une notification:
|
||||
"""
|
||||
⚠️ Impossible de synchroniser vos actions
|
||||
|
||||
15 actions en attente de synchronisation.
|
||||
Vérifiez votre connexion et réessayez.
|
||||
|
||||
[Réessayer maintenant] [Plus tard]
|
||||
```
|
||||
|
||||
Scénario: Actions conservées jusqu'à sync réussie
|
||||
Étant donné que la synchronisation échoue plusieurs fois
|
||||
Quand les tentatives continuent d'échouer
|
||||
Alors les actions restent dans pending_actions
|
||||
Et aucune action n'est perdue
|
||||
Et elles seront envoyées dès que la connexion sera stable
|
||||
|
||||
Scénario: Rétention max 7 jours - Purge automatique
|
||||
Étant donné qu'une action est en attente depuis 7 jours
|
||||
Quand le système détecte cette ancienneté
|
||||
Alors l'action est automatiquement supprimée de la queue
|
||||
Et je vois "1 action trop ancienne supprimée (>7 jours)"
|
||||
Et cela évite une queue infinie
|
||||
|
||||
Scénario: Justification rétention 7 jours
|
||||
Étant donné qu'un utilisateur ne se connecte jamais pendant 2 semaines
|
||||
Quand ses actions ont >7 jours
|
||||
Alors elles sont purgées automatiquement
|
||||
Car après 7 jours, l'action perd sa pertinence
|
||||
Et évite une queue qui grandit indéfiniment
|
||||
|
||||
Scénario: Retry manuel après échec
|
||||
Étant donné que la synchronisation a échoué
|
||||
Quand je clique sur "Réessayer maintenant"
|
||||
Alors une nouvelle tentative de synchronisation est lancée immédiatement
|
||||
Et si elle réussit, les actions sont synchronisées
|
||||
|
||||
# ===== CONFLITS CONTENUS SUPPRIMÉS =====
|
||||
|
||||
Scénario: Backend retourne contenus supprimés
|
||||
Étant donné que j'ai liké un contenu offline
|
||||
Mais que le contenu a été supprimé entre temps
|
||||
Quand le backend traite la synchronisation
|
||||
Alors il retourne:
|
||||
```json
|
||||
{
|
||||
"status": "partial_success",
|
||||
"deleted_content_ids": [123, 456],
|
||||
"failed_actions": [
|
||||
{"type": "like", "content_id": "123", "reason": "content_deleted"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Scénario: App supprime fichiers locaux contenus supprimés
|
||||
Étant donné que le backend retourne deleted_content_ids: [123, 456]
|
||||
Quand l'app traite la réponse
|
||||
Alors elle supprime les fichiers locaux des contenus 123 et 456
|
||||
Et libère l'espace disque
|
||||
Et les actions associées sont retirées de la queue
|
||||
|
||||
Scénario: Contenu supprimé en cours d'écoute
|
||||
Étant donné que j'écoute le contenu 123 en offline
|
||||
Et que la sync détecte que le contenu a été supprimé
|
||||
Quand la lecture actuelle se termine
|
||||
Alors l'app attend 2 secondes
|
||||
Et passe automatiquement au contenu suivant
|
||||
Et le fichier du contenu 123 est supprimé en arrière-plan
|
||||
|
||||
Scénario: Toast notification contenu retiré
|
||||
Étant donné que 2 contenus téléchargés ont été supprimés
|
||||
Quand la synchronisation se termine
|
||||
Alors je vois un toast:
|
||||
"""
|
||||
🗑️ 2 contenus téléchargés ont été retirés
|
||||
|
||||
Raison: Violation des règles de la plateforme
|
||||
"""
|
||||
|
||||
Scénario: Contenu modéré après téléchargement
|
||||
Étant donné que j'ai téléchargé un contenu qui est ensuite modéré
|
||||
Quand la synchronisation détecte la modération
|
||||
Alors le contenu est immédiatement supprimé du device
|
||||
Et je ne peux plus l'écouter
|
||||
Et cela garantit la conformité même offline
|
||||
|
||||
# ===== JUSTIFICATIONS =====
|
||||
|
||||
Scénario: Justification pas de conflit possible
|
||||
Étant donné que les actions offline sont unilatérales (likes, abonnements)
|
||||
Quand elles sont synchronisées
|
||||
Alors il n'y a pas de conflit de version possible
|
||||
Car l'utilisateur ajoute/retire simplement des préférences
|
||||
Et pas de merge complexe nécessaire
|
||||
|
||||
Scénario: Justification UX fluide offline
|
||||
Étant donné que toutes les actions fonctionnent offline
|
||||
Quand l'utilisateur interagit sans connexion
|
||||
Alors l'expérience est identique au mode online
|
||||
Et l'utilisateur n'est pas bloqué
|
||||
Et peut utiliser l'app normalement
|
||||
|
||||
Scénario: Justification batch = Économie requêtes
|
||||
Étant donné que 15 actions sont en attente
|
||||
Quand elles sont synchronisées en batch
|
||||
Alors 1 seule requête HTTP est envoyée (vs 15 si individuelles)
|
||||
Et cela économise la bande passante et la batterie
|
||||
Et réduit la charge serveur
|
||||
|
||||
Scénario: Justification conformité modération offline
|
||||
Étant donné qu'un contenu illégal est modéré pendant qu'un user est offline
|
||||
Quand le user se reconnecte
|
||||
Alors le contenu est immédiatement supprimé de son device
|
||||
Et cela garantit que les contenus illégaux disparaissent même offline
|
||||
|
||||
# ===== STATISTIQUES ET MONITORING =====
|
||||
|
||||
Scénario: Historique synchronisations
|
||||
Étant donné que j'accède à "Paramètres > Synchronisation"
|
||||
Quand je consulte l'historique
|
||||
Alors je vois:
|
||||
| date | actions sync | statut |
|
||||
| 15/06/2025 14:30:00 | 15 | Réussi ✅ |
|
||||
| 14/06/2025 09:15:00 | 7 | Réussi ✅ |
|
||||
| 13/06/2025 18:45:00 | 3 | Échec ❌ |
|
||||
|
||||
Scénario: Détail d'une synchronisation
|
||||
Étant donné que je clique sur une ligne de l'historique
|
||||
Quand le détail s'affiche
|
||||
Alors je vois:
|
||||
```
|
||||
Synchronisation du 15/06/2025 14:30:00
|
||||
|
||||
Actions synchronisées:
|
||||
• 3 likes
|
||||
• 1 abonnement
|
||||
• 1 signalement
|
||||
• 10 progressions audio-guides
|
||||
|
||||
Durée: 1.2s
|
||||
Statut: Réussi ✅
|
||||
```
|
||||
|
||||
Scénario: Compteur actions en attente visible
|
||||
Étant donné que j'ai 12 actions en attente de synchronisation
|
||||
Quand j'accède à l'onglet Profil
|
||||
Alors je vois un badge "12" sur l'icône de synchronisation
|
||||
Et je sais qu'il y a des actions en attente
|
||||
|
||||
Scénario: Synchronisation manuelle forcée
|
||||
Étant donné que je veux forcer une synchronisation immédiate
|
||||
Quand je vais dans "Paramètres > Synchronisation"
|
||||
Et que je clique sur "Synchroniser maintenant"
|
||||
Alors la synchronisation démarre immédiatement
|
||||
Et toutes les actions en attente sont envoyées
|
||||
|
||||
Scénario: Statistiques utilisateur - Syncs effectuées
|
||||
Étant donné que j'accède à mes statistiques
|
||||
Quand je consulte la section Synchronisation
|
||||
Alors je vois:
|
||||
| métrique | valeur |
|
||||
| Synchronisations depuis début | 87 |
|
||||
| Actions synchronisées total | 1,234 |
|
||||
| Taux de succès | 94% |
|
||||
| Dernière sync | Il y a 2h|
|
||||
|
||||
Scénario: Statistiques admin - Volume synchronisations
|
||||
Étant donné qu'un admin consulte les métriques de synchronisation
|
||||
Quand il accède au dashboard
|
||||
Alors il voit:
|
||||
| métrique | valeur |
|
||||
| Synchronisations/jour | 45,678 |
|
||||
| Actions synchronisées/jour | 234,567 |
|
||||
| Taux succès sync | 96.5% |
|
||||
| Temps moyen traitement batch | 0.8s |
|
||||
| Actions en attente (global) | 12,345 |
|
||||
|
||||
Scénario: Alerte admin si taux échec sync >10%
|
||||
Étant donné que le taux d'échec sync dépasse 10%
|
||||
Quand le système détecte cette anomalie
|
||||
Alors une alerte est envoyée:
|
||||
"""
|
||||
⚠️ Taux échec synchronisation anormal: 12.3%
|
||||
|
||||
Échecs aujourd'hui: 5,621 / 45,678 syncs
|
||||
Causes principales:
|
||||
- Timeout serveur: 3,245
|
||||
- Erreur réseau client: 1,876
|
||||
- Données invalides: 500
|
||||
|
||||
Action recommandée: Vérifier charge serveur + logs erreurs
|
||||
"""
|
||||
|
||||
# ===== TESTS PERFORMANCE =====
|
||||
|
||||
Scénario: Synchronisation rapide <2s
|
||||
Étant donné que j'ai 20 actions en attente
|
||||
Quand la synchronisation démarre
|
||||
Alors le traitement prend <2 secondes
|
||||
Et je ne remarque aucun ralentissement de l'app
|
||||
|
||||
Scénario: Synchronisation de gros batch (100 actions)
|
||||
Étant donné que je n'ai pas synchronisé pendant 1 semaine
|
||||
Et que j'ai 100 actions en attente
|
||||
Quand la synchronisation démarre
|
||||
Alors le batch de 100 actions est traité en <5 secondes
|
||||
Et toutes les actions sont synchronisées avec succès
|
||||
|
||||
Scénario: Gestion charge serveur - 10 000 syncs simultanées
|
||||
Étant donné que 10 000 utilisateurs se reconnectent simultanément
|
||||
Quand chacun envoie un batch de 20 actions
|
||||
Alors le serveur traite 200 000 actions
|
||||
Et grâce au traitement asynchrone (queue Redis), le temps de réponse reste <3s
|
||||
Et aucun timeout n'est constaté
|
||||
|
||||
Scénario: Stockage SQLite optimisé
|
||||
Étant donné que la table pending_actions stocke des centaines d'actions
|
||||
Quand des requêtes sont exécutées
|
||||
Alors la table est indexée sur created_at
|
||||
Et les requêtes SELECT et DELETE sont instantanées (<10ms)
|
||||
Et l'expérience utilisateur reste fluide
|
||||
|
||||
Scénario: Nettoyage automatique table pending_actions
|
||||
Étant donné que la table pending_actions grossit avec le temps
|
||||
Quand les actions sont synchronisées et supprimées
|
||||
Alors la table est automatiquement optimisée (VACUUM sur SQLite)
|
||||
Et l'espace disque est libéré
|
||||
Et les performances restent optimales
|
||||
|
||||
# ===== EDGE CASES =====
|
||||
|
||||
Scénario: Action dupliquée - Idempotence
|
||||
Étant donné que j'ai liké un contenu offline
|
||||
Et que la sync échoue et retry
|
||||
Quand le backend reçoit 2 fois le même like
|
||||
Alors il applique l'idempotence (1 seul like enregistré)
|
||||
Et le compteur de likes n'est pas faussé
|
||||
|
||||
Scénario: Séquence like/unlike offline
|
||||
Étant donné que j'ai liké puis unliké un contenu offline
|
||||
Quand les 2 actions sont synchronisées
|
||||
Alors le backend applique les 2 actions dans l'ordre
|
||||
Et le résultat final est "pas de like" (état correct)
|
||||
|
||||
Scénario: Abonnement puis désabonnement offline
|
||||
Étant donné que je me suis abonné puis désabonné d'un créateur offline
|
||||
Quand les 2 actions sont synchronisées
|
||||
Alors le backend applique les 2 actions dans l'ordre
|
||||
Et le résultat final est "pas abonné"
|
||||
Et les jauges évoluent correctement (+5% puis -5% = 0% net)
|
||||
|
||||
Scénario: Créateur supprimé pendant offline
|
||||
Étant donné que je me suis abonné à un créateur offline
|
||||
Mais que le créateur a supprimé son compte entre temps
|
||||
Quand la sync traite l'abonnement
|
||||
Alors le backend retourne "creator_deleted"
|
||||
Et l'action est ignorée silencieusement
|
||||
Et aucune erreur n'est affichée à l'utilisateur
|
||||
409
features/ui/mode-offline/telechargement.feature
Normal file
409
features/ui/mode-offline/telechargement.feature
Normal file
@@ -0,0 +1,409 @@
|
||||
# language: fr
|
||||
Fonctionnalité: Téléchargement de contenus offline
|
||||
En tant qu'utilisateur
|
||||
Je veux télécharger des contenus pour les écouter sans connexion
|
||||
Afin de profiter de RoadWave même dans les zones sans réseau
|
||||
|
||||
Contexte:
|
||||
Étant donné que je suis connecté à l'application RoadWave
|
||||
|
||||
# ===== SÉLECTION ZONE GÉOGRAPHIQUE =====
|
||||
|
||||
Scénario: Option "Autour de moi" - Rayon 50 km
|
||||
Étant donné que je suis à Paris (position GPS détectée)
|
||||
Quand je sélectionne "Télécharger > Autour de moi"
|
||||
Alors l'app recherche tous les contenus géolocalisés dans un rayon de 50 km
|
||||
Et je vois une liste de contenus de Paris et banlieue proche
|
||||
Et l'estimation affiche "~150 contenus disponibles"
|
||||
|
||||
Scénario: Option "Ma ville" - Limite administrative détectée
|
||||
Étant donné que je suis à Lyon (position GPS détectée)
|
||||
Quand je sélectionne "Télécharger > Ma ville"
|
||||
Alors l'app détecte automatiquement "Lyon" comme ville
|
||||
Et recherche tous les contenus géolocalisés "Lyon"
|
||||
Et je vois uniquement les contenus de la ville de Lyon (pas banlieue)
|
||||
|
||||
Scénario: Option "Mon département" - Sélection dans liste
|
||||
Étant donné que je veux télécharger des contenus pour un département
|
||||
Quand je sélectionne "Télécharger > Mon département"
|
||||
Alors je vois une liste de tous les départements français:
|
||||
| département |
|
||||
| 01 - Ain |
|
||||
| 02 - Aisne |
|
||||
| 75 - Paris |
|
||||
| 69 - Rhône |
|
||||
| ... |
|
||||
Et je peux choisir un département
|
||||
|
||||
Scénario: Sélection département et téléchargement contenus
|
||||
Étant donné que je sélectionne "75 - Paris" dans la liste des départements
|
||||
Quand la sélection est confirmée
|
||||
Alors l'app recherche tous les contenus géolocalisés "Paris"
|
||||
Et je vois "~234 contenus disponibles pour Paris"
|
||||
|
||||
Scénario: Option "Ma région" - Sélection dans liste
|
||||
Étant donné que je veux télécharger des contenus pour une région
|
||||
Quand je sélectionne "Télécharger > Ma région"
|
||||
Alors je vois une liste de toutes les régions françaises:
|
||||
| région |
|
||||
| Auvergne-Rhône-Alpes |
|
||||
| Bretagne |
|
||||
| Île-de-France |
|
||||
| Nouvelle-Aquitaine |
|
||||
| Occitanie |
|
||||
| ... |
|
||||
Et je peux choisir une région
|
||||
|
||||
Scénario: Sélection région et téléchargement contenus
|
||||
Étant donné que je sélectionne "Bretagne" dans la liste des régions
|
||||
Quand la sélection est confirmée
|
||||
Alors l'app recherche tous les contenus géolocalisés des départements bretons:
|
||||
| département |
|
||||
| Côtes-d'Armor (22) |
|
||||
| Finistère (29) |
|
||||
| Ille-et-Vilaine (35) |
|
||||
| Morbihan (56) |
|
||||
Et je vois "~487 contenus disponibles pour Bretagne"
|
||||
|
||||
Scénario: Recherche manuelle ville
|
||||
Étant donné que je veux télécharger des contenus pour une ville spécifique
|
||||
Quand je tape "Marseille" dans la barre de recherche
|
||||
Alors l'app propose des suggestions:
|
||||
| suggestion |
|
||||
| Marseille (13) |
|
||||
| Marseille-en-Beauvaisis |
|
||||
Et je peux sélectionner "Marseille (13)"
|
||||
|
||||
Scénario: Recherche manuelle avec autocomplétion
|
||||
Étant donné que je tape "Ly" dans la barre de recherche
|
||||
Quand l'autocomplétion s'active
|
||||
Alors je vois des suggestions:
|
||||
| suggestion |
|
||||
| Lyon (69) |
|
||||
| Lys-lez-Lannoy |
|
||||
Et je peux affiner ma recherche
|
||||
|
||||
# ===== LIMITES TÉLÉCHARGEMENT =====
|
||||
|
||||
Scénario: Utilisateur gratuit - Limite 50 contenus max
|
||||
Étant donné que je suis un utilisateur gratuit
|
||||
Et que j'ai déjà téléchargé 45 contenus
|
||||
Quand j'accède à la page Téléchargements
|
||||
Alors je vois "45 / 50 contenus téléchargés"
|
||||
Et je peux télécharger 5 contenus supplémentaires maximum
|
||||
|
||||
Scénario: Utilisateur gratuit - Tentative dépasser limite 50
|
||||
Étant donné que je suis gratuit et j'ai déjà 50 contenus téléchargés
|
||||
Quand j'essaie de télécharger un 51ème contenu
|
||||
Alors le téléchargement est refusé
|
||||
Et je vois le message:
|
||||
"""
|
||||
📥 Limite atteinte (50 contenus)
|
||||
|
||||
Vous avez atteint la limite de téléchargements gratuits.
|
||||
|
||||
Options:
|
||||
• Supprimez des contenus existants pour en télécharger de nouveaux
|
||||
• Passez Premium pour des téléchargements illimités
|
||||
|
||||
[Gérer mes téléchargements] [Découvrir Premium]
|
||||
"""
|
||||
|
||||
Scénario: Utilisateur Premium - Téléchargements illimités
|
||||
Étant donné que je suis un utilisateur Premium
|
||||
Et que j'ai déjà téléchargé 245 contenus
|
||||
Quand j'accède à la page Téléchargements
|
||||
Alors je vois "245 contenus (3.2 GB)"
|
||||
Et aucune limite n'est affichée
|
||||
Et je peux télécharger autant de contenus que je veux
|
||||
|
||||
Scénario: Limite Premium = Espace disque disponible
|
||||
Étant donné que je suis Premium
|
||||
Et que mon device a 500 MB d'espace disque disponible
|
||||
Quand j'essaie de télécharger 100 contenus (2 GB)
|
||||
Alors le téléchargement échoue après ~50 contenus (500 MB)
|
||||
Et je vois "Espace disque insuffisant. Libérez de l'espace pour continuer."
|
||||
|
||||
Scénario: Calcul temps écoute disponible gratuit
|
||||
Étant donné que je suis gratuit avec 50 contenus téléchargés
|
||||
Et que la durée moyenne d'un contenu est 5 minutes
|
||||
Quand je calcule le temps d'écoute disponible
|
||||
Alors 50 contenus × 5 min = 250 minutes = 4h10 d'écoute
|
||||
Et cela suffit pour un trajet quotidien ou road trip court
|
||||
|
||||
Scénario: Calcul temps écoute disponible Premium illimité
|
||||
Étant donné que je suis Premium avec 300 contenus téléchargés
|
||||
Et que la durée moyenne est 5 minutes
|
||||
Quand je calcule le temps d'écoute disponible
|
||||
Alors 300 contenus × 5 min = 1500 minutes = 25h d'écoute
|
||||
Et cela suffit pour un road trip de plusieurs jours
|
||||
|
||||
# ===== CONNEXION WIFI / MOBILE =====
|
||||
|
||||
Scénario: Téléchargement par défaut en WiFi uniquement
|
||||
Étant donné que je suis connecté en WiFi
|
||||
Quand je clique sur "Télécharger 20 contenus"
|
||||
Alors le téléchargement démarre immédiatement
|
||||
Et aucune popup de confirmation n'apparaît
|
||||
|
||||
Scénario: Tentative téléchargement en données mobiles - Popup confirmation
|
||||
Étant donné que je suis connecté en 4G (pas de WiFi)
|
||||
Quand je clique sur "Télécharger 20 contenus"
|
||||
Alors une popup apparaît:
|
||||
"""
|
||||
📡 Vous n'êtes pas connecté en WiFi
|
||||
|
||||
Télécharger via données mobiles consommera environ 72 MB.
|
||||
|
||||
[Attendre WiFi] [Continuer quand même]
|
||||
"""
|
||||
|
||||
Scénario: Calcul estimation consommation data mobile
|
||||
Étant donné que je veux télécharger 20 contenus
|
||||
Et que la durée moyenne est 5 minutes
|
||||
Et que la qualité Standard est 48 kbps Opus
|
||||
Quand l'estimation est calculée
|
||||
Alors consommation = 20 contenus × 5 min × 48 kbps / 8 = 72 MB
|
||||
Et ce montant est affiché dans la popup
|
||||
|
||||
Scénario: Confirmation téléchargement en données mobiles
|
||||
Étant donné que je vois la popup de confirmation données mobiles
|
||||
Quand je clique sur "Continuer quand même"
|
||||
Alors le téléchargement démarre immédiatement via 4G
|
||||
Et la consommation data est comptabilisée sur mon forfait mobile
|
||||
|
||||
Scénario: Refus téléchargement données mobiles - Attendre WiFi
|
||||
Étant donné que je vois la popup de confirmation données mobiles
|
||||
Quand je clique sur "Attendre WiFi"
|
||||
Alors les téléchargements sont mis en file d'attente
|
||||
Et ils démarreront automatiquement quand le WiFi sera détecté
|
||||
|
||||
Scénario: Détection automatique WiFi et reprise téléchargements
|
||||
Étant donné que j'ai mis 20 contenus en file d'attente (attente WiFi)
|
||||
Quand l'app détecte une connexion WiFi
|
||||
Alors les téléchargements démarrent automatiquement
|
||||
Et je reçois une notification "Téléchargements en cours via WiFi"
|
||||
|
||||
# ===== QUALITÉ AUDIO =====
|
||||
|
||||
Scénario: Qualité Standard (48 kbps) par défaut
|
||||
Étant donné que je configure mes téléchargements
|
||||
Quand j'accède aux paramètres de qualité
|
||||
Alors la qualité "Standard (48 kbps - ~20 MB/h)" est sélectionnée par défaut
|
||||
Et elle est disponible pour tous (gratuit + Premium)
|
||||
|
||||
Scénario: Qualité Basse (24 kbps) disponible pour tous
|
||||
Étant donné que j'ai peu d'espace disque disponible
|
||||
Quand je sélectionne qualité "Basse (24 kbps - ~10 MB/h)"
|
||||
Alors mes prochains téléchargements seront en 24 kbps
|
||||
Et l'espace utilisé sera divisé par 2 par rapport à Standard
|
||||
Et cette option est disponible pour gratuit + Premium
|
||||
|
||||
Scénario: Qualité Haute (64 kbps) réservée Premium
|
||||
Étant donné que je suis un utilisateur gratuit
|
||||
Quand je consulte les options de qualité
|
||||
Alors l'option "Haute (64 kbps - ~30 MB/h)" est grisée
|
||||
Et je vois "👑 Premium uniquement"
|
||||
Et je ne peux pas la sélectionner
|
||||
|
||||
Scénario: Utilisateur Premium peut choisir qualité Haute
|
||||
Étant donné que je suis un utilisateur Premium
|
||||
Quand je consulte les options de qualité
|
||||
Alors l'option "Haute (64 kbps - ~30 MB/h)" est disponible
|
||||
Et je peux la sélectionner pour mes téléchargements
|
||||
Et la qualité audio sera excellente (meilleure restitution voix et ambiances)
|
||||
|
||||
Scénario: Comparaison taille fichiers selon qualité
|
||||
Étant donné que je veux télécharger 50 contenus de 5 min chacun
|
||||
Quand je compare les qualités
|
||||
Alors les tailles totales sont:
|
||||
| qualité | bitrate | taille totale |
|
||||
| Basse | 24 kbps | ~250 MB |
|
||||
| Standard | 48 kbps | ~500 MB |
|
||||
| Haute | 64 kbps | ~650 MB |
|
||||
|
||||
Scénario: Justification Standard = Bon compromis
|
||||
Étant donné que le contenu RoadWave est principalement de la voix
|
||||
Quand la qualité Standard (48 kbps Opus) est utilisée
|
||||
Alors la qualité est très correcte pour la voix
|
||||
Et équivalente à la radio FM
|
||||
Et le compromis qualité/taille est optimal
|
||||
|
||||
Scénario: Justification Haute réservée Premium = Incitation upgrade
|
||||
Étant donné qu'un utilisateur gratuit veut la meilleure qualité
|
||||
Quand il voit que Haute est réservée Premium
|
||||
Alors cela l'incite à passer Premium pour 4.99€/mois
|
||||
Et c'est un avantage tangible supplémentaire de Premium
|
||||
|
||||
Scénario: Changement qualité après téléchargements existants
|
||||
Étant donné que j'ai déjà téléchargé 30 contenus en qualité Standard
|
||||
Quand je change la qualité vers Haute (si Premium)
|
||||
Alors les 30 contenus existants restent en Standard
|
||||
Et seuls les nouveaux téléchargements seront en Haute
|
||||
Et je peux manuellement re-télécharger les 30 contenus pour les avoir en Haute
|
||||
|
||||
# ===== PROCESSUS DE TÉLÉCHARGEMENT =====
|
||||
|
||||
Scénario: Téléchargement individuel d'un contenu
|
||||
Étant donné que je consulte la page d'un contenu
|
||||
Quand je clique sur l'icône de téléchargement 📥
|
||||
Alors le téléchargement démarre
|
||||
Et une barre de progression apparaît
|
||||
Et l'icône devient ✅ quand terminé
|
||||
|
||||
Scénario: Téléchargement batch de contenus sélectionnés
|
||||
Étant donné que je consulte une liste de contenus pour "Paris"
|
||||
Quand je sélectionne 15 contenus manuellement
|
||||
Et que je clique sur "Télécharger la sélection"
|
||||
Alors les 15 contenus sont téléchargés en parallèle (max 3 simultanés)
|
||||
Et une notification affiche "15 contenus téléchargés"
|
||||
|
||||
Scénario: Téléchargement automatique recommandations zone
|
||||
Étant donné que je sélectionne "Autour de moi" (Paris)
|
||||
Quand je clique sur "Télécharger les 50 meilleurs contenus"
|
||||
Alors l'algorithme sélectionne automatiquement les 50 contenus les mieux notés/récents
|
||||
Et les télécharge tous
|
||||
Et je n'ai pas besoin de choisir manuellement
|
||||
|
||||
Scénario: Barre de progression téléchargement global
|
||||
Étant donné que je télécharge 20 contenus
|
||||
Quand les téléchargements sont en cours
|
||||
Alors je vois une barre de progression globale:
|
||||
"""
|
||||
📥 Téléchargement en cours...
|
||||
7 / 20 contenus (35%)
|
||||
~45 MB restants
|
||||
Temps estimé: 2 min
|
||||
"""
|
||||
|
||||
Scénario: Téléchargements en tâche de fond
|
||||
Étant donné que je lance le téléchargement de 30 contenus
|
||||
Quand je ferme l'app ou passe à une autre activité
|
||||
Alors les téléchargements continuent en arrière-plan
|
||||
Et je reçois une notification quand tous sont terminés
|
||||
|
||||
Scénario: Pause et reprise téléchargements
|
||||
Étant donné que je télécharge 20 contenus
|
||||
Quand je clique sur "Pause"
|
||||
Alors les téléchargements en cours se terminent
|
||||
Et les téléchargements en attente sont mis en pause
|
||||
Et je peux cliquer sur "Reprendre" plus tard
|
||||
|
||||
Scénario: Annulation téléchargements
|
||||
Étant donné que je télécharge 20 contenus
|
||||
Quand je clique sur "Annuler"
|
||||
Alors tous les téléchargements sont arrêtés
|
||||
Et les fichiers partiels sont supprimés
|
||||
Et l'espace disque est libéré
|
||||
|
||||
Scénario: Gestion erreurs téléchargement
|
||||
Étant donné que je télécharge un contenu
|
||||
Mais que la connexion Internet coupe au milieu
|
||||
Quand la connexion revient
|
||||
Alors le téléchargement reprend automatiquement où il s'était arrêté
|
||||
Et aucune perte de progression n'a lieu
|
||||
|
||||
Scénario: Retry automatique après échec
|
||||
Étant donné qu'un téléchargement échoue 3 fois consécutives
|
||||
Quand l'échec est détecté
|
||||
Alors le contenu est marqué "Échec"
|
||||
Et je vois une notification "3 contenus n'ont pas pu être téléchargés"
|
||||
Et je peux retry manuellement en cliquant sur "Réessayer"
|
||||
|
||||
# ===== GESTION CONTENUS TÉLÉCHARGÉS =====
|
||||
|
||||
Scénario: Liste contenus téléchargés
|
||||
Étant donné que j'ai téléchargé 45 contenus
|
||||
Quand j'accède à "Téléchargements"
|
||||
Alors je vois la liste complète de mes 45 contenus
|
||||
Et pour chaque contenu: titre, créateur, durée, taille, date téléchargement
|
||||
|
||||
Scénario: Tri contenus téléchargés
|
||||
Étant donné que je consulte ma liste de téléchargements
|
||||
Quand je clique sur "Trier par"
|
||||
Alors je peux trier par:
|
||||
| critère | ordre |
|
||||
| Date téléchargement | Plus récent / Plus ancien|
|
||||
| Titre | A-Z / Z-A |
|
||||
| Créateur | A-Z / Z-A |
|
||||
| Durée | Plus long / Plus court |
|
||||
| Taille | Plus gros / Plus petit |
|
||||
|
||||
Scénario: Recherche dans contenus téléchargés
|
||||
Étant donné que j'ai 200 contenus téléchargés
|
||||
Quand je tape "Tesla" dans la barre de recherche
|
||||
Alors seuls les contenus contenant "Tesla" s'affichent
|
||||
Et je peux rapidement trouver un contenu spécifique
|
||||
|
||||
Scénario: Suppression individuelle contenu téléchargé
|
||||
Étant donné que je veux supprimer un contenu téléchargé
|
||||
Quand je swipe left (iOS) ou long press (Android) sur le contenu
|
||||
Et que je clique sur "Supprimer"
|
||||
Alors le fichier est supprimé du device
|
||||
Et l'espace disque est libéré
|
||||
Et le compteur est décrémenté (ex: 45/50 → 44/50)
|
||||
|
||||
Scénario: Suppression batch contenus téléchargés
|
||||
Étant donné que je veux supprimer plusieurs contenus
|
||||
Quand je sélectionne 10 contenus
|
||||
Et que je clique sur "Supprimer la sélection"
|
||||
Alors les 10 fichiers sont supprimés
|
||||
Et ~100 MB d'espace disque sont libérés
|
||||
Et une notification confirme "10 contenus supprimés"
|
||||
|
||||
Scénario: Suppression tous les contenus téléchargés
|
||||
Étant donné que j'ai 45 contenus téléchargés
|
||||
Quand je clique sur "Supprimer tout"
|
||||
Et que je confirme l'action
|
||||
Alors tous les 45 contenus sont supprimés
|
||||
Et l'espace disque total est libéré (~450 MB)
|
||||
Et le compteur repasse à 0/50
|
||||
|
||||
Scénario: Espace disque utilisé visible
|
||||
Étant donné que j'ai téléchargé 45 contenus
|
||||
Quand j'accède à la page Téléchargements
|
||||
Alors je vois l'espace disque utilisé:
|
||||
"""
|
||||
📥 Téléchargements
|
||||
45 / 50 contenus
|
||||
Espace utilisé: 478 MB
|
||||
"""
|
||||
|
||||
Scénario: Statistiques téléchargements
|
||||
Étant donné que j'accède à mes statistiques
|
||||
Quand je consulte la section Téléchargements
|
||||
Alors je vois:
|
||||
| métrique | valeur |
|
||||
| Contenus actuellement téléchargés | 45 |
|
||||
| Espace disque utilisé | 478 MB |
|
||||
| Contenus téléchargés depuis début | 287 |
|
||||
| Total data téléchargée | 3.2 GB |
|
||||
| Téléchargements via WiFi | 92% |
|
||||
| Téléchargements via mobile | 8% |
|
||||
|
||||
# ===== LECTURE OFFLINE =====
|
||||
|
||||
Scénario: Lecture contenu téléchargé sans connexion
|
||||
Étant donné que je n'ai aucune connexion Internet (mode avion)
|
||||
Et que j'ai des contenus téléchargés
|
||||
Quand je lance un contenu téléchargé
|
||||
Alors la lecture démarre normalement depuis le fichier local
|
||||
Et aucune erreur de connexion n'apparaît
|
||||
|
||||
Scénario: Badge "Téléchargé" sur contenus offline
|
||||
Étant donné que j'ai téléchargé certains contenus
|
||||
Quand je consulte une liste de contenus
|
||||
Alors les contenus téléchargés ont un badge ✅ "Offline"
|
||||
Et je sais immédiatement lesquels sont disponibles sans connexion
|
||||
|
||||
Scénario: Filtre "Téléchargés uniquement"
|
||||
Étant donné que je veux voir uniquement mes contenus offline
|
||||
Quand j'active le filtre "Téléchargés uniquement"
|
||||
Alors seuls les contenus téléchargés s'affichent
|
||||
Et je peux facilement naviguer dans mon catalogue offline
|
||||
|
||||
Scénario: Playlist offline automatique
|
||||
Étant donné que j'ai téléchargé 45 contenus
|
||||
Quand j'accède à "Téléchargements"
|
||||
Alors je peux lancer une playlist aléatoire de mes 45 contenus
|
||||
Et profiter d'une écoute continue offline
|
||||
335
features/ui/mode-offline/validite-renouvellement.feature
Normal file
335
features/ui/mode-offline/validite-renouvellement.feature
Normal file
@@ -0,0 +1,335 @@
|
||||
# language: fr
|
||||
Fonctionnalité: Validité et renouvellement contenus offline
|
||||
En tant qu'utilisateur
|
||||
Je veux que mes contenus téléchargés restent valides un certain temps
|
||||
Afin de garantir la légalité et la fraîcheur du contenu
|
||||
|
||||
Contexte:
|
||||
Étant donné que je suis connecté à l'application RoadWave
|
||||
Et que j'ai des contenus téléchargés
|
||||
|
||||
# ===== DURÉE DE VALIDITÉ =====
|
||||
|
||||
Scénario: Validité de 30 jours après téléchargement
|
||||
Étant donné que je télécharge un contenu le 1er juin 2025
|
||||
Quand le téléchargement est terminé
|
||||
Alors le contenu est valide jusqu'au 1er juillet 2025 (30 jours)
|
||||
Et la date d'expiration est stockée en local
|
||||
|
||||
Scénario: Affichage date expiration sur contenu téléchargé
|
||||
Étant donné que j'ai téléchargé un contenu il y a 20 jours
|
||||
Quand je consulte les détails du contenu
|
||||
Alors je vois "Expire dans 10 jours"
|
||||
Et je sais combien de temps il reste avant expiration
|
||||
|
||||
Scénario: Standard industrie aligné (Spotify, YouTube, Deezer)
|
||||
Étant donné que Spotify, YouTube Music et Deezer utilisent 30 jours
|
||||
Quand RoadWave fixe également 30 jours
|
||||
Alors c'est le standard accepté par les utilisateurs
|
||||
Et il n'y a pas de confusion avec les autres plateformes
|
||||
|
||||
Scénario: Justification 30 jours - Force reconnexion régulière
|
||||
Étant donné qu'un utilisateur ne se connecte jamais
|
||||
Quand ses contenus expirent après 30 jours
|
||||
Alors il est obligé de se reconnecter pour les renouveler
|
||||
Et le système peut vérifier:
|
||||
| vérification |
|
||||
| Abonnement Premium toujours actif|
|
||||
| Contenus non modérés/supprimés |
|
||||
| Métadonnées à jour |
|
||||
|
||||
Scénario: Justification 30 jours - Évite stockage obsolète
|
||||
Étant donné qu'un contenu a été modéré après téléchargement
|
||||
Quand le contenu expire après 30 jours maximum
|
||||
Alors le contenu illégal est automatiquement supprimé
|
||||
Et ne reste pas indéfiniment sur le device
|
||||
|
||||
# ===== RENOUVELLEMENT AUTOMATIQUE =====
|
||||
|
||||
Scénario: Détection WiFi et contenus >25 jours
|
||||
Étant donné que j'ai des contenus téléchargés il y a 26 jours
|
||||
Quand l'app détecte une connexion WiFi
|
||||
Alors une requête GET /offline/contents/refresh est envoyée
|
||||
Et le backend vérifie chaque contenu
|
||||
|
||||
Scénario: Vérification abonnement Premium toujours actif
|
||||
Étant donné qu'un contenu téléchargé en Premium est à renouveler
|
||||
Quand le backend vérifie le statut
|
||||
Et que l'abonnement Premium est toujours actif
|
||||
Alors la validité est renouvelée à 30 jours supplémentaires
|
||||
|
||||
Scénario: Abonnement Premium expiré - Contenu non renouvelé
|
||||
Étant donné qu'un contenu Premium téléchargé est à renouveler
|
||||
Quand le backend vérifie le statut
|
||||
Et que l'abonnement Premium a expiré
|
||||
Alors le contenu n'est pas renouvelé
|
||||
Et il sera supprimé à l'expiration (J-0)
|
||||
Et l'utilisateur voit "Contenu Premium expiré (abonnement inactif)"
|
||||
|
||||
Scénario: Vérification contenu pas modéré/supprimé
|
||||
Étant donné qu'un contenu téléchargé est à renouveler
|
||||
Quand le backend vérifie le statut
|
||||
Et que le contenu a été modéré ou supprimé entre temps
|
||||
Alors le contenu n'est pas renouvelé
|
||||
Et sera supprimé immédiatement du device
|
||||
Et l'utilisateur voit "1 contenu retiré (violation règles)"
|
||||
|
||||
Scénario: Mise à jour métadonnées lors du renouvellement
|
||||
Étant donné qu'un contenu téléchargé est renouvelé
|
||||
Quand le backend traite le renouvellement
|
||||
Alors les métadonnées sont mises à jour:
|
||||
| métadonnée | mise à jour si changée |
|
||||
| Titre | ✅ |
|
||||
| Nom créateur | ✅ |
|
||||
| Description | ✅ |
|
||||
| Tags | ✅ |
|
||||
| Statut Premium | ✅ |
|
||||
Et l'utilisateur voit les infos à jour
|
||||
|
||||
Scénario: Pas de re-téléchargement audio si fichier OK
|
||||
Étant donné qu'un contenu est renouvelé
|
||||
Quand le fichier audio local est intact
|
||||
Alors seules les métadonnées sont mises à jour
|
||||
Et le fichier audio n'est pas re-téléchargé
|
||||
Et cela économise la bande passante
|
||||
|
||||
Scénario: Re-téléchargement audio si fichier corrompu
|
||||
Étant donné qu'un contenu est renouvelé
|
||||
Quand le fichier audio local est corrompu (checksum invalide)
|
||||
Alors le fichier audio est re-téléchargé entièrement
|
||||
Et le nouveau fichier remplace le corrompu
|
||||
|
||||
Scénario: Renouvellement silencieux si WiFi régulier
|
||||
Étant donné que je me connecte en WiFi tous les jours
|
||||
Quand mes contenus atteignent 25-30 jours
|
||||
Alors ils sont automatiquement renouvelés en arrière-plan
|
||||
Et je ne vois aucune notification (processus transparent)
|
||||
Et mes contenus restent valides indéfiniment
|
||||
|
||||
Scénario: Renouvellement batch de plusieurs contenus
|
||||
Étant donné que j'ai 30 contenus à renouveler
|
||||
Quand le renouvellement automatique se déclenche
|
||||
Alors une requête batch est envoyée:
|
||||
```json
|
||||
POST /offline/contents/refresh
|
||||
{
|
||||
"content_ids": ["abc123", "def456", "ghi789", ...]
|
||||
}
|
||||
```
|
||||
Et le backend traite les 30 contenus en une seule requête
|
||||
Et cela économise les requêtes HTTP
|
||||
|
||||
Scénario: Temps de traitement renouvellement
|
||||
Étant donné que 30 contenus sont à renouveler
|
||||
Quand la requête batch est traitée
|
||||
Alors le backend répond en <2 secondes
|
||||
Et les métadonnées sont mises à jour localement
|
||||
Et l'utilisateur ne remarque aucun ralentissement
|
||||
|
||||
# ===== NOTIFICATIONS EXPIRATION =====
|
||||
|
||||
Scénario: Notification J-3 avant expiration
|
||||
Étant donné que j'ai 15 contenus qui expirent dans 3 jours
|
||||
Quand le système vérifie les expirations
|
||||
Alors je reçois une notification:
|
||||
"""
|
||||
⚠️ 15 contenus expirent dans 3 jours
|
||||
|
||||
Connectez-vous en WiFi pour les renouveler automatiquement.
|
||||
"""
|
||||
Et je peux agir avant l'expiration
|
||||
|
||||
Scénario: Pas de notification si connexion WiFi régulière
|
||||
Étant donné que je me connecte en WiFi tous les jours
|
||||
Et que mes contenus sont automatiquement renouvelés
|
||||
Quand le système vérifie les expirations
|
||||
Alors aucune notification J-3 n'est envoyée
|
||||
Car les contenus sont déjà renouvelés silencieusement
|
||||
|
||||
Scénario: Notification uniquement si contenus non renouvelés
|
||||
Étant donné que j'ai 20 contenus dont 15 renouvelés et 5 non renouvelés
|
||||
Quand le J-3 arrive pour les 5 non renouvelés
|
||||
Alors je reçois "5 contenus expirent dans 3 jours"
|
||||
Et seuls les contenus à risque sont mentionnés
|
||||
|
||||
Scénario: Action utilisateur après notification J-3
|
||||
Étant donné que je reçois la notification J-3
|
||||
Quand je clique sur la notification
|
||||
Alors l'app s'ouvre sur la page Téléchargements
|
||||
Et je vois les contenus qui vont expirer en rouge
|
||||
Et je peux me connecter en WiFi pour les renouveler
|
||||
|
||||
Scénario: Suppression automatique J-0 (expiration)
|
||||
Étant donné qu'un contenu n'a pas été renouvelé
|
||||
Quand le jour d'expiration arrive (J-0)
|
||||
Alors le fichier est automatiquement supprimé du device
|
||||
Et l'espace disque est libéré
|
||||
Et le compteur est décrémenté (ex: 45/50 → 44/50)
|
||||
|
||||
Scénario: Toast après suppression automatique J-0
|
||||
Étant donné que 15 contenus viennent d'expirer
|
||||
Quand l'utilisateur ouvre l'app
|
||||
Alors il voit un toast:
|
||||
"""
|
||||
🗑️ 15 contenus expirés ont été supprimés
|
||||
|
||||
Reconnectez-vous en WiFi régulièrement pour éviter les expirations.
|
||||
"""
|
||||
|
||||
Scénario: Liste contenus supprimés après expiration
|
||||
Étant donné que 15 contenus ont expiré
|
||||
Quand je consulte l'historique des suppressions
|
||||
Alors je vois la liste des 15 contenus supprimés:
|
||||
| titre | créateur | date expiration |
|
||||
| Mon épisode préféré | JeanDupont | 15 juin 2025 |
|
||||
| Road trip Bretagne | MarieLambert| 15 juin 2025 |
|
||||
| ... | ... | ... |
|
||||
Et je peux les re-télécharger si je veux
|
||||
|
||||
Scénario: Re-téléchargement après expiration
|
||||
Étant donné qu'un contenu a expiré et été supprimé
|
||||
Quand je retrouve ce contenu dans l'app
|
||||
Alors le badge ✅ "Offline" n'est plus affiché
|
||||
Et je peux le re-télécharger normalement
|
||||
Et la validité repart à 30 jours
|
||||
|
||||
# ===== CAS PARTICULIERS =====
|
||||
|
||||
Scénario: Utilisateur ne se connecte jamais pendant 30 jours
|
||||
Étant donné que je télécharge 50 contenus le 1er juin
|
||||
Mais que je ne me connecte jamais en WiFi pendant 30 jours
|
||||
Quand le 1er juillet arrive
|
||||
Alors tous les 50 contenus expirent
|
||||
Et sont automatiquement supprimés
|
||||
Et je n'ai plus aucun contenu offline
|
||||
|
||||
Scénario: Utilisateur en zone blanche 30+ jours
|
||||
Étant donné que je télécharge 50 contenus avant de partir en zone sans réseau
|
||||
Et que je reste 45 jours sans connexion
|
||||
Quand les contenus expirent après 30 jours
|
||||
Alors ils sont supprimés même si je ne peux pas me connecter
|
||||
Et je perds l'accès à mes contenus offline
|
||||
|
||||
Scénario: Recommandation téléchargement avant zone blanche longue
|
||||
Étant donné que je prépare un road trip de 60 jours
|
||||
Quand je consulte la FAQ
|
||||
Alors je vois la recommandation:
|
||||
"""
|
||||
⚠️ Road trips >30 jours
|
||||
|
||||
Les contenus téléchargés expirent après 30 jours.
|
||||
Pour les longs voyages sans connexion:
|
||||
• Téléchargez de nouveaux contenus tous les 25 jours si possible
|
||||
• Ou planifiez une reconnexion WiFi tous les 25 jours
|
||||
"""
|
||||
|
||||
Scénario: Changement statut Premium en gratuit pendant validité
|
||||
Étant donné que je suis Premium et j'ai téléchargé 200 contenus
|
||||
Quand mon abonnement Premium expire
|
||||
Et que je repasse en gratuit
|
||||
Alors au prochain renouvellement, seulement 50 contenus sont conservés
|
||||
Et les 150 autres sont supprimés (limite gratuit)
|
||||
Et je vois "Limite gratuit (50 contenus) appliquée. 150 contenus supprimés."
|
||||
|
||||
Scénario: Sélection automatique 50 meilleurs contenus si passage gratuit
|
||||
Étant donné que je repasse en gratuit avec 200 contenus téléchargés
|
||||
Quand le système applique la limite de 50
|
||||
Alors les 50 contenus les plus récemment écoutés sont conservés
|
||||
Et les 150 autres sont supprimés
|
||||
Et cela maximise les chances de garder les contenus que j'aime
|
||||
|
||||
Scénario: Contenus Premium exclusifs supprimés si abonnement expire
|
||||
Étant donné que j'ai téléchargé 20 contenus Premium exclusifs
|
||||
Quand mon abonnement Premium expire
|
||||
Alors les 20 contenus Premium sont immédiatement supprimés
|
||||
Car ils ne sont accessibles qu'aux abonnés Premium actifs
|
||||
Et je vois "20 contenus Premium supprimés (abonnement expiré)"
|
||||
|
||||
# ===== STATISTIQUES ET MONITORING =====
|
||||
|
||||
Scénario: Affichage temps restant avant expiration
|
||||
Étant donné que j'ai 45 contenus téléchargés
|
||||
Quand je consulte la page Téléchargements
|
||||
Alors je vois pour chaque contenu:
|
||||
| contenu | temps restant |
|
||||
| Mon épisode (récent)| Expire dans 28 jours |
|
||||
| Road trip (ancien) | Expire dans 3 jours |
|
||||
Et je sais lesquels sont prioritaires pour renouvellement
|
||||
|
||||
Scénario: Tri par date expiration
|
||||
Étant donné que j'ai 45 contenus avec différentes dates d'expiration
|
||||
Quand je trie par "Expiration"
|
||||
Alors les contenus qui expirent le plus tôt apparaissent en premier
|
||||
Et je peux voir rapidement lesquels nécessitent une reconnexion urgente
|
||||
|
||||
Scénario: Badge rouge si expiration <3 jours
|
||||
Étant donné qu'un contenu expire dans 2 jours
|
||||
Quand je consulte la liste des téléchargements
|
||||
Alors le contenu a un badge rouge "⚠️ Expire bientôt"
|
||||
Et il est visuellement mis en avant
|
||||
|
||||
Scénario: Statistiques utilisateur - Taux de renouvellement
|
||||
Étant donné que j'accède à mes statistiques
|
||||
Quand je consulte la section Téléchargements
|
||||
Alors je vois:
|
||||
| métrique | valeur |
|
||||
| Contenus actuels | 45 |
|
||||
| Contenus expirés depuis début | 87 |
|
||||
| Contenus renouvelés (auto) | 234 |
|
||||
| Taux renouvellement automatique | 73% |
|
||||
|
||||
Scénario: Statistiques admin - Taux expiration global
|
||||
Étant donné qu'un admin consulte les métriques offline
|
||||
Quand il accède au dashboard
|
||||
Alors il voit:
|
||||
| métrique | valeur |
|
||||
| Contenus téléchargés actifs | 1,234,567 |
|
||||
| Expirations ce mois | 45,678 |
|
||||
| Taux expiration | 3.7% |
|
||||
| Renouvellements automatiques/mois | 234,567 |
|
||||
|
||||
Scénario: Alerte admin si taux expiration >10%
|
||||
Étant donné que le taux d'expiration mensuel dépasse 10%
|
||||
Quand le système détecte cette anomalie
|
||||
Alors une alerte est envoyée:
|
||||
"""
|
||||
⚠️ Taux d'expiration anormal: 12.3%
|
||||
|
||||
Nombre expirations ce mois: 152,345
|
||||
Causes possibles:
|
||||
- Utilisateurs ne se connectent plus en WiFi
|
||||
- Problème renouvellement automatique ?
|
||||
- Churn utilisateurs augmenté ?
|
||||
|
||||
Action recommandée: Enquête technique + email rappel utilisateurs
|
||||
"""
|
||||
|
||||
Scénario: Email rappel si pas de connexion WiFi depuis 20 jours
|
||||
Étant donné que je n'ai pas connecté l'app en WiFi depuis 20 jours
|
||||
Et que j'ai 45 contenus téléchargés
|
||||
Quand le système détecte cette inactivité WiFi
|
||||
Alors je reçois un email:
|
||||
"""
|
||||
📡 Connectez-vous en WiFi pour conserver vos téléchargements
|
||||
|
||||
Vous n'avez pas connecté RoadWave en WiFi depuis 20 jours.
|
||||
Vos 45 contenus téléchargés expireront dans 10 jours si non renouvelés.
|
||||
|
||||
Connectez-vous en WiFi avant le 30 juin pour les renouveler automatiquement.
|
||||
"""
|
||||
|
||||
Scénario: Performance renouvellement avec 10 000 utilisateurs simultanés
|
||||
Étant donné que 10 000 utilisateurs se connectent en WiFi simultanément
|
||||
Quand chacun demande le renouvellement de 50 contenus
|
||||
Alors le serveur traite 500 000 vérifications
|
||||
Et grâce au cache Redis et index PostgreSQL, le temps de réponse reste <3s
|
||||
Et les serveurs gèrent la charge sans problème
|
||||
|
||||
Scénario: Logs audit renouvellements
|
||||
Étant donné qu'un contenu est renouvelé
|
||||
Quand l'opération se termine
|
||||
Alors un log est enregistré:
|
||||
| timestamp | user_id | content_id | action | résultat |
|
||||
| 2025-06-15 14:30:00 | abc123 | xyz789 | renew | success (+30d) |
|
||||
| 2025-06-15 14:30:01 | abc123 | def456 | renew | failed (deleted)|
|
||||
Et ces logs aident à débugger les problèmes
|
||||
Reference in New Issue
Block a user