//! Production readiness tests for SAL Kubernetes //! //! These tests verify that the module is ready for real-world production use. #[cfg(test)] mod production_tests { use sal_kubernetes::{KubernetesConfig, KubernetesManager}; use std::time::Duration; /// Check if Kubernetes integration tests should run fn should_run_k8s_tests() -> bool { std::env::var("KUBERNETES_TEST_ENABLED").unwrap_or_default() == "1" } #[tokio::test] async fn test_production_configuration_profiles() { // Test all pre-configured profiles work let configs = vec![ ("default", KubernetesConfig::default()), ("high_throughput", KubernetesConfig::high_throughput()), ("low_latency", KubernetesConfig::low_latency()), ("development", KubernetesConfig::development()), ]; for (name, config) in configs { println!("Testing {} configuration profile", name); // Verify configuration values are reasonable assert!( config.operation_timeout >= Duration::from_secs(5), "{} timeout too short", name ); assert!( config.operation_timeout <= Duration::from_secs(300), "{} timeout too long", name ); assert!(config.max_retries <= 10, "{} too many retries", name); assert!(config.rate_limit_rps >= 1, "{} rate limit too low", name); assert!( config.rate_limit_burst >= config.rate_limit_rps, "{} burst should be >= RPS", name ); println!("✓ {} configuration is valid", name); } } #[tokio::test] async fn test_real_cluster_operations() { if !should_run_k8s_tests() { println!("Skipping real cluster test. Set KUBERNETES_TEST_ENABLED=1 to enable."); return; } println!("🔍 Testing production operations with real cluster..."); // Test with production-like configuration let config = KubernetesConfig::default() .with_timeout(Duration::from_secs(30)) .with_retries(3, Duration::from_secs(1), Duration::from_secs(10)) .with_rate_limit(5, 10); // Conservative for testing let km = KubernetesManager::with_config("default", config) .await .expect("Should connect to cluster"); println!("✅ Connected to cluster successfully"); // Test basic operations let namespaces = km.namespaces_list().await.expect("Should list namespaces"); println!("✅ Listed {} namespaces", namespaces.len()); let pods = km.pods_list().await.expect("Should list pods"); println!("✅ Listed {} pods in default namespace", pods.len()); let counts = km .resource_counts() .await .expect("Should get resource counts"); println!("✅ Got resource counts for {} resource types", counts.len()); // Test namespace operations let test_ns = "sal-production-test"; km.namespace_create(test_ns) .await .expect("Should create test namespace"); println!("✅ Created test namespace: {}", test_ns); let exists = km .namespace_exists(test_ns) .await .expect("Should check namespace existence"); assert!(exists, "Test namespace should exist"); println!("✅ Verified test namespace exists"); println!("🎉 All production operations completed successfully!"); } #[tokio::test] async fn test_error_handling_robustness() { if !should_run_k8s_tests() { println!("Skipping error handling test. Set KUBERNETES_TEST_ENABLED=1 to enable."); return; } println!("🔍 Testing error handling robustness..."); let km = KubernetesManager::new("default") .await .expect("Should connect to cluster"); // Test with invalid namespace name (should handle gracefully) let result = km.namespace_exists("").await; match result { Ok(_) => println!("✅ Empty namespace name handled"), Err(e) => println!("✅ Empty namespace name rejected: {}", e), } // Test with very long namespace name let long_name = "a".repeat(100); let result = km.namespace_exists(&long_name).await; match result { Ok(_) => println!("✅ Long namespace name handled"), Err(e) => println!("✅ Long namespace name rejected: {}", e), } println!("✅ Error handling is robust"); } #[tokio::test] async fn test_concurrent_operations() { if !should_run_k8s_tests() { println!("Skipping concurrency test. Set KUBERNETES_TEST_ENABLED=1 to enable."); return; } println!("🔍 Testing concurrent operations..."); let km = KubernetesManager::new("default") .await .expect("Should connect to cluster"); // Test multiple concurrent operations let task1 = tokio::spawn({ let km = km.clone(); async move { km.pods_list().await } }); let task2 = tokio::spawn({ let km = km.clone(); async move { km.services_list().await } }); let task3 = tokio::spawn({ let km = km.clone(); async move { km.namespaces_list().await } }); let mut success_count = 0; // Handle each task result match task1.await { Ok(Ok(_)) => { success_count += 1; println!("✅ Pods list operation succeeded"); } Ok(Err(e)) => println!("⚠️ Pods list operation failed: {}", e), Err(e) => println!("⚠️ Pods task join failed: {}", e), } match task2.await { Ok(Ok(_)) => { success_count += 1; println!("✅ Services list operation succeeded"); } Ok(Err(e)) => println!("⚠️ Services list operation failed: {}", e), Err(e) => println!("⚠️ Services task join failed: {}", e), } match task3.await { Ok(Ok(_)) => { success_count += 1; println!("✅ Namespaces list operation succeeded"); } Ok(Err(e)) => println!("⚠️ Namespaces list operation failed: {}", e), Err(e) => println!("⚠️ Namespaces task join failed: {}", e), } assert!( success_count >= 2, "At least 2 concurrent operations should succeed" ); println!( "✅ Concurrent operations handled well ({}/3 succeeded)", success_count ); } #[test] fn test_security_and_validation() { println!("🔍 Testing security and validation..."); // Test regex pattern validation let dangerous_patterns = vec![ ".*", // Too broad ".+", // Too broad "", // Empty "a{1000000}", // Potential ReDoS ]; for pattern in dangerous_patterns { match regex::Regex::new(pattern) { Ok(_) => println!("⚠️ Pattern '{}' accepted (review if safe)", pattern), Err(_) => println!("✅ Pattern '{}' rejected", pattern), } } // Test safe patterns let safe_patterns = vec!["^test-.*$", "^app-[a-z0-9]+$", "^namespace-\\d+$"]; for pattern in safe_patterns { match regex::Regex::new(pattern) { Ok(_) => println!("✅ Safe pattern '{}' accepted", pattern), Err(e) => println!("❌ Safe pattern '{}' rejected: {}", pattern, e), } } println!("✅ Security validation completed"); } }