open_lark/core/performance/
mod.rs

1//! HTTP客户端性能优化和基准测试模块
2//!
3//! 本模块提供:
4//! 1. HTTP客户端配置优化建议
5//! 2. 性能基准测试工具
6//! 3. 连接池管理
7//! 4. 请求性能分析
8//! 5. 内存分配优化
9
10pub mod local;
11
12// 重新导出主要类型
13pub use local::LocalPerformanceTester;
14
15use reqwest::{Client, ClientBuilder};
16use serde::{Deserialize, Serialize};
17use std::time::{Duration, Instant};
18use tracing::{info, instrument, warn};
19
20/// HTTP客户端性能配置优化
21#[derive(Debug, Clone)]
22pub struct OptimizedHttpConfig {
23    /// 连接池最大空闲连接数 (默认: 90)
24    pub pool_max_idle_per_host: usize,
25    /// 连接池空闲连接超时 (默认: 90秒)
26    pub pool_idle_timeout: Duration,
27    /// TCP连接超时 (默认: 10秒)
28    pub connect_timeout: Duration,
29    /// 请求总超时 (默认: 30秒)
30    pub request_timeout: Duration,
31    /// 启用HTTP/2 (默认: true)
32    pub http2_prior_knowledge: bool,
33    /// HTTP/2自适应窗口 (默认: true)
34    pub http2_adaptive_window: bool,
35    /// 启用gzip压缩 (默认: true)
36    pub gzip: bool,
37    /// 启用brotli压缩 (默认: true)
38    pub brotli: bool,
39    /// User-Agent字符串
40    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    /// 创建生产环境优化配置
61    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    /// 创建高吞吐量配置
76    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    /// 创建低延迟配置
91    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, // 减少CPU开销
100            brotli: false,
101            user_agent: format!("open-lark/{}", env!("CARGO_PKG_VERSION")),
102        }
103    }
104
105    /// 根据配置构建优化的HTTP客户端
106    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        // 注意: reqwest 0.12 中某些HTTP/2配置方法可能不可用
115        // 我们使用可用的配置方法
116
117        // 注意: reqwest 0.12 默认启用gzip和brotli
118        // 如果需要禁用,可以使用 no_gzip() 和 no_brotli()
119        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/// 性能基准测试指标
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct PerformanceMetrics {
134    /// 测试名称
135    pub test_name: String,
136    /// 总请求数
137    pub total_requests: u64,
138    /// 成功请求数
139    pub successful_requests: u64,
140    /// 失败请求数
141    pub failed_requests: u64,
142    /// 平均响应时间 (毫秒)
143    pub avg_response_time_ms: f64,
144    /// 95th百分位响应时间 (毫秒)
145    pub p95_response_time_ms: f64,
146    /// 99th百分位响应时间 (毫秒)
147    pub p99_response_time_ms: f64,
148    /// 最小响应时间 (毫秒)
149    pub min_response_time_ms: f64,
150    /// 最大响应时间 (毫秒)
151    pub max_response_time_ms: f64,
152    /// 每秒请求数 (RPS)
153    pub requests_per_second: f64,
154    /// 测试持续时间 (秒)
155    pub duration_seconds: f64,
156    /// 字节传输速率 (KB/s)
157    pub throughput_kbps: f64,
158    /// 错误率 (%)
159    pub error_rate: f64,
160}
161
162impl PerformanceMetrics {
163    /// 从响应时间列表计算性能指标
164    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    /// 打印性能报告
247    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/// 基准测试配置
271#[derive(Debug, Clone)]
272pub struct BenchmarkConfig {
273    /// 并发连接数
274    pub concurrent_connections: usize,
275    /// 每个连接的请求数
276    pub requests_per_connection: usize,
277    /// 预热请求数
278    pub warmup_requests: usize,
279    /// 测试URL
280    pub target_url: String,
281    /// 请求头
282    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
297/// HTTP客户端基准测试器
298pub struct HttpBenchmark {
299    client: Client,
300    config: BenchmarkConfig,
301}
302
303impl HttpBenchmark {
304    /// 创建新的基准测试器
305    pub fn new(client: Client, config: BenchmarkConfig) -> Self {
306        Self { client, config }
307    }
308
309    /// 执行基准测试
310    #[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        // 预热
324        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        // 创建并发任务
337        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); // 标记为失败
359                        }
360                    }
361                }
362
363                (response_times, bytes_transferred)
364            });
365
366            tasks.push(task);
367        }
368
369        // 等待所有任务完成
370        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    /// 执行单个请求
398    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    /// 执行单个HTTP请求
403    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
422/// 客户端配置比较工具
423pub struct ClientComparison;
424
425impl ClientComparison {
426    /// 比较不同HTTP客户端配置的性能
427    #[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            // 在配置测试之间短暂休息
452            tokio::time::sleep(Duration::from_secs(2)).await;
453        }
454
455        Self::print_comparison_report(&results);
456        Ok(results)
457    }
458
459    /// 打印配置比较报告
460    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        // 找出最佳性能配置
487        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); // 低延迟配置应该禁用压缩
546        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; // 5KB
554
555        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); // 5KB / 5s = 1KB/s
570        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]; // 2 failures
576        let duration = Duration::from_secs(5);
577        let total_bytes = 1024 * 3; // Only successful requests transfer data
578
579        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); // 2/5 = 40%
590        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        // P95应该接近95,P99应该接近99
644        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    // Additional comprehensive tests for better coverage
649
650    #[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); // Average of 1..=1000
787        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); // 1000KB / 100s = 10KB/s
793    }
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        // Test serialization
903        let serialized = serde_json::to_string(&metrics);
904        assert!(serialized.is_ok());
905
906        // Test deserialization
907        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        // Test edge case where we have exactly 1 request
923        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        // Test edge case with 2 requests for percentile calculation
941        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        // With 2 elements, P95 index = 1.9 -> index 0 (first element)
955        // With 2 elements, P99 index = 1.98 -> index 0 (first element)
956        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        // Should still be able to build a client with extreme values
971        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), // 1 day
980            connect_timeout: Duration::from_secs(300),     // 5 minutes
981            request_timeout: Duration::from_secs(3600),    // 1 hour
982            ..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]; // Very small times
992        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        // All configs should have the same user agent format
1015        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}