Files
temp/features/steps/cli_main_cov3_steps.py
freemo 31472b5413 test(coverage): add Behave scenarios for 39 under-covered modules
Add Behave feature/step pairs that exercise uncovered branches across handlers, LSP, CLI, and service layers to reach the coverage gate.

ISSUES CLOSED: #1232
2026-03-31 21:47:12 +00:00

199 lines
7.2 KiB
Python

"""Step definitions for CLI main.py coverage round 3 (lines 104-110, 582-590, 593+)."""
from __future__ import annotations
from typing import Any
from unittest.mock import MagicMock, patch
from behave import given, then, when
# ---------------------------------------------------------------------------
# Lines 104-110: _register_subcommands exception handler
# ---------------------------------------------------------------------------
@given("cmcov3 the _subcommands_registered flag is reset to False")
def step_reset_subcommands_flag(context: Any) -> None:
"""Reset the module-level flag so _register_subcommands runs fresh."""
import importlib
mod = importlib.import_module("cleveragents.cli.main")
context.cmcov3_mod = mod
context.cmcov3_orig_registered = mod._subcommands_registered
mod._subcommands_registered = False
# Restore the flag after the scenario to avoid polluting other tests.
def _restore() -> None:
mod._subcommands_registered = context.cmcov3_orig_registered
context.add_cleanup(_restore)
@when("cmcov3 _register_subcommands is called and an import raises an error")
def step_register_subcommands_import_error(context: Any) -> None:
"""Patch a subcommand import so _register_subcommands hits its except block."""
import builtins
mod = context.cmcov3_mod
mock_err_console = MagicMock()
original_import = builtins.__import__
def failing_import(name: str, *args: Any, **kwargs: Any) -> Any:
if name == "cleveragents.cli.commands":
raise ImportError("cmcov3 forced import failure")
return original_import(name, *args, **kwargs)
patcher_console = patch.object(
mod, "get_err_console", return_value=mock_err_console
)
patcher_console.start()
context.add_cleanup(patcher_console.stop)
patcher_builtins = patch.object(builtins, "__import__", side_effect=failing_import)
patcher_builtins.start()
try:
mod._register_subcommands()
except Exception as exc:
context.cmcov3_error = exc
finally:
patcher_builtins.stop()
context.cmcov3_err_console = mock_err_console
@then("cmcov3 the error console should have printed the failure message")
def step_check_failure_message(context: Any) -> None:
"""Verify the error console received the failure message."""
mock_console = context.cmcov3_err_console
assert mock_console.print.called, "Expected err console print to be called"
all_calls = " ".join(str(c) for c in mock_console.print.call_args_list)
assert "Failed to register subcommands" in all_calls, (
f"Expected 'Failed to register subcommands' in output, got: {all_calls}"
)
@then("cmcov3 the _subcommands_registered flag should still be False")
def step_check_flag_still_false(context: Any) -> None:
"""The flag must remain False since registration failed."""
mod = context.cmcov3_mod
assert mod._subcommands_registered is False, (
f"Expected _subcommands_registered to be False, got {mod._subcommands_registered}"
)
# ---------------------------------------------------------------------------
# Lines 582-590: completion command with subprocess.run
# ---------------------------------------------------------------------------
@given('cmcov3 subprocess.run is mocked to return stdout "{stdout_text}"')
def step_mock_subprocess_with_stdout(context: Any, stdout_text: str) -> None:
"""Set up a mock for subprocess.run that returns the given stdout."""
mock_result = MagicMock()
mock_result.stdout = stdout_text
mock_result.stderr = ""
mock_result.returncode = 0
patcher = patch("subprocess.run", return_value=mock_result)
context.cmcov3_subprocess_mock = patcher.start()
context.add_cleanup(patcher.stop)
@given("cmcov3 subprocess.run is mocked to return empty stdout")
def step_mock_subprocess_empty_stdout(context: Any) -> None:
"""Set up a mock for subprocess.run that returns empty stdout."""
mock_result = MagicMock()
mock_result.stdout = ""
mock_result.stderr = ""
mock_result.returncode = 0
patcher = patch("subprocess.run", return_value=mock_result)
context.cmcov3_subprocess_mock = patcher.start()
context.add_cleanup(patcher.stop)
@when('cmcov3 the completion command is invoked with shell "{shell}"')
def step_invoke_completion(context: Any, shell: str) -> None:
"""Invoke the completion CLI command with the given shell argument."""
from typer.testing import CliRunner
from cleveragents.cli.main import app
runner = CliRunner()
result = runner.invoke(app, ["completion", shell])
context.cmcov3_cli_result = result
@then('cmcov3 the output should contain "{expected}"')
def step_check_output_contains(context: Any, expected: str) -> None:
"""Verify the CLI output contains the expected text."""
output = context.cmcov3_cli_result.output
assert expected in output, (
f"Expected output to contain {expected!r}, got: {output!r}"
)
# ---------------------------------------------------------------------------
# Lines 593+: convert_exit_code function
# ---------------------------------------------------------------------------
@when("cmcov3 convert_exit_code is called with None")
def step_convert_exit_code_none(context: Any) -> None:
"""Call convert_exit_code with None."""
from cleveragents.cli.main import convert_exit_code
try:
context.cmcov3_exit_code_result = convert_exit_code(None)
except Exception as exc:
context.cmcov3_error = exc
@when("cmcov3 convert_exit_code is called with an object having exit_code {code:d}")
def step_convert_exit_code_object(context: Any, code: int) -> None:
"""Call convert_exit_code with an object that has an exit_code attribute."""
from cleveragents.cli.main import convert_exit_code
obj = MagicMock()
obj.exit_code = code
try:
context.cmcov3_exit_code_result = convert_exit_code(obj)
except Exception as exc:
context.cmcov3_error = exc
@when("cmcov3 convert_exit_code is called with integer {code:d}")
def step_convert_exit_code_int(context: Any, code: int) -> None:
"""Call convert_exit_code with an integer."""
from cleveragents.cli.main import convert_exit_code
try:
context.cmcov3_exit_code_result = convert_exit_code(code)
except Exception as exc:
context.cmcov3_error = exc
@when("cmcov3 convert_exit_code is called with a non-convertible value")
def step_convert_exit_code_nonconvertible(context: Any) -> None:
"""Call convert_exit_code with a value that cannot be converted to int."""
from cleveragents.cli.main import convert_exit_code
try:
context.cmcov3_exit_code_result = convert_exit_code("not_a_number")
except Exception as exc:
context.cmcov3_error = exc
@then("cmcov3 the exit code result should be {expected:d}")
def step_check_exit_code_result(context: Any, expected: int) -> None:
"""Verify convert_exit_code returned the expected value."""
if hasattr(context, "cmcov3_error"):
raise AssertionError(
f"convert_exit_code raised an exception: {context.cmcov3_error}"
)
actual = context.cmcov3_exit_code_result
assert actual == expected, (
f"Expected convert_exit_code to return {expected}, got {actual}"
)