feat(gherkin): ajouter features interactions et navigation

Couverture complète des règles métier 05-interactions-navigation.md :

API (Backend) :
- File d'attente : pré-calcul 5 contenus, recalcul auto (>10km, 10min, <3 contenus), invalidation, Redis cache
- Notifications géolocalisées : calcul ETA, déclenchement 7s avant, quota 6/h, cooldown 10min, tracking GPS
- Jauges d'intérêt : architecture services séparés (Calculation + Update), pattern addition points absolus, persistance Redis/PostgreSQL

UI (Frontend) :
- Mode piéton : notifications push arrière-plan, rayon 200m, permissions stratégie progressive, geofencing iOS/Android
- Basculement automatique voiture↔piéton : détection vitesse GPS, hysteresis 10s, transition transparente

Fichiers créés :
- features/api/navigation/file-attente.feature
- features/api/navigation/notifications-geolocalisees.feature
- features/ui/navigation/mode-pieton-notifications-push.feature

Fichiers enrichis :
- features/api/interest-gauges/evolution-jauges.feature (ajout scénarios architecture backend)
This commit is contained in:
jpgiannetti
2026-02-02 22:41:00 +01:00
parent 852240b5ec
commit ea77aa8ac7
4 changed files with 1055 additions and 0 deletions

View File

