"""Tests for test execution utilities."""
from unittest.mock import patch
import pytest
from ra_aid.config import DEFAULT_TEST_CMD_TIMEOUT
from ra_aid.tools.handle_user_defined_test_cmd_execution import execute_test_command
# Test cases for execute_test_command
test_cases = [
# Format: (name, config, original_prompt, test_attempts, auto_test,
# mock_responses, expected_result)
# Case 1: No test command configured
(
"no_test_command",
{"other_config": "value"},
"original prompt",
0,
False,
{},
(True, "original prompt", False, 0),
),
# Case 2: User declines to run test
(
"user_declines_test",
{"test_cmd": "pytest"},
"original prompt",
0,
False,
{"ask_human_response": "n"},
(True, "original prompt", False, 0),
),
# Case 3: User enables auto-test
(
"user_enables_auto_test",
{"test_cmd": "pytest"},
"original prompt",
0,
False,
{
"ask_human_response": "a",
"shell_cmd_result": {"success": True, "output": "All tests passed"},
},
(True, "original prompt", True, 1),
),
# Case 4: Auto-test success
(
"auto_test_success",
{"test_cmd": "pytest"},
"original prompt",
0,
True,
{"shell_cmd_result": {"success": True, "output": "All tests passed"}},
(True, "original prompt", True, 1),
),
# Case 5: Auto-test failure with retry
(
"auto_test_failure_retry",
{"test_cmd": "pytest"},
"original prompt",
0,
True,
{"shell_cmd_result": {"success": False, "output": "Test failed"}},
(
False,
"original prompt. Previous attempt failed with: Test failed",
True,
1,
),
),
# Case 6: Max retries reached
(
"max_retries_reached",
{"test_cmd": "pytest", "max_test_cmd_retries": 3},
"original prompt",
3,
True,
{},
(True, "original prompt", True, 3),
),
# Case 7: User runs test manually
(
"manual_test_success",
{"test_cmd": "pytest"},
"original prompt",
0,
False,
{
"ask_human_response": "y",
"shell_cmd_result": {"success": True, "output": "All tests passed"},
},
(True, "original prompt", False, 1),
),
# Case 8: Manual test failure
(
"manual_test_failure",
{"test_cmd": "pytest"},
"original prompt",
0,
False,
{
"ask_human_response": "y",
"shell_cmd_result": {"success": False, "output": "Test failed"},
},
(
False,
"original prompt. Previous attempt failed with: Test failed",
False,
1,
),
),
# Case 9: Manual test error
(
"manual_test_error",
{"test_cmd": "pytest"},
"original prompt",
0,
False,
{
"ask_human_response": "y",
"shell_cmd_result_error": Exception("Command failed"),
},
(True, "original prompt", False, 1),
),
# Case 10: Auto-test error
(
"auto_test_error",
{"test_cmd": "pytest"},
"original prompt",
0,
True,
{"shell_cmd_result_error": Exception("Command failed")},
(True, "original prompt", True, 1),
),
]
@pytest.mark.parametrize(
"name,config,original_prompt,test_attempts,auto_test,mock_responses,expected",
test_cases,
ids=[case[0] for case in test_cases],
)
def test_execute_test_command(
name: str,
config: dict,
original_prompt: str,
test_attempts: int,
auto_test: bool,
mock_responses: dict,
expected: tuple,
) -> None:
"""Test execute_test_command with different scenarios.
Args:
name: Test case name
config: Test configuration
original_prompt: Original prompt text
test_attempts: Number of test attempts
auto_test: Auto-test flag
mock_responses: Mock response data
expected: Expected result tuple
"""
with (
patch(
"ra_aid.tools.handle_user_defined_test_cmd_execution.ask_human"
) as mock_ask_human,
patch(
"ra_aid.tools.handle_user_defined_test_cmd_execution.run_shell_command"
) as mock_run_cmd,
patch(
"ra_aid.tools.handle_user_defined_test_cmd_execution.console"
) as _mock_console,
patch(
"ra_aid.tools.handle_user_defined_test_cmd_execution.logger"
) as mock_logger,
):
# Configure mocks based on mock_responses
if "ask_human_response" in mock_responses:
mock_ask_human.invoke.return_value = mock_responses["ask_human_response"]
if "shell_cmd_result_error" in mock_responses:
mock_run_cmd.side_effect = mock_responses["shell_cmd_result_error"]
elif "shell_cmd_result" in mock_responses:
mock_run_cmd.return_value = mock_responses["shell_cmd_result"]
# Execute test command
result = execute_test_command(config, original_prompt, test_attempts, auto_test)
# Verify result matches expected
assert result == expected, f"Test case '{name}' failed"
# Verify mock interactions
if config.get("test_cmd") and not auto_test:
mock_ask_human.invoke.assert_called_once()
if auto_test and test_attempts < config.get("max_test_cmd_retries", 5):
if config.get("test_cmd"):
# Verify run_shell_command called with command and configured timeout
mock_run_cmd.assert_called_once_with(
config["test_cmd"],
timeout=config.get("test_cmd_timeout", DEFAULT_TEST_CMD_TIMEOUT),
)
# Verify logging for max retries
if test_attempts >= config.get("max_test_cmd_retries", 5):
mock_logger.warning.assert_called_once_with("Max test retries reached")
def test_execute_test_command_error_handling() -> None:
"""Test error handling in execute_test_command."""
config = {"test_cmd": "pytest"}
with (
patch(
"ra_aid.tools.handle_user_defined_test_cmd_execution.run_shell_command"
) as mock_run_cmd,
patch(
"ra_aid.tools.handle_user_defined_test_cmd_execution.logger"
) as mock_logger,
):
# Simulate run_shell_command raising an exception
mock_run_cmd.side_effect = Exception("Command failed")
result = execute_test_command(config, "original prompt", 0, True)
# Should handle error and continue
assert result == (True, "original prompt", True, 1)
mock_logger.warning.assert_called_once()