Files
ENI-projet-piscine/tests/load/README.md
T
Johan LEROY 2ed9e1cee4 test(charge): mesure l'API sous charge avec k6
Aucun garde-fou de performance n'existait, et le dépôt ne chiffrait aucun temps de réponse.

tests/load, quatre scénarios :
- smoke : une minute sur chaque route de lecture, joué à chaque PR ;
- charge : 50 utilisateurs, 40 sur le tableau de bord au rythme de son rafraîchissement,
  10 qui explorent les sites ;
- stress : débit croissant jusqu'à la rupture, arrêt au-delà de 10 % d'erreurs ;
- limitation-debit : par le proxy, vérifie que nginx répond 429 et jamais 5xx.

Seuils : p95 < 500 ms et p99 < 1 s sur les lectures, moins de 1 % d'échecs.

k6 tourne en service Compose (profil load) sur le réseau du projet : il vise backend:8000 et
mesure l'API plutôt que la limite de 20 req/s par adresse de nginx. Chaque tir écrit un rapport
HTML, une synthèse Markdown et le JSON brut dans tests/load/results.

make load-smoke, load-test, load-stress et load-limits ; le job E2E enchaîne le smoke et le
test de limitation après Playwright.

Closes #47
2026-09-23 09:29:20 +02:00

93 lines
4.3 KiB
Markdown

# Tests de charge (k6)
Scénarios k6 contre l'API EnerVision. Issue #47, décisions dans l'ADR 0015.
## Scénarios
| Script | Cible | Profil | Quand |
|---|---|---|---|
| `smoke.js` | API directe | 2 utilisateurs pendant 1 min, chaque route de lecture | Chaque PR (job E2E), `make load-smoke` |
| `charge.js` | API directe | 50 utilisateurs simultanés : montée 2 min, plateau 5 min, descente 1 min | `make load-test`, avant une livraison |
| `stress.js` | API directe | Débit croissant de 5 à 200 req/s, arrêt au-delà de 10 % d'erreurs | `make load-stress`, pour situer la rupture |
| `limitation-debit.js` | Proxy nginx | 80 req/s pendant 15 s depuis une seule adresse | Chaque PR (job E2E), `make load-limits` |
### Hypothèses de charge
Le cahier des charges ne chiffre ni volume d'utilisateurs ni temps de réponse. Les hypothèses
retenues :
- **50 utilisateurs simultanés.** C'est un gestionnaire d'énergie par site suivi, plus
l'exploitation, avec de la marge.
- **40 gardent le tableau de bord ouvert.** Le frontend interroge `/stats/summary` toutes les
10 s et `/alerts` toutes les 60 s, et charge `/predictions` une fois (`dashboard.ts`,
`alert-feed.ts`).
- **10 explorent les sites.** Ils ouvrent la liste, puis un détail avec sa mesure courante, ses
relevés sur 24 h et ses recommandations, avec 3 à 8 s de lecture entre deux pages.
Soit environ 12 req/s en régime établi, avec des pointes au démarrage des sessions.
### Seuils
Les seuils sont posés par l'ADR 0015 et partagés par `lib/config.js`.
| Seuil | Valeur | Raison |
|---|---|---|
| `http_req_failed` | < 1 % | Aucune erreur attendue en charge nominale |
| `checks` | > 99 % | Chaque réponse est un 200 |
| Lectures, p95 | < 500 ms | Rafraîchissement du tableau de bord imperceptible |
| Lectures, p99 | < 1 s | Borne des cas les plus lents |
| `/stats/summary`, p95 | < 500 ms | Route la plus appelée (`DISTINCT ON` sur tout l'historique) |
| `/readings`, p95 | < 800 ms | Fenêtre de 24 h, la plus volumineuse |
Un seuil franchi fait échouer le tir (code de sortie 99). Le test de stress n'a qu'un seuil
bloquant, 10 % d'erreurs : il cherche la rupture, pas un niveau de service.
## Pourquoi k6 ne passe pas par le proxy
nginx limite chaque adresse IP à 20 req/s, avec une rafale de 40 (ADR 0007). Un tir depuis une
seule machine mesurerait cette limite, pas l'API.
k6 tourne donc en service Compose (profil `load`) sur le réseau du projet, et vise
`backend:8000`. Seul `limitation-debit.js` passe par `https://proxy`, précisément pour vérifier
que la limite tient : des 429 au-delà du débit autorisé, jamais d'erreur serveur.
## Lancer un tir
Il faut une stack démarrée, des données et un compte `lecteur` **déjà activé** (mot de passe
définitif).
```bash
# Poste, stack de `make dev` (API sur l'hôte) : jeu de démonstration et comptes de test
make e2e-prepare
make load-smoke K6_BASE_URL=http://host.docker.internal:8000 \
K6_EMAIL="$(jq -r .lecteur.email tests/e2e/.comptes.json)" \
K6_PASSWORD="$(jq -r .lecteur.password tests/e2e/.comptes.json)"
# Poste, stack conteneurisée (make stack-up) : l'API est joignable en backend:8000
make load-test K6_EMAIL=... K6_PASSWORD=...
# Recette, sur la VM, dans /srv/enervision/rec : compte lecteur créé depuis l'interface
make load-test K6_EMAIL=charge@enervision.fr K6_PASSWORD=...
```
**Recette et prod partagent la VM** (ADR 0009). `make load-test` y reste raisonnable.
`make load-stress` sature l'hôte et ralentit la prod : ne le lancer qu'en accord avec l'équipe,
hors démonstration.
`scripts/comptes-test.sh` ne sert qu'aux bases jetables. En recette, un administrateur crée le
compte `lecteur` du tir depuis l'interface, puis on s'y connecte une fois pour changer le mot de
passe temporaire.
## Lire les résultats
Chaque tir écrit dans `tests/load/results/` (ignoré par git) :
- `<scénario>-<horodatage>.html` : rapport du tableau de bord web de k6 (courbes de débit, de
latence et de VUs), à joindre au dossier de preuves ;
- `<scénario>-<horodatage>.md` : synthèse et état de chaque seuil, aussi affichée en console et,
en CI, dans le résumé du job ;
- `<scénario>-<horodatage>.json` : métriques brutes.
Pendant un tir, le tableau de bord Grafana « API » (profil `monitoring`) montre le débit et les
latences vus par l'API elle-même.