Files
aditya 7348ca1911
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 14s
CI / build (pull_request) Successful in 14s
CI / quality (pull_request) Successful in 16s
CI / security (pull_request) Successful in 30s
CI / typecheck (pull_request) Successful in 47s
CI / unit_tests (pull_request) Successful in 2m13s
CI / docker (pull_request) Successful in 38s
CI / integration_tests (pull_request) Successful in 3m3s
CI / coverage (pull_request) Successful in 3m58s
CI / benchmark-regression (pull_request) Successful in 24m40s
fix(events): update tests for DecisionService.record_decision API change
- Update event_bus_steps.py to call record_decision() with individual
  parameters (plan_id, decision_type, question, chosen_option, rationale)
  instead of passing a Decision object, matching the master branch API
- Remove ctx.test_decision construction from DecisionService setup steps
  as it is no longer needed with the new signature
- Update robot/helper_event_bus.py decision_service_emits_event() to use
  the new record_decision() parameter signature
- All 9 Robot Framework event bus tests passing
- Behave scenarios at lines 125 and 131 now passing

Fixes event bus test failures after master merge.
2026-03-03 15:42:53 +00:00

214 lines
7.1 KiB
Python

"""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]} <command>", file=sys.stderr)
print(f"Commands: {list(_COMMANDS)}", file=sys.stderr)
sys.exit(1)
_COMMANDS[sys.argv[1]]()