@@ -0,0 +1,390 @@
# language: fr
Fonctionnalité: API - Notifications géolocalisées et quota anti-spam
En tant qu'API backend
Je veux gérer les notifications de contenus géolocalisés
Afin de respecter les quotas et détecter le bon timing
Contexte:
Étant donné que l'API RoadWave est disponible
Et que Redis est accessible
Et qu'un utilisateur "user123" existe en mode voiture
# Calcul ETA et déclenchement notification
Scénario: API calcule ETA vers point géolocalisé
Étant donné qu'un contenu géolocalisé existe à la position (48.8584, 2.2945) Tour Eiffel
Et que l'utilisateur roule à 50 km/h vers ce point
Et qu'il est actuellement à 98 mètres
Quand je POST /api/v1/geo-notifications/check-eta
"""json
{
"user_id": "user123",
"current_lat": 48.8577,
"current_lon": 2.2950,
"speed_kmh": 50,
"target_content_id": "content_tower"
}
"""
Alors le statut de réponse est 200
Et la réponse contient:
"""json
{
"eta_seconds": 7,
"distance_meters": 98,
"should_notify": true,
"notification_trigger": "eta_threshold"
}
"""
Scénario: API déclenche notification 7 secondes avant arrivée
Étant donné qu'un contenu géolocalisé existe au point GPS
Et que l'ETA calculé est 7 secondes
Quand je POST /api/v1/geo-notifications/trigger
"""json
{
"user_id": "user123",
"content_id": "content_tower",
"eta_seconds": 7
}
"""
Alors le statut de réponse est 201
Et une notification est envoyée au mobile
Et la notification contient:
| champ | valeur |
| type | geo_anchored |
| countdown | 7 |
| icon | 🏛 (selon type contenu) |
| sound | bip court |
Scénario: API ne déclenche pas si ETA >7 secondes
Étant donné qu'un contenu géolocalisé existe
Et que l'ETA calculé est 10 secondes
Quand je POST /api/v1/geo-notifications/check-eta
Alors le statut de réponse est 200
Et la réponse contient:
"""json
{
"eta_seconds": 10,
"should_notify": false,
"reason": "eta_above_threshold"
}
"""
Et aucune notification n'est envoyée
Scénario: API déclenche notification immédiate si vitesse faible et proche
Étant donné qu'un contenu géolocalisé existe
Et que l'utilisateur roule à 3 km/h
Et qu'il est à 40 mètres du point
Quand je POST /api/v1/geo-notifications/check-eta
Alors le statut de réponse est 200
Et la réponse contient:
"""json
{
"eta_seconds": 0,
"distance_meters": 40,
"speed_kmh": 3,
"should_notify": true,
"notification_trigger": "proximity_and_low_speed"
}
"""
Et une notification est envoyée immédiatement
# Quota anti-spam
Scénario: API vérifie quota avant notification (6 contenus max/heure)
Étant donné que l'utilisateur a déjà reçu 5 notifications géo dans la dernière heure
Quand je POST /api/v1/geo-notifications/check-quota
"""json
{
"user_id": "user123"
}
"""
Alors le statut de réponse est 200
Et la réponse contient:
"""json
{
"quota_available": true,
"notifications_count": 5,
"quota_limit": 6,
"remaining": 1
}
"""
Scénario: API refuse notification si quota atteint (6/6)
Étant donné que l'utilisateur a déjà reçu 6 notifications géo dans la dernière heure
Quand je POST /api/v1/geo-notifications/trigger
"""json
{
"user_id": "user123",
"content_id": "content_x"
}
"""
Alors le statut de réponse est 429
Et la réponse contient:
"""json
{
"error": "QUOTA_EXCEEDED",
"message": "Quota horaire de notifications géolocalisées atteint (6/6)",
"notifications_count": 6,
"quota_limit": 6,
"reset_in_seconds": 1800
}
"""
Et aucune notification n'est envoyée
Scénario: API incrémente quota après notification envoyée
Étant donné que l'utilisateur a reçu 3 notifications géo
Quand je POST /api/v1/geo-notifications/trigger (succès)
Alors le quota passe à 4/6
Et en Redis, la clé "user:user123:geo_quota" contient 4 entrées
Et le sorted set contient les timestamps des 4 notifications
Scénario: API utilise rolling window pour quota horaire
Étant donné que l'utilisateur a reçu 6 notifications:
| timestamp |
| 10:00:00 |
| 10:15:00 |
| 10:30:00 |
| 10:45:00 |
| 11:00:00 |
| 11:05:00 |
Et que l'heure actuelle est 11:10:00
Quand je vérifie le quota
Alors les notifications avant 10:10:00 sont expirées
Et seules 4 notifications sont comptées (10:15, 10:30, 10:45, 11:00, 11:05 = 5)
Et le quota disponible est 1/6
Scénario: API exclut séquences d'audio-guide multi-séquences du quota
Étant donné qu'un audio-guide "Visite Louvre" a 12 séquences
Et que l'utilisateur a écouté les 12 séquences
Quand je calcule le quota
Alors l'audio-guide compte pour 1 seule notification
Et les 12 séquences ne consomment pas 12 quota
Et le quota utilisé est 1/6
# Cooldown après ignorance
Scénario: API active cooldown 10 min si notification ignorée
Étant donné qu'une notification géo a été envoyée à 10:00:00
Et que l'utilisateur n'a pas appuyé sur "Suivant" dans les 7 secondes
Quand je POST /api/v1/geo-notifications/ignored
"""json
{
"user_id": "user123",
"notification_id": "notif123"
}
"""
Alors le statut de réponse est 200
Et un cooldown de 10 minutes est activé
Et en Redis, la clé "user:user123:geo_cooldown" est créée
Et le TTL est 600 secondes (10 minutes)
Scénario: API refuse notification si cooldown actif
Étant donné qu'un cooldown est actif depuis 5 minutes
Et qu'il reste 5 minutes de cooldown
Quand je POST /api/v1/geo-notifications/trigger
Alors le statut de réponse est 429
Et la réponse contient:
"""json
{
"error": "COOLDOWN_ACTIVE",
"message": "Cooldown actif après notification ignorée",
"cooldown_remaining_seconds": 300
}
"""
Et aucune notification n'est envoyée
Scénario: API autorise notification après expiration cooldown
Étant donné qu'un cooldown a été activé à 10:00:00
Et que l'heure actuelle est 10:11:00 (11 minutes après)
Quand je POST /api/v1/geo-notifications/check-quota
Alors le cooldown a expiré
Et les notifications sont à nouveau autorisées
# Tracking GPS temps réel
Scénario: API reçoit positions GPS toutes les secondes
Étant donné que le mobile envoie des positions GPS
Quand je POST /api/v1/gps/track
"""json
{
"user_id": "user123",
"latitude": 48.8577,
"longitude": 2.2950,
"speed_kmh": 50,
"timestamp": "2026-02-02T10:30:00Z"
}
"""
Alors le statut de réponse est 200
Et la position est stockée dans l'historique GPS (Redis)
Et l'historique conserve les 30 derniers points
Scénario: API calcule vitesse moyenne sur 30 derniers points
Étant donné que l'historique GPS contient 30 points
Et que les vitesses enregistrées sont variables
Quand je GET /api/v1/gps/average-speed
Alors le statut de réponse est 200
Et la réponse contient:
"""json
{
"average_speed_kmh": 48.5,
"samples_count": 30,
"period_seconds": 30
}
"""
Scénario: API détecte contenus géolocalisés dans rayon 500m
Étant donné que l'utilisateur est à la position (48.8577, 2.2950)
Et qu'un contenu géolocalisé existe à 300m
Quand je GET /api/v1/geo-notifications/nearby
"""json
{
"user_id": "user123",
"latitude": 48.8577,
"longitude": 2.2950,
"radius_meters": 500
}
"""
Alors le statut de réponse est 200
Et la réponse contient:
"""json
{
"nearby_contents": [
{
"id": "content_tower",
"distance_meters": 300,
"eta_seconds": 21,
"should_notify_soon": false
}
]
}
"""
# Validation contenu géolocalisé
Scénario: API valide que contenu géolocalisé accepté
Étant donné qu'une notification géo a été envoyée
Et que l'utilisateur a appuyé sur "Suivant" dans les 7 secondes
Quand je POST /api/v1/geo-notifications/accepted
"""json
{
"user_id": "user123",
"notification_id": "notif123",
"content_id": "content_tower"
}
"""
Alors le statut de réponse est 200
Et le contenu géolocalisé est inséré en priorité
Et un décompte de 5 secondes est déclenché côté client
Et le quota n'est PAS incrémenté (acceptation = pas de spam)
Scénario: API enregistre contenu géolocalisé perdu
Étant donné qu'une notification géo a été envoyée
Et que l'utilisateur n'a pas appuyé sur "Suivant"
Quand je POST /api/v1/geo-notifications/ignored
Alors le contenu géolocalisé est perdu
Et il n'est PAS inséré dans la file d'attente
Et une métrique "geo_notification_ignored" est enregistrée
Et le cooldown 10 min est activé
# Métriques
Scénario: API stocke métriques de notifications géolocalisées
Étant donné que l'API envoie des notifications géo
Quand je consulte les métriques
Alors les données suivantes sont disponibles:
| métrique | description |
| geo_notifications_sent | Nombre total envoyées |
| geo_notifications_accepted | Nombre acceptées (Suivant) |
| geo_notifications_ignored | Nombre ignorées |
| geo_acceptance_rate | % acceptation |
| average_eta_seconds | ETA moyen au déclenchement |
| quota_exceeded_count | Nombre de refus quota |
# Redis structures
Scénario: API utilise sorted set Redis pour quota
Étant donné que l'utilisateur a reçu 3 notifications
Quand je consulte Redis "user:user123:geo_quota"
Alors la structure est un sorted set:
"""
ZADD user:user123:geo_quota
1707047400 "notif1"
1707047700 "notif2"
1707048000 "notif3"
"""
Et les scores sont les timestamps Unix
Et le TTL est 3600 secondes (1 heure)
Scénario: API compte notifications avec ZCOUNT
Étant donné que le sorted set contient plusieurs notifications
Et que l'heure actuelle est 11:00:00 (timestamp 1707048000)
Quand l'API vérifie le quota
Alors la commande Redis est:
"""
ZCOUNT user:user123:geo_quota 1707044400 1707048000
"""
Et seules les notifications des 60 dernières minutes sont comptées
# Gestion erreurs
Scénario: API gère échec calcul ETA gracieusement
Étant donné qu'une position GPS est invalide
Quand je POST /api/v1/geo-notifications/check-eta
"""json
{
"user_id": "user123",
"current_lat": 200,
"current_lon": 300
}
"""
Alors le statut de réponse est 400
Et la réponse contient:
"""json
{
"error": "INVALID_GPS_COORDINATES",
"message": "Coordonnées GPS invalides"
}
"""
Scénario: API gère vitesse nulle sans crash
Étant donné que l'utilisateur est à l'arrêt (vitesse = 0)
Et qu'un contenu géolocalisé est à 100m
Quand je POST /api/v1/geo-notifications/check-eta
Alors l'ETA est incalculable (vitesse nulle)
Et aucune notification n'est envoyée
Et la réponse contient:
"""json
{
"should_notify": false,
"reason": "speed_zero"
}
"""
Plan du Scénario: Déclenchement selon ETA
Étant donné qu'un contenu géolocalisé existe
Et que l'ETA calculé est <eta> secondes
Quand je vérifie si notification doit être envoyée
Alors la décision est <decision>
Exemples:
| eta | decision |
| 3 | notifier |
| 5 | notifier |
| 7 | notifier |
| 8 | ne pas notifier |
| 10 | ne pas notifier |
| 15 | ne pas notifier |
Plan du Scénario: Quota selon nombre notifications
Étant donné que l'utilisateur a reçu <count> notifications dans l'heure
Quand je vérifie le quota
Alors le quota est <status>
Exemples:
| count | status |
| 0 | disponible |
| 3 | disponible |
| 5 | disponible |
| 6 | atteint |
| 7 | atteint |