lmn_core/output/report.rs
1use std::collections::BTreeMap;
2
3use serde::Serialize;
4
5use crate::threshold::ThresholdReport;
6
7// ── RunReport ─────────────────────────────────────────────────────────────────
8
9/// Versioned, serialization-ready report of a completed load test run.
10///
11/// This is the canonical contract between the Rust engine and all downstream
12/// consumers (CLI JSON output, NestJS SaaS platform, CI pipelines, cloud mode).
13/// The top-level `version` field allows consumers to gate on the schema version.
14#[derive(Serialize, Debug)]
15pub struct RunReport {
16 /// Schema version. Currently `2`. Increment on any breaking schema change.
17 pub version: u32,
18 pub run: RunMeta,
19 pub requests: RequestSummary,
20 pub latency: LatencyStats,
21 /// HTTP status code counts keyed by string code (e.g. `"200"`, `"404"`).
22 /// The special key `"error"` covers connection errors with no HTTP response.
23 pub status_codes: BTreeMap<String, u64>,
24 /// Present only when `--response-template` / `--response-alias` was used.
25 /// `null` when no response template was configured.
26 pub response_stats: Option<ResponseStatsReport>,
27 /// Present only when `mode == "curve"`. `null` in fixed mode.
28 pub curve_stages: Option<Vec<StageReport>>,
29 /// Present when thresholds were evaluated after the run. `null` otherwise.
30 pub thresholds: Option<ThresholdReport>,
31}
32
33// ── RunMeta ───────────────────────────────────────────────────────────────────
34
35/// Top-level metadata about the run's execution mode and timings.
36#[derive(Serialize, Debug)]
37pub struct RunMeta {
38 /// Execution mode: `"fixed"` or `"curve"`.
39 pub mode: String,
40 /// Total wall-clock elapsed time for the run in milliseconds.
41 pub elapsed_ms: f64,
42 /// Total curve duration in milliseconds. `null` in fixed mode.
43 pub curve_duration_ms: Option<f64>,
44 /// Time spent pre-generating template bodies in milliseconds. `null` when no
45 /// request template was used.
46 pub template_generation_ms: Option<f64>,
47}
48
49// ── RequestSummary ────────────────────────────────────────────────────────────
50
51/// Aggregated request counts and derived throughput / error rate metrics.
52#[derive(Serialize, Debug)]
53pub struct RequestSummary {
54 pub total: usize,
55 pub ok: usize,
56 pub failed: usize,
57 /// Fraction of failed requests: `failed / total`. `0.0` when `total == 0`.
58 pub error_rate: f64,
59 /// Requests per second: `total / elapsed_seconds`. `0.0` when elapsed is zero.
60 pub throughput_rps: f64,
61}
62
63// ── LatencyStats ──────────────────────────────────────────────────────────────
64
65/// Snapshot of latency percentiles and summary statistics in milliseconds.
66///
67/// All values are `f64` milliseconds. Fields are named with the `_ms` suffix
68/// to make the unit self-documenting for downstream consumers.
69#[derive(Serialize, Debug)]
70pub struct LatencyStats {
71 pub min_ms: f64,
72 pub p10_ms: f64,
73 pub p25_ms: f64,
74 pub p50_ms: f64,
75 pub p75_ms: f64,
76 pub p90_ms: f64,
77 pub p95_ms: f64,
78 pub p99_ms: f64,
79 pub max_ms: f64,
80 pub avg_ms: f64,
81}
82
83// ── ResponseStatsReport ───────────────────────────────────────────────────────
84
85/// Summary of response body field analysis from a response template.
86///
87/// This is derived from `ResponseStats` in the response template domain.
88/// `HashMaps` are promoted to `BTreeMap`s for stable JSON key ordering.
89#[derive(Serialize, Debug)]
90pub struct ResponseStatsReport {
91 /// Number of responses that were parsed and contributed to field statistics.
92 pub responses_parsed: u64,
93 /// Distribution of string-valued field extractions. Outer key is the field path,
94 /// inner key is the extracted value, value is the count.
95 pub string_fields: BTreeMap<String, BTreeMap<String, u64>>,
96 /// Summary statistics for float-valued field extractions.
97 pub float_fields: BTreeMap<String, FloatFieldSummary>,
98 /// Count of responses where a tracked field could not be extracted.
99 pub mismatch_counts: BTreeMap<String, u64>,
100}
101
102/// Summary statistics for a float response field.
103#[derive(Serialize, Debug)]
104pub struct FloatFieldSummary {
105 pub min: f64,
106 pub avg: f64,
107 pub p50: f64,
108 pub p95: f64,
109 pub p99: f64,
110 pub max: f64,
111}
112
113// ── StageReport ───────────────────────────────────────────────────────────────
114
115/// Per-stage metrics for a curve-mode run. Stage indices are 0-based.
116#[derive(Serialize, Debug)]
117pub struct StageReport {
118 /// 0-based stage index in the load curve.
119 pub index: usize,
120 /// Configured stage duration in milliseconds.
121 pub duration_ms: f64,
122 /// Configured target VU count for this stage.
123 pub target_vus: u32,
124 /// Ramp type: `"linear"` or `"step"`.
125 pub ramp: String,
126 pub requests: usize,
127 pub ok: usize,
128 pub failed: usize,
129 /// Fraction of failed requests within this stage.
130 pub error_rate: f64,
131 /// Requests per second within this stage's duration window.
132 pub throughput_rps: f64,
133 pub latency: LatencyStats,
134}