#!/usr/bin/env bash # summarize-artillery.sh — Extract counters, rates and percentile stats from an # Artillery JSON report. Mirrors scripts/loadtest/summarize-artillery.ps1. # Usage: ./scripts/loadtest/summarize-artillery.sh set -euo pipefail if [[ $# -lt 1 ]]; then echo "Usage: $0 " >&2 exit 2 fi report_file="$1" [[ -f "$report_file" ]] || { echo "Report file not found: $report_file" >&2; exit 1; } REPORT_FILE="$report_file" node <<'EOF' const fs = require('fs'); const report = JSON.parse(fs.readFileSync(process.env.REPORT_FILE, 'utf8')); const aggregate = report.aggregate; if (!aggregate) throw new Error('Artillery report is missing aggregate data.'); const get = (obj, name) => (obj && obj[name] !== undefined) ? obj[name] : undefined; const counters = aggregate.counters || {}; const rates = aggregate.rates || {}; const summaries = aggregate.summaries || {}; const created = Number(get(counters, 'vusers.created') || 0); const failed = Number(get(counters, 'vusers.failed') || 0); const completed = Number(get(counters, 'vusers.completed') || 0); const errorRate = created > 0 ? +((failed / created) * 100).toFixed(2) : 0; console.log('=== Artillery WebSocket Summary ==='); console.log(`source: ${process.env.REPORT_FILE}`); if (report.timestamp) console.log(`timestamp: ${report.timestamp}`); console.log(`vusers.created: ${created}`); console.log(`vusers.completed: ${completed}`); console.log(`vusers.failed: ${failed}`); console.log(`vusers.failure_rate_percent: ${errorRate}`); for (const metric of ['ws.messages_sent', 'ws.messages_received', 'ws.ping', 'plugins.metrics-by-endpoint.response_time']) { const v = get(counters, metric); if (v !== undefined) console.log(`${metric}: ${v}`); } for (const [k, v] of Object.entries(rates || {})) console.log(`rate.${k}: ${v}`); const fmt = (prefix, stats) => { if (!stats) return; const parts = []; for (const name of ['count', 'min', 'max', 'mean', 'median', 'p95', 'p99']) { const v = get(stats, name); if (v !== undefined) parts.push(`${name}=${v}`); } if (parts.length) console.log(`${prefix}: ${parts.join(', ')}`); }; for (const [k, v] of Object.entries(summaries)) fmt(`summary.${k}`, v); EOF