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) {
13 println!("\n{}", "=".repeat(60).bright_green());
14 println!("{}", "Load Test Complete! ✓".bright_green().bold());
15 println!("{}\n", "=".repeat(60).bright_green());
16
17 println!("{}", "Summary:".bold());
18 println!(" Total Requests: {}", results.total_requests.to_string().cyan());
19 println!(
20 " Successful: {} ({}%)",
21 (results.total_requests - results.failed_requests).to_string().green(),
22 format!("{:.2}", results.success_rate()).green()
23 );
24 println!(
25 " Failed: {} ({}%)",
26 results.failed_requests.to_string().red(),
27 format!("{:.2}", results.error_rate()).red()
28 );
29
30 println!("\n{}", "Response Times:".bold());
31 println!(" Min: {}ms", format!("{:.2}", results.min_duration_ms).cyan());
32 println!(" Avg: {}ms", format!("{:.2}", results.avg_duration_ms).cyan());
33 println!(" Med: {}ms", format!("{:.2}", results.med_duration_ms).cyan());
34 println!(" p90: {}ms", format!("{:.2}", results.p90_duration_ms).cyan());
35 println!(" p95: {}ms", format!("{:.2}", results.p95_duration_ms).cyan());
36 println!(" p99: {}ms", format!("{:.2}", results.p99_duration_ms).cyan());
37 println!(" Max: {}ms", format!("{:.2}", results.max_duration_ms).cyan());
38
39 println!("\n{}", "Throughput:".bold());
40 if results.rps > 0.0 {
41 println!(" RPS: {} req/s", format!("{:.1}", results.rps).cyan());
42 } else {
43 println!(
44 " RPS: {} req/s",
45 format!("{:.1}", results.total_requests as f64 / duration_secs as f64).cyan()
46 );
47 }
48 if results.vus_max > 0 {
49 println!(" Max VUs: {}", results.vus_max.to_string().cyan());
50 }
51
52 println!("\n{}", "=".repeat(60).bright_green());
53 }
54
55 pub fn print_header(
57 spec_file: &str,
58 target: &str,
59 num_operations: usize,
60 scenario: &str,
61 duration_secs: u64,
62 ) {
63 println!("\n{}\n", "MockForge Bench - Load Testing Mode".bright_green().bold());
64 println!("{}", "─".repeat(60).bright_black());
65
66 println!("{}: {}", "Specification".bold(), spec_file.cyan());
67 println!("{}: {}", "Target".bold(), target.cyan());
68 println!("{}: {} endpoints", "Operations".bold(), num_operations.to_string().cyan());
69 println!("{}: {}", "Scenario".bold(), scenario.cyan());
70 println!("{}: {}s", "Duration".bold(), duration_secs.to_string().cyan());
71
72 println!("{}\n", "─".repeat(60).bright_black());
73 }
74
75 pub fn print_progress(message: &str) {
77 println!("{} {}", "→".bright_green().bold(), message);
78 }
79
80 pub fn print_error(message: &str) {
82 eprintln!("{} {}", "✗".bright_red().bold(), message.red());
83 }
84
85 pub fn print_success(message: &str) {
87 println!("{} {}", "✓".bright_green().bold(), message.green());
88 }
89
90 pub fn print_warning(message: &str) {
92 println!("{} {}", "⚠".bright_yellow().bold(), message.yellow());
93 }
94
95 pub fn print_multi_target_summary(results: &AggregatedResults) {
97 println!("\n{}", "=".repeat(60).bright_green());
98 println!("{}", "Multi-Target Load Test Complete! ✓".bright_green().bold());
99 println!("{}\n", "=".repeat(60).bright_green());
100
101 println!("{}", "Overall Summary:".bold());
102 println!(" Total Targets: {}", results.total_targets.to_string().cyan());
103 println!(
104 " Successful: {} ({}%)",
105 results.successful_targets.to_string().green(),
106 format!(
107 "{:.1}",
108 (results.successful_targets as f64 / results.total_targets as f64) * 100.0
109 )
110 .green()
111 );
112 println!(
113 " Failed: {} ({}%)",
114 results.failed_targets.to_string().red(),
115 format!(
116 "{:.1}",
117 (results.failed_targets as f64 / results.total_targets as f64) * 100.0
118 )
119 .red()
120 );
121
122 println!("\n{}", "Aggregated Metrics:".bold());
123 println!(
124 " Total Requests: {}",
125 results.aggregated_metrics.total_requests.to_string().cyan()
126 );
127 println!(
128 " Failed Requests: {} ({}%)",
129 results.aggregated_metrics.total_failed_requests.to_string().red(),
130 format!("{:.2}", results.aggregated_metrics.error_rate).red()
131 );
132 println!(
133 " Total RPS: {} req/s",
134 format!("{:.1}", results.aggregated_metrics.total_rps).cyan()
135 );
136 println!(
137 " Avg RPS/target: {} req/s",
138 format!("{:.1}", results.aggregated_metrics.avg_rps).cyan()
139 );
140 println!(
141 " Total VUs: {}",
142 results.aggregated_metrics.total_vus_max.to_string().cyan()
143 );
144 println!(
145 " Avg Response Time: {}ms",
146 format!("{:.2}", results.aggregated_metrics.avg_duration_ms).cyan()
147 );
148 println!(
149 " p95 Response Time: {}ms",
150 format!("{:.2}", results.aggregated_metrics.p95_duration_ms).cyan()
151 );
152 println!(
153 " p99 Response Time: {}ms",
154 format!("{:.2}", results.aggregated_metrics.p99_duration_ms).cyan()
155 );
156
157 let print_target = |result: &crate::parallel_executor::TargetResult| {
159 let status = if result.success {
160 "✓".bright_green()
161 } else {
162 "✗".bright_red()
163 };
164 println!(" {} {}", status, result.target_url.cyan());
165 if result.success {
166 println!(
167 " Requests: {} RPS: {} VUs: {}",
168 result.results.total_requests.to_string().white(),
169 format!("{:.1}", result.results.rps).white(),
170 result.results.vus_max.to_string().white(),
171 );
172 println!(
173 " Latency: min={}ms avg={}ms med={}ms p90={}ms p95={}ms p99={}ms max={}ms",
174 format!("{:.1}", result.results.min_duration_ms),
175 format!("{:.1}", result.results.avg_duration_ms),
176 format!("{:.1}", result.results.med_duration_ms),
177 format!("{:.1}", result.results.p90_duration_ms),
178 format!("{:.1}", result.results.p95_duration_ms),
179 format!("{:.1}", result.results.p99_duration_ms),
180 format!("{:.1}", result.results.max_duration_ms),
181 );
182 }
183 if let Some(error) = &result.error {
184 println!(" Error: {}", error.red());
185 }
186 };
187
188 if results.total_targets <= 20 {
189 println!("\n{}", "Per-Target Results:".bold());
190 for result in &results.target_results {
191 print_target(result);
192 }
193 } else {
194 println!("\n{}", "Top 10 Targets (by requests):".bold());
196 let mut sorted_results = results.target_results.clone();
197 sorted_results.sort_by_key(|r| r.results.total_requests);
198 sorted_results.reverse();
199
200 for result in sorted_results.iter().take(10) {
201 print_target(result);
202 }
203
204 println!("\n{}", "Bottom 10 Targets:".bold());
205 for result in sorted_results.iter().rev().take(10) {
206 print_target(result);
207 }
208 }
209
210 println!("\n{}", "=".repeat(60).bright_green());
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn test_terminal_reporter_creation() {
220 let _reporter = TerminalReporter;
221 }
222}