"""Helper script for event_bus.robot smoke tests. Each subcommand is a self-contained check that prints a sentinel on success. """ from __future__ import annotations import sys from pathlib import Path # Ensure local source tree is importable _SRC = str(Path(__file__).resolve().parents[1] / "src") if _SRC not in sys.path: sys.path.insert(0, _SRC) from cleveragents.infrastructure.events import ( # noqa: E402 DomainEvent, EventBus, EventType, LoggingEventBus, ReactiveEventBus, ) # --------------------------------------------------------------------------- # Subcommands # --------------------------------------------------------------------------- def event_type_values() -> None: """Verify key EventType values are present.""" required = { "plan.created", "decision.created", "tool.invoked", "actor.completed", "budget.exceeded", } actual = {e.value for e in EventType} missing = required - actual if missing: print(f"FAIL: missing EventType values: {missing}", file=sys.stderr) sys.exit(1) print("event-type-values-ok") def domain_event_creation() -> None: """Verify DomainEvent constructs with defaults.""" event = DomainEvent(event_type=EventType.PLAN_CREATED) if event.plan_id is not None: print("FAIL: expected plan_id=None", file=sys.stderr) sys.exit(1) if not event.correlation_id: print("FAIL: expected non-empty correlation_id", file=sys.stderr) sys.exit(1) print("domain-event-creation-ok") def protocol_conformance() -> None: """Verify both bus implementations satisfy EventBus protocol.""" for cls in (ReactiveEventBus, LoggingEventBus): bus = cls() if not isinstance(bus, EventBus): print(f"FAIL: {cls.__name__} does not satisfy EventBus", file=sys.stderr) sys.exit(1) print("protocol-conformance-ok") def reactive_emit_subscribe() -> None: """Verify ReactiveEventBus dispatches events to handlers.""" received: list[DomainEvent] = [] bus = ReactiveEventBus() bus.subscribe(EventType.PLAN_CREATED, received.append) bus.emit(DomainEvent(event_type=EventType.PLAN_CREATED)) if len(received) != 1: print(f"FAIL: expected 1 event, got {len(received)}", file=sys.stderr) sys.exit(1) if str(received[0].event_type) != "plan.created": print(f"FAIL: wrong event type: {received[0].event_type}", file=sys.stderr) sys.exit(1) print("reactive-emit-subscribe-ok") def logging_emit_subscribe() -> None: """Verify LoggingEventBus dispatches events to handlers.""" received: list[DomainEvent] = [] bus = LoggingEventBus() bus.subscribe(EventType.DECISION_CREATED, received.append) bus.emit(DomainEvent(event_type=EventType.DECISION_CREATED)) if len(received) != 1: print(f"FAIL: expected 1 event, got {len(received)}", file=sys.stderr) sys.exit(1) print("logging-emit-subscribe-ok") def reactive_type_filtering() -> None: """Verify ReactiveEventBus only dispatches to matching event type.""" received: list[DomainEvent] = [] bus = ReactiveEventBus() bus.subscribe(EventType.PLAN_CREATED, received.append) bus.emit(DomainEvent(event_type=EventType.DECISION_CREATED)) if received: print("FAIL: handler should not have been called", file=sys.stderr) sys.exit(1) print("reactive-type-filtering-ok") def decision_service_emits_event() -> None: """Verify DecisionService emits DECISION_CREATED via injected bus.""" import sys from unittest.mock import MagicMock, create_autospec from ulid import ULID from cleveragents.application.services.decision_service import DecisionService from cleveragents.config.settings import Settings from cleveragents.domain.models.core.decision import DecisionType from cleveragents.infrastructure.database.unit_of_work import UnitOfWork received: list[DomainEvent] = [] bus = ReactiveEventBus() bus.subscribe(EventType.DECISION_CREATED, received.append) mock_uow = create_autospec(UnitOfWork, instance=True) mock_tx_ctx = MagicMock() mock_tx_ctx.decisions = MagicMock() mock_uow.transaction.return_value.__enter__.return_value = mock_tx_ctx mock_uow.transaction.return_value.__exit__.return_value = False svc = DecisionService( settings=create_autospec(Settings, instance=True), unit_of_work=mock_uow, event_bus=bus, ) plan_id = str(ULID()) svc.record_decision( plan_id=plan_id, decision_type=DecisionType.STRATEGY_CHOICE, question="Q?", chosen_option="A", rationale="because", ) if not received: print("FAIL: no DECISION_CREATED event emitted", file=sys.stderr) sys.exit(1) print("decision-service-emits-event-ok") def plan_lifecycle_emits_event() -> None: """Verify PlanLifecycleService emits PLAN_CREATED via injected bus.""" import sys from unittest.mock import create_autospec from cleveragents.application.services.plan_lifecycle_service import ( PlanLifecycleService, ) from cleveragents.config.settings import Settings received: list[DomainEvent] = [] bus = ReactiveEventBus() bus.subscribe(EventType.PLAN_CREATED, received.append) svc = PlanLifecycleService( settings=create_autospec(Settings, instance=True), event_bus=bus ) svc.create_action( name="test/action", description="test action", definition_of_done="Done when implemented", strategy_actor="local/strategist", execution_actor="local/executor", ) svc.use_action(action_name="test/action") if not received: print("FAIL: no PLAN_CREATED event emitted", file=sys.stderr) sys.exit(1) print("plan-lifecycle-emits-event-ok") def container_event_bus_singleton() -> None: """Verify DI container provides ReactiveEventBus as Singleton.""" from cleveragents.application.container import Container container = Container() bus_a = container.event_bus() bus_b = container.event_bus() if bus_a is not bus_b: print("FAIL: event_bus not a singleton", file=sys.stderr) sys.exit(1) print("container-event-bus-singleton-ok") # --------------------------------------------------------------------------- # Dispatch # --------------------------------------------------------------------------- _COMMANDS = { "event_type_values": event_type_values, "domain_event_creation": domain_event_creation, "protocol_conformance": protocol_conformance, "reactive_emit_subscribe": reactive_emit_subscribe, "logging_emit_subscribe": logging_emit_subscribe, "reactive_type_filtering": reactive_type_filtering, "decision_service_emits_event": decision_service_emits_event, "plan_lifecycle_emits_event": plan_lifecycle_emits_event, "container_event_bus_singleton": container_event_bus_singleton, } if __name__ == "__main__": if len(sys.argv) < 2 or sys.argv[1] not in _COMMANDS: print(f"Usage: {sys.argv[0]} ", file=sys.stderr) print(f"Commands: {list(_COMMANDS)}", file=sys.stderr) sys.exit(1) _COMMANDS[sys.argv[1]]()