sal/kubernetes/tests/integration_tests.rs
Mahmoud-Emad 52f2f7e3c4 feat: Add Kubernetes module to SAL
- 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)
2025-06-30 14:56:54 +03:00

386 lines
11 KiB
Rust

//! 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<String> = 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);
}
}
}