use sal_service_manager::{ ServiceConfig, ServiceManager, ServiceManagerError, ServiceStatus, ZinitServiceManager, }; use std::collections::HashMap; use std::time::Duration; use tokio::time::sleep; /// Helper function to find an available Zinit socket path async fn get_available_socket_path() -> Option { let socket_paths = [ "/var/run/zinit.sock", "/tmp/zinit.sock", "/run/zinit.sock", "./zinit.sock", ]; for path in &socket_paths { // Try to create a ZinitServiceManager to test connectivity if let Ok(manager) = ZinitServiceManager::new(path) { // Test if we can list services (basic connectivity test) if manager.list().is_ok() { println!("✓ Found working Zinit socket at: {}", path); return Some(path.to_string()); } } } None } /// Helper function to clean up test services async fn cleanup_test_service(manager: &dyn ServiceManager, service_name: &str) { let _ = manager.stop(service_name); let _ = manager.remove(service_name); } #[tokio::test] async fn test_zinit_service_manager_creation() { if let Some(socket_path) = get_available_socket_path().await { let manager = ZinitServiceManager::new(&socket_path); assert!( manager.is_ok(), "Should be able to create ZinitServiceManager" ); let manager = manager.unwrap(); // Test basic connectivity by listing services let list_result = manager.list(); assert!(list_result.is_ok(), "Should be able to list services"); println!("✓ ZinitServiceManager created successfully"); } else { println!("⚠ Skipping test_zinit_service_manager_creation: No Zinit socket available"); } } #[tokio::test] async fn test_service_lifecycle() { if let Some(socket_path) = get_available_socket_path().await { let manager = ZinitServiceManager::new(&socket_path).expect("Failed to create manager"); let service_name = "test-lifecycle-service"; // Clean up any existing service first cleanup_test_service(&manager, service_name).await; let config = ServiceConfig { name: service_name.to_string(), binary_path: "echo".to_string(), args: vec!["Hello from lifecycle test".to_string()], working_directory: Some("/tmp".to_string()), environment: HashMap::new(), auto_restart: false, }; // Test service creation and start println!("Testing service creation and start..."); let start_result = manager.start(&config); match start_result { Ok(_) => { println!("✓ Service started successfully"); // Wait a bit for the service to run sleep(Duration::from_millis(500)).await; // Test service exists let exists = manager.exists(service_name); assert!(exists.is_ok(), "Should be able to check if service exists"); if let Ok(true) = exists { println!("✓ Service exists check passed"); // Test service status let status_result = manager.status(service_name); match status_result { Ok(status) => { println!("✓ Service status: {:?}", status); assert!( matches!(status, ServiceStatus::Running | ServiceStatus::Stopped), "Service should be running or stopped (for oneshot)" ); } Err(e) => println!("⚠ Status check failed: {}", e), } // Test service logs let logs_result = manager.logs(service_name, None); match logs_result { Ok(logs) => { println!("✓ Retrieved logs: {}", logs.len()); // For echo command, we should have some output assert!( !logs.is_empty() || logs.contains("Hello"), "Should have log output" ); } Err(e) => println!("⚠ Logs retrieval failed: {}", e), } // Test service list let list_result = manager.list(); match list_result { Ok(services) => { println!("✓ Listed {} services", services.len()); assert!( services.contains(&service_name.to_string()), "Service should appear in list" ); } Err(e) => println!("⚠ List services failed: {}", e), } } // Test service stop println!("Testing service stop..."); let stop_result = manager.stop(service_name); match stop_result { Ok(_) => println!("✓ Service stopped successfully"), Err(e) => println!("⚠ Stop failed: {}", e), } // Test service removal println!("Testing service removal..."); let remove_result = manager.remove(service_name); match remove_result { Ok(_) => println!("✓ Service removed successfully"), Err(e) => println!("⚠ Remove failed: {}", e), } } Err(e) => { println!("⚠ Service creation/start failed: {}", e); // This might be expected if zinit doesn't allow service creation } } // Final cleanup cleanup_test_service(&manager, service_name).await; } else { println!("⚠ Skipping test_service_lifecycle: No Zinit socket available"); } } #[tokio::test] async fn test_service_start_and_confirm() { if let Some(socket_path) = get_available_socket_path().await { let manager = ZinitServiceManager::new(&socket_path).expect("Failed to create manager"); let service_name = "test-start-confirm-service"; // Clean up any existing service first cleanup_test_service(&manager, service_name).await; let config = ServiceConfig { name: service_name.to_string(), binary_path: "sleep".to_string(), args: vec!["5".to_string()], // Sleep for 5 seconds working_directory: Some("/tmp".to_string()), environment: HashMap::new(), auto_restart: false, }; // Test start_and_confirm with timeout println!("Testing start_and_confirm with timeout..."); let start_result = manager.start_and_confirm(&config, 10); match start_result { Ok(_) => { println!("✓ Service started and confirmed successfully"); // Verify it's actually running let status = manager.status(service_name); match status { Ok(ServiceStatus::Running) => println!("✓ Service confirmed running"), Ok(other_status) => { println!("⚠ Service in unexpected state: {:?}", other_status) } Err(e) => println!("⚠ Status check failed: {}", e), } } Err(e) => { println!("⚠ start_and_confirm failed: {}", e); } } // Test start_existing_and_confirm println!("Testing start_existing_and_confirm..."); let start_existing_result = manager.start_existing_and_confirm(service_name, 5); match start_existing_result { Ok(_) => println!("✓ start_existing_and_confirm succeeded"), Err(e) => println!("⚠ start_existing_and_confirm failed: {}", e), } // Cleanup cleanup_test_service(&manager, service_name).await; } else { println!("⚠ Skipping test_service_start_and_confirm: No Zinit socket available"); } } #[tokio::test] async fn test_service_restart() { if let Some(socket_path) = get_available_socket_path().await { let manager = ZinitServiceManager::new(&socket_path).expect("Failed to create manager"); let service_name = "test-restart-service"; // Clean up any existing service first cleanup_test_service(&manager, service_name).await; let config = ServiceConfig { name: service_name.to_string(), binary_path: "echo".to_string(), args: vec!["Restart test".to_string()], working_directory: Some("/tmp".to_string()), environment: HashMap::new(), auto_restart: true, // Enable auto-restart for this test }; // Start the service first let start_result = manager.start(&config); if start_result.is_ok() { // Wait for service to be established sleep(Duration::from_millis(1000)).await; // Test restart println!("Testing service restart..."); let restart_result = manager.restart(service_name); match restart_result { Ok(_) => { println!("✓ Service restarted successfully"); // Wait and check status sleep(Duration::from_millis(500)).await; let status_result = manager.status(service_name); match status_result { Ok(status) => { println!("✓ Service state after restart: {:?}", status); } Err(e) => println!("⚠ Status check after restart failed: {}", e), } } Err(e) => { println!("⚠ Restart failed: {}", e); } } } // Cleanup cleanup_test_service(&manager, service_name).await; } else { println!("⚠ Skipping test_service_restart: No Zinit socket available"); } } #[tokio::test] async fn test_error_handling() { if let Some(socket_path) = get_available_socket_path().await { let manager = ZinitServiceManager::new(&socket_path).expect("Failed to create manager"); // Test operations on non-existent service let non_existent_service = "non-existent-service-12345"; // Test status of non-existent service let status_result = manager.status(non_existent_service); match status_result { Err(ServiceManagerError::ServiceNotFound(_)) => { println!("✓ Correctly returned ServiceNotFound for non-existent service"); } Err(other_error) => { println!( "⚠ Got different error for non-existent service: {}", other_error ); } Ok(_) => { println!("⚠ Unexpectedly found non-existent service"); } } // Test exists for non-existent service let exists_result = manager.exists(non_existent_service); match exists_result { Ok(false) => println!("✓ Correctly reported non-existent service as not existing"), Ok(true) => println!("⚠ Incorrectly reported non-existent service as existing"), Err(e) => println!("⚠ Error checking existence: {}", e), } // Test stop of non-existent service let stop_result = manager.stop(non_existent_service); match stop_result { Err(_) => println!("✓ Correctly failed to stop non-existent service"), Ok(_) => println!("⚠ Unexpectedly succeeded in stopping non-existent service"), } println!("✓ Error handling tests completed"); } else { println!("⚠ Skipping test_error_handling: No Zinit socket available"); } }