use rhai::{Engine, EvalAltResult}; use sal_zinit_client::rhai::register_zinit_module; use std::path::Path; /// Helper function to create a Rhai engine with zinit functions registered fn create_zinit_engine() -> Result> { let mut engine = Engine::new(); register_zinit_module(&mut engine)?; Ok(engine) } /// Helper function to check if a zinit socket is available fn get_available_socket_path() -> Option { let common_paths = vec![ "/var/run/zinit.sock", "/tmp/zinit.sock", "/run/zinit.sock", "./zinit.sock", ]; for path in common_paths { if Path::new(path).exists() { println!("✓ Found Zinit socket at: {}", path); return Some(path.to_string()); } } println!("⚠ No Zinit socket found. Rhai integration tests will be skipped."); None } #[test] fn test_rhai_zinit_list() { if let Some(socket_path) = get_available_socket_path() { let engine = create_zinit_engine().expect("Failed to create Rhai engine"); let script = format!( r#" let socket_path = "{}"; let services = zinit_list(socket_path); services "#, socket_path ); let result: Result> = engine.eval(&script); match result { Ok(services) => { println!("✓ Rhai zinit_list returned {} services", services.len()); // Verify it's a proper map with valid service data // Verify all service names are non-empty strings for (name, _state) in services.iter() { assert!(!name.is_empty(), "Service name should not be empty"); } // Print some services for debugging for (name, state) in services.iter().take(3) { println!(" Service: {} -> {:?}", name, state); } } Err(e) => { println!("⚠ Rhai zinit_list failed: {}", e); // Don't fail the test - might be expected } } } else { println!("⚠ Skipping test_rhai_zinit_list: No Zinit socket available"); } } #[test] fn test_rhai_service_management() { if let Some(socket_path) = get_available_socket_path() { let engine = create_zinit_engine().expect("Failed to create Rhai engine"); let script = format!( r#" let socket_path = "{}"; let service_name = "rhai-test-service"; let exec_command = "echo 'Hello from Rhai test'"; let oneshot = true; // Clean up any existing service first try {{ zinit_stop(socket_path, service_name); zinit_forget(socket_path, service_name); zinit_delete_service(socket_path, service_name); }} catch(e) {{ // Ignore cleanup errors }} let results = #{{}}; // Test service creation try {{ let create_result = zinit_create_service(socket_path, service_name, exec_command, oneshot); results.create = create_result; // Test service monitoring try {{ let monitor_result = zinit_monitor(socket_path, service_name); results.monitor = monitor_result; // Test service start try {{ let start_result = zinit_start(socket_path, service_name); results.start = start_result; // Test service status try {{ let status_result = zinit_status(socket_path, service_name); results.status = status_result; }} catch(e) {{ results.status_error = e.to_string(); }} // Test service stop try {{ let stop_result = zinit_stop(socket_path, service_name); results.stop = stop_result; }} catch(e) {{ results.stop_error = e.to_string(); }} }} catch(e) {{ results.start_error = e.to_string(); }} // Test forget try {{ let forget_result = zinit_forget(socket_path, service_name); results.forget = forget_result; }} catch(e) {{ results.forget_error = e.to_string(); }} }} catch(e) {{ results.monitor_error = e.to_string(); }} // Test service deletion try {{ let delete_result = zinit_delete_service(socket_path, service_name); results.delete = delete_result; }} catch(e) {{ results.delete_error = e.to_string(); }} }} catch(e) {{ results.create_error = e.to_string(); }} results "#, socket_path ); let result: Result> = engine.eval(&script); match result { Ok(results) => { println!("✓ Rhai service management test completed"); for (operation, result) in results.iter() { println!(" {}: {:?}", operation, result); } // Verify we got meaningful results from service management operations assert!( !results.is_empty(), "Should have results from service operations" ); // Check that we attempted service creation (success or error) assert!( results.contains_key("create") || results.contains_key("create_error"), "Should have attempted service creation" ); } Err(e) => { println!("⚠ Rhai service management test failed: {}", e); } } } else { println!("⚠ Skipping test_rhai_service_management: No Zinit socket available"); } } #[test] fn test_rhai_logs_functionality() { if let Some(socket_path) = get_available_socket_path() { let engine = create_zinit_engine().expect("Failed to create Rhai engine"); let script = format!( r#" let socket_path = "{}"; let results = #{{}}; // Test getting all logs try {{ let all_logs = zinit_logs_all(socket_path); results.all_logs_count = all_logs.len(); if all_logs.len() > 0 {{ results.first_log = all_logs[0]; }} }} catch(e) {{ results.all_logs_error = e.to_string(); }} // Test getting filtered logs try {{ let filtered_logs = zinit_logs(socket_path, "zinit"); results.filtered_logs_count = filtered_logs.len(); }} catch(e) {{ results.filtered_logs_error = e.to_string(); }} results "#, socket_path ); let result: Result> = engine.eval(&script); match result { Ok(results) => { println!("✓ Rhai logs functionality test completed"); for (key, value) in results.iter() { println!(" {}: {:?}", key, value); } // Verify we got meaningful results from logs operations assert!( !results.is_empty(), "Should have results from logs operations" ); // Check that we attempted to get logs (success or error) assert!( results.contains_key("all_logs_count") || results.contains_key("all_logs_error"), "Should have attempted to retrieve all logs" ); } Err(e) => { println!("⚠ Rhai logs functionality test failed: {}", e); } } } else { println!("⚠ Skipping test_rhai_logs_functionality: No Zinit socket available"); } } #[test] fn test_rhai_kill_functionality() { if let Some(socket_path) = get_available_socket_path() { let engine = create_zinit_engine().expect("Failed to create Rhai engine"); let script = format!( r#" let socket_path = "{}"; let service_name = "rhai-kill-test-service"; let exec_command = "sleep 30"; let oneshot = false; let results = #{{}}; // Clean up any existing service first try {{ zinit_stop(socket_path, service_name); zinit_forget(socket_path, service_name); zinit_delete_service(socket_path, service_name); }} catch(e) {{ // Ignore cleanup errors }} // Create and start a long-running service for kill testing try {{ let create_result = zinit_create_service(socket_path, service_name, exec_command, oneshot); results.create = create_result; try {{ let monitor_result = zinit_monitor(socket_path, service_name); let start_result = zinit_start(socket_path, service_name); results.start = start_result; // Test kill with TERM signal try {{ let kill_result = zinit_kill(socket_path, service_name, "TERM"); results.kill = kill_result; }} catch(e) {{ results.kill_error = e.to_string(); }} }} catch(e) {{ results.start_error = e.to_string(); }} // Clean up try {{ zinit_stop(socket_path, service_name); zinit_forget(socket_path, service_name); zinit_delete_service(socket_path, service_name); }} catch(e) {{ // Ignore cleanup errors }} }} catch(e) {{ results.create_error = e.to_string(); }} results "#, socket_path ); let result: Result> = engine.eval(&script); match result { Ok(results) => { println!("✓ Rhai kill functionality test completed"); for (operation, result) in results.iter() { println!(" {}: {:?}", operation, result); } // Verify we got meaningful results from kill functionality operations assert!( !results.is_empty(), "Should have results from kill operations" ); // Check that we attempted service creation for kill testing (success or error) assert!( results.contains_key("create") || results.contains_key("create_error"), "Should have attempted service creation for kill testing" ); } Err(e) => { println!("⚠ Rhai kill functionality test failed: {}", e); } } } else { println!("⚠ Skipping test_rhai_kill_functionality: No Zinit socket available"); } } #[test] fn test_rhai_error_handling() { let engine = create_zinit_engine().expect("Failed to create Rhai engine"); let script = r#" let invalid_socket = "/invalid/path/to/zinit.sock"; let results = #{}; // Test with invalid socket path try { let services = zinit_list(invalid_socket); results.unexpected_success = true; } catch(e) { results.expected_error = e.to_string(); } results "#; let result: Result> = engine.eval(script); match result { Ok(results) => { println!("✓ Rhai error handling test completed"); for (key, value) in results.iter() { println!(" {}: {:?}", key, value); } // Should have caught an error assert!(results.contains_key("expected_error")); } Err(e) => { println!("⚠ Rhai error handling test failed: {}", e); } } } #[test] fn test_rhai_get_service_config() { if let Some(socket_path) = get_available_socket_path() { let engine = create_zinit_engine().expect("Failed to create Rhai engine"); let script = format!( r#" let socket_path = "{}"; let results = #{{}}; // First get list of services try {{ let services = zinit_list(socket_path); results.services_count = services.len(); if services.len() > 0 {{ // Get the first service name let service_names = services.keys(); if service_names.len() > 0 {{ let first_service = service_names[0]; results.test_service = first_service; // Try to get its configuration try {{ let config = zinit_get_service(socket_path, first_service); results.config_retrieved = true; results.config_type = type_of(config); }} catch(e) {{ results.config_error = e.to_string(); }} }} }} }} catch(e) {{ results.list_error = e.to_string(); }} results "#, socket_path ); let result: Result> = engine.eval(&script); match result { Ok(results) => { println!("✓ Rhai get service config test completed"); for (key, value) in results.iter() { println!(" {}: {:?}", key, value); } // Verify we got meaningful results from get service config operations assert!( !results.is_empty(), "Should have results from config operations" ); // Check that we attempted to list services (success or error) assert!( results.contains_key("services_count") || results.contains_key("list_error"), "Should have attempted to list services for config testing" ); } Err(e) => { println!("⚠ Rhai get service config test failed: {}", e); } } } else { println!("⚠ Skipping test_rhai_get_service_config: No Zinit socket available"); } }