// File: /root/code/git.ourworld.tf/herocode/sal/src/virt/nerdctl/container_test.rs #[cfg(test)] mod tests { use super::super::container_types::Container; use std::process::Command; use std::thread; use std::time::Duration; // Helper function to check if nerdctl is available fn is_nerdctl_available() -> bool { match Command::new("which").arg("nerdctl").output() { Ok(output) => output.status.success(), Err(_) => false, } } #[test] fn test_container_builder_pattern() { // Skip test if nerdctl is not available if !is_nerdctl_available() { println!("Skipping test: nerdctl is not available"); return; } // Create a container with builder pattern let container = Container::new("test-container") .unwrap() .with_port("8080:80") .with_volume("/tmp:/data") .with_env("TEST_ENV", "test_value") .with_detach(true); // Verify container properties assert_eq!(container.name, "test-container"); assert_eq!(container.ports.len(), 1); assert_eq!(container.ports[0], "8080:80"); assert_eq!(container.volumes.len(), 1); assert_eq!(container.volumes[0], "/tmp:/data"); assert_eq!(container.env_vars.len(), 1); assert_eq!(container.env_vars.get("TEST_ENV").unwrap(), "test_value"); assert_eq!(container.detach, true); } #[test] fn test_container_from_image() { // Skip test if nerdctl is not available if !is_nerdctl_available() { println!("Skipping test: nerdctl is not available"); return; } // Create a container from image let container = Container::from_image("test-container", "alpine:latest").unwrap(); // Verify container properties assert_eq!(container.name, "test-container"); assert_eq!(container.image.as_ref().unwrap(), "alpine:latest"); } #[test] fn test_container_health_check() { // Skip test if nerdctl is not available if !is_nerdctl_available() { println!("Skipping test: nerdctl is not available"); return; } // Create a container with health check let container = Container::new("test-container") .unwrap() .with_health_check("curl -f http://localhost/ || exit 1"); // Verify health check assert!(container.health_check.is_some()); let health_check = container.health_check.unwrap(); assert_eq!(health_check.cmd, "curl -f http://localhost/ || exit 1"); assert!(health_check.interval.is_none()); assert!(health_check.timeout.is_none()); assert!(health_check.retries.is_none()); assert!(health_check.start_period.is_none()); } #[test] fn test_container_health_check_options() { // Skip test if nerdctl is not available if !is_nerdctl_available() { println!("Skipping test: nerdctl is not available"); return; } // Create a container with health check options let container = Container::new("test-container") .unwrap() .with_health_check_options( "curl -f http://localhost/ || exit 1", Some("30s"), Some("10s"), Some(3), Some("5s"), ); // Verify health check options assert!(container.health_check.is_some()); let health_check = container.health_check.unwrap(); assert_eq!(health_check.cmd, "curl -f http://localhost/ || exit 1"); assert_eq!(health_check.interval.as_ref().unwrap(), "30s"); assert_eq!(health_check.timeout.as_ref().unwrap(), "10s"); assert_eq!(health_check.retries.unwrap(), 3); assert_eq!(health_check.start_period.as_ref().unwrap(), "5s"); } #[test] #[ignore] // Ignore by default as it requires nerdctl to be installed and running fn test_container_runtime_and_resources() { // Check if nerdctl is available and properly configured let nerdctl_check = super::super::execute_nerdctl_command(&["info"]); if nerdctl_check.is_err() { println!("Skipping test: nerdctl is not available or properly configured"); println!("Error: {:?}", nerdctl_check.err()); return; } // Create a unique container name for this test let container_name = format!( "test-runtime-{}", std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_secs() ); // Create and build a container that will use resources // Use a simple container with a basic command to avoid dependency on external images let container_result = Container::from_image(&container_name, "busybox:latest") .unwrap() .with_detach(true) .build(); // Check if the build was successful if container_result.is_err() { println!("Failed to build container: {:?}", container_result.err()); return; } let container = container_result.unwrap(); println!("Container created successfully: {}", container_name); // Start the container with a simple command let start_result = container.exec("sh -c 'for i in $(seq 1 10); do echo $i; sleep 1; done'"); if start_result.is_err() { println!("Failed to start container: {:?}", start_result.err()); // Try to clean up let _ = container.remove(); return; } println!("Container started successfully"); // Wait for the container to start and consume resources thread::sleep(Duration::from_secs(3)); // Check container status let status_result = container.status(); if status_result.is_err() { println!("Failed to get container status: {:?}", status_result.err()); // Try to clean up let _ = container.stop(); let _ = container.remove(); return; } let status = status_result.unwrap(); println!("Container status: {:?}", status); // Verify the container is running if status.status != "running" { println!("Container is not running, status: {}", status.status); // Try to clean up let _ = container.remove(); return; } // Check resource usage let resources_result = container.resources(); if resources_result.is_err() { println!("Failed to get resource usage: {:?}", resources_result.err()); // Try to clean up let _ = container.stop(); let _ = container.remove(); return; } let resources = resources_result.unwrap(); println!("Container resources: {:?}", resources); // Verify the container is using memory (if we can get the information) if resources.memory_usage == "0B" || resources.memory_usage == "unknown" { println!( "Warning: Container memory usage is {}", resources.memory_usage ); } else { println!("Container is using memory: {}", resources.memory_usage); } // Clean up - stop and remove the container println!("Stopping container..."); let stop_result = container.stop(); if stop_result.is_err() { println!("Warning: Failed to stop container: {:?}", stop_result.err()); } println!("Removing container..."); let remove_result = container.remove(); if remove_result.is_err() { println!( "Warning: Failed to remove container: {:?}", remove_result.err() ); } println!("Test completed successfully"); } #[test] fn test_container_with_custom_command() { // Skip test if nerdctl is not available if !is_nerdctl_available() { println!("Skipping test: nerdctl is not available"); return; } // Create a container with a custom command let container = Container::new("test-command-container") .unwrap() .with_port("8080:80") .with_volume("/tmp:/data") .with_env("TEST_ENV", "test_value") .with_detach(true); // Verify container properties assert_eq!(container.name, "test-command-container"); assert_eq!(container.ports.len(), 1); assert_eq!(container.ports[0], "8080:80"); assert_eq!(container.volumes.len(), 1); assert_eq!(container.volumes[0], "/tmp:/data"); assert_eq!(container.env_vars.len(), 1); assert_eq!(container.env_vars.get("TEST_ENV").unwrap(), "test_value"); assert_eq!(container.detach, true); // Convert the container to a command string that would be used to run it let command_args = container_to_command_args(&container); // Verify the command arguments contain all the expected options assert!(command_args.contains(&"--name".to_string())); assert!(command_args.contains(&"test-command-container".to_string())); assert!(command_args.contains(&"-p".to_string())); assert!(command_args.contains(&"8080:80".to_string())); assert!(command_args.contains(&"-v".to_string())); assert!(command_args.contains(&"/tmp:/data".to_string())); assert!(command_args.contains(&"-e".to_string())); assert!(command_args.contains(&"TEST_ENV=test_value".to_string())); assert!(command_args.contains(&"-d".to_string())); println!("Command args: {:?}", command_args); } // Helper function to convert a container to command arguments fn container_to_command_args(container: &Container) -> Vec { let mut args = Vec::new(); args.push("run".to_string()); if container.detach { args.push("-d".to_string()); } args.push("--name".to_string()); args.push(container.name.clone()); // Add port mappings for port in &container.ports { args.push("-p".to_string()); args.push(port.clone()); } // Add volume mounts for volume in &container.volumes { args.push("-v".to_string()); args.push(volume.clone()); } // Add environment variables for (key, value) in &container.env_vars { args.push("-e".to_string()); args.push(format!("{}={}", key, value)); } // Add image if available if let Some(image) = &container.image { args.push(image.clone()); } args } }