pulseengine_mcp_auth/
performance.rs

1//! Performance testing and benchmarking utilities
2//!
3//! This module provides comprehensive performance testing tools for the
4//! authentication framework including load testing, stress testing, and
5//! performance monitoring capabilities.
6
7use crate::{
8    AuthConfig, AuthenticationManager, ConsentConfig, ConsentManager, MemoryConsentStorage, Role,
9};
10use chrono::{DateTime, Utc};
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13use std::sync::Arc;
14use std::time::{Duration, Instant};
15use tokio::time::sleep;
16use tracing::info;
17use uuid::Uuid;
18
19/// Performance test configuration
20#[derive(Debug, Clone)]
21pub struct PerformanceConfig {
22    /// Number of concurrent users to simulate
23    pub concurrent_users: usize,
24
25    /// Duration of the test in seconds
26    pub test_duration_secs: u64,
27
28    /// Request rate per second per user
29    pub requests_per_second: f64,
30
31    /// Warmup duration in seconds
32    pub warmup_duration_secs: u64,
33
34    /// Cool down duration in seconds
35    pub cooldown_duration_secs: u64,
36
37    /// Enable detailed metrics collection
38    pub enable_detailed_metrics: bool,
39
40    /// Target operations to test
41    pub test_operations: Vec<TestOperation>,
42}
43
44impl Default for PerformanceConfig {
45    fn default() -> Self {
46        Self {
47            concurrent_users: 100,
48            test_duration_secs: 60,
49            requests_per_second: 10.0,
50            warmup_duration_secs: 10,
51            cooldown_duration_secs: 5,
52            enable_detailed_metrics: true,
53            test_operations: vec![
54                TestOperation::ValidateApiKey,
55                TestOperation::CreateApiKey,
56                TestOperation::ListApiKeys,
57                TestOperation::RateLimitCheck,
58            ],
59        }
60    }
61}
62
63/// Types of operations to test
64#[derive(Debug, Clone, PartialEq)]
65pub enum TestOperation {
66    /// Test API key validation
67    ValidateApiKey,
68
69    /// Test API key creation
70    CreateApiKey,
71
72    /// Test API key listing
73    ListApiKeys,
74
75    /// Test rate limiting
76    RateLimitCheck,
77
78    /// Test JWT token generation
79    GenerateJwtToken,
80
81    /// Test JWT token validation
82    ValidateJwtToken,
83
84    /// Test consent checking
85    CheckConsent,
86
87    /// Test consent granting
88    GrantConsent,
89
90    /// Test vault operations
91    VaultOperations,
92}
93
94impl std::fmt::Display for TestOperation {
95    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96        match self {
97            TestOperation::ValidateApiKey => write!(f, "Validate API Key"),
98            TestOperation::CreateApiKey => write!(f, "Create API Key"),
99            TestOperation::ListApiKeys => write!(f, "List API Keys"),
100            TestOperation::RateLimitCheck => write!(f, "Rate Limit Check"),
101            TestOperation::GenerateJwtToken => write!(f, "Generate JWT Token"),
102            TestOperation::ValidateJwtToken => write!(f, "Validate JWT Token"),
103            TestOperation::CheckConsent => write!(f, "Check Consent"),
104            TestOperation::GrantConsent => write!(f, "Grant Consent"),
105            TestOperation::VaultOperations => write!(f, "Vault Operations"),
106        }
107    }
108}
109
110/// Performance test results
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct PerformanceResults {
113    /// Test configuration used
114    pub config: TestConfig,
115
116    /// Test start time
117    pub start_time: DateTime<Utc>,
118
119    /// Test end time
120    pub end_time: DateTime<Utc>,
121
122    /// Total duration including warmup/cooldown
123    pub total_duration_secs: f64,
124
125    /// Actual test duration (excluding warmup/cooldown)
126    pub test_duration_secs: f64,
127
128    /// Operation-specific results
129    pub operation_results: HashMap<String, OperationResults>,
130
131    /// Overall statistics
132    pub overall_stats: OverallStats,
133
134    /// Resource usage during test
135    pub resource_usage: ResourceUsage,
136
137    /// Error summary
138    pub error_summary: ErrorSummary,
139}
140
141/// Configuration used for testing (serializable version)
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct TestConfig {
144    pub concurrent_users: usize,
145    pub test_duration_secs: u64,
146    pub requests_per_second: f64,
147    pub warmup_duration_secs: u64,
148    pub cooldown_duration_secs: u64,
149    pub operations_tested: Vec<String>,
150}
151
152/// Results for a specific operation
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct OperationResults {
155    /// Total requests made
156    pub total_requests: u64,
157
158    /// Successful requests
159    pub successful_requests: u64,
160
161    /// Failed requests
162    pub failed_requests: u64,
163
164    /// Success rate as percentage
165    pub success_rate: f64,
166
167    /// Requests per second
168    pub requests_per_second: f64,
169
170    /// Response time statistics in milliseconds
171    pub response_times: ResponseTimeStats,
172
173    /// Error breakdown
174    pub errors: HashMap<String, u64>,
175}
176
177/// Response time statistics
178#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct ResponseTimeStats {
180    /// Average response time in milliseconds
181    pub avg_ms: f64,
182
183    /// Minimum response time
184    pub min_ms: f64,
185
186    /// Maximum response time
187    pub max_ms: f64,
188
189    /// 50th percentile (median)
190    pub p50_ms: f64,
191
192    /// 90th percentile
193    pub p90_ms: f64,
194
195    /// 95th percentile
196    pub p95_ms: f64,
197
198    /// 99th percentile
199    pub p99_ms: f64,
200}
201
202/// Overall test statistics
203#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct OverallStats {
205    /// Total requests across all operations
206    pub total_requests: u64,
207
208    /// Total successful requests
209    pub successful_requests: u64,
210
211    /// Overall success rate
212    pub success_rate: f64,
213
214    /// Overall requests per second
215    pub overall_rps: f64,
216
217    /// Peak requests per second achieved
218    pub peak_rps: f64,
219
220    /// Average concurrent users active
221    pub avg_concurrent_users: f64,
222}
223
224/// Resource usage during test
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct ResourceUsage {
227    /// Peak memory usage in MB
228    pub peak_memory_mb: f64,
229
230    /// Average memory usage in MB
231    pub avg_memory_mb: f64,
232
233    /// Peak CPU usage percentage
234    pub peak_cpu_percent: f64,
235
236    /// Average CPU usage percentage
237    pub avg_cpu_percent: f64,
238
239    /// Number of threads created
240    pub thread_count: u32,
241}
242
243/// Error summary
244#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct ErrorSummary {
246    /// Total errors
247    pub total_errors: u64,
248
249    /// Error rate as percentage
250    pub error_rate: f64,
251
252    /// Breakdown by error type
253    pub error_types: HashMap<String, u64>,
254
255    /// Most common error
256    pub most_common_error: Option<String>,
257}
258
259/// Performance test runner
260pub struct PerformanceTest {
261    config: PerformanceConfig,
262    auth_manager: Arc<AuthenticationManager>,
263    consent_manager: Option<Arc<ConsentManager>>,
264}
265
266impl PerformanceTest {
267    /// Create a new performance test
268    pub async fn new(config: PerformanceConfig) -> Result<Self, Box<dyn std::error::Error>> {
269        // Create auth manager with optimized config for testing
270        let auth_config = AuthConfig {
271            enabled: true,
272            storage: crate::config::StorageConfig::Environment {
273                prefix: "PERF_TEST".to_string(),
274            },
275            cache_size: 10000, // Larger cache for performance testing
276            session_timeout_secs: 3600,
277            max_failed_attempts: 10,
278            rate_limit_window_secs: 60,
279        };
280
281        let auth_manager = Arc::new(AuthenticationManager::new(auth_config).await?);
282
283        // Create consent manager if consent operations are being tested
284        let consent_manager = if config.test_operations.iter().any(|op| {
285            matches!(
286                op,
287                TestOperation::CheckConsent | TestOperation::GrantConsent
288            )
289        }) {
290            let consent_config = ConsentConfig::default();
291            let storage = Arc::new(MemoryConsentStorage::new());
292            Some(Arc::new(ConsentManager::new(consent_config, storage)))
293        } else {
294            None
295        };
296
297        Ok(Self {
298            config,
299            auth_manager,
300            consent_manager,
301        })
302    }
303
304    /// Run the performance test
305    pub async fn run(&mut self) -> Result<PerformanceResults, Box<dyn std::error::Error>> {
306        info!(
307            "Starting performance test with {} concurrent users for {} seconds",
308            self.config.concurrent_users, self.config.test_duration_secs
309        );
310
311        let start_time = Utc::now();
312        let test_start = Instant::now();
313
314        // Warmup phase
315        if self.config.warmup_duration_secs > 0 {
316            info!(
317                "Warming up for {} seconds...",
318                self.config.warmup_duration_secs
319            );
320            self.warmup_phase().await?;
321        }
322
323        // Main test phase
324        info!("Starting main test phase...");
325        let main_test_start = Instant::now();
326        let operation_results = self.run_main_test().await?;
327        let main_test_duration = main_test_start.elapsed();
328
329        // Cool down phase
330        if self.config.cooldown_duration_secs > 0 {
331            info!(
332                "Cooling down for {} seconds...",
333                self.config.cooldown_duration_secs
334            );
335            sleep(Duration::from_secs(self.config.cooldown_duration_secs)).await;
336        }
337
338        let end_time = Utc::now();
339        let total_duration = test_start.elapsed();
340
341        // Calculate overall statistics
342        let overall_stats = self.calculate_overall_stats(&operation_results, main_test_duration);
343        let resource_usage = self.collect_resource_usage();
344        let error_summary = self.calculate_error_summary(&operation_results);
345
346        let results = PerformanceResults {
347            config: TestConfig {
348                concurrent_users: self.config.concurrent_users,
349                test_duration_secs: self.config.test_duration_secs,
350                requests_per_second: self.config.requests_per_second,
351                warmup_duration_secs: self.config.warmup_duration_secs,
352                cooldown_duration_secs: self.config.cooldown_duration_secs,
353                operations_tested: self
354                    .config
355                    .test_operations
356                    .iter()
357                    .map(|op| op.to_string())
358                    .collect(),
359            },
360            start_time,
361            end_time,
362            total_duration_secs: total_duration.as_secs_f64(),
363            test_duration_secs: main_test_duration.as_secs_f64(),
364            operation_results,
365            overall_stats,
366            resource_usage,
367            error_summary,
368        };
369
370        info!("Performance test completed successfully");
371        Ok(results)
372    }
373
374    /// Warmup phase to prepare the system
375    async fn warmup_phase(&mut self) -> Result<(), Box<dyn std::error::Error>> {
376        // Create some initial API keys for testing
377        for i in 0..50 {
378            let key_name = format!("warmup-key-{}", i);
379            let _ = self
380                .auth_manager
381                .create_api_key(
382                    key_name,
383                    Role::Operator,
384                    None,
385                    Some(vec!["127.0.0.1".to_string()]),
386                )
387                .await;
388        }
389
390        // Warm up consent manager if needed
391        if let Some(consent_manager) = &self.consent_manager {
392            for i in 0..20 {
393                let subject_id = format!("warmup-user-{}", i);
394                let _ = consent_manager
395                    .request_consent_individual(
396                        subject_id,
397                        crate::ConsentType::DataProcessing,
398                        crate::LegalBasis::Consent,
399                        "Warmup consent".to_string(),
400                        vec![],
401                        "performance_test".to_string(),
402                        None,
403                    )
404                    .await;
405            }
406        }
407
408        // Brief pause to let things settle
409        sleep(Duration::from_millis(100)).await;
410
411        Ok(())
412    }
413
414    /// Run the main test phase
415    async fn run_main_test(
416        &self,
417    ) -> Result<HashMap<String, OperationResults>, Box<dyn std::error::Error>> {
418        let mut operation_results = HashMap::new();
419
420        // Run tests for each operation
421        for operation in &self.config.test_operations {
422            info!("Testing operation: {}", operation);
423            let results = self.test_operation(operation.clone()).await?;
424            operation_results.insert(operation.to_string(), results);
425        }
426
427        Ok(operation_results)
428    }
429
430    /// Test a specific operation
431    async fn test_operation(
432        &self,
433        operation: TestOperation,
434    ) -> Result<OperationResults, Box<dyn std::error::Error>> {
435        let mut handles = Vec::new();
436        let mut response_times = Vec::new();
437        let mut errors = HashMap::new();
438        let mut total_requests = 0u64;
439        let mut successful_requests = 0u64;
440
441        let test_start = Instant::now();
442        let test_duration = Duration::from_secs(self.config.test_duration_secs);
443
444        // Spawn concurrent workers
445        for user_id in 0..self.config.concurrent_users {
446            let operation = operation.clone();
447            let auth_manager = Arc::clone(&self.auth_manager);
448            let consent_manager = self.consent_manager.as_ref().map(|cm| Arc::clone(cm));
449            let requests_per_second = self.config.requests_per_second;
450
451            let handle = tokio::spawn(async move {
452                let mut user_response_times = Vec::new();
453                let mut user_errors = HashMap::new();
454                let mut user_requests = 0u64;
455                let mut user_successful = 0u64;
456
457                let request_interval = Duration::from_secs_f64(1.0 / requests_per_second);
458                let mut next_request = Instant::now();
459
460                while test_start.elapsed() < test_duration {
461                    if Instant::now() >= next_request {
462                        let request_start = Instant::now();
463
464                        let result = match &operation {
465                            TestOperation::ValidateApiKey => {
466                                Self::test_validate_api_key(&*auth_manager, user_id).await
467                            }
468                            TestOperation::CreateApiKey => {
469                                Self::test_create_api_key(&*auth_manager, user_id).await
470                            }
471                            TestOperation::ListApiKeys => {
472                                Self::test_list_api_keys(&*auth_manager).await
473                            }
474                            TestOperation::RateLimitCheck => {
475                                Self::test_rate_limit_check(&*auth_manager, user_id).await
476                            }
477                            TestOperation::CheckConsent => {
478                                if let Some(consent_mgr) = &consent_manager {
479                                    Self::test_check_consent(&**consent_mgr, user_id).await
480                                } else {
481                                    Ok(())
482                                }
483                            }
484                            TestOperation::GrantConsent => {
485                                if let Some(consent_mgr) = &consent_manager {
486                                    Self::test_grant_consent(&**consent_mgr, user_id).await
487                                } else {
488                                    Ok(())
489                                }
490                            }
491                            _ => Ok(()), // Other operations not implemented yet
492                        };
493
494                        let response_time = request_start.elapsed();
495                        user_response_times.push(response_time.as_secs_f64() * 1000.0); // Convert to ms
496                        user_requests += 1;
497
498                        match result {
499                            Ok(_) => user_successful += 1,
500                            Err(e) => {
501                                let error_type = format!("{:?}", e);
502                                *user_errors.entry(error_type).or_insert(0) += 1;
503                            }
504                        }
505
506                        next_request = Instant::now() + request_interval;
507                    } else {
508                        // Small sleep to prevent busy waiting
509                        sleep(Duration::from_millis(1)).await;
510                    }
511                }
512
513                (
514                    user_response_times,
515                    user_errors,
516                    user_requests,
517                    user_successful,
518                )
519            });
520
521            handles.push(handle);
522        }
523
524        // Collect results from all workers
525        for handle in handles {
526            let (user_response_times, user_errors, user_requests, user_successful) = handle.await?;
527            response_times.extend(user_response_times);
528            total_requests += user_requests;
529            successful_requests += user_successful;
530
531            for (error_type, count) in user_errors {
532                *errors.entry(error_type).or_insert(0) += count;
533            }
534        }
535
536        let failed_requests = total_requests - successful_requests;
537        let success_rate = if total_requests > 0 {
538            (successful_requests as f64 / total_requests as f64) * 100.0
539        } else {
540            0.0
541        };
542
543        let test_duration_secs = test_start.elapsed().as_secs_f64();
544        let requests_per_second = if test_duration_secs > 0.0 {
545            total_requests as f64 / test_duration_secs
546        } else {
547            0.0
548        };
549
550        // Calculate response time statistics
551        response_times.sort_by(|a, b| a.partial_cmp(b).unwrap());
552        let response_time_stats = if !response_times.is_empty() {
553            ResponseTimeStats {
554                avg_ms: response_times.iter().sum::<f64>() / response_times.len() as f64,
555                min_ms: response_times[0],
556                max_ms: response_times[response_times.len() - 1],
557                p50_ms: Self::percentile(&response_times, 50.0),
558                p90_ms: Self::percentile(&response_times, 90.0),
559                p95_ms: Self::percentile(&response_times, 95.0),
560                p99_ms: Self::percentile(&response_times, 99.0),
561            }
562        } else {
563            ResponseTimeStats {
564                avg_ms: 0.0,
565                min_ms: 0.0,
566                max_ms: 0.0,
567                p50_ms: 0.0,
568                p90_ms: 0.0,
569                p95_ms: 0.0,
570                p99_ms: 0.0,
571            }
572        };
573
574        Ok(OperationResults {
575            total_requests,
576            successful_requests,
577            failed_requests,
578            success_rate,
579            requests_per_second,
580            response_times: response_time_stats,
581            errors,
582        })
583    }
584
585    /// Test API key validation
586    async fn test_validate_api_key(
587        auth_manager: &AuthenticationManager,
588        user_id: usize,
589    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
590        // Create a test key for this user if it doesn't exist
591        let key_name = format!("test-key-{}", user_id);
592        let api_key = auth_manager
593            .create_api_key(
594                key_name,
595                Role::Operator,
596                None,
597                Some(vec!["127.0.0.1".to_string()]),
598            )
599            .await?;
600
601        // Validate the key
602        auth_manager
603            .validate_api_key(&api_key.key, Some("127.0.0.1"))
604            .await?;
605
606        Ok(())
607    }
608
609    /// Test API key creation
610    async fn test_create_api_key(
611        auth_manager: &AuthenticationManager,
612        user_id: usize,
613    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
614        let key_name = format!("perf-key-{}-{}", user_id, Uuid::new_v4());
615        auth_manager
616            .create_api_key(key_name, Role::Monitor, None, None)
617            .await?;
618
619        Ok(())
620    }
621
622    /// Test API key listing
623    async fn test_list_api_keys(
624        auth_manager: &AuthenticationManager,
625    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
626        let _ = auth_manager.list_keys().await;
627        Ok(())
628    }
629
630    /// Test rate limiting (simplified - just test key validation which includes rate limiting)
631    async fn test_rate_limit_check(
632        auth_manager: &AuthenticationManager,
633        user_id: usize,
634    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
635        let client_ip = format!("192.168.1.{}", (user_id % 254) + 1);
636        // Create a test key and validate it to trigger rate limiting
637        let key_name = format!("rate-test-key-{}", user_id);
638        let api_key = auth_manager
639            .create_api_key(
640                key_name,
641                Role::Operator,
642                None,
643                Some(vec![client_ip.clone()]),
644            )
645            .await?;
646
647        // Validate the key which will trigger rate limiting checks
648        auth_manager
649            .validate_api_key(&api_key.key, Some(&client_ip))
650            .await?;
651        Ok(())
652    }
653
654    /// Test consent checking
655    async fn test_check_consent(
656        consent_manager: &ConsentManager,
657        user_id: usize,
658    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
659        let subject_id = format!("perf-user-{}", user_id);
660        consent_manager
661            .check_consent(&subject_id, &crate::ConsentType::DataProcessing)
662            .await?;
663        Ok(())
664    }
665
666    /// Test consent granting
667    async fn test_grant_consent(
668        consent_manager: &ConsentManager,
669        user_id: usize,
670    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
671        let subject_id = format!("perf-user-{}", user_id);
672
673        // First request consent
674        let _ = consent_manager
675            .request_consent_individual(
676                subject_id.clone(),
677                crate::ConsentType::Analytics,
678                crate::LegalBasis::Consent,
679                "Performance test consent".to_string(),
680                vec![],
681                "performance_test".to_string(),
682                None,
683            )
684            .await;
685
686        // Then grant it
687        consent_manager
688            .grant_consent(
689                &subject_id,
690                &crate::ConsentType::Analytics,
691                None,
692                "performance_test".to_string(),
693            )
694            .await?;
695
696        Ok(())
697    }
698
699    /// Calculate percentile from sorted data
700    fn percentile(sorted_data: &[f64], percentile: f64) -> f64 {
701        if sorted_data.is_empty() {
702            return 0.0;
703        }
704
705        let index = (percentile / 100.0) * (sorted_data.len() - 1) as f64;
706        let lower = index.floor() as usize;
707        let upper = index.ceil() as usize;
708
709        if lower == upper {
710            sorted_data[lower]
711        } else {
712            let weight = index - lower as f64;
713            sorted_data[lower] * (1.0 - weight) + sorted_data[upper] * weight
714        }
715    }
716
717    /// Calculate overall statistics
718    fn calculate_overall_stats(
719        &self,
720        operation_results: &HashMap<String, OperationResults>,
721        test_duration: Duration,
722    ) -> OverallStats {
723        let total_requests: u64 = operation_results.values().map(|r| r.total_requests).sum();
724        let successful_requests: u64 = operation_results
725            .values()
726            .map(|r| r.successful_requests)
727            .sum();
728
729        let success_rate = if total_requests > 0 {
730            (successful_requests as f64 / total_requests as f64) * 100.0
731        } else {
732            0.0
733        };
734
735        let test_duration_secs = test_duration.as_secs_f64();
736        let overall_rps = if test_duration_secs > 0.0 {
737            total_requests as f64 / test_duration_secs
738        } else {
739            0.0
740        };
741
742        // Peak RPS is estimated as the maximum RPS from any operation
743        let peak_rps = operation_results
744            .values()
745            .map(|r| r.requests_per_second)
746            .fold(0.0, f64::max);
747
748        OverallStats {
749            total_requests,
750            successful_requests,
751            success_rate,
752            overall_rps,
753            peak_rps,
754            avg_concurrent_users: self.config.concurrent_users as f64,
755        }
756    }
757
758    /// Collect resource usage (simplified version)
759    fn collect_resource_usage(&self) -> ResourceUsage {
760        // In a real implementation, you'd collect actual system metrics
761        // For now, return estimated values based on test scale
762        ResourceUsage {
763            peak_memory_mb: (self.config.concurrent_users as f64 * 0.5).max(10.0),
764            avg_memory_mb: (self.config.concurrent_users as f64 * 0.3).max(5.0),
765            peak_cpu_percent: (self.config.concurrent_users as f64 * 0.1).min(80.0),
766            avg_cpu_percent: (self.config.concurrent_users as f64 * 0.05).min(50.0),
767            thread_count: self.config.concurrent_users as u32 + 10,
768        }
769    }
770
771    /// Calculate error summary
772    fn calculate_error_summary(
773        &self,
774        operation_results: &HashMap<String, OperationResults>,
775    ) -> ErrorSummary {
776        let total_requests: u64 = operation_results.values().map(|r| r.total_requests).sum();
777        let total_errors: u64 = operation_results.values().map(|r| r.failed_requests).sum();
778
779        let error_rate = if total_requests > 0 {
780            (total_errors as f64 / total_requests as f64) * 100.0
781        } else {
782            0.0
783        };
784
785        let mut all_errors = HashMap::new();
786        for result in operation_results.values() {
787            for (error_type, count) in &result.errors {
788                *all_errors.entry(error_type.clone()).or_insert(0) += count;
789            }
790        }
791
792        let most_common_error = all_errors
793            .iter()
794            .max_by_key(|(_, count)| *count)
795            .map(|(error_type, _)| error_type.clone());
796
797        ErrorSummary {
798            total_errors,
799            error_rate,
800            error_types: all_errors,
801            most_common_error,
802        }
803    }
804}
805
806#[cfg(test)]
807mod tests {
808    use super::*;
809
810    #[tokio::test]
811    async fn test_performance_config_default() {
812        let config = PerformanceConfig::default();
813        assert_eq!(config.concurrent_users, 100);
814        assert_eq!(config.test_duration_secs, 60);
815        assert!(!config.test_operations.is_empty());
816    }
817
818    #[tokio::test]
819    async fn test_percentile_calculation() {
820        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
821        assert_eq!(PerformanceTest::percentile(&data, 50.0), 3.0);
822        assert_eq!(PerformanceTest::percentile(&data, 90.0), 4.6);
823    }
824
825    #[tokio::test]
826    async fn test_performance_test_creation() {
827        let config = PerformanceConfig {
828            concurrent_users: 10,
829            test_duration_secs: 5,
830            requests_per_second: 1.0,
831            warmup_duration_secs: 1,
832            cooldown_duration_secs: 1,
833            enable_detailed_metrics: true,
834            test_operations: vec![TestOperation::ValidateApiKey],
835        };
836
837        let test = PerformanceTest::new(config).await;
838        assert!(test.is_ok());
839    }
840}