Files
cleveragents-core/features/steps/tui_commands_coverage_steps.py
freemo 051ee7c290 test(coverage): add Behave BDD tests to improve coverage across 52 source files
Added 52 new .feature files and corresponding _steps.py files targeting
previously uncovered code paths in the following areas:

- TUI layer: app, commands, persona (state/schema/registry), widgets,
  input (shell_exec, reference_parser)
- Application services: plan lifecycle/service/executor, session,
  project, repo indexing, correction, checkpoint, actor, llm_actors,
  strategy coordinator, resource file watcher, service retry wiring
- CLI commands: session, resource, repl, plan, db, automation_profile
- Domain models: retry_policy, resource_type, cost_budget,
  docker_compose_analyzer, detail_level, _sql_string_aware,
  _postgresql_helpers
- Core: circuit_breaker, retry_service_patterns
- Infrastructure: repositories, transaction_sandbox, strategy_registry,
  plugins/loader, container
- Config: settings
- Agents: plan_generation, context_analysis, auto_debug
- A2A: facade

All new tests follow the Behave/Gherkin BDD standard. Resolved step
definition collisions with unique prefixes. Fixed Alembic fileConfig
logger disabling issue (disable_existing_loggers=False).

ISSUES CLOSED: #1068
2026-03-20 21:22:10 +00:00

234 lines
8.3 KiB
Python

