...
This commit is contained in:
303
packages/system/kubernetes/tests/unit_tests.rs
Normal file
303
packages/system/kubernetes/tests/unit_tests.rs
Normal file
@@ -0,0 +1,303 @@
|
||||
//! Unit tests for SAL Kubernetes
|
||||
//!
|
||||
//! These tests focus on testing individual components and error handling
|
||||
//! without requiring a live Kubernetes cluster.
|
||||
|
||||
use sal_kubernetes::KubernetesError;
|
||||
|
||||
#[test]
|
||||
fn test_kubernetes_error_creation() {
|
||||
let config_error = KubernetesError::config_error("Test config error");
|
||||
assert!(matches!(config_error, KubernetesError::ConfigError(_)));
|
||||
assert_eq!(
|
||||
config_error.to_string(),
|
||||
"Configuration error: Test config error"
|
||||
);
|
||||
|
||||
let operation_error = KubernetesError::operation_error("Test operation error");
|
||||
assert!(matches!(
|
||||
operation_error,
|
||||
KubernetesError::OperationError(_)
|
||||
));
|
||||
assert_eq!(
|
||||
operation_error.to_string(),
|
||||
"Operation failed: Test operation error"
|
||||
);
|
||||
|
||||
let namespace_error = KubernetesError::namespace_error("Test namespace error");
|
||||
assert!(matches!(
|
||||
namespace_error,
|
||||
KubernetesError::NamespaceError(_)
|
||||
));
|
||||
assert_eq!(
|
||||
namespace_error.to_string(),
|
||||
"Namespace error: Test namespace error"
|
||||
);
|
||||
|
||||
let permission_error = KubernetesError::permission_denied("Test permission error");
|
||||
assert!(matches!(
|
||||
permission_error,
|
||||
KubernetesError::PermissionDenied(_)
|
||||
));
|
||||
assert_eq!(
|
||||
permission_error.to_string(),
|
||||
"Permission denied: Test permission error"
|
||||
);
|
||||
|
||||
let timeout_error = KubernetesError::timeout("Test timeout error");
|
||||
assert!(matches!(timeout_error, KubernetesError::Timeout(_)));
|
||||
assert_eq!(
|
||||
timeout_error.to_string(),
|
||||
"Operation timed out: Test timeout error"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_regex_error_conversion() {
|
||||
use regex::Regex;
|
||||
|
||||
// Test invalid regex pattern
|
||||
let invalid_pattern = "[invalid";
|
||||
let regex_result = Regex::new(invalid_pattern);
|
||||
assert!(regex_result.is_err());
|
||||
|
||||
// Convert to KubernetesError
|
||||
let k8s_error = KubernetesError::from(regex_result.unwrap_err());
|
||||
assert!(matches!(k8s_error, KubernetesError::RegexError(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_display() {
|
||||
let errors = vec![
|
||||
KubernetesError::config_error("Config test"),
|
||||
KubernetesError::operation_error("Operation test"),
|
||||
KubernetesError::namespace_error("Namespace test"),
|
||||
KubernetesError::permission_denied("Permission test"),
|
||||
KubernetesError::timeout("Timeout test"),
|
||||
];
|
||||
|
||||
for error in errors {
|
||||
let error_string = error.to_string();
|
||||
assert!(!error_string.is_empty());
|
||||
assert!(error_string.contains("test"));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
#[test]
|
||||
fn test_rhai_module_registration() {
|
||||
use rhai::Engine;
|
||||
use sal_kubernetes::rhai::register_kubernetes_module;
|
||||
|
||||
let mut engine = Engine::new();
|
||||
let result = register_kubernetes_module(&mut engine);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Failed to register Kubernetes module: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "rhai")]
|
||||
#[test]
|
||||
fn test_rhai_functions_registered() {
|
||||
use rhai::Engine;
|
||||
use sal_kubernetes::rhai::register_kubernetes_module;
|
||||
|
||||
let mut engine = Engine::new();
|
||||
register_kubernetes_module(&mut engine).unwrap();
|
||||
|
||||
// Test that functions are registered by checking if they exist in the engine
|
||||
// We can't actually call async functions without a runtime, so we just verify registration
|
||||
|
||||
// Check that the main functions are registered by looking for them in the engine
|
||||
let function_names = vec![
|
||||
"kubernetes_manager_new",
|
||||
"pods_list",
|
||||
"services_list",
|
||||
"deployments_list",
|
||||
"delete",
|
||||
"namespace_create",
|
||||
"namespace_exists",
|
||||
];
|
||||
|
||||
for function_name in function_names {
|
||||
// Try to parse a script that references the function
|
||||
// This will succeed if the function is registered, even if we don't call it
|
||||
let script = format!("let f = {};", function_name);
|
||||
let result = engine.compile(&script);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Function '{}' should be registered in the engine",
|
||||
function_name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_namespace_validation() {
|
||||
// Test valid namespace names
|
||||
let valid_names = vec!["default", "kube-system", "my-app", "test123"];
|
||||
for name in valid_names {
|
||||
assert!(!name.is_empty());
|
||||
assert!(name.chars().all(|c| c.is_alphanumeric() || c == '-'));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resource_name_patterns() {
|
||||
use regex::Regex;
|
||||
|
||||
// Test common patterns that might be used with the delete function
|
||||
let patterns = vec![
|
||||
r"test-.*", // Match anything starting with "test-"
|
||||
r".*-temp$", // Match anything ending with "-temp"
|
||||
r"^pod-\d+$", // Match "pod-" followed by digits
|
||||
r"app-[a-z]+", // Match "app-" followed by lowercase letters
|
||||
];
|
||||
|
||||
for pattern in patterns {
|
||||
let regex = Regex::new(pattern);
|
||||
assert!(regex.is_ok(), "Pattern '{}' should be valid", pattern);
|
||||
|
||||
let regex = regex.unwrap();
|
||||
|
||||
// Test some example matches based on the pattern
|
||||
match pattern {
|
||||
r"test-.*" => {
|
||||
assert!(regex.is_match("test-pod"));
|
||||
assert!(regex.is_match("test-service"));
|
||||
assert!(!regex.is_match("prod-pod"));
|
||||
}
|
||||
r".*-temp$" => {
|
||||
assert!(regex.is_match("my-pod-temp"));
|
||||
assert!(regex.is_match("service-temp"));
|
||||
assert!(!regex.is_match("temp-pod"));
|
||||
}
|
||||
r"^pod-\d+$" => {
|
||||
assert!(regex.is_match("pod-123"));
|
||||
assert!(regex.is_match("pod-1"));
|
||||
assert!(!regex.is_match("pod-abc"));
|
||||
assert!(!regex.is_match("service-123"));
|
||||
}
|
||||
r"app-[a-z]+" => {
|
||||
assert!(regex.is_match("app-frontend"));
|
||||
assert!(regex.is_match("app-backend"));
|
||||
assert!(!regex.is_match("app-123"));
|
||||
assert!(!regex.is_match("service-frontend"));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_regex_patterns() {
|
||||
use regex::Regex;
|
||||
|
||||
// Test invalid regex patterns that should fail
|
||||
let invalid_patterns = vec![
|
||||
"[invalid", // Unclosed bracket
|
||||
"*invalid", // Invalid quantifier
|
||||
"(?invalid)", // Invalid group
|
||||
"\\", // Incomplete escape
|
||||
];
|
||||
|
||||
for pattern in invalid_patterns {
|
||||
let regex = Regex::new(pattern);
|
||||
assert!(regex.is_err(), "Pattern '{}' should be invalid", pattern);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kubernetes_config_creation() {
|
||||
use sal_kubernetes::KubernetesConfig;
|
||||
use std::time::Duration;
|
||||
|
||||
// Test default configuration
|
||||
let default_config = KubernetesConfig::default();
|
||||
assert_eq!(default_config.operation_timeout, Duration::from_secs(30));
|
||||
assert_eq!(default_config.max_retries, 3);
|
||||
assert_eq!(default_config.rate_limit_rps, 10);
|
||||
assert_eq!(default_config.rate_limit_burst, 20);
|
||||
|
||||
// Test custom configuration
|
||||
let custom_config = KubernetesConfig::new()
|
||||
.with_timeout(Duration::from_secs(60))
|
||||
.with_retries(5, Duration::from_secs(2), Duration::from_secs(60))
|
||||
.with_rate_limit(50, 100);
|
||||
|
||||
assert_eq!(custom_config.operation_timeout, Duration::from_secs(60));
|
||||
assert_eq!(custom_config.max_retries, 5);
|
||||
assert_eq!(custom_config.retry_base_delay, Duration::from_secs(2));
|
||||
assert_eq!(custom_config.retry_max_delay, Duration::from_secs(60));
|
||||
assert_eq!(custom_config.rate_limit_rps, 50);
|
||||
assert_eq!(custom_config.rate_limit_burst, 100);
|
||||
|
||||
// Test pre-configured profiles
|
||||
let high_throughput = KubernetesConfig::high_throughput();
|
||||
assert_eq!(high_throughput.rate_limit_rps, 50);
|
||||
assert_eq!(high_throughput.rate_limit_burst, 100);
|
||||
|
||||
let low_latency = KubernetesConfig::low_latency();
|
||||
assert_eq!(low_latency.operation_timeout, Duration::from_secs(10));
|
||||
assert_eq!(low_latency.max_retries, 2);
|
||||
|
||||
let development = KubernetesConfig::development();
|
||||
assert_eq!(development.operation_timeout, Duration::from_secs(120));
|
||||
assert_eq!(development.rate_limit_rps, 100);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_retryable_error_detection() {
|
||||
use kube::Error as KubeError;
|
||||
use sal_kubernetes::kubernetes_manager::is_retryable_error;
|
||||
|
||||
// Test that the function exists and works with basic error types
|
||||
// Note: We can't easily create all error types, so we test what we can
|
||||
|
||||
// Test API errors with different status codes
|
||||
let api_error_500 = KubeError::Api(kube::core::ErrorResponse {
|
||||
status: "Failure".to_string(),
|
||||
message: "Internal server error".to_string(),
|
||||
reason: "InternalError".to_string(),
|
||||
code: 500,
|
||||
});
|
||||
assert!(
|
||||
is_retryable_error(&api_error_500),
|
||||
"500 errors should be retryable"
|
||||
);
|
||||
|
||||
let api_error_429 = KubeError::Api(kube::core::ErrorResponse {
|
||||
status: "Failure".to_string(),
|
||||
message: "Too many requests".to_string(),
|
||||
reason: "TooManyRequests".to_string(),
|
||||
code: 429,
|
||||
});
|
||||
assert!(
|
||||
is_retryable_error(&api_error_429),
|
||||
"429 errors should be retryable"
|
||||
);
|
||||
|
||||
let api_error_404 = KubeError::Api(kube::core::ErrorResponse {
|
||||
status: "Failure".to_string(),
|
||||
message: "Not found".to_string(),
|
||||
reason: "NotFound".to_string(),
|
||||
code: 404,
|
||||
});
|
||||
assert!(
|
||||
!is_retryable_error(&api_error_404),
|
||||
"404 errors should not be retryable"
|
||||
);
|
||||
|
||||
let api_error_400 = KubeError::Api(kube::core::ErrorResponse {
|
||||
status: "Failure".to_string(),
|
||||
message: "Bad request".to_string(),
|
||||
reason: "BadRequest".to_string(),
|
||||
code: 400,
|
||||
});
|
||||
assert!(
|
||||
!is_retryable_error(&api_error_400),
|
||||
"400 errors should not be retryable"
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user