feat(architecture): Added skeleton for phase 1
This commit is contained in:
+74
-148
@@ -1,4 +1,4 @@
|
||||
"""Step definitions for the CleverAgents CLI feature."""
|
||||
"""Step definitions for the CleverAgents CLI feature - Fixed for Typer."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -11,10 +11,10 @@ from unittest.mock import MagicMock, patch
|
||||
|
||||
import click
|
||||
from behave import then, when
|
||||
from click.testing import CliRunner
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from cleveragents.__main__ import run
|
||||
from cleveragents.cli import _convert_exit_code, cli, main
|
||||
from cleveragents.cli import app, cli, convert_exit_code, main
|
||||
from cleveragents.platform import ensure_cli_importable
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@ def _run_cli(context: Any, args: Sequence[str]) -> None:
|
||||
"exit_code": exit_code,
|
||||
"output": buffer.getvalue(),
|
||||
}
|
||||
# Also set context.output for compatibility with shared steps
|
||||
context.output = buffer.getvalue()
|
||||
context.exit_code = exit_code
|
||||
|
||||
|
||||
@when('I run the CleverAgents CLI with "--help"')
|
||||
@@ -33,10 +36,7 @@ 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"]
|
||||
# Removed specific @then - using generic one from cli_coverage_steps.py instead
|
||||
|
||||
|
||||
@when('I run the CleverAgents CLI with "--version"')
|
||||
@@ -44,10 +44,7 @@ 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"]
|
||||
# Removed specific @then - using generic one from cli_coverage_steps.py instead
|
||||
|
||||
|
||||
# New generic steps for other commands
|
||||
@@ -55,33 +52,40 @@ def step_output_version(context: Any) -> None:
|
||||
def step_run_cli_with_args(context, args):
|
||||
"""Execute the CLI with specific arguments."""
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, args.split())
|
||||
result = runner.invoke(app, args.split())
|
||||
# Combine stdout and stderr for error checking
|
||||
output = result.stdout
|
||||
if result.stderr:
|
||||
output += result.stderr
|
||||
context.result = {
|
||||
"exit_code": result.exit_code,
|
||||
"output": result.output,
|
||||
"output": output,
|
||||
"runner_result": result,
|
||||
}
|
||||
# Also set context.output for compatibility with shared steps
|
||||
context.output = output
|
||||
context.exit_code = result.exit_code
|
||||
|
||||
|
||||
@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']}"
|
||||
)
|
||||
# Removed duplicate @then - using generic one from cli_coverage_steps.py instead
|
||||
|
||||
|
||||
@then("the command should fail")
|
||||
def step_command_should_fail(context):
|
||||
def step_command_fails(context):
|
||||
"""Verify the command failed."""
|
||||
assert context.result["exit_code"] != 0
|
||||
assert context.result["exit_code"] != 0, (
|
||||
f"Expected non-zero exit code, got {context.result['exit_code']}"
|
||||
)
|
||||
|
||||
|
||||
@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"
|
||||
"""Verify the error output contains expected text."""
|
||||
# For Typer, errors may also be in stdout
|
||||
output = context.result.get("output", "")
|
||||
assert text.lower() in output.lower(), (
|
||||
f"Expected '{text}' not found in output: {output}"
|
||||
)
|
||||
|
||||
|
||||
# Exit code conversion steps
|
||||
@@ -89,9 +93,9 @@ def step_error_should_contain(context, text):
|
||||
def step_convert_exit_code(context, code):
|
||||
"""Convert an exit code."""
|
||||
if code == "None":
|
||||
context.converted_code = _convert_exit_code(None)
|
||||
context.converted_code = convert_exit_code(None)
|
||||
else:
|
||||
context.converted_code = _convert_exit_code(int(code))
|
||||
context.converted_code = convert_exit_code(int(code))
|
||||
|
||||
|
||||
@then("the result should be {expected}")
|
||||
@@ -104,16 +108,15 @@ def step_result_should_be(context, expected):
|
||||
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)
|
||||
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())
|
||||
# Directly call main function
|
||||
context.main_result = main(args.split())
|
||||
|
||||
|
||||
@then("main should process the version flag")
|
||||
@@ -125,133 +128,56 @@ def step_main_processes_version(context):
|
||||
@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()
|
||||
# Capture output to check if help is displayed
|
||||
buffer = io.StringIO()
|
||||
err_buffer = io.StringIO()
|
||||
|
||||
with contextlib.redirect_stdout(buffer), contextlib.redirect_stderr(err_buffer):
|
||||
try:
|
||||
context.main_result = main([])
|
||||
context.main_output = buffer.getvalue()
|
||||
context.main_stderr = err_buffer.getvalue()
|
||||
except SystemExit as e:
|
||||
context.main_result = e.code
|
||||
context.main_output = buffer.getvalue()
|
||||
context.main_stderr = err_buffer.getvalue()
|
||||
|
||||
|
||||
@then("main should display help")
|
||||
def step_main_displays_help(context):
|
||||
"""Verify main displays help."""
|
||||
assert context.main_result == 0
|
||||
# Check that help text was displayed
|
||||
output = context.main_output + context.main_stderr
|
||||
assert "Usage:" in output or "Commands:" in output or "help" in output.lower(), (
|
||||
f"Expected help output, got: {output}"
|
||||
)
|
||||
|
||||
|
||||
@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"])
|
||||
"""Call main with SystemExit."""
|
||||
# We need to mock at a lower level since Typer doesn't use __call__ directly
|
||||
# Mock the version command to raise SystemExit
|
||||
from cleveragents.cli import main as cli_main_module
|
||||
|
||||
# Use an invalid command that will trigger error handling
|
||||
# but mock the error handling to raise SystemExit with our code
|
||||
with patch("cleveragents.cli.main.err_console.print") as mock_print:
|
||||
# Make the function raise SystemExit when error is printed
|
||||
def raise_exit(*args, **kwargs):
|
||||
raise SystemExit(int(code))
|
||||
|
||||
mock_print.side_effect = raise_exit
|
||||
|
||||
buffer = io.StringIO()
|
||||
with contextlib.redirect_stdout(buffer):
|
||||
# Use an invalid command to trigger the error path
|
||||
context.main_result = main(["invalid-test-command-xyz"])
|
||||
|
||||
|
||||
@then("main should return {code}")
|
||||
def step_main_returns_code(context, code):
|
||||
@then("main should return {expected}")
|
||||
def step_main_returns_code(context, expected):
|
||||
"""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
|
||||
assert context.main_result == int(expected), (
|
||||
f"Expected {expected}, got {context.main_result}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user