//! Integration tests for SAL Kubernetes //! //! These tests require a running Kubernetes cluster and appropriate credentials. //! Set KUBERNETES_TEST_ENABLED=1 to run these tests. use sal_kubernetes::KubernetesManager; /// 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_kubernetes_manager_creation() { if !should_run_k8s_tests() { println!("Skipping Kubernetes integration tests. Set KUBERNETES_TEST_ENABLED=1 to enable."); return; } let result = KubernetesManager::new("default").await; match result { Ok(_) => println!("Successfully created KubernetesManager"), Err(e) => println!("Failed to create KubernetesManager: {}", e), } } #[tokio::test] async fn test_namespace_operations() { if !should_run_k8s_tests() { return; } let km = match KubernetesManager::new("default").await { Ok(km) => km, Err(_) => return, // Skip if can't connect }; // Test namespace creation (should be idempotent) let test_namespace = "sal-test-namespace"; let result = km.namespace_create(test_namespace).await; assert!(result.is_ok(), "Failed to create namespace: {:?}", result); // Test creating the same namespace again (should not error) let result = km.namespace_create(test_namespace).await; assert!( result.is_ok(), "Failed to create namespace idempotently: {:?}", result ); } #[tokio::test] async fn test_pods_list() { if !should_run_k8s_tests() { return; } let km = match KubernetesManager::new("default").await { Ok(km) => km, Err(_) => return, // Skip if can't connect }; let result = km.pods_list().await; match result { Ok(pods) => { println!("Found {} pods in default namespace", pods.len()); // Verify pod structure for pod in pods.iter().take(3) { // Check first 3 pods assert!(pod.metadata.name.is_some()); assert!(pod.metadata.namespace.is_some()); println!( "Pod: {} in namespace: {}", pod.metadata.name.as_ref().unwrap(), pod.metadata.namespace.as_ref().unwrap() ); } } Err(e) => { println!("Failed to list pods: {}", e); // Don't fail the test if we can't list pods due to permissions } } } #[tokio::test] async fn test_services_list() { if !should_run_k8s_tests() { return; } let km = match KubernetesManager::new("default").await { Ok(km) => km, Err(_) => return, }; let result = km.services_list().await; match result { Ok(services) => { println!("Found {} services in default namespace", services.len()); // Verify service structure for service in services.iter().take(3) { assert!(service.metadata.name.is_some()); println!("Service: {}", service.metadata.name.as_ref().unwrap()); } } Err(e) => { println!("Failed to list services: {}", e); } } } #[tokio::test] async fn test_deployments_list() { if !should_run_k8s_tests() { return; } let km = match KubernetesManager::new("default").await { Ok(km) => km, Err(_) => return, }; let result = km.deployments_list().await; match result { Ok(deployments) => { println!( "Found {} deployments in default namespace", deployments.len() ); // Verify deployment structure for deployment in deployments.iter().take(3) { assert!(deployment.metadata.name.is_some()); println!("Deployment: {}", deployment.metadata.name.as_ref().unwrap()); } } Err(e) => { println!("Failed to list deployments: {}", e); } } } #[tokio::test] async fn test_resource_counts() { if !should_run_k8s_tests() { return; } let km = match KubernetesManager::new("default").await { Ok(km) => km, Err(_) => return, }; let result = km.resource_counts().await; match result { Ok(counts) => { println!("Resource counts: {:?}", counts); // Verify expected resource types are present assert!(counts.contains_key("pods")); assert!(counts.contains_key("services")); assert!(counts.contains_key("deployments")); assert!(counts.contains_key("configmaps")); assert!(counts.contains_key("secrets")); // Verify counts are reasonable (counts are usize, so always non-negative) for (resource_type, count) in counts { // Verify we got a count for each resource type println!("Resource type '{}' has {} items", resource_type, count); // Counts should be reasonable (not impossibly large) assert!( count < 10000, "Count for {} seems unreasonably high: {}", resource_type, count ); } } Err(e) => { println!("Failed to get resource counts: {}", e); } } } #[tokio::test] async fn test_namespaces_list() { if !should_run_k8s_tests() { return; } let km = match KubernetesManager::new("default").await { Ok(km) => km, Err(_) => return, }; let result = km.namespaces_list().await; match result { Ok(namespaces) => { println!("Found {} namespaces", namespaces.len()); // Should have at least default namespace let namespace_names: Vec = namespaces .iter() .filter_map(|ns| ns.metadata.name.as_ref()) .cloned() .collect(); println!("Namespaces: {:?}", namespace_names); assert!(namespace_names.contains(&"default".to_string())); } Err(e) => { println!("Failed to list namespaces: {}", e); } } } #[tokio::test] async fn test_pattern_matching_dry_run() { if !should_run_k8s_tests() { return; } let km = match KubernetesManager::new("default").await { Ok(km) => km, Err(_) => return, }; // Test pattern matching without actually deleting anything // We'll just verify that the regex patterns work correctly let test_patterns = vec![ "test-.*", // Should match anything starting with "test-" ".*-temp$", // Should match anything ending with "-temp" "nonexistent-.*", // Should match nothing (hopefully) ]; for pattern in test_patterns { println!("Testing pattern: {}", pattern); // Get all pods first if let Ok(pods) = km.pods_list().await { let regex = regex::Regex::new(pattern).unwrap(); let matching_pods: Vec<_> = pods .iter() .filter_map(|pod| pod.metadata.name.as_ref()) .filter(|name| regex.is_match(name)) .collect(); println!( "Pattern '{}' would match {} pods: {:?}", pattern, matching_pods.len(), matching_pods ); } } } #[tokio::test] async fn test_namespace_exists_functionality() { if !should_run_k8s_tests() { return; } let km = match KubernetesManager::new("default").await { Ok(km) => km, Err(_) => return, }; // Test that default namespace exists let result = km.namespace_exists("default").await; match result { Ok(exists) => { assert!(exists, "Default namespace should exist"); println!("Default namespace exists: {}", exists); } Err(e) => { println!("Failed to check if default namespace exists: {}", e); } } // Test that a non-existent namespace doesn't exist let result = km.namespace_exists("definitely-does-not-exist-12345").await; match result { Ok(exists) => { assert!(!exists, "Non-existent namespace should not exist"); println!("Non-existent namespace exists: {}", exists); } Err(e) => { println!("Failed to check if non-existent namespace exists: {}", e); } } } #[tokio::test] async fn test_manager_namespace_property() { if !should_run_k8s_tests() { return; } let test_namespace = "test-namespace"; let km = match KubernetesManager::new(test_namespace).await { Ok(km) => km, Err(_) => return, }; // Verify the manager knows its namespace assert_eq!(km.namespace(), test_namespace); println!("Manager namespace: {}", km.namespace()); } #[tokio::test] async fn test_error_handling() { if !should_run_k8s_tests() { return; } let km = match KubernetesManager::new("default").await { Ok(km) => km, Err(_) => return, }; // Test getting a non-existent pod let result = km.pod_get("definitely-does-not-exist-12345").await; assert!(result.is_err(), "Getting non-existent pod should fail"); if let Err(e) = result { println!("Expected error for non-existent pod: {}", e); // Verify it's the right kind of error match e { sal_kubernetes::KubernetesError::ApiError(_) => { println!("Correctly got API error for non-existent resource"); } _ => { println!("Got unexpected error type: {:?}", e); } } } } #[tokio::test] async fn test_configmaps_and_secrets() { if !should_run_k8s_tests() { return; } let km = match KubernetesManager::new("default").await { Ok(km) => km, Err(_) => return, }; // Test configmaps listing let result = km.configmaps_list().await; match result { Ok(configmaps) => { println!("Found {} configmaps in default namespace", configmaps.len()); for cm in configmaps.iter().take(3) { if let Some(name) = &cm.metadata.name { println!("ConfigMap: {}", name); } } } Err(e) => { println!("Failed to list configmaps: {}", e); } } // Test secrets listing let result = km.secrets_list().await; match result { Ok(secrets) => { println!("Found {} secrets in default namespace", secrets.len()); for secret in secrets.iter().take(3) { if let Some(name) = &secret.metadata.name { println!("Secret: {}", name); } } } Err(e) => { println!("Failed to list secrets: {}", e); } } }