- 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)
355 lines
12 KiB
Rust
355 lines
12 KiB
Rust
//! Rhai integration tests for SAL Kubernetes
|
|
//!
|
|
//! These tests verify that the Rhai wrappers work correctly and can execute
|
|
//! the Rhai test scripts in the tests/rhai/ directory.
|
|
|
|
#[cfg(feature = "rhai")]
|
|
mod rhai_tests {
|
|
use rhai::Engine;
|
|
use sal_kubernetes::rhai::*;
|
|
use std::fs;
|
|
use std::path::Path;
|
|
|
|
/// Check if Kubernetes integration tests should run
|
|
fn should_run_k8s_tests() -> bool {
|
|
std::env::var("KUBERNETES_TEST_ENABLED").unwrap_or_default() == "1"
|
|
}
|
|
|
|
#[test]
|
|
fn test_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
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_kubernetes_functions_registered() {
|
|
let mut engine = Engine::new();
|
|
register_kubernetes_module(&mut engine).unwrap();
|
|
|
|
// Test that the constructor function is registered
|
|
let script = r#"
|
|
let result = "";
|
|
try {
|
|
let km = kubernetes_manager_new("test");
|
|
result = "constructor_exists";
|
|
} catch(e) {
|
|
result = "constructor_exists_but_failed";
|
|
}
|
|
result
|
|
"#;
|
|
|
|
let result = engine.eval::<String>(script);
|
|
assert!(result.is_ok());
|
|
let result_value = result.unwrap();
|
|
assert!(
|
|
result_value == "constructor_exists" || result_value == "constructor_exists_but_failed",
|
|
"Expected constructor to be registered, got: {}",
|
|
result_value
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_rhai_function_signatures() {
|
|
let mut engine = Engine::new();
|
|
register_kubernetes_module(&mut engine).unwrap();
|
|
|
|
// Test that the new object-oriented API methods work correctly
|
|
// These will fail without a cluster, but should not fail due to missing methods
|
|
let test_scripts = vec![
|
|
// List methods (still function-based for listing)
|
|
("pods_list", "let km = kubernetes_manager_new(\"test\"); km.pods_list();"),
|
|
("services_list", "let km = kubernetes_manager_new(\"test\"); km.services_list();"),
|
|
("deployments_list", "let km = kubernetes_manager_new(\"test\"); km.deployments_list();"),
|
|
("namespaces_list", "let km = kubernetes_manager_new(\"test\"); km.namespaces_list();"),
|
|
("resource_counts", "let km = kubernetes_manager_new(\"test\"); km.resource_counts();"),
|
|
|
|
// Create methods (object-oriented)
|
|
("create_namespace", "let km = kubernetes_manager_new(\"test\"); km.create_namespace(\"test-ns\");"),
|
|
("create_pod", "let km = kubernetes_manager_new(\"test\"); km.create_pod(\"test-pod\", \"nginx\", #{});"),
|
|
("create_service", "let km = kubernetes_manager_new(\"test\"); km.create_service(\"test-svc\", #{}, 80, 80);"),
|
|
|
|
// Get methods (object-oriented)
|
|
("get_pod", "let km = kubernetes_manager_new(\"test\"); km.get_pod(\"test-pod\");"),
|
|
("get_service", "let km = kubernetes_manager_new(\"test\"); km.get_service(\"test-svc\");"),
|
|
|
|
// Delete methods (object-oriented)
|
|
("delete_pod", "let km = kubernetes_manager_new(\"test\"); km.delete_pod(\"test-pod\");"),
|
|
("delete_service", "let km = kubernetes_manager_new(\"test\"); km.delete_service(\"test-service\");"),
|
|
("delete_deployment", "let km = kubernetes_manager_new(\"test\"); km.delete_deployment(\"test-deployment\");"),
|
|
("delete_namespace", "let km = kubernetes_manager_new(\"test\"); km.delete_namespace(\"test-ns\");"),
|
|
|
|
// Utility methods
|
|
("namespace_exists", "let km = kubernetes_manager_new(\"test\"); km.namespace_exists(\"test-ns\");"),
|
|
("namespace", "let km = kubernetes_manager_new(\"test\"); namespace(km);"),
|
|
("delete_pattern", "let km = kubernetes_manager_new(\"test\"); km.delete(\"test-.*\");"),
|
|
];
|
|
|
|
for (function_name, script) in test_scripts {
|
|
println!("Testing function: {}", function_name);
|
|
let result = engine.eval::<rhai::Dynamic>(script);
|
|
|
|
// The function should be registered (not get a "function not found" error)
|
|
// It may fail due to no Kubernetes cluster, but that's expected
|
|
match result {
|
|
Ok(_) => {
|
|
println!("Function {} executed successfully", function_name);
|
|
}
|
|
Err(e) => {
|
|
let error_msg = e.to_string();
|
|
// Should not be a "function not found" error
|
|
assert!(
|
|
!error_msg.contains("Function not found")
|
|
&& !error_msg.contains("Unknown function"),
|
|
"Function {} not registered: {}",
|
|
function_name,
|
|
error_msg
|
|
);
|
|
println!(
|
|
"Function {} failed as expected (no cluster): {}",
|
|
function_name, error_msg
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_rhai_with_real_cluster() {
|
|
if !should_run_k8s_tests() {
|
|
println!("Skipping Rhai Kubernetes integration tests. Set KUBERNETES_TEST_ENABLED=1 to enable.");
|
|
return;
|
|
}
|
|
|
|
let mut engine = Engine::new();
|
|
register_kubernetes_module(&mut engine).unwrap();
|
|
|
|
// Test basic functionality with a real cluster
|
|
let script = r#"
|
|
let km = kubernetes_manager_new("default");
|
|
let ns = namespace(km);
|
|
ns
|
|
"#;
|
|
|
|
let result = engine.eval::<String>(script);
|
|
match result {
|
|
Ok(namespace) => {
|
|
assert_eq!(namespace, "default");
|
|
println!("Successfully got namespace from Rhai: {}", namespace);
|
|
}
|
|
Err(e) => {
|
|
println!("Failed to execute Rhai script with real cluster: {}", e);
|
|
// Don't fail the test if we can't connect to cluster
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_rhai_pods_list() {
|
|
if !should_run_k8s_tests() {
|
|
return;
|
|
}
|
|
|
|
let mut engine = Engine::new();
|
|
register_kubernetes_module(&mut engine).unwrap();
|
|
|
|
let script = r#"
|
|
let km = kubernetes_manager_new("default");
|
|
let pods = pods_list(km);
|
|
pods.len()
|
|
"#;
|
|
|
|
let result = engine.eval::<i64>(script);
|
|
match result {
|
|
Ok(count) => {
|
|
assert!(count >= 0);
|
|
println!("Successfully listed {} pods from Rhai", count);
|
|
}
|
|
Err(e) => {
|
|
println!("Failed to list pods from Rhai: {}", e);
|
|
// Don't fail the test if we can't connect to cluster
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_rhai_resource_counts() {
|
|
if !should_run_k8s_tests() {
|
|
return;
|
|
}
|
|
|
|
let mut engine = Engine::new();
|
|
register_kubernetes_module(&mut engine).unwrap();
|
|
|
|
let script = r#"
|
|
let km = kubernetes_manager_new("default");
|
|
let counts = resource_counts(km);
|
|
counts
|
|
"#;
|
|
|
|
let result = engine.eval::<rhai::Map>(script);
|
|
match result {
|
|
Ok(counts) => {
|
|
println!("Successfully got resource counts from Rhai: {:?}", counts);
|
|
|
|
// Verify expected keys are present
|
|
assert!(counts.contains_key("pods"));
|
|
assert!(counts.contains_key("services"));
|
|
assert!(counts.contains_key("deployments"));
|
|
}
|
|
Err(e) => {
|
|
println!("Failed to get resource counts from Rhai: {}", e);
|
|
// Don't fail the test if we can't connect to cluster
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_rhai_namespace_operations() {
|
|
if !should_run_k8s_tests() {
|
|
return;
|
|
}
|
|
|
|
let mut engine = Engine::new();
|
|
register_kubernetes_module(&mut engine).unwrap();
|
|
|
|
// Test namespace existence check
|
|
let script = r#"
|
|
let km = kubernetes_manager_new("default");
|
|
let exists = namespace_exists(km, "default");
|
|
exists
|
|
"#;
|
|
|
|
let result = engine.eval::<bool>(script);
|
|
match result {
|
|
Ok(exists) => {
|
|
assert!(exists, "Default namespace should exist");
|
|
println!(
|
|
"Successfully checked namespace existence from Rhai: {}",
|
|
exists
|
|
);
|
|
}
|
|
Err(e) => {
|
|
println!("Failed to check namespace existence from Rhai: {}", e);
|
|
// Don't fail the test if we can't connect to cluster
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_rhai_error_handling() {
|
|
let mut engine = Engine::new();
|
|
register_kubernetes_module(&mut engine).unwrap();
|
|
|
|
// Test that errors are properly converted to Rhai errors
|
|
let script = r#"
|
|
let km = kubernetes_manager_new("invalid-namespace-name-that-should-fail");
|
|
pods_list(km)
|
|
"#;
|
|
|
|
let result = engine.eval::<rhai::Array>(script);
|
|
assert!(result.is_err(), "Expected error for invalid configuration");
|
|
|
|
if let Err(e) = result {
|
|
let error_msg = e.to_string();
|
|
println!("Got expected error: {}", error_msg);
|
|
assert!(error_msg.contains("Kubernetes error") || error_msg.contains("error"));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_rhai_script_files_exist() {
|
|
// Test that our Rhai test files exist and are readable
|
|
let test_files = [
|
|
"tests/rhai/basic_kubernetes.rhai",
|
|
"tests/rhai/namespace_operations.rhai",
|
|
"tests/rhai/resource_management.rhai",
|
|
"tests/rhai/run_all_tests.rhai",
|
|
];
|
|
|
|
for test_file in test_files {
|
|
let path = Path::new(test_file);
|
|
assert!(path.exists(), "Rhai test file should exist: {}", test_file);
|
|
|
|
// Try to read the file to ensure it's valid
|
|
let content = fs::read_to_string(path)
|
|
.unwrap_or_else(|e| panic!("Failed to read {}: {}", test_file, e));
|
|
|
|
assert!(
|
|
!content.is_empty(),
|
|
"Rhai test file should not be empty: {}",
|
|
test_file
|
|
);
|
|
assert!(
|
|
content.contains("print("),
|
|
"Rhai test file should contain print statements: {}",
|
|
test_file
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_basic_rhai_script_syntax() {
|
|
// Test that we can at least parse our basic Rhai script
|
|
let mut engine = Engine::new();
|
|
register_kubernetes_module(&mut engine).unwrap();
|
|
|
|
// Simple script that should parse without errors
|
|
let script = r#"
|
|
print("Testing Kubernetes Rhai integration");
|
|
let functions = ["kubernetes_manager_new", "pods_list", "namespace"];
|
|
for func in functions {
|
|
print("Function: " + func);
|
|
}
|
|
print("Basic syntax test completed");
|
|
"#;
|
|
|
|
let result = engine.eval::<()>(script);
|
|
assert!(
|
|
result.is_ok(),
|
|
"Basic Rhai script should parse and execute: {:?}",
|
|
result
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_rhai_script_execution_with_cluster() {
|
|
if !should_run_k8s_tests() {
|
|
println!(
|
|
"Skipping Rhai script execution test. Set KUBERNETES_TEST_ENABLED=1 to enable."
|
|
);
|
|
return;
|
|
}
|
|
|
|
let mut engine = Engine::new();
|
|
register_kubernetes_module(&mut engine).unwrap();
|
|
|
|
// Try to execute a simple script that creates a manager
|
|
let script = r#"
|
|
let km = kubernetes_manager_new("default");
|
|
let ns = namespace(km);
|
|
print("Created manager for namespace: " + ns);
|
|
ns
|
|
"#;
|
|
|
|
let result = engine.eval::<String>(script);
|
|
match result {
|
|
Ok(namespace) => {
|
|
assert_eq!(namespace, "default");
|
|
println!("Successfully executed Rhai script with cluster");
|
|
}
|
|
Err(e) => {
|
|
println!(
|
|
"Rhai script execution failed (expected if no cluster): {}",
|
|
e
|
|
);
|
|
// Don't fail the test if we can't connect to cluster
|
|
}
|
|
}
|
|
}
|
|
}
|