"""Step definitions for tui_commands_coverage.feature.
Targets uncovered lines in cleveragents.tui.commands:
- Lines 22-31: TuiCommandRouter.handle() method
- Lines 34-42: TuiCommandRouter._persona_command() method
- Lines 46-48: TuiCommandRouter._session_command() method
- Lines 51-64: run_tui() function (headless and non-headless)
"""
import json
from dataclasses import dataclass, field
from io import StringIO
from pathlib import Path
from unittest.mock import MagicMock, patch
from behave import given, then, when
from cleveragents.tui.commands import TuiCommandRouter, run_tui
# ---------------------------------------------------------------------------
# Lightweight fakes for PersonaRegistry and PersonaState
# ---------------------------------------------------------------------------
@dataclass
class FakePersona:
"""Minimal persona stand-in."""
name: str
@dataclass
class FakePersonaRegistry:
"""Registry that returns a canned list of personas."""
_personas: list[FakePersona] = field(default_factory=list)
personas_dir: Path = field(default_factory=lambda: Path("/tmp/fake-personas"))
def list_personas(self) -> list[FakePersona]:
return list(self._personas)
@dataclass
class FakePersonaState:
"""State that tracks set_active_persona calls."""
_active: dict[str, FakePersona] = field(default_factory=dict)
def set_active_persona(self, session_id: str, name: str) -> FakePersona:
persona = FakePersona(name=name)
self._active[session_id] = persona
return persona
def active_name(self, session_id: str) -> str:
p = self._active.get(session_id)
return p.name if p else "default"
# ---------------------------------------------------------------------------
# Background
# ---------------------------------------------------------------------------
@given("the TUI commands module is imported")
def step_tui_commands_imported(context):
"""Verify the commands module is importable."""
assert TuiCommandRouter is not None
assert run_tui is not None
# ---------------------------------------------------------------------------
# Router construction helpers
# ---------------------------------------------------------------------------
@given("a TuiCommandRouter with a mock registry and state")
def step_router_with_mock_deps(context):
"""Create a router with an empty registry and basic state."""
context.registry = FakePersonaRegistry()
context.state = FakePersonaState()
context.router = TuiCommandRouter(
persona_registry=context.registry,
persona_state=context.state,
)
@given("a TuiCommandRouter with a mock registry and state that supports set")
def step_router_with_set_support(context):
"""Create a router whose state can set an active persona."""
context.registry = FakePersonaRegistry()
context.state = FakePersonaState()
context.router = TuiCommandRouter(
persona_registry=context.registry,
persona_state=context.state,
)
@given("a TuiCommandRouter with a mock registry containing two personas")
def step_router_with_two_personas(context):
"""Create a router whose registry returns two personas."""
context.registry = FakePersonaRegistry(
_personas=[FakePersona(name="Alice"), FakePersona(name="Bob")]
)
context.state = FakePersonaState()
context.router = TuiCommandRouter(
persona_registry=context.registry,
persona_state=context.state,
)
@given("a TuiCommandRouter with an empty mock registry")
def step_router_with_empty_registry(context):
"""Create a router whose registry returns no personas."""
context.registry = FakePersonaRegistry(_personas=[])
context.state = FakePersonaState()
context.router = TuiCommandRouter(
persona_registry=context.registry,
persona_state=context.state,
)
# ---------------------------------------------------------------------------
# handle() invocation
# ---------------------------------------------------------------------------
@when('I call handle with raw input "{raw}"')
def step_call_handle(context, raw):
"""Invoke TuiCommandRouter.handle() with the given raw string."""
context.handle_result = context.router.handle(raw, session_id="test-session")
# ---------------------------------------------------------------------------
# handle() assertions
# ---------------------------------------------------------------------------
@then('the handle result should be "{expected}"')
def step_handle_result_exact(context, expected):
assert context.handle_result == expected, (
f"Expected {expected!r}, got {context.handle_result!r}"
)
@then("the handle result should contain persona listing")
def step_handle_result_persona_listing(context):
"""An empty registry returns 'No personas'."""
assert context.handle_result == "No personas" or isinstance(
context.handle_result, str
)
@then('the handle result should start with "{prefix}"')
def step_handle_result_starts_with(context, prefix):
assert context.handle_result.startswith(prefix), (
f"Expected result to start with {prefix!r}, got {context.handle_result!r}"
)
# ---------------------------------------------------------------------------
# run_tui() headless scenario
# ---------------------------------------------------------------------------
@when("I call run_tui in headless mode with mocked dependencies")
def step_run_tui_headless(context):
"""Call run_tui(headless=True) with fully mocked container."""
fake_personas_dir = Path("/tmp/fake-tui-headless-personas")
mock_registry = MagicMock()
mock_registry.list_personas.return_value = []
mock_registry.personas_dir = fake_personas_dir
mock_registry.ensure_default.return_value = FakePersona(name="default")
mock_registry.get_last_persona.return_value = None
mock_state = MagicMock()
mock_state.active_name.return_value = "default"
mock_container = MagicMock()
mock_container.persona_registry.return_value = mock_registry
mock_container.persona_state.return_value = mock_state
captured = StringIO()
with (
patch("cleveragents.tui.commands.get_container", return_value=mock_container),
patch("cleveragents.tui.commands.PersonaRegistry", return_value=mock_registry),
patch("cleveragents.tui.commands.PersonaState", return_value=mock_state),
patch("builtins.print", side_effect=lambda *a, **kw: captured.write(str(a[0]))),
):
context.run_tui_rc = run_tui(headless=True)
context.run_tui_output = captured.getvalue()
@when("I call run_tui in non-headless mode with mocked dependencies")
def step_run_tui_non_headless(context):
"""Call run_tui(headless=False) with a mocked app."""
mock_registry = MagicMock()
mock_state = MagicMock()
mock_container = MagicMock()
mock_container.persona_registry.return_value = mock_registry
mock_container.persona_state.return_value = mock_state
mock_app = MagicMock()
mock_app.run = MagicMock()
with (
patch("cleveragents.tui.commands.get_container", return_value=mock_container),
patch("cleveragents.tui.commands.PersonaRegistry", return_value=mock_registry),
patch("cleveragents.tui.commands.PersonaState", return_value=mock_state),
patch(
"cleveragents.tui.commands.CleverAgentsTuiApp",
return_value=mock_app,
),
):
context.run_tui_rc = run_tui(headless=False)
context.mock_app = mock_app
# ---------------------------------------------------------------------------
# run_tui() assertions
# ---------------------------------------------------------------------------
@then("the run_tui return code should be 0")
def step_verify_run_tui_rc(context):
assert context.run_tui_rc == 0, f"Expected return code 0, got {context.run_tui_rc}"
@then("the printed JSON should contain textual_available key")
def step_verify_json_textual_available(context):
payload = json.loads(context.run_tui_output)
assert "textual_available" in payload, (
f"Expected 'textual_available' key in {payload}"
)
@then("the printed JSON should contain help key")
def step_verify_json_help(context):
payload = json.loads(context.run_tui_output)
assert "help" in payload, f"Expected 'help' key in {payload}"
@then("the mocked app run method should have been called")
def step_verify_app_run_called(context):
context.mock_app.run.assert_called_once()