"""Step definitions for CleverClaude CLI features.""" import os import subprocess import tempfile from pathlib import Path from behave import given, then, when from click.testing import CliRunner from hypothesis import given as hypothesis_given from hypothesis import strategies as st from cleverclaude.cli.main import app @given("the CleverClaude CLI is available") def step_cli_available(_context): """Ensure CleverClaude CLI is importable.""" _context.runner = CliRunner() assert _context.runner is not None @given("I have a test environment") def step_test_environment(_context): """Set up test environment.""" # Use the test context from environment.py assert hasattr(_context, "test_context") @given("I have an empty directory") def step_empty_directory(_context): """Create an empty test directory.""" _context.test_dir = Path(tempfile.mkdtemp(prefix="cleverclaude_cli_test_")) os.chdir(_context.test_dir) @given("I have a target directory {dirname}") def step_target_directory(_context, dirname): """Create a target directory.""" _context.test_dir = Path(tempfile.mkdtemp(prefix="cleverclaude_cli_test_")) _context.target_dir = _context.test_dir / dirname os.chdir(_context.test_dir) @given("I have a directory with existing files") def step_directory_with_files(_context): """Create a directory with existing files.""" _context.test_dir = Path(tempfile.mkdtemp(prefix="cleverclaude_cli_test_")) os.chdir(_context.test_dir) # Create some existing files (_context.test_dir / "existing_file.txt").write_text("This file already exists") (_context.test_dir / "README.md").write_text("# Existing Project") @given("I have an initialized CleverClaude project") def step_initialized_project(_context): """Create an initialized CleverClaude project.""" _context.test_dir = Path(tempfile.mkdtemp(prefix="cleverclaude_cli_test_")) os.chdir(_context.test_dir) # Run init command to set up project result = _context.runner.invoke(app, ["init"]) assert result.exit_code == 0 @given("CleverClaude is running") def step_cleverclaude_running(_context): """Ensure CleverClaude system is running for testing.""" # This would start a test instance of CleverClaude # For now, we'll mock this _context.cleverclaude_running = True @when('I run "{command}"') def step_run_command(context, command): """Execute a CLI command.""" parts = command.split() # Handle different command formats if parts[0] == "cleverclaude": args = parts[1:] # Remove "cleverclaude" context.result = context.runner.invoke(app, args) elif len(parts) >= 3 and parts[0] == "python" and parts[1] == "-m" and parts[2] == "cleverclaude": args = parts[3:] # Remove "python -m cleverclaude" context.result = context.runner.invoke(app, args) else: # Direct subprocess call for integration testing try: result = subprocess.run( parts, capture_output=True, text=True, timeout=30, cwd=getattr(context, "test_dir", None) ) # Create a mock result object class MockResult: def __init__(self, returncode, stdout, stderr): self.exit_code = returncode self.output = stdout + stderr context.result = MockResult(result.returncode, result.stdout, result.stderr) except subprocess.TimeoutExpired: class MockResult: def __init__(self): self.exit_code = 124 # Timeout exit code self.output = "Command timed out" context.result = MockResult() @when("I start the orchestration system in test mode") def step_start_orchestration(_context): """Start CleverClaude orchestration in test mode.""" # This would involve starting the system asynchronously # For testing, we'll simulate this _context.orchestration_started = True _context.result = type("MockResult", (), {"exit_code": 0, "output": "System started successfully"})() @then("the exit code should be {code:d}") def step_check_exit_code(context, code): """Verify exit code.""" assert context.result.exit_code == code, f"Expected exit code {code}, got {context.result.exit_code}" @then('the output should contain "{text}"') def step_output_contains(context, text): """Check if output contains text.""" assert text in context.result.output, f"Output does not contain '{text}'. Output was: {context.result.output}" @then('the directory "{dirname}" should exist') def step_directory_exists(context, dirname): """Check if directory exists.""" test_dir = getattr(context, "test_dir", Path.cwd()) dir_path = test_dir / dirname assert dir_path.exists() and dir_path.is_dir(), f"Directory '{dirname}' does not exist at {test_dir}" @then('the file "{filename}" should exist') def step_file_exists(context, filename): """Check if file exists.""" test_dir = getattr(context, "test_dir", Path.cwd()) file_path = test_dir / filename assert file_path.exists() and file_path.is_file(), f"File '{filename}' does not exist at {test_dir}" @then('the file "{filename}" should contain "{text}"') def step_file_contains(context, filename, text): """Check if file contains specific text.""" test_dir = getattr(context, "test_dir", Path.cwd()) file_path = test_dir / filename assert file_path.exists(), f"File '{filename}' does not exist" content = file_path.read_text() assert text in content, f"File '{filename}' does not contain '{text}'" @then("the system should initialize successfully") def step_system_initializes(_context): """Verify system initialization.""" assert getattr(_context, "orchestration_started", False), "System did not start" @then("the agent manager should be running") def step_agent_manager_running(_context): """Verify agent manager is running.""" # This would check if the agent manager is actually running # For testing, we'll assume success if orchestration started assert getattr(_context, "orchestration_started", False), "Agent manager not running" @then("the API server should be accessible") def step_api_server_accessible(_context): """Verify API server is accessible.""" # This would check if the API server is responding # For testing, we'll simulate this assert getattr(_context, "orchestration_started", False), "API server not accessible" @then("the output should contain system health information") def step_output_contains_health_info(context): """Check if output contains system health information.""" health_indicators = ["status", "health", "running", "active"] output_lower = context.result.output.lower() assert any(indicator in output_lower for indicator in health_indicators), "No health information found in output" @then("the output should contain agent count") def step_output_contains_agent_count(context): """Check if output contains agent count information.""" output_lower = context.result.output.lower() agent_indicators = ["agent", "count", "total", "active"] assert any(indicator in output_lower for indicator in agent_indicators), ( "No agent count information found in output" ) @then("the output should contain memory usage") def step_output_contains_memory_usage(context): """Check if output contains memory usage information.""" output_lower = context.result.output.lower() memory_indicators = ["memory", "usage", "ram", "heap"] assert any(indicator in output_lower for indicator in memory_indicators), ( "No memory usage information found in output" ) @then("the output should contain command-specific help") def step_output_contains_help(context): """Check if output contains command-specific help.""" help_indicators = ["help", "usage", "options", "commands"] output_lower = context.result.output.lower() assert any(indicator in output_lower for indicator in help_indicators), "No help information found in output" @when("I fuzz test the CLI with random invalid arguments") def step_fuzz_cli_invalid(context): """Fuzz test the CLI with random invalid arguments.""" runner = context.runner results = [] @hypothesis_given( st.lists( st.one_of( st.text(min_size=1, max_size=50), st.integers(), st.floats(allow_nan=False, allow_infinity=False) ), min_size=1, max_size=10, ) ) def test_random_invalid_args(args): # Convert all args to strings str_args = [str(arg) for arg in args] try: result = runner.invoke(app, str_args) results.append(result) # Should either succeed (exit code 0) or fail gracefully (non-zero but not crash) assert result.exit_code in [0, 1, 2], f"Unexpected exit code: {result.exit_code}" except Exception as e: # Should not raise unhandled exceptions raise AssertionError(f"CLI crashed with unhandled exception: {e}") from e # Run the hypothesis test test_random_invalid_args() context.fuzz_results = results @then("all invocations should either succeed or fail gracefully") def step_all_succeed_or_fail_gracefully(context): """Verify all fuzz test invocations succeeded or failed gracefully.""" assert hasattr(context, "fuzz_results"), "No fuzz test results found" # If we get here, hypothesis didn't raise any assertion errors @then("no invocation should crash the system") def step_no_crashes(_context): """Verify no invocations crashed the system.""" # This is verified by the fuzz test above - if we reach here, no crashes occurred pass