- Add Kubernetes cluster management and operations - Include pod, service, and deployment management - Implement pattern-based resource deletion - Support namespace creation and management - Provide Rhai scripting wrappers for all functions - Include production safety features (timeouts, retries, rate limiting)
232 lines
7.9 KiB
Rust
232 lines
7.9 KiB
Rust
//! 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");
|
|
}
|
|
}
|