Files
cleveragents-core/features/steps/cli_coverage_steps.py
T
2025-11-05 15:11:03 -05:00

205 lines
6.9 KiB
Python

"""Step definitions for CLI coverage tests."""
import subprocess
from unittest.mock import patch
from behave import then, when
from click.exceptions import Exit as ClickExit
from typer import Exit
from cleveragents.cli.main import app, convert_exit_code, main
@when('I run "{command}"')
def step_run_command(context, command):
"""Run a CLI command using subprocess."""
try:
result = subprocess.run(
command.split(), capture_output=True, text=True, timeout=5
)
context.exit_code = result.returncode
context.output = result.stdout + result.stderr
except subprocess.TimeoutExpired:
context.exit_code = -1
context.output = "Command timed out"
except Exception as e:
context.exit_code = -1
context.output = str(e)
@then("the command should succeed")
def step_command_succeeds(context):
"""Check that command succeeded."""
assert context.exit_code == 0, f"Expected exit code 0, got {context.exit_code}"
@then("the command should fail with exit code {code:d}")
def step_command_fails_with_code(context, code):
"""Check that command failed with specific code."""
assert context.exit_code == code, (
f"Expected exit code {code}, got {context.exit_code}"
)
@then('the output should contain "{text}"')
def step_output_contains(context, text):
"""Check that output contains text."""
assert text in context.output, f"'{text}' not found in output: {context.output}"
@when("I call the main function directly with no args")
def step_call_main_no_args(context):
"""Call main function with no arguments."""
with patch("sys.argv", ["cleveragents"]):
context.exit_code = main()
@when('I call the main function directly with "{args}"')
def step_call_main_with_args(context, args):
"""Call main function with specific arguments."""
argv = ["cleveragents", *args.split()]
with patch("sys.argv", argv):
context.exit_code = main()
@then("it should return {code:d}")
def step_check_return_code(context, code):
"""Check the return code."""
assert context.exit_code == code, f"Expected {code}, got {context.exit_code}"
@when("I run a command that gets interrupted")
def step_run_interrupted_command(context):
"""Simulate a command that gets interrupted."""
with patch("cleveragents.cli.main.app") as mock_app:
mock_app.side_effect = KeyboardInterrupt()
context.exit_code = main(["info"])
@then("it should handle the KeyboardInterrupt gracefully")
def step_check_keyboard_interrupt_handled(context):
"""Check that KeyboardInterrupt was handled."""
assert context.exit_code == 130, (
f"Expected 130 for KeyboardInterrupt, got {context.exit_code}"
)
@when("I run a command that calls sys.exit(1)")
def step_run_sys_exit_command(context):
"""Simulate a command that calls sys.exit."""
with patch("cleveragents.cli.main.app") as mock_app:
mock_app.side_effect = SystemExit(1)
context.exit_code = main(["info"])
@then("it should propagate the exit code correctly")
def step_check_exit_code_propagated(context):
"""Check that exit code was propagated."""
assert context.exit_code == 1, f"Expected 1, got {context.exit_code}"
# Additional steps to increase coverage
@when("I test convert_exit_code with various inputs")
def step_test_convert_exit_code(context):
"""Test the convert_exit_code function."""
context.results = []
# Test with int
context.results.append(convert_exit_code(0))
context.results.append(convert_exit_code(1))
context.results.append(convert_exit_code(-1))
# Test with None
context.results.append(convert_exit_code(None))
# Test with Exit exception
context.results.append(convert_exit_code(Exit(2)))
# Test with ClickExit exception
context.results.append(convert_exit_code(ClickExit(3)))
# Test with other object
context.results.append(convert_exit_code("string"))
@then("convert_exit_code should handle all types correctly")
def step_check_convert_exit_code(context):
"""Check convert_exit_code results."""
expected = [0, 1, -1, 0, 2, 3, 1] # string returns 1, not 0
assert context.results == expected, f"Expected {expected}, got {context.results}"
@when("I test the CLI app commands")
def step_test_cli_commands(context):
"""Test CLI app commands directly."""
from typer.testing import CliRunner
runner = CliRunner()
context.results = []
# Test version
result = runner.invoke(app, ["--version"])
context.results.append((result.exit_code, "1.0.0" in result.stdout))
# Test help
result = runner.invoke(app, ["--help"])
context.results.append((result.exit_code, "Usage" in result.stdout))
# Test info
result = runner.invoke(app, ["info"])
context.results.append(
(result.exit_code, "CleverAgents Information" in result.stdout)
)
# Test diagnostics
result = runner.invoke(app, ["diagnostics"])
context.results.append(
(result.exit_code, "CleverAgents Diagnostics" in result.stdout)
)
# Test init (not implemented) - use no args since init doesn't take positional args
result = runner.invoke(app, ["init"])
context.results.append((result.exit_code, "not yet implemented" in result.stdout))
@then("all CLI commands should work correctly")
def step_check_cli_commands(context):
"""Check CLI command results."""
for idx, (exit_code, output_check) in enumerate(context.results):
# The init command is expected to return 1 (not implemented)
if idx == 4: # The init command is the 5th one (0-indexed)
assert exit_code == 1, f"Init command should return 1, got {exit_code}"
else:
assert exit_code == 0, f"Command {idx} failed with exit code {exit_code}"
assert output_check, f"Command {idx} output check failed"
@when("I test exception handling in main")
def step_test_exception_handling(context):
"""Test exception handling in main function."""
context.results = []
# Test CleverAgentsError
from cleveragents.core.exceptions import CleverAgentsError
with patch("cleveragents.cli.main.app") as mock_app:
mock_app.side_effect = CleverAgentsError("Test error")
result = main(["info"])
context.results.append(result)
# Test generic Exception
with patch("cleveragents.cli.main.app") as mock_app:
mock_app.side_effect = Exception("Generic error")
result = main(["info"])
context.results.append(result)
# Test Exit with code
with patch("cleveragents.cli.main.app") as mock_app:
mock_app.side_effect = Exit(5)
result = main(["info"])
context.results.append(result)
@then("exception handling should return correct codes")
def step_check_exception_handling(context):
"""Check exception handling results."""
expected = [1, 1, 5]
assert context.results == expected, f"Expected {expected}, got {context.results}"