"""Step definitions for uncovered plan command branches.""" from __future__ import annotations import asyncio import os import re from io import StringIO from types import SimpleNamespace from unittest.mock import MagicMock, patch from behave import given, then, when from rich.console import Console from typer.testing import CliRunner from cleveragents.application.services.plan_service import PlanService from cleveragents.cli.commands.plan import _tell_streaming, app as plan_app class _StreamingPlanService: def __init__(self, events: list[dict[str, object]], error: Exception | None = None): self._events = events self._error = error async def generate_plan_streaming(self, *args, **kwargs): for event in self._events: yield event if self._error: raise self._error class _PlanTellContainer: def __init__(self, plan_service, project_service, actor_service): self._plan_service = plan_service self._project_service = project_service self._actor_service = actor_service def plan_service(self): return self._plan_service def project_service(self): return self._project_service def actor_service(self): return self._actor_service def _strip_rich_markup(text: str) -> str: return re.sub(r"\[[^\]]+\]", "", text) def _run_streaming_helper(context, plan_service, description: str) -> None: output = StringIO() test_console = Console(file=output, force_terminal=False, width=120) error_message = "" success = False try: with patch("cleveragents.cli.commands.plan.console", test_console): asyncio.run( _tell_streaming( context.streaming_project, description, None, plan_service, ) ) success = True except Exception as exc: error_message = str(exc) finally: rendered = _strip_rich_markup(output.getvalue()) context.command_output = rendered if success else f"{rendered}{error_message}" context.command_success = success context.command_error = error_message @given("I have a stub streaming project") def step_stub_streaming_project(context): context.streaming_project = SimpleNamespace(name="streaming-project") @when("I run the streaming plan helper with only an end event") def step_run_streaming_end_event(context): service = _StreamingPlanService(events=[{"__end__": True}]) _run_streaming_helper(context, service, "End-only streaming plan") @when("I run the streaming plan helper with a pre-node exception") def step_run_streaming_pre_node_exception(context): service = _StreamingPlanService(events=[], error=RuntimeError("boom")) _run_streaming_helper(context, service, "Exception streaming plan") @then("the streaming helper should complete successfully") def step_streaming_helper_success(context): assert context.command_success, f"Streaming failed: {context.command_output}" @then("the streaming output should mention plan creation success") def step_streaming_output_mentions_success(context): output = context.command_output assert "Plan created and built" in output or "Plan generated successfully" in output @then("the streaming helper should fail with an error") def step_streaming_helper_failure(context): assert not context.command_success assert context.command_output or context.command_error @then("the streaming output should show an error without node failure details") def step_streaming_output_no_node_failure(context): output = context.command_output.lower() assert "error" in output assert "failed" not in output @when("I execute plan tell with testing mode disabled and no actor registry") def step_plan_tell_testing_mode_disabled(context): runner = CliRunner() prompt = "Check branch coverage" plan_service = MagicMock(spec=PlanService) plan_service.create_plan.return_value = SimpleNamespace( name="coverage-plan", prompt=prompt, ) project_service = MagicMock() project_service.get_current_project.return_value = SimpleNamespace(name="project") actor_service = MagicMock() container = _PlanTellContainer(plan_service, project_service, actor_service) env = os.environ.copy() env["CLEVERAGENTS_TESTING_USE_MOCK_AI"] = "false" with patch( "cleveragents.application.container.get_container", return_value=container ): result = runner.invoke(plan_app, ["tell", prompt], env=env) context.result = result context.actor_service = actor_service @then("the mock actor should not be initialized") def step_mock_actor_not_initialized(context): context.actor_service.ensure_default_mock_actor.assert_not_called()