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
This commit is contained in:
Johan LEROY
2026-09-23 09:29:20 +02:00
parent a197af91ff
commit 2ed9e1cee4
11 changed files with 460 additions and 2 deletions
+43
View File
@@ -0,0 +1,43 @@
// Vérifie par le proxy que la limitation de nginx tient : au-delà de 20 requêtes par seconde et
// de la rafale de 40, une même adresse doit recevoir des 429, et jamais une erreur serveur.
import { check } from 'k6';
import http from 'k6/http';
import { Counter } from 'k6/metrics';
import { PROXY_URL, STATISTIQUES } from './lib/config.js';
import { rapport } from './lib/rapport.js';
const limitees = new Counter('reponses_limitees');
http.setResponseCallback(http.expectedStatuses(200, 429));
export const options = {
insecureSkipTLSVerify: true,
scenarios: {
rafale: {
executor: 'constant-arrival-rate',
rate: 80,
timeUnit: '1s',
duration: '15s',
preAllocatedVUs: 20,
maxVUs: 80,
},
},
thresholds: {
reponses_limitees: ['count>0'],
http_req_failed: ['rate<0.01'],
},
summaryTrendStats: STATISTIQUES,
};
export default function () {
const reponse = http.get(`${PROXY_URL}/api/v1/health/live`, { tags: { name: 'health_live' } });
if (reponse.status === 429) {
limitees.add(1);
}
check(reponse, { 'servie ou limitée, jamais en erreur': (r) => [200, 429].includes(r.status) });
}
export function handleSummary(data) {
return rapport('limitation-debit', data);
}