Files
temp/features/steps/cli_steps.py
T

258 lines
7.8 KiB
Python

"""Step definitions for the CleverAgents CLI feature."""
from __future__ import annotations
import contextlib
import io
import sys
from collections.abc import Sequence
from typing import Any
from unittest.mock import MagicMock, patch
import click
from behave import then, when
from click.testing import CliRunner
from cleveragents.__main__ import run
from cleveragents.cli import _convert_exit_code, cli, main
from cleveragents.platform import ensure_cli_importable
def _run_cli(context: Any, args: Sequence[str]) -> None:
buffer = io.StringIO()
with contextlib.redirect_stdout(buffer):
exit_code = main(list(args))
context.result = {
"exit_code": exit_code,
"output": buffer.getvalue(),
}
@when('I run the CleverAgents CLI with "--help"')
def step_run_help(context: Any) -> None:
_run_cli(context, ["--help"])
@then('the output should contain "CleverAgents"')
def step_output_help(context: Any) -> None:
assert context.result["exit_code"] == 0
assert "CleverAgents" in context.result["output"]
@when('I run the CleverAgents CLI with "--version"')
def step_run_version(context: Any) -> None:
_run_cli(context, ["--version"])
@then('the output should contain "1.0.0"')
def step_output_version(context: Any) -> None:
assert context.result["exit_code"] == 0
assert "1.0.0" in context.result["output"]
# New generic steps for other commands
@when('I run the CleverAgents CLI with "{args}"')
def step_run_cli_with_args(context, args):
"""Execute the CLI with specific arguments."""
runner = CliRunner()
result = runner.invoke(cli, args.split())
context.result = {
"exit_code": result.exit_code,
"output": result.output,
"runner_result": result,
}
@then('the output should contain "{text}"')
def step_output_should_contain(context, text):
"""Verify the CLI output contains expected text."""
assert text in context.result["output"], (
f"Expected '{text}' not found in output: {context.result['output']}"
)
@then("the command should fail")
def step_command_should_fail(context):
"""Verify the command failed."""
assert context.result["exit_code"] != 0
@then('the error should contain "{text}"')
def step_error_should_contain(context, text):
"""Verify error output contains text."""
output = context.result["output"].lower() if "output" in context.result else ""
assert text.lower() in output, f"Expected '{text}' not found in error output"
# Exit code conversion steps
@when("I convert exit code {code}")
def step_convert_exit_code(context, code):
"""Convert an exit code."""
if code == "None":
context.converted_code = _convert_exit_code(None)
else:
context.converted_code = _convert_exit_code(int(code))
@then("the result should be {expected}")
def step_result_should_be(context, expected):
"""Verify the conversion result."""
assert context.converted_code == int(expected)
@when("I convert a Click Exit exception with code {code}")
def step_convert_click_exit(context, code):
"""Convert a Click Exit exception."""
exit_exc = click.exceptions.Exit(int(code))
context.converted_code = _convert_exit_code(exit_exc)
# Main function steps
@when('I call main with arguments "{args}"')
def step_call_main_with_args(context, args):
"""Call main function with arguments."""
with patch("cleveragents.cli.cli.main") as mock_cli:
mock_cli.side_effect = SystemExit(0)
context.main_result = main(args.split())
@then("main should process the version flag")
def step_main_processes_version(context):
"""Verify main processed the version flag."""
assert context.main_result == 0
@when("I call main with no arguments")
def step_call_main_no_args(context):
"""Call main with no arguments."""
with patch("cleveragents.cli.cli.main") as mock_cli:
mock_cli.side_effect = SystemExit(0)
context.main_result = main()
@then("main should display help")
def step_main_displays_help(context):
"""Verify main displays help."""
assert context.main_result == 0
@when("I call main and CLI raises SystemExit {code}")
def step_main_system_exit(context, code):
"""Simulate CLI raising SystemExit."""
with patch("cleveragents.cli.cli.main") as mock_cli:
mock_cli.side_effect = SystemExit(int(code))
context.main_result = main(["bad-command"])
@then("main should return {code}")
def step_main_returns_code(context, code):
"""Verify main returns expected code."""
assert context.main_result == int(code)
# Module steps
@when("I execute the __main__ module")
def step_execute_main_module(context):
"""Execute the __main__ module."""
with patch("sys.argv", ["agents", "--help"]):
with patch("cleveragents.__main__.main") as mock_main:
mock_main.return_value = 0
with patch("sys.exit") as mock_exit:
run()
context.main_called = mock_main.called
mock_exit.assert_called_once_with(0)
@then("the CLI should start")
def step_cli_should_start(context):
"""Verify CLI started."""
assert context.main_called
@when("I import the __main__ module")
def step_import_main_module(context):
"""Import the __main__ module."""
from cleveragents import __main__
context.main_module = __main__
@then("it should have a run function")
def step_has_run_function(context):
"""Verify module has run function."""
assert hasattr(context.main_module, "run")
assert callable(context.main_module.run)
# Platform module steps
@when("I call ensure_cli_importable")
def step_call_ensure_cli_importable(context):
"""Call ensure_cli_importable."""
context.cli_module = ensure_cli_importable()
@then("it should return the CLI module")
def step_returns_cli_module(context):
"""Verify it returns CLI module."""
assert context.cli_module is not None
@then("the module should have a cli attribute")
def step_module_has_cli(context):
"""Verify module has cli attribute."""
assert hasattr(context.cli_module, "cli")
@then("the module should have a main attribute")
def step_module_has_main(context):
"""Verify module has main attribute."""
assert hasattr(context.cli_module, "main")
@when("the CLI module is already imported")
def step_cli_already_imported(context):
"""Setup already imported module."""
context.mock_module = MagicMock()
context.original_module = sys.modules.get("cleveragents.cli")
sys.modules["cleveragents.cli"] = context.mock_module
@then("it should return the cached module")
def step_returns_cached_module(context):
"""Verify cached module returned."""
result = ensure_cli_importable()
assert result is context.mock_module
# Restore original
if context.original_module:
sys.modules["cleveragents.cli"] = context.original_module
else:
sys.modules.pop("cleveragents.cli", None)
@when("the CLI module import fails")
def step_cli_import_fails(context):
"""Setup import failure."""
# Save original module
context.original_module = sys.modules.get("cleveragents.cli")
# Remove from sys.modules to force import
sys.modules.pop("cleveragents.cli", None)
context.import_error = ImportError("Test error")
@then("it should propagate the import error")
def step_propagates_import_error(context):
"""Verify import error propagated."""
# Ensure module is not in sys.modules
sys.modules.pop("cleveragents.cli", None)
with patch("cleveragents.platform.import_module") as mock_import:
mock_import.side_effect = ImportError("Test error")
try:
ensure_cli_importable()
assert False, "Should have raised ImportError"
except ImportError as e:
assert str(e) == "Test error"
# Restore original module
if context.original_module:
sys.modules["cleveragents.cli"] = context.original_module