Files
roadwave/features/ui/mode-offline/synchronisation-actions.feature
jpgiannetti 37c62206ad 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>
2026-02-01 11:31:41 +01:00

426 lines
17 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é: 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