sal-virt 0.1.0

SAL Virt - Virtualization and containerization tools including Buildah, Nerdctl, and RFS
Documentation
#[cfg(test)]
mod tests {
    use crate::buildah::BuildahError;
    use lazy_static::lazy_static;
    use sal_process::CommandResult;
    use std::sync::Mutex;

    // Create a test-specific implementation of the containers module functions
    // that we can use to verify the correct arguments are passed

    lazy_static! {
        static ref LAST_COMMAND: Mutex<Vec<String>> = Mutex::new(Vec::new());
        static ref SHOULD_FAIL: Mutex<bool> = Mutex::new(false);
        static ref TEST_MUTEX: Mutex<()> = Mutex::new(()); // Add a mutex for test synchronization
    }

    fn reset_test_state() {
        let mut cmd = LAST_COMMAND.lock().unwrap();
        cmd.clear();
        let mut fail = SHOULD_FAIL.lock().unwrap();
        *fail = false;
    }

    fn set_should_fail(fail: bool) {
        let mut should_fail = SHOULD_FAIL.lock().unwrap();
        *should_fail = fail;
    }

    fn get_last_command() -> Vec<String> {
        let cmd = LAST_COMMAND.lock().unwrap();
        cmd.clone()
    }

    // Test-specific implementation of execute_buildah_command
    fn test_execute_buildah_command(args: &[&str]) -> Result<CommandResult, BuildahError> {
        // Record the command
        {
            let mut cmd = LAST_COMMAND.lock().unwrap();
            cmd.clear();
            for arg in args {
                cmd.push(arg.to_string());
            }
        }

        // Check if we should fail
        let should_fail = {
            let fail = SHOULD_FAIL.lock().unwrap();
            *fail
        };

        if should_fail {
            Err(BuildahError::CommandFailed("Command failed".to_string()))
        } else {
            Ok(CommandResult {
                stdout: "mock stdout".to_string(),
                stderr: "".to_string(),
                success: true,
                code: 0,
            })
        }
    }

    // Test implementations of the container functions
    fn test_from(image: &str) -> Result<CommandResult, BuildahError> {
        test_execute_buildah_command(&["from", image])
    }

    fn test_run(container: &str, command: &str) -> Result<CommandResult, BuildahError> {
        test_execute_buildah_command(&["run", container, "sh", "-c", command])
    }

    fn test_bah_run_with_isolation(
        container: &str,
        command: &str,
        isolation: &str,
    ) -> Result<CommandResult, BuildahError> {
        test_execute_buildah_command(&[
            "run",
            "--isolation",
            isolation,
            container,
            "sh",
            "-c",
            command,
        ])
    }

    fn test_bah_copy(
        container: &str,
        source: &str,
        dest: &str,
    ) -> Result<CommandResult, BuildahError> {
        test_execute_buildah_command(&["copy", container, source, dest])
    }

    fn test_bah_add(
        container: &str,
        source: &str,
        dest: &str,
    ) -> Result<CommandResult, BuildahError> {
        test_execute_buildah_command(&["add", container, source, dest])
    }

    fn test_bah_commit(container: &str, image_name: &str) -> Result<CommandResult, BuildahError> {
        test_execute_buildah_command(&["commit", container, image_name])
    }

    fn test_bah_remove(container: &str) -> Result<CommandResult, BuildahError> {
        test_execute_buildah_command(&["rm", container])
    }

    fn test_bah_list() -> Result<CommandResult, BuildahError> {
        test_execute_buildah_command(&["containers"])
    }

    fn test_bah_build(
        tag: Option<&str>,
        context_dir: &str,
        file: &str,
        isolation: Option<&str>,
    ) -> Result<CommandResult, BuildahError> {
        let mut args = Vec::new();
        args.push("build");

        if let Some(tag_value) = tag {
            args.push("-t");
            args.push(tag_value);
        }

        if let Some(isolation_value) = isolation {
            args.push("--isolation");
            args.push(isolation_value);
        }

        args.push("-f");
        args.push(file);

        args.push(context_dir);

        test_execute_buildah_command(&args)
    }

    // Tests for each function
    #[test]
    fn test_from_function() {
        let _lock = TEST_MUTEX.lock().unwrap(); // Acquire lock for test
        reset_test_state();

        let image = "alpine:latest";
        let result = test_from(image);

        assert!(result.is_ok());
        let cmd = get_last_command();
        assert_eq!(cmd, vec!["from", "alpine:latest"]);
    }

    #[test]
    fn test_run_function() {
        let _lock = TEST_MUTEX.lock().unwrap(); // Acquire lock for test
        reset_test_state();

        let container = "my-container";
        let command = "echo hello";

        // Test without isolation
        let result = test_run(container, command);
        assert!(result.is_ok());
        let cmd = get_last_command();
        assert_eq!(cmd, vec!["run", "my-container", "sh", "-c", "echo hello"]);
    }

