vx-cli 0.4.0

CLI interface for vx tool manager
Documentation
//! Tests for the execute command module

use super::execute::*;
use crate::test_utils::*;

#[tokio::test]
async fn test_execute_tool_success() {
    let env = TestEnvironment::new();

    // Test successful tool execution
    let result = execute_tool(&env.registry, "echo", &["hello".to_string()], false).await;

    // Note: This test depends on system having 'echo' command
    // In a real implementation, we'd mock the command execution
    match result {
        Ok(exit_code) => assert_eq!(exit_code, 0),
        Err(_) => {
            // If echo is not available, that's also a valid test result
            // showing our error handling works
        }
    }
}

#[tokio::test]
async fn test_execute_tool_not_found() {
    let env = TestEnvironment::new();

    // Test tool not found
    let result = execute_tool(&env.registry, "nonexistent-tool-12345", &[], false).await;

    assert!(result.is_err());
    let error_msg = result.unwrap_err().to_string();
    assert!(error_msg.contains("Tool not found"));
}

#[tokio::test]
async fn test_execute_tool_with_args() {
    let env = TestEnvironment::new();

    // Test tool execution with arguments
    let args = vec!["--version".to_string()];
    let result = execute_tool(&env.registry, "echo", &args, false).await;

    // This should work if echo is available
    match result {
        Ok(exit_code) => assert_eq!(exit_code, 0),
        Err(_) => {
            // Error is acceptable if tool is not found
        }
    }
}

#[tokio::test]
async fn test_handle_execute_success() {
    let env = TestEnvironment::new();

    // Test the handle function with a simple command
    let result = handle(&env.registry, "echo", &["test".to_string()], false).await;

    // The handle function should not panic and should handle errors gracefully
    match result {
        Ok(_) => {
            // Success case
        }
        Err(_) => {
            // Error case is also acceptable for this test
        }
    }
}

#[tokio::test]
async fn test_execute_with_system_path_flag() {
    let env = TestEnvironment::new();

    // Test execution with use_system_path flag
    let result = execute_tool(&env.registry, "echo", &["test".to_string()], true).await;

    match result {
        Ok(exit_code) => assert_eq!(exit_code, 0),
        Err(_) => {
            // Error is acceptable if tool is not found
        }
    }
}

#[test]
fn test_execute_system_tool_mock() {
    // This test demonstrates how we would test with mocked commands
    // In a real implementation, we'd inject a command executor

    let tool_name = "test-tool";
    let args = vec!["--version".to_string()];

    // Create mock executor
    let mut mock_executor = MockCommandExecutor::new()
        .expect_command(tool_name, vec!["--version"])
        .with_response(mock_success_output("test-tool 1.0.0\n"));

    // Execute mock command
    let result = mock_executor.execute(tool_name, &args);

    assert!(result.is_ok());
    let output = result.unwrap();
    assert_eq!(output.status.code(), Some(0));
    assert_eq!(String::from_utf8_lossy(&output.stdout), "test-tool 1.0.0\n");
}

#[test]
fn test_execute_system_tool_error_mock() {
    let tool_name = "nonexistent-tool";
    let args = vec![];

    // Create mock executor that returns an error
    let mut mock_executor = MockCommandExecutor::new()
        .expect_command(tool_name, vec![])
        .with_error(std::io::Error::new(
            std::io::ErrorKind::NotFound,
            "command not found",
        ));

    // Execute mock command
    let result = mock_executor.execute(tool_name, &args);

    assert!(result.is_err());
    let error = result.unwrap_err();
    assert_eq!(error.kind(), std::io::ErrorKind::NotFound);
}

#[test]
fn test_mock_command_executor() {
    let mut executor = MockCommandExecutor::new()
        .expect_command("node", vec!["--version"])
        .with_response(mock_success_output("v18.0.0\n"))
        .expect_command("npm", vec!["--version"])
        .with_response(mock_success_output("8.0.0\n"));

    // First command
    let result1 = executor.execute("node", &["--version".to_string()]);
    assert!(result1.is_ok());
    assert_eq!(
        String::from_utf8_lossy(&result1.unwrap().stdout),
        "v18.0.0\n"
    );

    // Second command
    let result2 = executor.execute("npm", &["--version".to_string()]);
    assert!(result2.is_ok());
    assert_eq!(String::from_utf8_lossy(&result2.unwrap().stdout), "8.0.0\n");
}

#[test]
#[should_panic(expected = "Command mismatch")]
fn test_mock_command_executor_unexpected_call() {
    let mut executor = MockCommandExecutor::new()
        .expect_command("node", vec!["--version"])
        .with_response(mock_success_output("v18.0.0\n"));

    // This should panic because we didn't expect this command
    let _ = executor.execute("npm", &["--version".to_string()]);
}

#[test]
#[should_panic(expected = "Command mismatch")]
fn test_mock_command_executor_wrong_command() {
    let mut executor = MockCommandExecutor::new()
        .expect_command("node", vec!["--version"])
        .with_response(mock_success_output("v18.0.0\n"));

    // This should panic because the command doesn't match
    let _ = executor.execute("npm", &["--version".to_string()]);
}

#[test]
#[should_panic(expected = "Arguments mismatch")]
fn test_mock_command_executor_wrong_args() {
    let mut executor = MockCommandExecutor::new()
        .expect_command("node", vec!["--version"])
        .with_response(mock_success_output("v18.0.0\n"));

    // This should panic because the arguments don't match
    let _ = executor.execute("node", &["--help".to_string()]);
}