f1bb0bf075
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
136 lines
4.4 KiB
Python
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}"
|
|
)
|