forked from HAL9000/cleveragents-core
051ee7c290
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
382 lines
15 KiB
Python
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__}"
|
|
)
|