feat(gherkin): ajouter features API pour jauges d'intérêt

Création de 3 features Gherkin pour les tests backend des jauges d'intérêt:

- evolution-jauges.feature: Tests API pour calculs de jauges (likes auto/manuels,
  abonnements créateurs, skips), persistence PostgreSQL, bornes 0-100%, cache Redis

- jauge-initiale.feature: Tests API pour initialisation à 50% lors inscription,
  questionnaire optionnel post-MVP, recommandations cold start

- degradation-temporelle.feature: Tests API confirmant absence de dégradation
  automatique, réinitialisation manuelle avec snapshot et audit log

Complète les features UI existantes avec les aspects techniques backend.
This commit is contained in:
jpgiannetti
2026-02-02 22:32:29 +01:00
parent 2cc9da29ff
commit 718581b954
3 changed files with 1114 additions and 0 deletions

View File

@@ -0,0 +1,481 @@
# language: fr
Fonctionnalité: API - Évolution des jauges d'intérêt
En tant qu'API backend
Je veux calculer et persister les évolutions de jauges d'intérêt
Afin d'alimenter l'algorithme de recommandation
Contexte:
Étant donné que l'API RoadWave est disponible
Et que la base de données PostgreSQL est accessible
Et qu'un utilisateur "user123" existe avec token JWT valide
Scénario: API calcule like automatique renforcé (≥80% écoute)
Étant donné que l'utilisateur "user123" a une jauge "Automobile" à 45% en base
Et qu'un contenu "content456" de 5 minutes est tagué "Automobile"
Quand je POST /api/v1/listening-events
"""json
{
"user_id": "user123",
"content_id": "content456",
"listened_duration_seconds": 270,
"total_duration_seconds": 300,
"completion_percentage": 90
}
"""
Alors le statut de réponse est 201
Et la réponse contient:
"""json
{
"like_type": "automatic_reinforced",
"gauge_updates": [
{
"category": "Automobile",
"previous_value": 45,
"delta": 2,
"new_value": 47
}
]
}
"""
Et en base de données, la jauge "Automobile" de "user123" est à 47%
Scénario: API calcule like automatique standard (30-79% écoute)
Étant donné que l'utilisateur "user123" a une jauge "Voyage" à 60% en base
Et qu'un contenu "content789" de 10 minutes est tagué "Voyage"
Quand je POST /api/v1/listening-events
"""json
{
"user_id": "user123",
"content_id": "content789",
"listened_duration_seconds": 300,
"total_duration_seconds": 600,
"completion_percentage": 50
}
"""
Alors le statut de réponse est 201
Et la réponse contient:
"""json
{
"like_type": "automatic_standard",
"gauge_updates": [
{
"category": "Voyage",
"previous_value": 60,
"delta": 1,
"new_value": 61
}
]
}
"""
Et en base de données, la jauge "Voyage" de "user123" est à 61%
Scénario: API applique like manuel explicite (+2%)
Étant donné que l'utilisateur "user123" a une jauge "Musique" à 55% en base
Et qu'un contenu "content999" est tagué "Musique"
Quand je POST /api/v1/likes
"""json
{
"user_id": "user123",
"content_id": "content999",
"like_type": "manual"
}
"""
Alors le statut de réponse est 201
Et la réponse contient:
"""json
{
"like_id": "<uuid>",
"gauge_updates": [
{
"category": "Musique",
"previous_value": 55,
"delta": 2,
"new_value": 57
}
]
}
"""
Et en base de données, la jauge "Musique" de "user123" est à 57%
Scénario: API applique unlike (retire like manuel)
Étant donné que l'utilisateur "user123" a une jauge "Sport" à 57% en base
Et qu'il a liké manuellement le contenu "content888" tagué "Sport"
Et que le like a l'ID "like_abc123"
Quand je DELETE /api/v1/likes/like_abc123
Alors le statut de réponse est 204
Et en base de données, la jauge "Sport" de "user123" est à 55%
Et le like "like_abc123" est supprimé de la table likes
Scénario: API refuse unlike d'un like automatique
Étant donné que l'utilisateur "user123" a écouté un contenu à 90%
Et qu'il a reçu un like automatique "like_auto456"
Quand je DELETE /api/v1/likes/like_auto456
Alors le statut de réponse est 403
Et la réponse contient:
"""json
{
"error": "CANNOT_UNLIKE_AUTOMATIC",
"message": "Les likes automatiques ne peuvent pas être retirés"
}
"""
Et en base de données, le like "like_auto456" existe toujours
Scénario: API cumule like automatique + like manuel
Étant donné que l'utilisateur "user123" a une jauge "Technologie" à 50% en base
Et qu'un contenu "content777" de 10 minutes est tagué "Technologie"
Quand je POST /api/v1/listening-events
"""json
{
"user_id": "user123",
"content_id": "content777",
"completion_percentage": 50
}
"""
Alors la jauge "Technologie" passe à 51% (+1% auto)
Quand je POST ensuite /api/v1/likes
"""json
{
"user_id": "user123",
"content_id": "content777",
"like_type": "manual"
}
"""
Alors la jauge "Technologie" passe à 53% (+2% manuel)
Et le delta total est de 3%
Scénario: API applique bonus abonnement créateur (+5% tous tags)
Étant donné que l'utilisateur "user123" a les jauges suivantes:
| catégorie | niveau |
| Automobile | 50% |
| Technologie | 45% |
Et qu'un créateur "creator456" publie des contenus tagués "Automobile" et "Technologie"
Quand je POST /api/v1/subscriptions
"""json
{
"user_id": "user123",
"creator_id": "creator456"
}
"""
Alors le statut de réponse est 201
Et en base de données:
| catégorie | niveau |
| Automobile | 55% |
| Technologie | 50% |
Et l'abonnement est créé avec bonus appliqué
Scénario: API retire bonus désabonnement créateur (-5% tous tags)
Étant donné que l'utilisateur "user123" a les jauges suivantes:
| catégorie | niveau |
| Voyage | 65% |
| Culture | 58% |
Et qu'il est abonné au créateur "creator789" qui publie "Voyage" et "Culture"
Quand je DELETE /api/v1/subscriptions/creator789
Alors le statut de réponse est 204
Et en base de données:
| catégorie | niveau |
| Voyage | 60% |
| Culture | 53% |
Scénario: API applique pénalité skip rapide (<10s)
Étant donné que l'utilisateur "user123" a une jauge "Politique" à 50% en base
Et qu'un contenu "content555" de 300 secondes est tagué "Politique"
Quand je POST /api/v1/skip-events
"""json
{
"user_id": "user123",
"content_id": "content555",
"listened_duration_seconds": 5,
"total_duration_seconds": 300
}
"""
Alors le statut de réponse est 201
Et la réponse contient:
"""json
{
"skip_type": "early",
"gauge_updates": [
{
"category": "Politique",
"previous_value": 50,
"delta": -0.5,
"new_value": 49.5
}
]
}
"""
Et en base de données, la jauge "Politique" de "user123" est à 49.5%
Scénario: API n'applique pas de pénalité pour skip ≥10s et <30%
Étant donné que l'utilisateur "user123" a une jauge "Économie" à 60% en base
Et qu'un contenu "content333" de 600 secondes est tagué "Économie"
Quand je POST /api/v1/skip-events
"""json
{
"user_id": "user123",
"content_id": "content333",
"listened_duration_seconds": 120,
"total_duration_seconds": 600,
"completion_percentage": 20
}
"""
Alors le statut de réponse est 201
Et la réponse contient:
"""json
{
"skip_type": "neutral",
"gauge_updates": []
}
"""
Et en base de données, la jauge "Économie" de "user123" reste à 60%
Scénario: API n'applique pas de pénalité pour skip ≥30%
Étant donné que l'utilisateur "user123" a une jauge "Sport" à 55% en base
Et qu'un contenu "content222" de 600 secondes est tagué "Sport"
Quand je POST /api/v1/skip-events
"""json
{
"user_id": "user123",
"content_id": "content222",
"listened_duration_seconds": 300,
"total_duration_seconds": 600,
"completion_percentage": 50
}
"""
Alors le statut de réponse est 201
Et la réponse contient:
"""json
{
"skip_type": "late",
"gauge_updates": []
}
"""
Et en base de données, la jauge "Sport" de "user123" reste à 55%
Scénario: API applique évolution sur plusieurs tags simultanément
Étant donné que l'utilisateur "user123" a les jauges suivantes:
| catégorie | niveau |
| Automobile | 45% |
| Voyage | 60% |
Et qu'un contenu "content111" est tagué "Automobile" et "Voyage"
Quand je POST /api/v1/listening-events
"""json
{
"user_id": "user123",
"content_id": "content111",
"completion_percentage": 90
}
"""
Alors le statut de réponse est 201
Et la réponse contient:
"""json
{
"like_type": "automatic_reinforced",
"gauge_updates": [
{
"category": "Automobile",
"previous_value": 45,
"delta": 2,
"new_value": 47
},
{
"category": "Voyage",
"previous_value": 60,
"delta": 2,
"new_value": 62
}
]
}
"""
Et en base de données:
| catégorie | niveau |
| Automobile | 47% |
| Voyage | 62% |
Scénario: API respecte la borne maximum 100%
Étant donné que l'utilisateur "user123" a une jauge "Cryptomonnaie" à 99% en base
Et qu'un contenu "content_crypto" est tagué "Cryptomonnaie"
Quand je POST /api/v1/listening-events
"""json
{
"user_id": "user123",
"content_id": "content_crypto",
"completion_percentage": 95
}
"""
Alors le statut de réponse est 201
Et la réponse contient:
"""json
{
"like_type": "automatic_reinforced",
"gauge_updates": [
{
"category": "Cryptomonnaie",
"previous_value": 99,
"delta": 2,
"new_value": 100,
"capped": true
}
]
}
"""
Et en base de données, la jauge "Cryptomonnaie" de "user123" est à 100%
Et la jauge n'a pas dépassé 100%
Scénario: API respecte la borne minimum 0%
Étant donné que l'utilisateur "user123" a une jauge "Politique" à 0.3% en base
Et qu'un contenu "content_pol" est tagué "Politique"
Quand je POST /api/v1/skip-events
"""json
{
"user_id": "user123",
"content_id": "content_pol",
"listened_duration_seconds": 3,
"total_duration_seconds": 300
}
"""
Alors le statut de réponse est 201
Et la réponse contient:
"""json
{
"skip_type": "early",
"gauge_updates": [
{
"category": "Politique",
"previous_value": 0.3,
"delta": -0.5,
"new_value": 0,
"capped": true
}
]
}
"""
Et en base de données, la jauge "Politique" de "user123" est à 0%
Et la jauge n'est pas devenue négative
Scénario: API respecte borne minimum lors désabonnement
Étant donné que l'utilisateur "user123" a une jauge "Économie" à 3% en base
Et qu'il est abonné au créateur "creator_eco" qui publie "Économie"
Quand je DELETE /api/v1/subscriptions/creator_eco
Alors le statut de réponse est 204
Et en base de données, la jauge "Économie" de "user123" est à 0% (et non -2%)
Scénario: API GET retourne toutes les jauges utilisateur
Étant donné que l'utilisateur "user123" a les jauges suivantes en base:
| catégorie | niveau |
| Automobile | 67% |
| Voyage | 82% |
| Économie | 34% |
| Sport | 50% |
| Musique | 45% |
| Technologie | 71% |
Quand je GET /api/v1/users/user123/interest-gauges
Alors le statut de réponse est 200
Et la réponse contient les 12 catégories avec leurs niveaux:
"""json
{
"user_id": "user123",
"gauges": [
{"category": "Automobile", "level": 67},
{"category": "Voyage", "level": 82},
{"category": "Économie", "level": 34},
{"category": "Sport", "level": 50},
{"category": "Musique", "level": 45},
{"category": "Technologie", "level": 71}
]
}
"""
Scénario: API calcule évolution immédiate (pas de batch différé)
Étant donné que l'utilisateur "user123" a une jauge "Voyage" à 50% en base
Quand je POST /api/v1/listening-events à 12:00:00
"""json
{
"user_id": "user123",
"content_id": "content_travel",
"completion_percentage": 85
}
"""
Alors le statut de réponse est 201
Quand je GET /api/v1/users/user123/interest-gauges à 12:00:01 (1 seconde après)
Alors la jauge "Voyage" est à 52%
Et la mise à jour est visible immédiatement
Scénario: API rejette token JWT invalide
Quand je POST /api/v1/listening-events sans token JWT
Alors le statut de réponse est 401
Et la réponse contient:
"""json
{
"error": "UNAUTHORIZED",
"message": "Token JWT manquant ou invalide"
}
"""
Scénario: API valide format des données d'entrée
Quand je POST /api/v1/listening-events
"""json
{
"user_id": "user123",
"content_id": "content456",
"completion_percentage": 150
}
"""
Alors le statut de réponse est 400
Et la réponse contient:
"""json
{
"error": "VALIDATION_ERROR",
"message": "completion_percentage doit être entre 0 et 100"
}
"""
Scénario: API gère contenu avec tags inexistants en base
Étant donné qu'un contenu "content_new" est tagué "NouvelleCategorie" (non encore en base)
Quand je POST /api/v1/listening-events
"""json
{
"user_id": "user123",
"content_id": "content_new",
"completion_percentage": 90
}
"""
Alors le statut de réponse est 201
Et une nouvelle ligne est créée dans la table interest_gauges:
| user_id | category | level |
| user123 | NouvelleCategorie | 52 |
Et l'initialisation démarre à 50% + 2% de like auto = 52%
Scénario: API persiste historique des modifications de jauges
Étant donné que l'utilisateur "user123" a une jauge "Sport" à 50%
Quand je POST /api/v1/listening-events qui applique +2%
Alors une ligne est insérée dans interest_gauge_history:
| user_id | category | previous_value | delta | new_value | event_type | event_id | timestamp |
| user123 | Sport | 50 | 2 | 52 | listening_event| <event_uuid> | 2026-02-02T12:00:00 |
Et cet historique permet d'auditer les évolutions
Scénario: API retourne métriques d'évolution utilisateur
Étant donné que l'utilisateur "user123" a un historique d'évolution en base
Quand je GET /api/v1/users/user123/interest-gauges/evolution?since=7d
Alors le statut de réponse est 200
Et la réponse contient:
"""json
{
"period": "7d",
"evolution": [
{
"category": "Automobile",
"start_value": 60,
"end_value": 67,
"delta": 7,
"events_count": 15
},
{
"category": "Voyage",
"start_value": 80,
"end_value": 82,
"delta": 2,
"events_count": 3
}
]
}
"""