#!/usr/bin/env python3 """ Load Test Report Generator This script generates HTML reports from Locust JSON output files. It provides a comprehensive summary of load test results with metrics and endpoint breakdowns. Usage: python generate_load_report.py --input results.json --output report.html Reference: Phase 209 Plan 05 Task 3 """ import argparse import json import sys from datetime import datetime from pathlib import Path def load_results(filepath: str) -> dict: """ Load Locust JSON results from file. Args: filepath: Path to Locust JSON output file Returns: Parsed JSON data as dictionary Raises: FileNotFoundError: If file doesn't exist json.JSONDecodeError: If file is not valid JSON """ path = Path(filepath) if not path.exists(): raise FileNotFoundError(f"Results file not found: {filepath}") with open(path, 'r') as f: try: data = json.load(f) except json.JSONDecodeError as e: raise json.JSONDecodeError(f"Invalid JSON in {filepath}: {e}", e.doc, e.pos) return data def format_response_time(ms: float) -> str: """Format response time in milliseconds.""" return f"{ms:.0f}ms" def format_percentage(value: float) -> str: """Format percentage with 2 decimal places.""" return f"{value:.2f}%" def generate_color_class(failure_rate: float) -> str: """Generate CSS class based on failure rate.""" if failure_rate == 0: return "success" elif failure_rate < 1: return "warning" else: return "error" def generate_html_report(data: dict) -> str: """ Generate HTML report from Locust JSON data. Args: data: Parsed Locust JSON output Returns: HTML string """ # Extract test metadata start_time = datetime.fromtimestamp(data.get('start_time', 0)).strftime('%Y-%m-%d %H:%M:%S') duration_seconds = data.get('state', {}).get('run_time', 0) duration = f"{duration_seconds // 60}m {duration_seconds % 60}s" # Calculate summary metrics stats = data.get('stats', []) total_requests = sum(s.get('num_requests', 0) for s in stats) total_failures = sum(s.get('num_failures', 0) for s in stats) failure_rate = (total_failures / total_requests * 100) if total_requests > 0 else 0 # Calculate response times avg_response_time = sum( s.get('avg_response_time', 0) * s.get('num_requests', 0) for s in stats ) / total_requests if total_requests > 0 else 0 # Calculate RPS (requests per second) rps = total_requests / (duration_seconds / 1000) if duration_seconds > 0 else 0 # Generate CSS css = """ """ # Generate HTML html = f"""
| Endpoint | Requests | Failures | Avg | P50 | P95 | P99 |
|---|---|---|---|---|---|---|
| {name} | {num_requests:,} | {num_failures} ({format_percentage(failure_rate)}) | {format_response_time(avg_response)} | {format_response_time(p50)} | {format_response_time(p95)} | {format_response_time(p99)} |