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
48 lines
1.6 KiB
JavaScript
48 lines
1.6 KiB
JavaScript
export function rapport(nom, data) {
|
|
const horodatage = new Date().toISOString().replace(/[:.]/g, '-');
|
|
const markdown = enMarkdown(nom, data);
|
|
const sorties = {
|
|
stdout: `${markdown}\n`,
|
|
[`/results/${nom}-${horodatage}.json`]: JSON.stringify(data, null, 2),
|
|
[`/results/${nom}-${horodatage}.md`]: markdown,
|
|
};
|
|
if (__ENV.K6_RESUME) {
|
|
sorties[__ENV.K6_RESUME] = markdown;
|
|
}
|
|
return sorties;
|
|
}
|
|
|
|
function valeur(metrique, statistique, unite) {
|
|
const brut = metrique?.values?.[statistique];
|
|
if (brut === undefined) {
|
|
return '-';
|
|
}
|
|
return unite === '%' ? `${(brut * 100).toFixed(2)} %` : `${brut.toFixed(1)} ${unite}`;
|
|
}
|
|
|
|
function enMarkdown(nom, data) {
|
|
const m = data.metrics;
|
|
const lignes = [
|
|
`### Tir k6 « ${nom} »`,
|
|
'',
|
|
'| Mesure | Valeur |',
|
|
'|---|---|',
|
|
`| Requêtes | ${m.http_reqs?.values?.count ?? 0} (${valeur(m.http_reqs, 'rate', 'req/s')}) |`,
|
|
`| Échecs HTTP | ${valeur(m.http_req_failed, 'rate', '%')} |`,
|
|
`| Vérifications réussies | ${valeur(m.checks, 'rate', '%')} |`,
|
|
`| Durée médiane | ${valeur(m.http_req_duration, 'med', 'ms')} |`,
|
|
`| Durée p95 | ${valeur(m.http_req_duration, 'p(95)', 'ms')} |`,
|
|
`| Durée p99 | ${valeur(m.http_req_duration, 'p(99)', 'ms')} |`,
|
|
`| VUs au plus haut | ${m.vus_max?.values?.max ?? '-'} |`,
|
|
'',
|
|
'| Seuil | Résultat |',
|
|
'|---|---|',
|
|
];
|
|
for (const [metrique, detail] of Object.entries(m)) {
|
|
for (const [expression, resultat] of Object.entries(detail.thresholds || {})) {
|
|
lignes.push(`| \`${metrique}\` ${expression} | ${resultat.ok ? 'tenu' : '**franchi**'} |`);
|
|
}
|
|
}
|
|
return lignes.join('\n');
|
|
}
|