"""Step definitions for application_container_coverage_r3.feature. Targets remaining uncovered lines in ``cleveragents.application.container``: - Lines 117, 119: ``_build_stream_router`` function body - Lines 124, 126: ``_build_langgraph_bridge`` function body - Lines 131, 133: ``_build_route_bridge`` function body - Lines 138, 142: ``_lazy_register_registry_agents`` function body - Lines 386-391, 393: ``_build_skill_service`` ImportError fallback - Lines 851-857: ``get_container`` audit_event_subscriber exception """ from __future__ import annotations import builtins import sys from typing import Any from unittest.mock import MagicMock, patch from behave import given, then, when from cleveragents.application.container import ( _build_langgraph_bridge, _build_route_bridge, _build_skill_service, _build_stream_router, _lazy_register_registry_agents, get_container, reset_container, ) _IN_MEMORY_URL = "sqlite:///:memory:" # ------------------------------------------------------------------- # Background # ------------------------------------------------------------------- @given("acncov3 a clean container state") def step_acncov3_clean_container(context: Any) -> None: """Reset the global container to avoid cross-test interference.""" reset_container() # ------------------------------------------------------------------- # _build_stream_router (lines 117, 119) # ------------------------------------------------------------------- @when("acncov3 I call _build_stream_router") def step_acncov3_call_build_stream_router(context: Any) -> None: """Call _build_stream_router which lazily imports ReactiveStreamRouter.""" try: context.acncov3_stream_router_result = _build_stream_router() context.acncov3_error = None except Exception as exc: context.acncov3_stream_router_result = None context.acncov3_error = exc @then("acncov3 the result should be a ReactiveStreamRouter instance") def step_acncov3_verify_stream_router(context: Any) -> None: """Assert the returned object is a ReactiveStreamRouter.""" from cleveragents.reactive.stream_router import ReactiveStreamRouter assert context.acncov3_error is None, ( f"Expected no error, got: {context.acncov3_error}" ) result = context.acncov3_stream_router_result assert isinstance(result, ReactiveStreamRouter), ( f"Expected ReactiveStreamRouter, got {type(result).__name__}" ) # ------------------------------------------------------------------- # _build_langgraph_bridge (lines 124, 126) # ------------------------------------------------------------------- @given("acncov3 a mock stream router") def step_acncov3_mock_stream_router(context: Any) -> None: """Create a mock stream router for injection.""" context.acncov3_mock_stream_router = MagicMock( name="MockReactiveStreamRouter", ) @when("acncov3 I call _build_langgraph_bridge with the mock stream router") def step_acncov3_call_build_langgraph_bridge(context: Any) -> None: """Call _build_langgraph_bridge with the mock stream router.""" try: context.acncov3_bridge_result = _build_langgraph_bridge( context.acncov3_mock_stream_router, ) context.acncov3_error = None except Exception as exc: context.acncov3_bridge_result = None context.acncov3_error = exc @then("acncov3 the result should be a RxPyLangGraphBridge instance") def step_acncov3_verify_langgraph_bridge(context: Any) -> None: """Assert the returned object is a RxPyLangGraphBridge.""" from cleveragents.langgraph.bridge import RxPyLangGraphBridge assert context.acncov3_error is None, ( f"Expected no error, got: {context.acncov3_error}" ) result = context.acncov3_bridge_result assert isinstance(result, RxPyLangGraphBridge), ( f"Expected RxPyLangGraphBridge, got {type(result).__name__}" ) @then("acncov3 the bridge should reference the mock stream router") def step_acncov3_verify_bridge_router_ref(context: Any) -> None: """Assert the bridge stores the stream router we injected.""" bridge = context.acncov3_bridge_result assert bridge.stream_router is context.acncov3_mock_stream_router, ( "Expected the bridge to reference the mock stream router" ) # ------------------------------------------------------------------- # _build_route_bridge (lines 131, 133) # ------------------------------------------------------------------- @given("acncov3 a mock agents registry") def step_acncov3_mock_agents(context: Any) -> None: """Create a mock agents / actor registry for injection.""" context.acncov3_mock_agents = MagicMock(name="MockActorRegistry") @when("acncov3 I call _build_route_bridge with the mock stream router and agents") def step_acncov3_call_build_route_bridge(context: Any) -> None: """Call _build_route_bridge with mock stream_router and agents.""" try: context.acncov3_route_bridge_result = _build_route_bridge( context.acncov3_mock_stream_router, context.acncov3_mock_agents, ) context.acncov3_error = None except Exception as exc: context.acncov3_route_bridge_result = None context.acncov3_error = exc @then("acncov3 the result should be a RouteBridge instance") def step_acncov3_verify_route_bridge(context: Any) -> None: """Assert the returned object is a RouteBridge.""" from cleveragents.reactive.route_bridge import RouteBridge assert context.acncov3_error is None, ( f"Expected no error, got: {context.acncov3_error}" ) result = context.acncov3_route_bridge_result assert isinstance(result, RouteBridge), ( f"Expected RouteBridge, got {type(result).__name__}" ) @then("acncov3 the route bridge should reference the mock stream router") def step_acncov3_verify_route_bridge_router_ref(context: Any) -> None: """Assert the route bridge stores the stream router we injected.""" bridge = context.acncov3_route_bridge_result assert bridge.stream_router is context.acncov3_mock_stream_router, ( "Expected the route bridge to reference the mock stream router" ) # ------------------------------------------------------------------- # _lazy_register_registry_agents (lines 138, 142) # ------------------------------------------------------------------- @given("acncov3 a mock stream router for registration") def step_acncov3_mock_stream_router_for_reg(context: Any) -> None: """Create a mock stream router for the registration call.""" context.acncov3_reg_stream_router = MagicMock( name="MockStreamRouterForReg", ) @given("acncov3 a mock route bridge for registration") def step_acncov3_mock_route_bridge_for_reg(context: Any) -> None: """Create a mock route bridge for the registration call.""" context.acncov3_reg_route_bridge = MagicMock(name="MockRouteBridgeForReg") @given("acncov3 a mock actor registry for registration") def step_acncov3_mock_actor_registry_for_reg(context: Any) -> None: """Create a mock actor registry that returns an empty list.""" mock_registry = MagicMock(name="MockActorRegistryForReg") mock_registry.list_actors.return_value = [] context.acncov3_reg_actor_registry = mock_registry @when("acncov3 I call _lazy_register_registry_agents") def step_acncov3_call_lazy_register(context: Any) -> None: """Call _lazy_register_registry_agents with mock dependencies. We patch ``register_registry_agents`` in the adapter module so we can verify it was called without side-effects, while still exercising the lazy import path inside the container function. """ patcher = patch( "cleveragents.application.reactive_registry_adapter.register_registry_agents", ) mock_register = patcher.start() context.add_cleanup(patcher.stop) context.acncov3_mock_register_fn = mock_register try: context.acncov3_reg_result = _lazy_register_registry_agents( context.acncov3_reg_stream_router, context.acncov3_reg_route_bridge, context.acncov3_reg_actor_registry, ) context.acncov3_error = None except Exception as exc: context.acncov3_reg_result = None context.acncov3_error = exc @then("acncov3 register_registry_agents should have been invoked") def step_acncov3_verify_register_called(context: Any) -> None: """Assert that register_registry_agents was called with our mocks.""" assert context.acncov3_error is None, ( f"Expected no error, got: {context.acncov3_error}" ) context.acncov3_mock_register_fn.assert_called_once_with( context.acncov3_reg_stream_router, context.acncov3_reg_route_bridge, context.acncov3_reg_actor_registry, ) # ------------------------------------------------------------------- # _build_skill_service ImportError (lines 386-391, 393) # ------------------------------------------------------------------- @when("acncov3 I call _build_skill_service with SQLAlchemy import blocked") def step_acncov3_skill_service_import_blocked(context: Any) -> None: """Force an ImportError for sqlalchemy inside _build_skill_service. We temporarily remove sqlalchemy entries from ``sys.modules`` and install a custom ``__import__`` that raises ``ImportError`` for sqlalchemy. This exercises the except-ImportError fallback path (lines 386-393). """ # 1. Save and remove all sqlalchemy entries from sys.modules saved_modules: dict[str, Any] = {} for key in list(sys.modules.keys()): if key == "sqlalchemy" or key.startswith("sqlalchemy."): saved_modules[key] = sys.modules.pop(key) # 2. Install a blocking __import__ original_import = builtins.__import__ def blocking_import(name: str, *args: Any, **kwargs: Any) -> Any: if name == "sqlalchemy" or name.startswith("sqlalchemy."): raise ImportError(f"acncov3-mock: No module named '{name}'") return original_import(name, *args, **kwargs) builtins.__import__ = blocking_import # type: ignore[assignment] try: context.acncov3_skill_result = _build_skill_service(_IN_MEMORY_URL) context.acncov3_error = None except Exception as exc: context.acncov3_skill_result = None context.acncov3_error = exc finally: # 3. Restore original __import__ and sys.modules builtins.__import__ = original_import # type: ignore[assignment] sys.modules.update(saved_modules) @then("acncov3 the result should be an in-memory SkillService") def step_acncov3_verify_inmemory_skill(context: Any) -> None: """Assert the returned object is a SkillService (in-memory fallback).""" from cleveragents.application.services.skill_service import SkillService assert context.acncov3_error is None, ( f"Expected no error from _build_skill_service, got: {context.acncov3_error}" ) result = context.acncov3_skill_result assert isinstance(result, SkillService), ( f"Expected SkillService, got {type(result).__name__}" ) @then("acncov3 the in-memory SkillService should have no repository") def step_acncov3_verify_skill_no_repo(context: Any) -> None: """Assert the fallback SkillService has no DB-backed repository.""" assert context.acncov3_skill_result._skill_repo is None, ( "Expected no SkillRepository for the ImportError fallback" ) # ------------------------------------------------------------------- # get_container audit_event_subscriber exception (lines 851-857) # ------------------------------------------------------------------- @given("acncov3 the container is reset") def step_acncov3_container_reset(context: Any) -> None: """Ensure the global container is None so get_container creates fresh.""" reset_container() @given("acncov3 audit_event_subscriber is patched to raise an error") def step_acncov3_patch_audit_subscriber(context: Any) -> None: """Patch AuditEventSubscriber.__init__ to raise RuntimeError. When get_container() calls ``_container.audit_event_subscriber()`` the Singleton provider tries to construct AuditEventSubscriber. Making its ``__init__`` raise exercises the except branch (lines 851-857). """ patcher = patch( "cleveragents.application.services.audit_event_subscriber" ".AuditEventSubscriber.__init__", side_effect=RuntimeError("acncov3-mock: DB not initialised"), ) patcher.start() context.add_cleanup(patcher.stop) @when("acncov3 I call get_container") def step_acncov3_call_get_container(context: Any) -> None: """Call get_container which should catch the audit subscriber error.""" try: context.acncov3_container = get_container() context.acncov3_error = None except Exception as exc: context.acncov3_container = None context.acncov3_error = exc @then("acncov3 a valid container should be returned despite the error") def step_acncov3_verify_container_returned(context: Any) -> None: """Assert get_container returned a Container even though the subscriber failed. dependency-injector's ``DeclarativeContainer()`` returns a ``DynamicContainer`` at runtime, so we check against the base ``containers.Container`` type. """ from dependency_injector import containers as di_containers assert context.acncov3_error is None, ( f"Expected no error from get_container, got: {context.acncov3_error}" ) assert context.acncov3_container is not None, ( "Expected a container instance, got None" ) assert isinstance(context.acncov3_container, di_containers.Container), ( f"Expected a DI Container, got {type(context.acncov3_container).__name__}" ) # Verify it has the expected providers assert hasattr(context.acncov3_container, "settings"), ( "Container should have a 'settings' provider" ) assert hasattr(context.acncov3_container, "audit_event_subscriber"), ( "Container should have an 'audit_event_subscriber' provider" ) @then("acncov3 the container should be cached as the global singleton") def step_acncov3_verify_container_cached(context: Any) -> None: """Assert the returned container is cached (second call returns same).""" second_call = get_container() assert second_call is context.acncov3_container, ( "Expected the same container instance on second call (singleton)" ) # Clean up: reset for other tests reset_container()