#!/usr/bin/env bash # summarize-k6.sh — Extract per-endpoint p95 and failure rates from a k6 JSON # summary export. Mirrors scripts/loadtest/summarize-k6.ps1. # Usage: ./scripts/loadtest/summarize-k6.sh set -euo pipefail if [[ $# -lt 1 ]]; then echo "Usage: $0 " >&2 exit 2 fi summary_file="$1" [[ -f "$summary_file" ]] || { echo "Summary file not found: $summary_file" >&2; exit 1; } SUMMARY_FILE="$summary_file" node <<'EOF' const fs = require('fs'); const summary = JSON.parse(fs.readFileSync(process.env.SUMMARY_FILE, 'utf8')); const metrics = summary.metrics; if (!metrics) throw new Error('Invalid k6 summary file: missing metrics object.'); const val = (m, key) => (m && m.values && m.values[key] !== undefined) ? Number(m.values[key]) : undefined; const globalDuration = val(metrics.http_req_duration, 'p(95)'); const globalFailRate = val(metrics.http_req_failed, 'rate'); console.log(`k6 Summary: ${process.env.SUMMARY_FILE}`); if (globalDuration !== undefined) console.log(`Global p95 duration (ms): ${globalDuration.toFixed(2)}`); if (globalFailRate !== undefined) console.log(`Global failure rate: ${(globalFailRate * 100).toFixed(2)}%`); console.log(''); const endpoints = ['overview', 'analytics', 'chat_log', 'users']; const rows = endpoints.map((ep) => { const dP95 = val(metrics[`http_req_duration{endpoint:${ep}}`], 'p(95)'); const fRate = val(metrics[`http_req_failed{endpoint:${ep}}`], 'rate'); return { Endpoint: ep, P95Ms: dP95 !== undefined ? dP95.toFixed(2) : 'n/a', FailurePct: fRate !== undefined ? (fRate * 100).toFixed(4) : 'n/a', }; }); const pad = (s, n) => String(s).padEnd(n); console.log(pad('ENDPOINT', 12) + pad('P95_MS', 10) + 'FAILURE_PCT'); for (const r of rows) console.log(pad(r.Endpoint, 12) + pad(r.P95Ms, 10) + r.FailurePct); EOF