wrkflw-executor 0.7.3

Workflow execution engine for wrkflw
Documentation
use bollard::Docker;
use std::{sync::Arc, path::Path};
use tokio::sync::Mutex;

use crate::{
    executor::{docker::{self, DockerRuntime}, RuntimeType},
    runtime::container::{ContainerRuntime, ContainerOutput}
};

#[cfg(test)]
mod docker_cleanup_tests {
    use super::*;

    // Helper function to check if Docker tests should be skipped
    fn should_skip_docker_tests() -> bool {
        std::env::var("WRKFLW_TEST_SKIP_DOCKER").is_ok() || 
        !docker::is_available()
    }

    /// Helper function to create a Docker container that should be tracked
    async fn create_test_container(docker_client: &Docker) -> Option<String> {
        if should_skip_docker_tests() {
            return None;
        }

        // Try to create a container runtime
        let runtime = match DockerRuntime::new() {
            Ok(rt) => rt,
            Err(_) => return None,
        };

        // Run a simple container that finishes quickly
        let result = runtime
            .run_container(
                "alpine:latest",
                &["echo", "test"],
                &[],
                Path::new("/"),
                &[],
            )
            .await;
        
        // The container should be automatically removed by the runtime after execution
        // but we can verify if it's tracked first
        
        let running_containers = docker::get_tracked_containers();
        
        // Since run_container internally cleans up, we'll simulate tracking a container
        if let Some(container_id) = running_containers.first() {
            return Some(container_id.clone());
        }
        
        // Manually track a container for testing
        let container_id = format!("test-container-{}", uuid::Uuid::new_v4());
        docker::track_container(&container_id);
        
        Some(container_id)
    }

    /// Helper function to create a Docker network that should be tracked
    async fn create_test_network(docker_client: &Docker) -> Option<String> {
        if should_skip_docker_tests() {
            return None;
        }

        // Create a test network
        match docker::create_job_network(docker_client).await {
            Ok(network_id) => Some(network_id),
            Err(_) => None,
        }
    }

    #[tokio::test]
    async fn test_docker_container_cleanup() {
        if should_skip_docker_tests() {
            println!("Docker tests disabled or Docker not available, skipping test");
            return;
        }

        // Connect to Docker
        let docker = match Docker::connect_with_local_defaults() {
            Ok(client) => client,
            Err(_) => {
                println!("Could not connect to Docker, skipping test");
                return;
            }
        };

        // Create a test container
        let container_id = match create_test_container(&docker).await {
            Some(id) => id,
            None => {
                println!("Could not create test container, skipping test");
                return;
            }
        };
        
        // Verify container is tracked
        let containers = docker::get_tracked_containers();
        let is_tracked = containers.contains(&container_id);
        
        assert!(is_tracked, "Container should be tracked for cleanup");

        // Run cleanup
        docker::cleanup_containers(&docker).await;

        // Verify container is removed from tracking
        let containers = docker::get_tracked_containers();
        let still_tracked = containers.contains(&container_id);
        
        assert!(!still_tracked, "Container should be removed from tracking after cleanup");
    }

    #[tokio::test]
    async fn test_docker_network_cleanup() {
        if should_skip_docker_tests() {
            println!("Docker tests disabled or Docker not available, skipping test");
            return;
        }

        // Connect to Docker
        let docker = match Docker::connect_with_local_defaults() {
            Ok(client) => client,
            Err(_) => {
                println!("Could not connect to Docker, skipping test");
                return;
            }
        };

        // Create a test network
        let network_id = match create_test_network(&docker).await {
            Some(id) => id,
            None => {
                println!("Could not create test network, skipping test");
                return;
            }
        };
        
        // Verify network is tracked
        let networks = docker::get_tracked_networks();
        let is_tracked = networks.contains(&network_id);
        
        assert!(is_tracked, "Network should be tracked for cleanup");

        // Run cleanup
        docker::cleanup_networks(&docker).await;

        // Verify network is removed from tracking
        let networks = docker::get_tracked_networks();
        let still_tracked = networks.contains(&network_id);
        
        assert!(!still_tracked, "Network should be removed from tracking after cleanup");
    }

    #[tokio::test]
    async fn test_full_resource_cleanup() {
        if should_skip_docker_tests() {
            println!("Docker tests disabled or Docker not available, skipping test");
            return;
        }

        // Connect to Docker
        let docker = match Docker::connect_with_local_defaults() {
            Ok(client) => client,
            Err(_) => {
                println!("Could not connect to Docker, skipping test");
                return;
            }
        };

        // Create a test container
        let _ = create_test_container(&docker).await;
        
        // Create a test network
        let _ = create_test_network(&docker).await;
        
        // Count resources before cleanup
        let container_count = docker::get_tracked_containers().len();
        let network_count = docker::get_tracked_networks().len();
        
        // Ensure we have at least one resource to clean up
        if container_count == 0 && network_count == 0 {
            println!("No resources created for testing, skipping test");
            return;
        }

        // Run full cleanup
        docker::cleanup_resources(&docker).await;

        // Verify all resources are cleaned up
        let remaining_containers = docker::get_tracked_containers().len();
        let remaining_networks = docker::get_tracked_networks().len();
        
        assert_eq!(remaining_containers, 0, "All containers should be cleaned up");
        assert_eq!(remaining_networks, 0, "All networks should be cleaned up");
    }
}