1pub mod local;
11
12pub use local::LocalPerformanceTester;
14
15use reqwest::{Client, ClientBuilder};
16use serde::{Deserialize, Serialize};
17use std::time::{Duration, Instant};
18use tracing::{info, instrument, warn};
19
20#[derive(Debug, Clone)]
22pub struct OptimizedHttpConfig {
23 pub pool_max_idle_per_host: usize,
25 pub pool_idle_timeout: Duration,
27 pub connect_timeout: Duration,
29 pub request_timeout: Duration,
31 pub http2_prior_knowledge: bool,
33 pub http2_adaptive_window: bool,
35 pub gzip: bool,
37 pub brotli: bool,
39 pub user_agent: String,
41}
42
43impl Default for OptimizedHttpConfig {
44 fn default() -> Self {
45 Self {
46 pool_max_idle_per_host: 90,
47 pool_idle_timeout: Duration::from_secs(90),
48 connect_timeout: Duration::from_secs(10),
49 request_timeout: Duration::from_secs(30),
50 http2_prior_knowledge: true,
51 http2_adaptive_window: true,
52 gzip: true,
53 brotli: true,
54 user_agent: format!("open-lark/{}", env!("CARGO_PKG_VERSION")),
55 }
56 }
57}
58
59impl OptimizedHttpConfig {
60 pub fn production() -> Self {
62 Self {
63 pool_max_idle_per_host: 100,
64 pool_idle_timeout: Duration::from_secs(120),
65 connect_timeout: Duration::from_secs(5),
66 request_timeout: Duration::from_secs(30),
67 http2_prior_knowledge: true,
68 http2_adaptive_window: true,
69 gzip: true,
70 brotli: true,
71 user_agent: format!("open-lark/{}", env!("CARGO_PKG_VERSION")),
72 }
73 }
74
75 pub fn high_throughput() -> Self {
77 Self {
78 pool_max_idle_per_host: 200,
79 pool_idle_timeout: Duration::from_secs(180),
80 connect_timeout: Duration::from_secs(3),
81 request_timeout: Duration::from_secs(15),
82 http2_prior_knowledge: true,
83 http2_adaptive_window: true,
84 gzip: true,
85 brotli: true,
86 user_agent: format!("open-lark/{}", env!("CARGO_PKG_VERSION")),
87 }
88 }
89
90 pub fn low_latency() -> Self {
92 Self {
93 pool_max_idle_per_host: 50,
94 pool_idle_timeout: Duration::from_secs(60),
95 connect_timeout: Duration::from_secs(2),
96 request_timeout: Duration::from_secs(10),
97 http2_prior_knowledge: true,
98 http2_adaptive_window: true,
99 gzip: false, brotli: false,
101 user_agent: format!("open-lark/{}", env!("CARGO_PKG_VERSION")),
102 }
103 }
104
105 pub fn build_client(&self) -> Result<Client, reqwest::Error> {
107 let mut builder = ClientBuilder::new()
108 .pool_max_idle_per_host(self.pool_max_idle_per_host)
109 .pool_idle_timeout(self.pool_idle_timeout)
110 .connect_timeout(self.connect_timeout)
111 .timeout(self.request_timeout)
112 .user_agent(&self.user_agent);
113
114 if !self.gzip {
120 builder = builder.no_gzip();
121 }
122
123 if !self.brotli {
124 builder = builder.no_brotli();
125 }
126
127 builder.build()
128 }
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct PerformanceMetrics {
134 pub test_name: String,
136 pub total_requests: u64,
138 pub successful_requests: u64,
140 pub failed_requests: u64,
142 pub avg_response_time_ms: f64,
144 pub p95_response_time_ms: f64,
146 pub p99_response_time_ms: f64,
148 pub min_response_time_ms: f64,
150 pub max_response_time_ms: f64,
152 pub requests_per_second: f64,
154 pub duration_seconds: f64,
156 pub throughput_kbps: f64,
158 pub error_rate: f64,
160}
161
162impl PerformanceMetrics {
163 pub fn calculate(
165 test_name: String,
166 response_times_ms: Vec<f64>,
167 total_duration: Duration,
168 total_bytes: u64,
169 ) -> Self {
170 let total_requests = response_times_ms.len() as u64;
171 let failed_requests = response_times_ms.iter().filter(|&&t| t < 0.0).count() as u64;
172 let successful_requests = total_requests - failed_requests;
173
174 let mut sorted_times: Vec<f64> = response_times_ms
175 .iter()
176 .filter(|&&t| t >= 0.0)
177 .copied()
178 .collect();
179 sorted_times.sort_by(|a, b| a.partial_cmp(b).unwrap());
180
181 let avg_response_time_ms = if sorted_times.is_empty() {
182 0.0
183 } else {
184 sorted_times.iter().sum::<f64>() / sorted_times.len() as f64
185 };
186
187 let p95_response_time_ms = if sorted_times.is_empty() {
188 0.0
189 } else {
190 let index = ((sorted_times.len() as f64) * 0.95) as usize;
191 sorted_times
192 .get(index.saturating_sub(1))
193 .copied()
194 .unwrap_or(0.0)
195 };
196
197 let p99_response_time_ms = if sorted_times.is_empty() {
198 0.0
199 } else {
200 let index = ((sorted_times.len() as f64) * 0.99) as usize;
201 sorted_times
202 .get(index.saturating_sub(1))
203 .copied()
204 .unwrap_or(0.0)
205 };
206
207 let min_response_time_ms = sorted_times.first().copied().unwrap_or(0.0);
208 let max_response_time_ms = sorted_times.last().copied().unwrap_or(0.0);
209
210 let duration_seconds = total_duration.as_secs_f64();
211 let requests_per_second = if duration_seconds > 0.0 {
212 total_requests as f64 / duration_seconds
213 } else {
214 0.0
215 };
216
217 let throughput_kbps = if duration_seconds > 0.0 {
218 (total_bytes as f64) / (duration_seconds * 1024.0)
219 } else {
220 0.0
221 };
222
223 let error_rate = if total_requests > 0 {
224 (failed_requests as f64 / total_requests as f64) * 100.0
225 } else {
226 0.0
227 };
228
229 Self {
230 test_name,
231 total_requests,
232 successful_requests,
233 failed_requests,
234 avg_response_time_ms,
235 p95_response_time_ms,
236 p99_response_time_ms,
237 min_response_time_ms,
238 max_response_time_ms,
239 requests_per_second,
240 duration_seconds,
241 throughput_kbps,
242 error_rate,
243 }
244 }
245
246 pub fn print_report(&self) {
248 info!("╔══════════════════════════════════════════════════════════╗");
249 info!("║ 性能测试报告 ║");
250 info!("╠══════════════════════════════════════════════════════════╣");
251 info!("║ 测试名称: {:<45} ║", self.test_name);
252 info!("║ 总请求数: {:<45} ║", self.total_requests);
253 info!("║ 成功请求: {:<45} ║", self.successful_requests);
254 info!("║ 失败请求: {:<45} ║", self.failed_requests);
255 info!("║ 错误率: {:<43.2}% ║", self.error_rate);
256 info!("╠══════════════════════════════════════════════════════════╣");
257 info!("║ 平均响应时间: {:<39.2}ms ║", self.avg_response_time_ms);
258 info!("║ 95th百分位: {:<39.2}ms ║", self.p95_response_time_ms);
259 info!("║ 99th百分位: {:<39.2}ms ║", self.p99_response_time_ms);
260 info!("║ 最小响应时间: {:<39.2}ms ║", self.min_response_time_ms);
261 info!("║ 最大响应时间: {:<39.2}ms ║", self.max_response_time_ms);
262 info!("╠══════════════════════════════════════════════════════════╣");
263 info!("║ 吞吐量: {:<39.2}RPS ║", self.requests_per_second);
264 info!("║ 数据传输率: {:<37.2}KB/s ║", self.throughput_kbps);
265 info!("║ 测试时长: {:<39.2}s ║", self.duration_seconds);
266 info!("╚══════════════════════════════════════════════════════════╝");
267 }
268}
269
270#[derive(Debug, Clone)]
272pub struct BenchmarkConfig {
273 pub concurrent_connections: usize,
275 pub requests_per_connection: usize,
277 pub warmup_requests: usize,
279 pub target_url: String,
281 pub headers: std::collections::HashMap<String, String>,
283}
284
285impl Default for BenchmarkConfig {
286 fn default() -> Self {
287 Self {
288 concurrent_connections: 10,
289 requests_per_connection: 100,
290 warmup_requests: 10,
291 target_url: "https://httpbin.org/json".to_string(),
292 headers: std::collections::HashMap::new(),
293 }
294 }
295}
296
297pub struct HttpBenchmark {
299 client: Client,
300 config: BenchmarkConfig,
301}
302
303impl HttpBenchmark {
304 pub fn new(client: Client, config: BenchmarkConfig) -> Self {
306 Self { client, config }
307 }
308
309 #[instrument(skip(self), fields(
311 concurrent_connections = self.config.concurrent_connections,
312 requests_per_connection = self.config.requests_per_connection,
313 target_url = %self.config.target_url
314 ))]
315 pub async fn run_benchmark(
316 &self,
317 ) -> Result<PerformanceMetrics, Box<dyn std::error::Error + Send + Sync>> {
318 info!("开始HTTP客户端基准测试...");
319 info!("目标URL: {}", self.config.target_url);
320 info!("并发连接数: {}", self.config.concurrent_connections);
321 info!("每连接请求数: {}", self.config.requests_per_connection);
322
323 if self.config.warmup_requests > 0 {
325 info!("预热阶段: {} 个请求", self.config.warmup_requests);
326 for _ in 0..self.config.warmup_requests {
327 let _ = self.make_request().await;
328 }
329 info!("预热完成");
330 }
331
332 let start_time = Instant::now();
333 let mut tasks = Vec::new();
334 let mut total_bytes = 0u64;
335
336 for connection_id in 0..self.config.concurrent_connections {
338 let client = self.client.clone();
339 let config = self.config.clone();
340
341 let task = tokio::spawn(async move {
342 let mut response_times = Vec::new();
343 let mut bytes_transferred = 0u64;
344
345 for request_id in 0..config.requests_per_connection {
346 let request_start = Instant::now();
347
348 match Self::make_single_request(&client, &config.target_url, &config.headers)
349 .await
350 {
351 Ok(bytes) => {
352 let elapsed = request_start.elapsed().as_secs_f64() * 1000.0;
353 response_times.push(elapsed);
354 bytes_transferred += bytes;
355 }
356 Err(e) => {
357 warn!("连接 {} 请求 {} 失败: {}", connection_id, request_id, e);
358 response_times.push(-1.0); }
360 }
361 }
362
363 (response_times, bytes_transferred)
364 });
365
366 tasks.push(task);
367 }
368
369 let mut all_response_times = Vec::new();
371 for task in tasks {
372 match task.await {
373 Ok((response_times, bytes)) => {
374 all_response_times.extend(response_times);
375 total_bytes += bytes;
376 }
377 Err(e) => {
378 warn!("任务执行失败: {}", e);
379 }
380 }
381 }
382
383 let total_duration = start_time.elapsed();
384 let metrics = PerformanceMetrics::calculate(
385 "HTTP客户端基准测试".to_string(),
386 all_response_times,
387 total_duration,
388 total_bytes,
389 );
390
391 info!("基准测试完成");
392 metrics.print_report();
393
394 Ok(metrics)
395 }
396
397 async fn make_request(&self) -> Result<u64, Box<dyn std::error::Error + Send + Sync>> {
399 Self::make_single_request(&self.client, &self.config.target_url, &self.config.headers).await
400 }
401
402 async fn make_single_request(
404 client: &Client,
405 url: &str,
406 headers: &std::collections::HashMap<String, String>,
407 ) -> Result<u64, Box<dyn std::error::Error + Send + Sync>> {
408 let mut request = client.get(url);
409
410 for (key, value) in headers {
411 request = request.header(key, value);
412 }
413
414 let response = request.send().await?;
415 let bytes = response.content_length().unwrap_or(0);
416 let _body = response.bytes().await?;
417
418 Ok(bytes)
419 }
420}
421
422pub struct ClientComparison;
424
425impl ClientComparison {
426 #[instrument]
428 pub async fn compare_configurations(
429 configs: Vec<(&str, OptimizedHttpConfig)>,
430 benchmark_config: BenchmarkConfig,
431 ) -> Result<Vec<(String, PerformanceMetrics)>, Box<dyn std::error::Error + Send + Sync>> {
432 let mut results = Vec::new();
433
434 for (config_name, http_config) in configs {
435 info!("测试配置: {}", config_name);
436
437 let client = http_config.build_client()?;
438 let benchmark = HttpBenchmark::new(client, benchmark_config.clone());
439
440 match benchmark.run_benchmark().await {
441 Ok(metrics) => {
442 let mut named_metrics = metrics;
443 named_metrics.test_name = config_name.to_string();
444 results.push((config_name.to_string(), named_metrics));
445 }
446 Err(e) => {
447 warn!("配置 {} 测试失败: {}", config_name, e);
448 }
449 }
450
451 tokio::time::sleep(Duration::from_secs(2)).await;
453 }
454
455 Self::print_comparison_report(&results);
456 Ok(results)
457 }
458
459 fn print_comparison_report(results: &[(String, PerformanceMetrics)]) {
461 if results.is_empty() {
462 warn!("没有可比较的结果");
463 return;
464 }
465
466 info!("╔══════════════════════════════════════════════════════════════════════════╗");
467 info!("║ 配置性能比较报告 ║");
468 info!("╠══════════════════════════════════════════════════════════════════════════╣");
469 info!("║ 配置名称 │ RPS │ 平均延迟(ms) │ P95(ms) │ P99(ms) │ 错误率(%) ║");
470 info!("╠═════════════════╪═════════╪══════════════╪═════════╪═════════╪═══════════╣");
471
472 for (name, metrics) in results {
473 info!(
474 "║ {:<15} │ {:>7.1} │ {:>12.2} │ {:>7.2} │ {:>7.2} │ {:>9.2} ║",
475 name.chars().take(15).collect::<String>(),
476 metrics.requests_per_second,
477 metrics.avg_response_time_ms,
478 metrics.p95_response_time_ms,
479 metrics.p99_response_time_ms,
480 metrics.error_rate
481 );
482 }
483
484 info!("╚═════════════════╧═════════╧══════════════╧═════════╧═════════╧═══════════╝");
485
486 if let Some((best_name, best_metrics)) = results.iter().max_by(|a, b| {
488 a.1.requests_per_second
489 .partial_cmp(&b.1.requests_per_second)
490 .unwrap()
491 }) {
492 info!(
493 "🏆 最佳吞吐量配置: {} ({:.1} RPS)",
494 best_name, best_metrics.requests_per_second
495 );
496 }
497
498 if let Some((best_name, best_metrics)) = results.iter().min_by(|a, b| {
499 a.1.avg_response_time_ms
500 .partial_cmp(&b.1.avg_response_time_ms)
501 .unwrap()
502 }) {
503 info!(
504 "🚀 最低延迟配置: {} ({:.2}ms 平均延迟)",
505 best_name, best_metrics.avg_response_time_ms
506 );
507 }
508 }
509}
510
511#[cfg(test)]
512mod tests {
513 use super::*;
514
515 #[test]
516 fn test_optimized_http_config_default() {
517 let config = OptimizedHttpConfig::default();
518 assert_eq!(config.pool_max_idle_per_host, 90);
519 assert_eq!(config.connect_timeout, Duration::from_secs(10));
520 assert!(config.http2_prior_knowledge);
521 assert!(config.gzip);
522 }
523
524 #[test]
525 fn test_optimized_http_config_production() {
526 let config = OptimizedHttpConfig::production();
527 assert_eq!(config.pool_max_idle_per_host, 100);
528 assert_eq!(config.connect_timeout, Duration::from_secs(5));
529 assert_eq!(config.request_timeout, Duration::from_secs(30));
530 }
531
532 #[test]
533 fn test_optimized_http_config_high_throughput() {
534 let config = OptimizedHttpConfig::high_throughput();
535 assert_eq!(config.pool_max_idle_per_host, 200);
536 assert_eq!(config.connect_timeout, Duration::from_secs(3));
537 assert_eq!(config.request_timeout, Duration::from_secs(15));
538 }
539
540 #[test]
541 fn test_optimized_http_config_low_latency() {
542 let config = OptimizedHttpConfig::low_latency();
543 assert_eq!(config.pool_max_idle_per_host, 50);
544 assert_eq!(config.connect_timeout, Duration::from_secs(2));
545 assert!(!config.gzip); assert!(!config.brotli);
547 }
548
549 #[test]
550 fn test_performance_metrics_calculation() {
551 let response_times = vec![100.0, 200.0, 150.0, 300.0, 250.0];
552 let duration = Duration::from_secs(5);
553 let total_bytes = 1024 * 5; let metrics = PerformanceMetrics::calculate(
556 "test".to_string(),
557 response_times,
558 duration,
559 total_bytes,
560 );
561
562 assert_eq!(metrics.total_requests, 5);
563 assert_eq!(metrics.successful_requests, 5);
564 assert_eq!(metrics.failed_requests, 0);
565 assert_eq!(metrics.avg_response_time_ms, 200.0);
566 assert_eq!(metrics.min_response_time_ms, 100.0);
567 assert_eq!(metrics.max_response_time_ms, 300.0);
568 assert_eq!(metrics.requests_per_second, 1.0);
569 assert_eq!(metrics.throughput_kbps, 1.0); assert_eq!(metrics.error_rate, 0.0);
571 }
572
573 #[test]
574 fn test_performance_metrics_with_failures() {
575 let response_times = vec![100.0, -1.0, 150.0, -1.0, 250.0]; let duration = Duration::from_secs(5);
577 let total_bytes = 1024 * 3; let metrics = PerformanceMetrics::calculate(
580 "test_with_failures".to_string(),
581 response_times,
582 duration,
583 total_bytes,
584 );
585
586 assert_eq!(metrics.total_requests, 5);
587 assert_eq!(metrics.successful_requests, 3);
588 assert_eq!(metrics.failed_requests, 2);
589 assert_eq!(metrics.error_rate, 40.0); assert_eq!(metrics.avg_response_time_ms, (100.0 + 150.0 + 250.0) / 3.0);
591 }
592
593 #[test]
594 fn test_benchmark_config_default() {
595 let config = BenchmarkConfig::default();
596 assert_eq!(config.concurrent_connections, 10);
597 assert_eq!(config.requests_per_connection, 100);
598 assert_eq!(config.warmup_requests, 10);
599 assert_eq!(config.target_url, "https://httpbin.org/json");
600 }
601
602 #[tokio::test]
603 async fn test_http_config_build_client() {
604 let config = OptimizedHttpConfig::default();
605 let client = config.build_client();
606 assert!(client.is_ok(), "应该能够成功构建HTTP客户端");
607 }
608
609 #[test]
610 fn test_performance_metrics_empty_response_times() {
611 let response_times: Vec<f64> = vec![];
612 let duration = Duration::from_secs(1);
613 let total_bytes = 0;
614
615 let metrics = PerformanceMetrics::calculate(
616 "empty_test".to_string(),
617 response_times,
618 duration,
619 total_bytes,
620 );
621
622 assert_eq!(metrics.total_requests, 0);
623 assert_eq!(metrics.avg_response_time_ms, 0.0);
624 assert_eq!(metrics.requests_per_second, 0.0);
625 }
626
627 #[test]
628 fn test_performance_metrics_percentiles() {
629 let response_times = (1..=100).map(|i| i as f64).collect::<Vec<_>>();
630 let duration = Duration::from_secs(10);
631 let total_bytes = 10240;
632
633 let metrics = PerformanceMetrics::calculate(
634 "percentile_test".to_string(),
635 response_times,
636 duration,
637 total_bytes,
638 );
639
640 assert_eq!(metrics.total_requests, 100);
641 assert_eq!(metrics.min_response_time_ms, 1.0);
642 assert_eq!(metrics.max_response_time_ms, 100.0);
643 assert!((metrics.p95_response_time_ms - 95.0).abs() < 2.0);
645 assert!((metrics.p99_response_time_ms - 99.0).abs() < 2.0);
646 }
647
648 #[test]
651 fn test_optimized_http_config_clone() {
652 let config = OptimizedHttpConfig::production();
653 let cloned_config = config.clone();
654
655 assert_eq!(
656 config.pool_max_idle_per_host,
657 cloned_config.pool_max_idle_per_host
658 );
659 assert_eq!(config.connect_timeout, cloned_config.connect_timeout);
660 assert_eq!(config.user_agent, cloned_config.user_agent);
661 }
662
663 #[test]
664 fn test_optimized_http_config_debug() {
665 let config = OptimizedHttpConfig::default();
666 let debug_str = format!("{:?}", config);
667
668 assert!(debug_str.contains("OptimizedHttpConfig"));
669 assert!(debug_str.contains("pool_max_idle_per_host"));
670 assert!(debug_str.contains("user_agent"));
671 }
672
673 #[test]
674 fn test_optimized_http_config_custom_user_agent() {
675 let config = OptimizedHttpConfig {
676 user_agent: "custom-agent/1.0".to_string(),
677 ..Default::default()
678 };
679
680 assert_eq!(config.user_agent, "custom-agent/1.0");
681 }
682
683 #[test]
684 fn test_optimized_http_config_compression_settings() {
685 let low_latency = OptimizedHttpConfig::low_latency();
686 assert!(!low_latency.gzip);
687 assert!(!low_latency.brotli);
688
689 let production = OptimizedHttpConfig::production();
690 assert!(production.gzip);
691 assert!(production.brotli);
692 }
693
694 #[tokio::test]
695 async fn test_http_config_build_client_with_no_compression() {
696 let config = OptimizedHttpConfig {
697 gzip: false,
698 brotli: false,
699 ..Default::default()
700 };
701
702 let client = config.build_client();
703 assert!(client.is_ok());
704 }
705
706 #[test]
707 fn test_performance_metrics_zero_duration() {
708 let response_times = vec![100.0, 200.0, 150.0];
709 let duration = Duration::from_secs(0);
710 let total_bytes = 1024;
711
712 let metrics = PerformanceMetrics::calculate(
713 "zero_duration_test".to_string(),
714 response_times,
715 duration,
716 total_bytes,
717 );
718
719 assert_eq!(metrics.duration_seconds, 0.0);
720 assert_eq!(metrics.requests_per_second, 0.0);
721 assert_eq!(metrics.throughput_kbps, 0.0);
722 }
723
724 #[test]
725 fn test_performance_metrics_single_request() {
726 let response_times = vec![123.45];
727 let duration = Duration::from_secs(1);
728 let total_bytes = 512;
729
730 let metrics = PerformanceMetrics::calculate(
731 "single_request_test".to_string(),
732 response_times,
733 duration,
734 total_bytes,
735 );
736
737 assert_eq!(metrics.total_requests, 1);
738 assert_eq!(metrics.successful_requests, 1);
739 assert_eq!(metrics.failed_requests, 0);
740 assert_eq!(metrics.avg_response_time_ms, 123.45);
741 assert_eq!(metrics.min_response_time_ms, 123.45);
742 assert_eq!(metrics.max_response_time_ms, 123.45);
743 assert_eq!(metrics.p95_response_time_ms, 123.45);
744 assert_eq!(metrics.p99_response_time_ms, 123.45);
745 assert_eq!(metrics.requests_per_second, 1.0);
746 assert_eq!(metrics.error_rate, 0.0);
747 }
748
749 #[test]
750 fn test_performance_metrics_all_failures() {
751 let response_times = vec![-1.0, -1.0, -1.0];
752 let duration = Duration::from_secs(3);
753 let total_bytes = 0;
754
755 let metrics = PerformanceMetrics::calculate(
756 "all_failures_test".to_string(),
757 response_times,
758 duration,
759 total_bytes,
760 );
761
762 assert_eq!(metrics.total_requests, 3);
763 assert_eq!(metrics.successful_requests, 0);
764 assert_eq!(metrics.failed_requests, 3);
765 assert_eq!(metrics.error_rate, 100.0);
766 assert_eq!(metrics.avg_response_time_ms, 0.0);
767 assert_eq!(metrics.min_response_time_ms, 0.0);
768 assert_eq!(metrics.max_response_time_ms, 0.0);
769 }
770
771 #[test]
772 fn test_performance_metrics_large_dataset() {
773 let response_times: Vec<f64> = (1..=1000).map(|i| i as f64).collect();
774 let duration = Duration::from_secs(100);
775 let total_bytes = 1024 * 1000;
776
777 let metrics = PerformanceMetrics::calculate(
778 "large_dataset_test".to_string(),
779 response_times,
780 duration,
781 total_bytes,
782 );
783
784 assert_eq!(metrics.total_requests, 1000);
785 assert_eq!(metrics.successful_requests, 1000);
786 assert_eq!(metrics.avg_response_time_ms, 500.5); assert_eq!(metrics.min_response_time_ms, 1.0);
788 assert_eq!(metrics.max_response_time_ms, 1000.0);
789 assert!((metrics.p95_response_time_ms - 950.0).abs() < 5.0);
790 assert!((metrics.p99_response_time_ms - 990.0).abs() < 5.0);
791 assert_eq!(metrics.requests_per_second, 10.0);
792 assert_eq!(metrics.throughput_kbps, 10.0); }
794
795 #[test]
796 fn test_performance_metrics_mixed_results() {
797 let response_times = vec![50.0, -1.0, 75.0, 100.0, -1.0, 125.0, 150.0];
798 let duration = Duration::from_secs(7);
799 let total_bytes = 2048;
800
801 let metrics = PerformanceMetrics::calculate(
802 "mixed_results_test".to_string(),
803 response_times,
804 duration,
805 total_bytes,
806 );
807
808 assert_eq!(metrics.total_requests, 7);
809 assert_eq!(metrics.successful_requests, 5);
810 assert_eq!(metrics.failed_requests, 2);
811 assert_eq!(metrics.error_rate, (2.0 / 7.0) * 100.0);
812 assert_eq!(
813 metrics.avg_response_time_ms,
814 (50.0 + 75.0 + 100.0 + 125.0 + 150.0) / 5.0
815 );
816 assert_eq!(metrics.min_response_time_ms, 50.0);
817 assert_eq!(metrics.max_response_time_ms, 150.0);
818 }
819
820 #[test]
821 fn test_benchmark_config_clone() {
822 let config = BenchmarkConfig::default();
823 let cloned_config = config.clone();
824
825 assert_eq!(
826 config.concurrent_connections,
827 cloned_config.concurrent_connections
828 );
829 assert_eq!(
830 config.requests_per_connection,
831 cloned_config.requests_per_connection
832 );
833 assert_eq!(config.target_url, cloned_config.target_url);
834 assert_eq!(config.headers.len(), cloned_config.headers.len());
835 }
836
837 #[test]
838 fn test_benchmark_config_debug() {
839 let config = BenchmarkConfig::default();
840 let debug_str = format!("{:?}", config);
841
842 assert!(debug_str.contains("BenchmarkConfig"));
843 assert!(debug_str.contains("concurrent_connections"));
844 assert!(debug_str.contains("target_url"));
845 }
846
847 #[test]
848 fn test_benchmark_config_custom_headers() {
849 let mut config = BenchmarkConfig::default();
850 config
851 .headers
852 .insert("Authorization".to_string(), "Bearer token".to_string());
853 config
854 .headers
855 .insert("Content-Type".to_string(), "application/json".to_string());
856
857 assert_eq!(config.headers.len(), 2);
858 assert_eq!(
859 config.headers.get("Authorization"),
860 Some(&"Bearer token".to_string())
861 );
862 assert_eq!(
863 config.headers.get("Content-Type"),
864 Some(&"application/json".to_string())
865 );
866 }
867
868 #[test]
869 fn test_benchmark_config_custom_values() {
870 let config = BenchmarkConfig {
871 concurrent_connections: 50,
872 requests_per_connection: 200,
873 warmup_requests: 25,
874 target_url: "https://example.com/api".to_string(),
875 ..Default::default()
876 };
877
878 assert_eq!(config.concurrent_connections, 50);
879 assert_eq!(config.requests_per_connection, 200);
880 assert_eq!(config.warmup_requests, 25);
881 assert_eq!(config.target_url, "https://example.com/api");
882 }
883
884 #[test]
885 fn test_performance_metrics_serialize_deserialize() {
886 let metrics = PerformanceMetrics {
887 test_name: "serialize_test".to_string(),
888 total_requests: 100,
889 successful_requests: 95,
890 failed_requests: 5,
891 avg_response_time_ms: 123.45,
892 p95_response_time_ms: 200.0,
893 p99_response_time_ms: 250.0,
894 min_response_time_ms: 50.0,
895 max_response_time_ms: 300.0,
896 requests_per_second: 10.5,
897 duration_seconds: 9.52,
898 throughput_kbps: 15.3,
899 error_rate: 5.0,
900 };
901
902 let serialized = serde_json::to_string(&metrics);
904 assert!(serialized.is_ok());
905
906 let deserialized: Result<PerformanceMetrics, _> =
908 serde_json::from_str(&serialized.unwrap());
909 assert!(deserialized.is_ok());
910
911 let deserialized_metrics = deserialized.unwrap();
912 assert_eq!(metrics.test_name, deserialized_metrics.test_name);
913 assert_eq!(metrics.total_requests, deserialized_metrics.total_requests);
914 assert_eq!(
915 metrics.avg_response_time_ms,
916 deserialized_metrics.avg_response_time_ms
917 );
918 }
919
920 #[test]
921 fn test_performance_metrics_edge_case_percentiles() {
922 let response_times = vec![42.0];
924 let duration = Duration::from_secs(1);
925 let total_bytes = 100;
926
927 let metrics = PerformanceMetrics::calculate(
928 "single_percentile_test".to_string(),
929 response_times,
930 duration,
931 total_bytes,
932 );
933
934 assert_eq!(metrics.p95_response_time_ms, 42.0);
935 assert_eq!(metrics.p99_response_time_ms, 42.0);
936 }
937
938 #[test]
939 fn test_performance_metrics_edge_case_small_dataset() {
940 let response_times = vec![10.0, 20.0];
942 let duration = Duration::from_secs(1);
943 let total_bytes = 200;
944
945 let metrics = PerformanceMetrics::calculate(
946 "small_dataset_test".to_string(),
947 response_times,
948 duration,
949 total_bytes,
950 );
951
952 assert_eq!(metrics.total_requests, 2);
953 assert_eq!(metrics.avg_response_time_ms, 15.0);
954 assert_eq!(metrics.p95_response_time_ms, 10.0);
957 assert_eq!(metrics.p99_response_time_ms, 10.0);
958 }
959
960 #[test]
961 fn test_optimized_http_config_extreme_values() {
962 let config = OptimizedHttpConfig {
963 pool_max_idle_per_host: 0,
964 pool_idle_timeout: Duration::from_secs(0),
965 connect_timeout: Duration::from_millis(1),
966 request_timeout: Duration::from_millis(1),
967 ..Default::default()
968 };
969
970 let client = config.build_client();
972 assert!(client.is_ok());
973 }
974
975 #[test]
976 fn test_optimized_http_config_very_large_values() {
977 let config = OptimizedHttpConfig {
978 pool_max_idle_per_host: 10000,
979 pool_idle_timeout: Duration::from_secs(86400), connect_timeout: Duration::from_secs(300), request_timeout: Duration::from_secs(3600), ..Default::default()
983 };
984
985 let client = config.build_client();
986 assert!(client.is_ok());
987 }
988
989 #[test]
990 fn test_performance_metrics_precision() {
991 let response_times = vec![0.001, 0.002, 0.003]; let duration = Duration::from_millis(1);
993 let total_bytes = 1;
994
995 let metrics = PerformanceMetrics::calculate(
996 "precision_test".to_string(),
997 response_times,
998 duration,
999 total_bytes,
1000 );
1001
1002 assert_eq!(metrics.avg_response_time_ms, 0.002);
1003 assert_eq!(metrics.min_response_time_ms, 0.001);
1004 assert_eq!(metrics.max_response_time_ms, 0.003);
1005 }
1006
1007 #[test]
1008 fn test_performance_metrics_user_agent_format() {
1009 let default_config = OptimizedHttpConfig::default();
1010 let production_config = OptimizedHttpConfig::production();
1011 let high_throughput_config = OptimizedHttpConfig::high_throughput();
1012 let low_latency_config = OptimizedHttpConfig::low_latency();
1013
1014 let version = env!("CARGO_PKG_VERSION");
1016 let expected_user_agent = format!("open-lark/{}", version);
1017
1018 assert_eq!(default_config.user_agent, expected_user_agent);
1019 assert_eq!(production_config.user_agent, expected_user_agent);
1020 assert_eq!(high_throughput_config.user_agent, expected_user_agent);
1021 assert_eq!(low_latency_config.user_agent, expected_user_agent);
1022 }
1023}