Skip to main content

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}