Files
placeholder/features/steps/application_container_coverage_boost_r2_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

382 lines
15 KiB
Python

"""Step definitions for application_container_coverage_boost_r2.feature.
Targets uncovered lines in ``cleveragents.application.container``:
- Lines 201-206: ``_build_repo_indexing_service``
- Lines 213-218: ``_build_resource_registry_service``
- Lines 221-230: ``_build_namespaced_project_repo``
- Lines 269-278: ``_resolve_auto_reindex``
- Lines 283-287: ``_build_analyzer_registry``
- Lines 290-298: ``_build_resource_file_watcher``
- Lines 316-357: ``_build_session_service``
All step text uses the ``r2cov-`` prefix to avoid collisions with
existing step definitions.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from unittest.mock import MagicMock, patch
from behave import given, then, when
from behave.runner import Context
from cleveragents.application.container import (
_build_analyzer_registry,
_build_namespaced_project_repo,
_build_repo_indexing_service,
_build_resource_file_watcher,
_build_resource_registry_service,
_build_session_service,
_resolve_auto_reindex,
reset_container,
)
from cleveragents.application.services.repo_indexing_service import RepoIndexingService
from cleveragents.application.services.resource_file_watcher import ResourceFileWatcher
from cleveragents.application.services.resource_registry_service import (
ResourceRegistryService,
)
from cleveragents.application.services.session_service import PersistentSessionService
from cleveragents.domain.models.acms.analyzers import AnalyzerRegistry
from cleveragents.infrastructure.database.repositories import (
NamespacedProjectRepository,
)
_IN_MEMORY_URL = "sqlite:///:memory:"
# -------------------------------------------------------------------
# Background
# -------------------------------------------------------------------
@given("a clean container state for r2 coverage tests")
def step_clean_container_r2(context: Context) -> None:
"""Reset the global container to avoid cross-test interference."""
reset_container()
# -------------------------------------------------------------------
# _build_repo_indexing_service (lines 201-206)
# -------------------------------------------------------------------
@when("r2cov- I build a repo indexing service with an in-memory database")
def step_build_repo_indexing_service(context: Context) -> None:
"""Call _build_repo_indexing_service with in-memory SQLite.
We patch RepoIndexingService to avoid needing real DB tables
(cleanup_stale_indexing runs in __init__), while still executing
the engine/sessionmaker creation on lines 201-206.
"""
with patch("cleveragents.application.container.RepoIndexingService") as mock_cls:
# Make the mock constructor return a stub that remembers the factory
def capture_factory(session_factory: Any) -> MagicMock:
inst = MagicMock(spec=RepoIndexingService)
inst._session_factory = session_factory
inst._mock_marker = True
return inst
mock_cls.side_effect = capture_factory
context.r2cov_repo_indexing_svc = _build_repo_indexing_service(_IN_MEMORY_URL)
@then("r2cov- the result should be a RepoIndexingService instance")
def step_verify_repo_indexing_svc(context: Context) -> None:
"""Assert a service-like object was returned (mocked to avoid DB)."""
svc = context.r2cov_repo_indexing_svc
assert svc is not None, "Expected a service instance, got None"
assert hasattr(svc, "_session_factory"), "Service should have _session_factory"
@then("r2cov- the service should have a session factory")
def step_verify_repo_indexing_session_factory(context: Context) -> None:
"""Assert the session factory was passed to the service."""
svc = context.r2cov_repo_indexing_svc
assert svc._session_factory is not None, "Expected a session_factory, got None"
assert callable(svc._session_factory), "session_factory should be callable"
# -------------------------------------------------------------------
# _build_resource_registry_service (lines 213-218)
# -------------------------------------------------------------------
@when("r2cov- I build a resource registry service with an in-memory database")
def step_build_resource_registry_service(context: Context) -> None:
"""Call _build_resource_registry_service with in-memory SQLite.
Patch ResourceRegistryService constructor to avoid DB table requirements.
"""
with patch(
"cleveragents.application.container.ResourceRegistryService"
) as mock_cls:
def capture_factory(session_factory: Any) -> MagicMock:
inst = MagicMock(spec=ResourceRegistryService)
inst._session_factory = session_factory
return inst
mock_cls.side_effect = capture_factory
context.r2cov_resource_registry_svc = _build_resource_registry_service(
_IN_MEMORY_URL
)
@then("r2cov- the result should be a ResourceRegistryService instance")
def step_verify_resource_registry_svc(context: Context) -> None:
"""Assert a service-like object was returned."""
svc = context.r2cov_resource_registry_svc
assert svc is not None, "Expected a service instance, got None"
assert hasattr(svc, "_session_factory"), "Service should have _session_factory"
# -------------------------------------------------------------------
# _build_namespaced_project_repo (lines 221-230)
# -------------------------------------------------------------------
@when("r2cov- I build a namespaced project repo with an in-memory database")
def step_build_namespaced_project_repo(context: Context) -> None:
"""Call _build_namespaced_project_repo with in-memory SQLite.
Patch NamespacedProjectRepository constructor to avoid DB table requirements.
"""
with patch(
"cleveragents.application.container.NamespacedProjectRepository"
) as mock_cls:
def capture_factory(session_factory: Any) -> MagicMock:
inst = MagicMock(spec=NamespacedProjectRepository)
inst._session_factory = session_factory
return inst
mock_cls.side_effect = capture_factory
context.r2cov_namespaced_project_repo = _build_namespaced_project_repo(
_IN_MEMORY_URL
)
@then("r2cov- the result should be a NamespacedProjectRepository instance")
def step_verify_namespaced_project_repo(context: Context) -> None:
"""Assert a repo-like object was returned."""
repo = context.r2cov_namespaced_project_repo
assert repo is not None, "Expected a repo instance, got None"
assert hasattr(repo, "_session_factory"), "Repo should have _session_factory"
# -------------------------------------------------------------------
# _resolve_auto_reindex (lines 269-278)
# -------------------------------------------------------------------
@dataclass
class _FakeResolvedValue:
"""Minimal stand-in for ConfigService.resolve() return value."""
value: Any
@given("r2cov- ConfigService resolve returns a truthy value for auto-reindex")
def step_config_resolve_truthy(context: Context) -> None:
"""Patch ConfigService so resolve returns a truthy value."""
mock_svc = MagicMock()
mock_svc.resolve.return_value = _FakeResolvedValue(value=True)
context.r2cov_config_patcher = patch(
"cleveragents.application.container.ConfigService",
return_value=mock_svc,
)
# Import ConfigService lazily inside _resolve_auto_reindex, so we
# need to patch it in the container module's import path.
# The function does:
# from cleveragents.application.services.config_service import ConfigService
# But since it's a local import, we patch the source module.
context.r2cov_config_patcher = patch(
"cleveragents.application.services.config_service.ConfigService",
return_value=mock_svc,
)
context.r2cov_config_patcher.start()
def cleanup() -> None:
context.r2cov_config_patcher.stop()
context.add_cleanup(cleanup)
@given("r2cov- ConfigService resolve returns None for auto-reindex")
def step_config_resolve_none(context: Context) -> None:
"""Patch ConfigService so resolve returns None value (defaults to True)."""
mock_svc = MagicMock()
mock_svc.resolve.return_value = _FakeResolvedValue(value=None)
context.r2cov_config_patcher = patch(
"cleveragents.application.services.config_service.ConfigService",
return_value=mock_svc,
)
context.r2cov_config_patcher.start()
def cleanup() -> None:
context.r2cov_config_patcher.stop()
context.add_cleanup(cleanup)
@given("r2cov- ConfigService resolve returns a falsy value for auto-reindex")
def step_config_resolve_falsy(context: Context) -> None:
"""Patch ConfigService so resolve returns a falsy (False) value."""
mock_svc = MagicMock()
mock_svc.resolve.return_value = _FakeResolvedValue(value=False)
context.r2cov_config_patcher = patch(
"cleveragents.application.services.config_service.ConfigService",
return_value=mock_svc,
)
context.r2cov_config_patcher.start()
def cleanup() -> None:
context.r2cov_config_patcher.stop()
context.add_cleanup(cleanup)
@given("r2cov- ConfigService raises an exception during resolve")
def step_config_resolve_exception(context: Context) -> None:
"""Patch ConfigService so its constructor raises, hitting the except branch."""
context.r2cov_config_patcher = patch(
"cleveragents.application.services.config_service.ConfigService",
side_effect=RuntimeError("config unavailable"),
)
context.r2cov_config_patcher.start()
def cleanup() -> None:
context.r2cov_config_patcher.stop()
context.add_cleanup(cleanup)
@when("r2cov- I call _resolve_auto_reindex")
def step_call_resolve_auto_reindex(context: Context) -> None:
"""Call _resolve_auto_reindex and capture the result."""
context.r2cov_auto_reindex_result = _resolve_auto_reindex()
@then("r2cov- the result should be True")
def step_verify_result_true(context: Context) -> None:
"""Assert the result is True."""
assert context.r2cov_auto_reindex_result is True, (
f"Expected True, got {context.r2cov_auto_reindex_result!r}"
)
@then("r2cov- the result should be False")
def step_verify_result_false(context: Context) -> None:
"""Assert the result is False."""
assert context.r2cov_auto_reindex_result is False, (
f"Expected False, got {context.r2cov_auto_reindex_result!r}"
)
# -------------------------------------------------------------------
# _build_analyzer_registry (lines 283-287)
# -------------------------------------------------------------------
@when("r2cov- I build an analyzer registry")
def step_build_analyzer_registry(context: Context) -> None:
"""Call _build_analyzer_registry directly."""
context.r2cov_analyzer_registry = _build_analyzer_registry()
@then("r2cov- the result should be an AnalyzerRegistry instance")
def step_verify_analyzer_registry(context: Context) -> None:
"""Assert the returned object is an AnalyzerRegistry."""
assert isinstance(context.r2cov_analyzer_registry, AnalyzerRegistry), (
f"Expected AnalyzerRegistry, got "
f"{type(context.r2cov_analyzer_registry).__name__}"
)
@then("r2cov- the registry should have a PythonAnalyzer for .py files")
def step_verify_python_analyzer_registered(context: Context) -> None:
"""Assert a PythonAnalyzer is registered for .py extension."""
from cleveragents.domain.models.acms.python_analyzer import PythonAnalyzer
registry: AnalyzerRegistry = context.r2cov_analyzer_registry
analyzer = registry.get_for_extension(".py")
assert analyzer is not None, "Expected an analyzer for .py, got None"
assert isinstance(analyzer, PythonAnalyzer), (
f"Expected PythonAnalyzer, got {type(analyzer).__name__}"
)
# -------------------------------------------------------------------
# _build_resource_file_watcher (lines 290-298)
# -------------------------------------------------------------------
@given("r2cov- a mock event bus for the file watcher")
def step_mock_event_bus_for_watcher(context: Context) -> None:
"""Create a mock ReactiveEventBus for ResourceFileWatcher construction."""
context.r2cov_mock_event_bus = MagicMock()
@when("r2cov- I build a resource file watcher with the mock event bus")
def step_build_resource_file_watcher(context: Context) -> None:
"""Call _build_resource_file_watcher with a mock event bus."""
context.r2cov_file_watcher = _build_resource_file_watcher(
event_bus=context.r2cov_mock_event_bus,
)
@then("r2cov- the result should be a ResourceFileWatcher instance")
def step_verify_file_watcher(context: Context) -> None:
"""Assert the returned object is a ResourceFileWatcher."""
assert isinstance(context.r2cov_file_watcher, ResourceFileWatcher), (
f"Expected ResourceFileWatcher, got {type(context.r2cov_file_watcher).__name__}"
)
@then("r2cov- the watcher should have auto_reindex enabled")
def step_verify_watcher_auto_reindex(context: Context) -> None:
"""Assert the watcher was constructed with auto_reindex=True."""
watcher: ResourceFileWatcher = context.r2cov_file_watcher
assert watcher._auto_reindex is True, (
f"Expected auto_reindex=True, got {watcher._auto_reindex!r}"
)
# -------------------------------------------------------------------
# _build_session_service (lines 316-357)
# -------------------------------------------------------------------
@given("r2cov- a mock event bus for the session service")
def step_mock_event_bus_for_session(context: Context) -> None:
"""Create a mock ReactiveEventBus for session service construction."""
context.r2cov_session_event_bus = MagicMock()
@when("r2cov- I build a session service with an in-memory database and no event bus")
def step_build_session_service_no_bus(context: Context) -> None:
"""Call _build_session_service with in-memory SQLite and no event bus."""
context.r2cov_session_svc = _build_session_service(_IN_MEMORY_URL)
@when(
"r2cov- I build a session service with an in-memory database and the mock event bus"
)
def step_build_session_service_with_bus(context: Context) -> None:
"""Call _build_session_service with in-memory SQLite and a mock event bus."""
context.r2cov_session_svc = _build_session_service(
_IN_MEMORY_URL, event_bus=context.r2cov_session_event_bus
)
@then("r2cov- the result should be a PersistentSessionService instance")
def step_verify_session_svc(context: Context) -> None:
"""Assert the returned object is a PersistentSessionService."""
assert isinstance(context.r2cov_session_svc, PersistentSessionService), (
f"Expected PersistentSessionService, got "
f"{type(context.r2cov_session_svc).__name__}"
)