Files
cleveragents-core/features/steps/reactive_registry_adapter_steps.py
HAL9000 f1bb0bf075 fix(application): Remove error suppression in reactive_registry_adapter.py
Remove two try...except Exception: blocks that were silently suppressing
errors in register_registry_agents(), violating CONTRIBUTING.md fail-fast
policy.

Changes:
- Remove try/except around actor_registry.list_actors() call; exceptions
  now propagate to the caller instead of silently returning
- Remove try/except around route_bridge.agents refresh; exceptions now
  propagate instead of silently resetting to empty dict
- Update docstring to document the fail-fast propagation behaviour
- Update Behave scenarios to verify exceptions propagate correctly:
  * RuntimeError from list_actors() propagates
  * AttributeError from actors without .name attribute propagates
  * TypeError from None actors list propagates

Closes #9060
2026-04-27 07:33:18 +00:00

136 lines
4.4 KiB
Python

"""Behave steps for reactive_registry_adapter coverage."""
from types import SimpleNamespace
from behave import given, then, when
from cleveragents.application.reactive_registry_adapter import register_registry_agents
from cleveragents.reactive.stream_router import ReactiveStreamRouter
@given("a reactive stream router and route bridge")
def step_setup_router_and_bridge(context):
context.stream_router = ReactiveStreamRouter()
context.route_bridge = SimpleNamespace(agents={"seed": "sentinel"})
@given("an actor registry that raises on list")
def step_registry_raises(context):
class FailingRegistry:
def list_actors(self):
raise RuntimeError("list failed")
context.actor_registry = FailingRegistry()
@given("an actor registry with mixed capabilities")
def step_registry_mixed(context):
class DummyActor:
def __init__(self, name: str, sync: bool = False, async_capable: bool = False):
self.name = name
if sync:
self.process_message_sync = lambda content, metadata: (
f"{name}:{content}"
)
if async_capable:
self.process_message = lambda content, metadata: f"{name}:{content}"
context.actor_registry = SimpleNamespace(
list_actors=lambda: [
DummyActor("sync_actor", sync=True),
DummyActor("async_actor", async_capable=True),
DummyActor("plain_actor"),
]
)
@given("an actor registry that returns actors without names")
def step_registry_missing_names(context):
context.actor_registry = SimpleNamespace(list_actors=lambda: [object()])
@given("an actor registry that returns none")
def step_registry_returns_none(context):
context.actor_registry = SimpleNamespace(list_actors=lambda: None)
@when("I register registry actors with the adapter")
def step_register_actors(context):
register_registry_agents(
context.stream_router, context.route_bridge, context.actor_registry
)
@when("I attempt to register registry actors with the adapter")
def step_attempt_register_actors(context):
"""Attempt registration, capturing any exception that propagates."""
context.raised_exception = None
try:
register_registry_agents(
context.stream_router, context.route_bridge, context.actor_registry
)
except Exception as exc:
context.raised_exception = exc
@then("no agents are registered")
def step_assert_no_agents(context):
assert context.stream_router.agents == {}
@then("the route bridge agents remain unchanged")
def step_assert_bridge_unchanged(context):
assert context.route_bridge.agents == {"seed": "sentinel"}
@then("only actors with processing are registered on the stream router")
def step_assert_registered_processing(context):
assert set(context.stream_router.agents.keys()) == {"sync_actor", "async_actor"}
@then("the route bridge agents cache includes all listed actors")
def step_assert_bridge_cache(context):
assert set(context.route_bridge.agents.keys()) == {
"sync_actor",
"async_actor",
"plain_actor",
}
@then("the route bridge agents cache is cleared")
def step_assert_bridge_cleared(context):
assert context.route_bridge.agents == {}
@then("a RuntimeError is raised")
def step_assert_runtime_error(context):
assert context.raised_exception is not None, (
"Expected RuntimeError but no exception was raised"
)
assert isinstance(context.raised_exception, RuntimeError), (
f"Expected RuntimeError but got {type(context.raised_exception).__name__}: "
f"{context.raised_exception}"
)
@then("an AttributeError is raised")
def step_assert_attribute_error(context):
assert context.raised_exception is not None, (
"Expected AttributeError but no exception was raised"
)
assert isinstance(context.raised_exception, AttributeError), (
f"Expected AttributeError but got {type(context.raised_exception).__name__}: "
f"{context.raised_exception}"
)
@then("a TypeError is raised")
def step_assert_type_error(context):
assert context.raised_exception is not None, (
"Expected TypeError but no exception was raised"
)
assert isinstance(context.raised_exception, TypeError), (
f"Expected TypeError but got {type(context.raised_exception).__name__}: "
f"{context.raised_exception}"
)