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!(" Avg: {}ms", format!("{:.2}", results.avg_duration_ms).cyan());
32 println!(" p95: {}ms", format!("{:.2}", results.p95_duration_ms).cyan());
33 println!(" p99: {}ms", format!("{:.2}", results.p99_duration_ms).cyan());
34
35 println!(
36 "\n Throughput: {} req/s",
37 format!("{:.1}", results.total_requests as f64 / duration_secs as f64).cyan()
38 );
39
40 println!("\n{}", "=".repeat(60).bright_green());
41 }
42
43 pub fn print_header(
45 spec_file: &str,
46 target: &str,
47 num_operations: usize,
48 scenario: &str,
49 duration_secs: u64,
50 ) {
51 println!("\n{}\n", "MockForge Bench - Load Testing Mode".bright_green().bold());
52 println!("{}", "─".repeat(60).bright_black());
53
54 println!("{}: {}", "Specification".bold(), spec_file.cyan());
55 println!("{}: {}", "Target".bold(), target.cyan());
56 println!("{}: {} endpoints", "Operations".bold(), num_operations.to_string().cyan());
57 println!("{}: {}", "Scenario".bold(), scenario.cyan());
58 println!("{}: {}s", "Duration".bold(), duration_secs.to_string().cyan());
59
60 println!("{}\n", "─".repeat(60).bright_black());
61 }
62
63 pub fn print_progress(message: &str) {
65 println!("{} {}", "→".bright_green().bold(), message);
66 }
67
68 pub fn print_error(message: &str) {
70 eprintln!("{} {}", "✗".bright_red().bold(), message.red());
71 }
72
73 pub fn print_success(message: &str) {
75 println!("{} {}", "✓".bright_green().bold(), message.green());
76 }
77
78 pub fn print_warning(message: &str) {
80 println!("{} {}", "⚠".bright_yellow().bold(), message.yellow());
81 }
82
83 pub fn print_multi_target_summary(results: &AggregatedResults) {
85 println!("\n{}", "=".repeat(60).bright_green());
86 println!("{}", "Multi-Target Load Test Complete! ✓".bright_green().bold());
87 println!("{}\n", "=".repeat(60).bright_green());
88
89 println!("{}", "Overall Summary:".bold());
90 println!(" Total Targets: {}", results.total_targets.to_string().cyan());
91 println!(
92 " Successful: {} ({}%)",
93 results.successful_targets.to_string().green(),
94 format!(
95 "{:.1}",
96 (results.successful_targets as f64 / results.total_targets as f64) * 100.0
97 )
98 .green()
99 );
100 println!(
101 " Failed: {} ({}%)",
102 results.failed_targets.to_string().red(),
103 format!(
104 "{:.1}",
105 (results.failed_targets as f64 / results.total_targets as f64) * 100.0
106 )
107 .red()
108 );
109
110 println!("\n{}", "Aggregated Metrics:".bold());
111 println!(
112 " Total Requests: {}",
113 results.aggregated_metrics.total_requests.to_string().cyan()
114 );
115 println!(
116 " Failed Requests: {} ({}%)",
117 results.aggregated_metrics.total_failed_requests.to_string().red(),
118 format!("{:.2}", results.aggregated_metrics.error_rate).red()
119 );
120 println!(
121 " Avg Response Time: {}ms",
122 format!("{:.2}", results.aggregated_metrics.avg_duration_ms).cyan()
123 );
124 println!(
125 " p95 Response Time: {}ms",
126 format!("{:.2}", results.aggregated_metrics.p95_duration_ms).cyan()
127 );
128 println!(
129 " p99 Response Time: {}ms",
130 format!("{:.2}", results.aggregated_metrics.p99_duration_ms).cyan()
131 );
132
133 if results.total_targets <= 20 {
135 println!("\n{}", "Per-Target Results:".bold());
136 for result in &results.target_results {
137 let status = if result.success {
138 "✓".bright_green()
139 } else {
140 "✗".bright_red()
141 };
142 println!(
143 " {} {} - {} requests, {}ms avg",
144 status,
145 result.target_url.cyan(),
146 result.results.total_requests,
147 format!("{:.2}", result.results.avg_duration_ms)
148 );
149 if let Some(error) = &result.error {
150 println!(" Error: {}", error.red());
151 }
152 }
153 } else {
154 println!("\n{}", "Top 10 Targets (by requests):".bold());
156 let mut sorted_results = results.target_results.clone();
157 sorted_results.sort_by_key(|r| r.results.total_requests);
158 sorted_results.reverse();
159
160 for result in sorted_results.iter().take(10) {
161 let status = if result.success {
162 "✓".bright_green()
163 } else {
164 "✗".bright_red()
165 };
166 println!(
167 " {} {} - {} requests, {}ms avg",
168 status,
169 result.target_url.cyan(),
170 result.results.total_requests,
171 format!("{:.2}", result.results.avg_duration_ms)
172 );
173 }
174
175 println!("\n{}", "Bottom 10 Targets:".bold());
176 for result in sorted_results.iter().rev().take(10) {
177 let status = if result.success {
178 "✓".bright_green()
179 } else {
180 "✗".bright_red()
181 };
182 println!(
183 " {} {} - {} requests, {}ms avg",
184 status,
185 result.target_url.cyan(),
186 result.results.total_requests,
187 format!("{:.2}", result.results.avg_duration_ms)
188 );
189 }
190 }
191
192 println!("\n{}", "=".repeat(60).bright_green());
193 }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::*;
199
200 #[test]
201 fn test_terminal_reporter_creation() {
202 let _reporter = TerminalReporter;
203 }
204}