    #[test]
    fn test_bah_run_with_isolation_function() {
        let _lock = TEST_MUTEX.lock().unwrap(); // Acquire lock for test
        reset_test_state();

        let container = "my-container";
        let command = "echo hello";
        let isolation = "chroot";

        let result = test_bah_run_with_isolation(container, command, isolation);
        assert!(result.is_ok());
        let cmd = get_last_command();
        assert_eq!(
            cmd,
            vec![
                "run",
                "--isolation",
                "chroot",
                "my-container",
                "sh",
                "-c",
                "echo hello"
            ]
        );
    }

    #[test]
    fn test_bah_copy_function() {
        let _lock = TEST_MUTEX.lock().unwrap(); // Acquire lock for test
        reset_test_state();

        let container = "my-container";
        let source = "/local/path";
        let dest = "/container/path";
        let result = test_bah_copy(container, source, dest);

        assert!(result.is_ok());
        let cmd = get_last_command();
        assert_eq!(
            cmd,
            vec!["copy", "my-container", "/local/path", "/container/path"]
        );
    }

    #[test]
    fn test_bah_add_function() {
        let _lock = TEST_MUTEX.lock().unwrap(); // Acquire lock for test
        reset_test_state();

        let container = "my-container";
        let source = "/local/path";
        let dest = "/container/path";
        let result = test_bah_add(container, source, dest);

        assert!(result.is_ok());
        let cmd = get_last_command();
        assert_eq!(
            cmd,
            vec!["add", "my-container", "/local/path", "/container/path"]
        );
    }

    #[test]
    fn test_bah_commit_function() {
        let _lock = TEST_MUTEX.lock().unwrap(); // Acquire lock for test
        reset_test_state();

        let container = "my-container";
        let image_name = "my-image:latest";
        let result = test_bah_commit(container, image_name);

        assert!(result.is_ok());
        let cmd = get_last_command();
        assert_eq!(cmd, vec!["commit", "my-container", "my-image:latest"]);
    }

    #[test]
    fn test_bah_remove_function() {
        let _lock = TEST_MUTEX.lock().unwrap(); // Acquire lock for test
        reset_test_state();

        let container = "my-container";
        let result = test_bah_remove(container);

        assert!(result.is_ok());
        let cmd = get_last_command();
        assert_eq!(cmd, vec!["rm", "my-container"]);
    }

    #[test]
    fn test_bah_list_function() {
        let _lock = TEST_MUTEX.lock().unwrap(); // Acquire lock for test
        reset_test_state();

        let result = test_bah_list();

        assert!(result.is_ok());
        let cmd = get_last_command();
        assert_eq!(cmd, vec!["containers"]);
    }

    #[test]
    fn test_bah_build_function() {
        let _lock = TEST_MUTEX.lock().unwrap(); // Acquire lock for test
        reset_test_state();

        // Test with tag, context directory, file, and no isolation
        let result = test_bah_build(Some("my-app:latest"), ".", "Dockerfile", None);
        assert!(result.is_ok());
        let cmd = get_last_command();
        assert_eq!(
            cmd,
            vec!["build", "-t", "my-app:latest", "-f", "Dockerfile", "."]
        );

        reset_test_state(); // Reset state between sub-tests

        // Test with tag, context directory, file, and isolation
        let result = test_bah_build(
            Some("my-app:latest"),
            ".",
            "Dockerfile.custom",
            Some("chroot"),
        );
        assert!(result.is_ok());
        let cmd = get_last_command();
        assert_eq!(
            cmd,
            vec![
                "build",
                "-t",
                "my-app:latest",
                "--isolation",
                "chroot",
                "-f",
                "Dockerfile.custom",
                "."
            ]
        );

        reset_test_state(); // Reset state between sub-tests

        // Test with just context directory and file
        let result = test_bah_build(None, ".", "Dockerfile", None);
        assert!(result.is_ok());
        let cmd = get_last_command();
        assert_eq!(cmd, vec!["build", "-f", "Dockerfile", "."]);
    }

    #[test]
    fn test_error_handling() {
        let _lock = TEST_MUTEX.lock().unwrap(); // Acquire lock for test
        reset_test_state();
        set_should_fail(true);

        let image = "alpine:latest";
        let result = test_from(image);

        assert!(result.is_err());
        match result {
            Err(BuildahError::CommandFailed(msg)) => {
                assert_eq!(msg, "Command failed");
            }
            _ => panic!("Expected CommandFailed error"),
        }
    }
}