1use crate::{
8 adapters::mock::MockAdapter,
9 config::WebServerConfig,
10 error::Result,
11 types::{Response, StatusCode},
12};
13use serde::{Deserialize, Serialize};
14use std::{
15 collections::HashMap,
16 sync::Arc,
17 time::{Duration, Instant},
18};
19use tokio::{sync::RwLock, time::timeout};
20
21pub struct CrossPlatformTestRunner {
23 test_suites: Vec<TestSuite>,
24 platform_info: PlatformInfo,
25 #[allow(dead_code)]
26 results: Arc<RwLock<Vec<TestResult>>>,
27}
28
29impl CrossPlatformTestRunner {
30 pub fn new() -> Self {
31 Self {
32 test_suites: Vec::new(),
33 platform_info: PlatformInfo::detect(),
34 results: Arc::new(RwLock::new(Vec::new())),
35 }
36 }
37
38 pub fn add_test_suite(&mut self, suite: TestSuite) {
40 self.test_suites.push(suite);
41 }
42
43 pub async fn run_all_tests(&self) -> CrossPlatformTestResults {
45 let start_time = Instant::now();
46 let mut suite_results = Vec::new();
47 let mut total_passed = 0;
48 let mut total_failed = 0;
49
50 for suite in &self.test_suites {
51 let suite_result = self.run_test_suite(suite).await;
52 total_passed += suite_result.passed_count;
53 total_failed += suite_result.failed_count;
54 suite_results.push(suite_result);
55 }
56
57 CrossPlatformTestResults {
58 platform_info: self.platform_info.clone(),
59 suite_results,
60 total_duration: start_time.elapsed(),
61 total_passed,
62 total_failed,
63 overall_success: total_failed == 0,
64 }
65 }
66
67 async fn run_test_suite(&self, suite: &TestSuite) -> TestSuiteResult {
69 let start_time = Instant::now();
70 let mut test_results = Vec::new();
71 let mut passed_count = 0;
72 let mut failed_count = 0;
73
74 for test_case in &suite.test_cases {
75 let result = self.run_test_case(test_case).await;
76 if result.passed {
77 passed_count += 1;
78 } else {
79 failed_count += 1;
80 }
81 test_results.push(result);
82 }
83
84 TestSuiteResult {
85 name: suite.name.clone(),
86 test_results,
87 duration: start_time.elapsed(),
88 passed_count,
89 failed_count,
90 success: failed_count == 0,
91 }
92 }
93
94 async fn run_test_case(&self, test_case: &TestCase) -> TestResult {
96 let start_time = Instant::now();
97
98 let result = match timeout(Duration::from_secs(30), async {
99 self.execute_test_case(test_case).await
100 })
101 .await
102 {
103 Ok(result) => result,
104 Err(_) => TestCaseResult::Failed("Test case timed out".to_string()),
105 };
106
107 let duration = start_time.elapsed();
108 let passed = matches!(result, TestCaseResult::Passed);
109
110 TestResult {
111 name: test_case.name.clone(),
112 category: test_case.category.clone(),
113 platform_specific: test_case.platform_specific,
114 duration,
115 passed,
116 result,
117 }
118 }
119
120 async fn execute_test_case(&self, test_case: &TestCase) -> TestCaseResult {
122 match &test_case.test_type {
123 TestType::FrameworkCompatibility { adapter_name } => {
124 self.test_framework_compatibility(adapter_name).await
125 }
126 TestType::PerformanceBenchmark {
127 min_rps,
128 max_latency_ms,
129 } => {
130 self.test_performance_benchmark(*min_rps, *max_latency_ms)
131 .await
132 }
133 TestType::SecurityValidation { test_vectors } => {
134 self.test_security_validation(test_vectors).await
135 }
136 TestType::ConfigurationTest { config } => self.test_configuration(config).await,
137 TestType::FFIIntegration { language } => self.test_ffi_integration(language).await,
138 TestType::ConcurrencyTest {
139 concurrent_requests,
140 } => self.test_concurrency(*concurrent_requests).await,
141 TestType::MemoryLeakTest { duration_secs } => {
142 self.test_memory_leaks(*duration_secs).await
143 }
144 TestType::PlatformSpecific { platform, test_fn } => {
145 if self.platform_info.matches_platform(platform) {
146 test_fn().await
147 } else {
148 TestCaseResult::Skipped("Platform not supported".to_string())
149 }
150 }
151 }
152 }
153
154 async fn test_framework_compatibility(&self, adapter_name: &str) -> TestCaseResult {
156 match self.create_test_server(adapter_name).await {
158 Ok(server) => {
159 match self.test_basic_functionality(&server).await {
161 Ok(_) => TestCaseResult::Passed,
162 Err(e) => {
163 TestCaseResult::Failed(format!("Basic functionality test failed: {}", e))
164 }
165 }
166 }
167 Err(e) => TestCaseResult::Failed(format!("Failed to create server: {}", e)),
168 }
169 }
170
171 async fn test_performance_benchmark(
173 &self,
174 min_rps: u64,
175 max_latency_ms: u64,
176 ) -> TestCaseResult {
177 let server = match self.create_test_server("mock").await {
178 Ok(server) => server,
179 Err(e) => return TestCaseResult::Failed(format!("Failed to create server: {}", e)),
180 };
181
182 let start_time = Instant::now();
184 let mut successful_requests = 0;
185 let mut total_latency = Duration::ZERO;
186 let test_duration = Duration::from_secs(10);
187
188 while start_time.elapsed() < test_duration {
189 let request_start = Instant::now();
190
191 if let Ok(_) = self.make_test_request(&server).await {
192 successful_requests += 1;
193 total_latency += request_start.elapsed();
194 }
195 }
196
197 let actual_rps = successful_requests * 1000 / test_duration.as_millis() as u64;
198 let avg_latency_ms = if successful_requests > 0 {
199 total_latency.as_millis() as u64 / successful_requests
200 } else {
201 u64::MAX
202 };
203
204 if actual_rps >= min_rps && avg_latency_ms <= max_latency_ms {
205 TestCaseResult::Passed
206 } else {
207 TestCaseResult::Failed(format!(
208 "Performance test failed: {}rps (min: {}), {}ms latency (max: {}ms)",
209 actual_rps, min_rps, avg_latency_ms, max_latency_ms
210 ))
211 }
212 }
213
214 async fn test_security_validation(
216 &self,
217 test_vectors: &[SecurityTestVector],
218 ) -> TestCaseResult {
219 let server = match self.create_test_server("mock").await {
220 Ok(server) => server,
221 Err(e) => return TestCaseResult::Failed(format!("Failed to create server: {}", e)),
222 };
223
224 for test_vector in test_vectors {
225 match self.test_security_vector(&server, test_vector).await {
226 Ok(false) => {
227 return TestCaseResult::Failed(format!(
228 "Security test failed for: {}",
229 test_vector.description
230 ));
231 }
232 Err(e) => {
233 return TestCaseResult::Failed(format!(
234 "Security test error for {}: {}",
235 test_vector.description, e
236 ));
237 }
238 Ok(true) => {} }
240 }
241
242 TestCaseResult::Passed
243 }
244
245 async fn test_configuration(&self, config: &TestConfiguration) -> TestCaseResult {
247 match self.load_test_configuration(config).await {
248 Ok(_) => TestCaseResult::Passed,
249 Err(e) => TestCaseResult::Failed(format!("Configuration test failed: {}", e)),
250 }
251 }
252
253 async fn test_ffi_integration(&self, language: &str) -> TestCaseResult {
255 match language {
256 "c" => self.test_c_ffi().await,
257 "python" => self.test_python_ffi().await,
258 "nodejs" => self.test_nodejs_ffi().await,
259 "go" => self.test_go_ffi().await,
260 _ => TestCaseResult::Failed(format!("Unsupported FFI language: {}", language)),
261 }
262 }
263
264 async fn test_concurrency(&self, concurrent_requests: u32) -> TestCaseResult {
266 let server = match self.create_test_server("mock").await {
267 Ok(server) => server,
268 Err(e) => return TestCaseResult::Failed(format!("Failed to create server: {}", e)),
269 };
270
271 let mut handles = Vec::new();
272 let start_time = Instant::now();
273
274 for _ in 0..concurrent_requests {
276 let server_clone = server.clone();
277 handles.push(tokio::spawn(async move {
278 Self::make_test_request_static(&server_clone).await
279 }));
280 }
281
282 let mut successful = 0;
284 for handle in handles {
285 if let Ok(Ok(_)) = handle.await {
286 successful += 1;
287 }
288 }
289
290 let duration = start_time.elapsed();
291
292 if successful == concurrent_requests {
293 TestCaseResult::Passed
294 } else {
295 TestCaseResult::Failed(format!(
296 "Concurrency test failed: {}/{} requests successful in {:?}",
297 successful, concurrent_requests, duration
298 ))
299 }
300 }
301
302 async fn test_memory_leaks(&self, duration_secs: u64) -> TestCaseResult {
304 let initial_memory = self.get_memory_usage().await;
305
306 let server = match self.create_test_server("mock").await {
307 Ok(server) => server,
308 Err(e) => return TestCaseResult::Failed(format!("Failed to create server: {}", e)),
309 };
310
311 let end_time = Instant::now() + Duration::from_secs(duration_secs);
313 while Instant::now() < end_time {
314 let _ = self.make_test_request(&server).await;
315 tokio::task::yield_now().await;
316 }
317
318 let final_memory = self.get_memory_usage().await;
319 let memory_growth = final_memory - initial_memory;
320
321 if memory_growth > initial_memory / 2 {
323 TestCaseResult::Failed(format!(
324 "Potential memory leak detected: {}MB -> {}MB (+{}MB)",
325 initial_memory / 1024 / 1024,
326 final_memory / 1024 / 1024,
327 memory_growth / 1024 / 1024
328 ))
329 } else {
330 TestCaseResult::Passed
331 }
332 }
333
334 async fn create_test_server(&self, _adapter_name: &str) -> Result<Arc<MockAdapter>> {
336 Ok(Arc::new(MockAdapter::new()))
339 }
340
341 async fn test_basic_functionality(&self, _server: &MockAdapter) -> Result<()> {
342 Ok(())
344 }
345
346 async fn make_test_request(&self, _server: &MockAdapter) -> Result<Response> {
347 Ok(Response::new(StatusCode::OK))
349 }
350
351 async fn make_test_request_static(_server: &MockAdapter) -> Result<Response> {
352 Ok(Response::new(StatusCode::OK))
353 }
354
355 async fn test_security_vector(
356 &self,
357 _server: &MockAdapter,
358 _vector: &SecurityTestVector,
359 ) -> Result<bool> {
360 Ok(true)
362 }
363
364 async fn load_test_configuration(
365 &self,
366 _config: &TestConfiguration,
367 ) -> Result<WebServerConfig> {
368 Ok(WebServerConfig::default())
369 }
370
371 async fn test_c_ffi(&self) -> TestCaseResult {
372 TestCaseResult::Passed
374 }
375
376 async fn test_python_ffi(&self) -> TestCaseResult {
377 TestCaseResult::Passed
379 }
380
381 async fn test_nodejs_ffi(&self) -> TestCaseResult {
382 TestCaseResult::Passed
384 }
385
386 async fn test_go_ffi(&self) -> TestCaseResult {
387 TestCaseResult::Passed
389 }
390
391 async fn get_memory_usage(&self) -> u64 {
392 1024 * 1024 * 100 }
395}
396
397impl Default for CrossPlatformTestRunner {
398 fn default() -> Self {
399 Self::new()
400 }
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize)]
405pub struct PlatformInfo {
406 pub os: String,
407 pub arch: String,
408 pub rust_version: String,
409 pub features: Vec<String>,
410}
411
412impl PlatformInfo {
413 pub fn detect() -> Self {
414 Self {
415 os: std::env::consts::OS.to_string(),
416 arch: std::env::consts::ARCH.to_string(),
417 rust_version: std::env::var("RUSTC_VERSION").unwrap_or_else(|_| "unknown".to_string()),
418 features: Self::detect_features(),
419 }
420 }
421
422 fn detect_features() -> Vec<String> {
423 let mut features = Vec::new();
424
425 #[cfg(feature = "axum")]
426 features.push("axum".to_string());
427
428 #[cfg(feature = "actix-web")]
429 features.push("actix-web".to_string());
430
431 #[cfg(feature = "warp")]
432 features.push("warp".to_string());
433
434 #[cfg(feature = "rocket")]
435 features.push("rocket".to_string());
436
437 #[cfg(feature = "security")]
438 features.push("security".to_string());
439
440 features
441 }
442
443 pub fn matches_platform(&self, platform: &str) -> bool {
444 match platform {
445 "windows" => self.os == "windows",
446 "linux" => self.os == "linux",
447 "macos" => self.os == "macos",
448 "unix" => self.os != "windows",
449 "x86_64" => self.arch == "x86_64",
450 "aarch64" => self.arch == "aarch64",
451 _ => false,
452 }
453 }
454}
455
456#[derive(Debug, Clone)]
458pub struct TestSuite {
459 pub name: String,
460 pub description: String,
461 pub test_cases: Vec<TestCase>,
462}
463
464#[derive(Debug, Clone)]
466pub struct TestCase {
467 pub name: String,
468 pub category: TestCategory,
469 pub platform_specific: bool,
470 pub test_type: TestType,
471}
472
473#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
475pub enum TestCategory {
476 FrameworkCompatibility,
477 Performance,
478 Security,
479 Configuration,
480 FFI,
481 Concurrency,
482 Memory,
483 Platform,
484}
485
486#[derive(Debug, Clone)]
488pub enum TestType {
489 FrameworkCompatibility {
490 adapter_name: String,
491 },
492 PerformanceBenchmark {
493 min_rps: u64,
494 max_latency_ms: u64,
495 },
496 SecurityValidation {
497 test_vectors: Vec<SecurityTestVector>,
498 },
499 ConfigurationTest {
500 config: TestConfiguration,
501 },
502 FFIIntegration {
503 language: String,
504 },
505 ConcurrencyTest {
506 concurrent_requests: u32,
507 },
508 MemoryLeakTest {
509 duration_secs: u64,
510 },
511 PlatformSpecific {
512 platform: String,
513 test_fn:
514 fn() -> std::pin::Pin<Box<dyn std::future::Future<Output = TestCaseResult> + Send>>,
515 },
516}
517
518#[derive(Debug, Clone)]
520pub struct SecurityTestVector {
521 pub description: String,
522 pub input: String,
523 pub expected_blocked: bool,
524}
525
526#[derive(Debug, Clone)]
528pub struct TestConfiguration {
529 pub config_source: String,
530 pub expected_values: HashMap<String, String>,
531}
532
533#[derive(Debug, Clone, Serialize, Deserialize)]
535pub struct CrossPlatformTestResults {
536 pub platform_info: PlatformInfo,
537 pub suite_results: Vec<TestSuiteResult>,
538 pub total_duration: Duration,
539 pub total_passed: usize,
540 pub total_failed: usize,
541 pub overall_success: bool,
542}
543
544#[derive(Debug, Clone, Serialize, Deserialize)]
545pub struct TestSuiteResult {
546 pub name: String,
547 pub test_results: Vec<TestResult>,
548 pub duration: Duration,
549 pub passed_count: usize,
550 pub failed_count: usize,
551 pub success: bool,
552}
553
554#[derive(Debug, Clone, Serialize, Deserialize)]
555pub struct TestResult {
556 pub name: String,
557 pub category: TestCategory,
558 pub platform_specific: bool,
559 pub duration: Duration,
560 pub passed: bool,
561 pub result: TestCaseResult,
562}
563
564#[derive(Debug, Clone, Serialize, Deserialize)]
565pub enum TestCaseResult {
566 Passed,
567 Failed(String),
568 Skipped(String),
569}
570
571pub mod test_suites {
573 use super::*;
574
575 pub fn core_functionality() -> TestSuite {
577 TestSuite {
578 name: "Core Functionality".to_string(),
579 description: "Tests basic web server functionality across all adapters".to_string(),
580 test_cases: vec![
581 TestCase {
582 name: "Axum Compatibility".to_string(),
583 category: TestCategory::FrameworkCompatibility,
584 platform_specific: false,
585 test_type: TestType::FrameworkCompatibility {
586 adapter_name: "axum".to_string(),
587 },
588 },
589 TestCase {
590 name: "Actix-Web Compatibility".to_string(),
591 category: TestCategory::FrameworkCompatibility,
592 platform_specific: false,
593 test_type: TestType::FrameworkCompatibility {
594 adapter_name: "actix-web".to_string(),
595 },
596 },
597 TestCase {
598 name: "Basic Performance".to_string(),
599 category: TestCategory::Performance,
600 platform_specific: false,
601 test_type: TestType::PerformanceBenchmark {
602 min_rps: 1000,
603 max_latency_ms: 10,
604 },
605 },
606 ],
607 }
608 }
609
610 pub fn security_validation() -> TestSuite {
612 TestSuite {
613 name: "Security Validation".to_string(),
614 description: "Comprehensive security testing".to_string(),
615 test_cases: vec![
616 TestCase {
617 name: "SQL Injection Protection".to_string(),
618 category: TestCategory::Security,
619 platform_specific: false,
620 test_type: TestType::SecurityValidation {
621 test_vectors: vec![
622 SecurityTestVector {
623 description: "Basic SQL injection".to_string(),
624 input: "'; DROP TABLE users; --".to_string(),
625 expected_blocked: true,
626 },
627 SecurityTestVector {
628 description: "Union-based injection".to_string(),
629 input: "' UNION SELECT * FROM users --".to_string(),
630 expected_blocked: true,
631 },
632 ],
633 },
634 },
635 TestCase {
636 name: "XSS Protection".to_string(),
637 category: TestCategory::Security,
638 platform_specific: false,
639 test_type: TestType::SecurityValidation {
640 test_vectors: vec![SecurityTestVector {
641 description: "Script tag injection".to_string(),
642 input: "<script>alert('xss')</script>".to_string(),
643 expected_blocked: true,
644 }],
645 },
646 },
647 ],
648 }
649 }
650
651 pub fn ffi_integration() -> TestSuite {
653 TestSuite {
654 name: "FFI Integration".to_string(),
655 description: "Multi-language FFI integration tests".to_string(),
656 test_cases: vec![
657 TestCase {
658 name: "C FFI".to_string(),
659 category: TestCategory::FFI,
660 platform_specific: false,
661 test_type: TestType::FFIIntegration {
662 language: "c".to_string(),
663 },
664 },
665 TestCase {
666 name: "Python FFI".to_string(),
667 category: TestCategory::FFI,
668 platform_specific: false,
669 test_type: TestType::FFIIntegration {
670 language: "python".to_string(),
671 },
672 },
673 ],
674 }
675 }
676
677 pub fn performance_benchmarks() -> TestSuite {
679 TestSuite {
680 name: "Performance Benchmarks".to_string(),
681 description: "Ultra-low latency and high throughput validation".to_string(),
682 test_cases: vec![
683 TestCase {
684 name: "Ultra Low Latency".to_string(),
685 category: TestCategory::Performance,
686 platform_specific: false,
687 test_type: TestType::PerformanceBenchmark {
688 min_rps: 10000,
689 max_latency_ms: 1,
690 },
691 },
692 TestCase {
693 name: "High Concurrency".to_string(),
694 category: TestCategory::Concurrency,
695 platform_specific: false,
696 test_type: TestType::ConcurrencyTest {
697 concurrent_requests: 1000,
698 },
699 },
700 TestCase {
701 name: "Memory Stability".to_string(),
702 category: TestCategory::Memory,
703 platform_specific: false,
704 test_type: TestType::MemoryLeakTest { duration_secs: 60 },
705 },
706 ],
707 }
708 }
709}
710
711#[cfg(test)]
712mod tests {
713 use super::*;
714
715 #[tokio::test]
716 async fn test_platform_detection() {
717 let platform = PlatformInfo::detect();
718 assert!(!platform.os.is_empty());
719 assert!(!platform.arch.is_empty());
720 assert!(!platform.rust_version.is_empty());
721 }
722
723 #[tokio::test]
724 async fn test_basic_test_runner() {
725 let mut runner = CrossPlatformTestRunner::new();
726 runner.add_test_suite(test_suites::core_functionality());
727
728 let results = runner.run_all_tests().await;
729 assert!(!results.suite_results.is_empty());
730 }
731
732 #[test]
733 fn test_platform_matching() {
734 let platform = PlatformInfo::detect();
735
736 if platform.os == "windows" {
738 assert!(platform.matches_platform("windows"));
739 assert!(!platform.matches_platform("linux"));
740 }
741
742 if platform.arch == "x86_64" {
744 assert!(platform.matches_platform("x86_64"));
745 }
746 }
747
748 #[tokio::test]
749 async fn test_security_test_vectors() {
750 let test_vectors = vec![SecurityTestVector {
751 description: "SQL injection test".to_string(),
752 input: "'; DROP TABLE users; --".to_string(),
753 expected_blocked: true,
754 }];
755
756 assert_eq!(test_vectors.len(), 1);
757 assert!(test_vectors[0].expected_blocked);
758 }
759}