1use crate::executor::K6Results;
4use crate::parallel_executor::AggregatedResults;
5use colored::*;
6
7pub struct TerminalReporter;
9
10impl TerminalReporter {
11 pub fn print_summary(results: &K6Results, duration_secs: u64) {
19 Self::print_summary_with_mode(results, duration_secs, false);
20 }
21
22 pub fn print_summary_with_mode(results: &K6Results, duration_secs: u64, cps_mode: bool) {
24 println!("\n{}", "=".repeat(60).bright_green());
25 println!("{}", "Load Test Complete! ✓".bright_green().bold());
26 println!("{}\n", "=".repeat(60).bright_green());
27
28 println!("{}", "Summary:".bold());
29 println!(" Total Requests: {}", results.total_requests.to_string().cyan());
30 println!(
31 " Successful: {} ({}%)",
32 (results.total_requests - results.failed_requests).to_string().green(),
33 format!("{:.2}", results.success_rate()).green()
34 );
35 println!(
36 " Failed: {} ({}%)",
37 results.failed_requests.to_string().red(),
38 format!("{:.2}", results.error_rate()).red()
39 );
40
41 println!("\n{}", "Response Times:".bold());
42 println!(" Min: {}ms", format!("{:.2}", results.min_duration_ms).cyan());
43 println!(" Avg: {}ms", format!("{:.2}", results.avg_duration_ms).cyan());
44 println!(" Med: {}ms", format!("{:.2}", results.med_duration_ms).cyan());
45 println!(" p90: {}ms", format!("{:.2}", results.p90_duration_ms).cyan());
46 println!(" p95: {}ms", format!("{:.2}", results.p95_duration_ms).cyan());
47 println!(" p99: {}ms", format!("{:.2}", results.p99_duration_ms).cyan());
48 println!(" Max: {}ms", format!("{:.2}", results.max_duration_ms).cyan());
49
50 println!("\n{}", "Throughput:".bold());
51 if results.rps > 0.0 {
52 println!(" RPS: {} req/s", format!("{:.1}", results.rps).cyan());
53 } else {
54 println!(
55 " RPS: {} req/s",
56 format!("{:.1}", results.total_requests as f64 / duration_secs as f64).cyan()
57 );
58 }
59 if results.vus_max > 0 {
60 println!(" Max VUs: {}", results.vus_max.to_string().cyan());
61 }
62
63 if cps_mode {
68 let cps = if results.rps > 0.0 {
69 results.rps
70 } else {
71 results.total_requests as f64 / duration_secs.max(1) as f64
72 };
73 println!(" CPS: {} conn/s (--cps)", format!("{:.1}", cps).cyan());
74 println!(" Total Connections: {}", results.total_requests.to_string().cyan());
75 if results.tcp_connect_samples > 0 {
76 println!(
77 " TCP connect: avg {:.2}ms, max {:.2}ms",
78 results.tcp_connect_avg_ms, results.tcp_connect_max_ms,
79 );
80 }
81 if results.tls_handshake_samples > 0 {
82 println!(
83 " TLS handshake: avg {:.2}ms, max {:.2}ms",
84 results.tls_handshake_avg_ms, results.tls_handshake_max_ms,
85 );
86 }
87 }
88
89 if results.server_injected_latency_samples > 0
94 || results.server_injected_jitter_samples > 0
95 || results.server_reported_faults > 0
96 {
97 println!("\n{}", "Server-Injected (chaos):".bold());
98 if results.server_injected_latency_samples > 0 {
99 println!(
100 " Latency samples: {} (avg {:.2}ms, max {:.2}ms)",
101 results.server_injected_latency_samples.to_string().cyan(),
102 results.server_injected_latency_avg_ms,
103 results.server_injected_latency_max_ms,
104 );
105 }
106 if results.server_injected_jitter_samples > 0 {
107 println!(
108 " Jitter samples: {} (avg {:.2}ms)",
109 results.server_injected_jitter_samples.to_string().cyan(),
110 results.server_injected_jitter_avg_ms,
111 );
112 }
113 if results.server_reported_faults > 0 {
114 println!(
115 " Fault-marked resps: {}",
116 results.server_reported_faults.to_string().cyan(),
117 );
118 }
119 }
120
121 println!("\n{}", "=".repeat(60).bright_green());
122 }
123
124 pub fn print_header(
126 spec_file: &str,
127 target: &str,
128 num_operations: usize,
129 scenario: &str,
130 duration_secs: u64,
131 ) {
132 println!("\n{}\n", "MockForge Bench - Load Testing Mode".bright_green().bold());
133 println!("{}", "─".repeat(60).bright_black());
134
135 println!("{}: {}", "Specification".bold(), spec_file.cyan());
136 println!("{}: {}", "Target".bold(), target.cyan());
137 println!("{}: {} endpoints", "Operations".bold(), num_operations.to_string().cyan());
138 println!("{}: {}", "Scenario".bold(), scenario.cyan());
139 println!("{}: {}s", "Duration".bold(), duration_secs.to_string().cyan());
140
141 println!("{}\n", "─".repeat(60).bright_black());
142 }
143
144 pub fn print_progress(message: &str) {
146 println!("{} {}", "→".bright_green().bold(), message);
147 }
148
149 pub fn print_error(message: &str) {
151 eprintln!("{} {}", "✗".bright_red().bold(), message.red());
152 }
153
154 pub fn print_success(message: &str) {
156 println!("{} {}", "✓".bright_green().bold(), message.green());
157 }
158
159 pub fn print_warning(message: &str) {
161 println!("{} {}", "⚠".bright_yellow().bold(), message.yellow());
162 }
163
164 pub fn print_multi_target_summary(results: &AggregatedResults) {
166 println!("\n{}", "=".repeat(60).bright_green());
167 println!("{}", "Multi-Target Load Test Complete! ✓".bright_green().bold());
168 println!("{}\n", "=".repeat(60).bright_green());
169
170 println!("{}", "Overall Summary:".bold());
171 println!(" Total Targets: {}", results.total_targets.to_string().cyan());
172 println!(
173 " Successful: {} ({}%)",
174 results.successful_targets.to_string().green(),
175 format!(
176 "{:.1}",
177 (results.successful_targets as f64 / results.total_targets as f64) * 100.0
178 )
179 .green()
180 );
181 println!(
182 " Failed: {} ({}%)",
183 results.failed_targets.to_string().red(),
184 format!(
185 "{:.1}",
186 (results.failed_targets as f64 / results.total_targets as f64) * 100.0
187 )
188 .red()
189 );
190
191 println!("\n{}", "Aggregated Metrics:".bold());
192 println!(
193 " Total Requests: {}",
194 results.aggregated_metrics.total_requests.to_string().cyan()
195 );
196 println!(
197 " Failed Requests: {} ({}%)",
198 results.aggregated_metrics.total_failed_requests.to_string().red(),
199 format!("{:.2}", results.aggregated_metrics.error_rate).red()
200 );
201 println!(
202 " Total RPS: {} req/s",
203 format!("{:.1}", results.aggregated_metrics.total_rps).cyan()
204 );
205 println!(
206 " Avg RPS/target: {} req/s",
207 format!("{:.1}", results.aggregated_metrics.avg_rps).cyan()
208 );
209 println!(
210 " Total VUs: {}",
211 results.aggregated_metrics.total_vus_max.to_string().cyan()
212 );
213 println!(
214 " Avg Response Time: {}ms",
215 format!("{:.2}", results.aggregated_metrics.avg_duration_ms).cyan()
216 );
217 println!(
218 " p95 Response Time: {}ms",
219 format!("{:.2}", results.aggregated_metrics.p95_duration_ms).cyan()
220 );
221 println!(
222 " p99 Response Time: {}ms",
223 format!("{:.2}", results.aggregated_metrics.p99_duration_ms).cyan()
224 );
225
226 let print_target = |result: &crate::parallel_executor::TargetResult| {
228 let status = if result.success {
229 "✓".bright_green()
230 } else {
231 "✗".bright_red()
232 };
233 println!(" {} {}", status, result.target_url.cyan());
234 if result.success {
235 println!(
236 " Requests: {} RPS: {} VUs: {}",
237 result.results.total_requests.to_string().white(),
238 format!("{:.1}", result.results.rps).white(),
239 result.results.vus_max.to_string().white(),
240 );
241 println!(
242 " Latency: min={:.1}ms avg={:.1}ms med={:.1}ms p90={:.1}ms p95={:.1}ms p99={:.1}ms max={:.1}ms",
243 result.results.min_duration_ms,
244 result.results.avg_duration_ms,
245 result.results.med_duration_ms,
246 result.results.p90_duration_ms,
247 result.results.p95_duration_ms,
248 result.results.p99_duration_ms,
249 result.results.max_duration_ms,
250 );
251 }
252 if let Some(error) = &result.error {
253 println!(" Error: {}", error.red());
254 }
255 };
256
257 if results.total_targets <= 20 {
258 println!("\n{}", "Per-Target Results:".bold());
259 for result in &results.target_results {
260 print_target(result);
261 }
262 } else {
263 println!("\n{}", "Top 10 Targets (by requests):".bold());
265 let mut sorted_results = results.target_results.clone();
266 sorted_results.sort_by_key(|r| r.results.total_requests);
267 sorted_results.reverse();
268
269 for result in sorted_results.iter().take(10) {
270 print_target(result);
271 }
272
273 println!("\n{}", "Bottom 10 Targets:".bold());
274 for result in sorted_results.iter().rev().take(10) {
275 print_target(result);
276 }
277 }
278
279 println!("\n{}", "=".repeat(60).bright_green());
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 #[test]
288 fn test_terminal_reporter_creation() {
289 let _reporter = TerminalReporter;
290 }
291}