e3d3c7926d
CI / integration_tests (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / build (push) Has been cancelled
CI / benchmark-publish (push) Has been cancelled
CI / e2e_tests (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / quality (push) Has been cancelled
CI / status-check (push) Has been cancelled
CI / security (push) Has been cancelled
CI / coverage (push) Has been cancelled
CI / unit_tests (push) Has been cancelled
CI / helm (push) Has been cancelled
CI / benchmark-regression (push) Has been cancelled
CI / docker (push) Has been cancelled
Co-authored-by: Brent Edwards <brent.edwards@cleverthis.com> Co-committed-by: Brent Edwards <brent.edwards@cleverthis.com>
550 lines
20 KiB
Python
550 lines
20 KiB
Python
"""Step definitions for event_system_taxonomy.feature.
|
|
|
|
Tests the complete Event System Domain Event Taxonomy: EventType enum
|
|
completeness, DomainEvent model fields and serialisation, EventBus Protocol,
|
|
ReactiveEventBus emit/subscribe/stream/audit_log behaviour, and log
|
|
correlation.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from datetime import UTC
|
|
from typing import Any
|
|
|
|
from behave import given, then, when # type: ignore[import-untyped]
|
|
from behave.runner import Context # type: ignore[import-untyped]
|
|
from pydantic import ValidationError
|
|
from rx.core.observable.observable import Observable
|
|
from ulid import ULID
|
|
|
|
from cleveragents.infrastructure.events import (
|
|
DomainEvent,
|
|
EventBus,
|
|
EventType,
|
|
LoggingEventBus,
|
|
ReactiveEventBus,
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_DOT_PATTERN = re.compile(r"^[a-z_]+\.[a-z_]+$")
|
|
|
|
|
|
def _make_event(event_type_str: str, **kwargs: Any) -> DomainEvent:
|
|
return DomainEvent(event_type=EventType(event_type_str), **kwargs)
|
|
|
|
|
|
class _TaxonomyCollector:
|
|
"""In-test stub that collects emitted events."""
|
|
|
|
def __init__(self) -> None:
|
|
self.received: list[DomainEvent] = []
|
|
|
|
def __call__(self, event: DomainEvent) -> None:
|
|
self.received.append(event)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# EventType enum completeness
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then("the EventType enum should have at least {n:d} members")
|
|
def step_event_type_min_members(ctx: Context, n: int) -> None:
|
|
count = len(EventType)
|
|
assert count >= n, f"EventType has {count} members, expected >= {n}"
|
|
|
|
|
|
@then('EventType should contain "{value}"')
|
|
def step_event_type_contains_value(ctx: Context, value: str) -> None:
|
|
values = {e.value for e in EventType}
|
|
assert value in values, f"{value!r} not in EventType values: {sorted(values)}"
|
|
|
|
|
|
@then('every EventType value should match the pattern "<domain>.<action>"')
|
|
def step_event_type_dot_pattern(ctx: Context) -> None:
|
|
for member in EventType:
|
|
assert _DOT_PATTERN.match(member.value), (
|
|
f"EventType.{member.name} value {member.value!r} does not match "
|
|
f"the <domain>.<action> pattern"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# DomainEvent model
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when('I create a DomainEvent for taxonomy with event_type "{et}"')
|
|
def step_create_taxonomy_event(ctx: Context, et: str) -> None:
|
|
ctx.taxonomy_event = _make_event(et)
|
|
|
|
|
|
@then('the taxonomy event should have field "{field}"')
|
|
def step_taxonomy_event_has_field(ctx: Context, field: str) -> None:
|
|
assert field in ctx.taxonomy_event.model_fields, (
|
|
f"DomainEvent missing field {field!r}"
|
|
)
|
|
|
|
|
|
@then("the taxonomy event correlation_id should be {n:d} characters long")
|
|
def step_taxonomy_correlation_id_length(ctx: Context, n: int) -> None:
|
|
cid = ctx.taxonomy_event.correlation_id
|
|
assert len(cid) == n, f"correlation_id length {len(cid)}, expected {n}"
|
|
|
|
|
|
@then("the taxonomy event timestamp should be UTC")
|
|
def step_taxonomy_timestamp_utc(ctx: Context) -> None:
|
|
ts = ctx.taxonomy_event.timestamp
|
|
assert ts.tzinfo is not None, "timestamp has no timezone"
|
|
assert ts.tzinfo == UTC, f"timestamp timezone is {ts.tzinfo!r}, expected UTC"
|
|
|
|
|
|
@then("mutating the taxonomy event should raise a validation error")
|
|
def step_taxonomy_event_frozen(ctx: Context) -> None:
|
|
raised = False
|
|
try:
|
|
ctx.taxonomy_event.event_type = EventType.PLAN_CANCELLED # type: ignore[misc]
|
|
except ValidationError:
|
|
raised = True
|
|
assert raised, "Expected ValidationError when mutating frozen DomainEvent"
|
|
|
|
|
|
@when("I create a DomainEvent for taxonomy with all fields populated")
|
|
def step_create_taxonomy_full_event(ctx: Context) -> None:
|
|
ctx.taxonomy_event = DomainEvent(
|
|
event_type=EventType.PLAN_CREATED,
|
|
plan_id=str(ULID()),
|
|
root_plan_id=str(ULID()),
|
|
session_id=str(ULID()),
|
|
actor_name="local/strategist",
|
|
project_name="my-project",
|
|
details={"phase": "strategize", "actor": "strategists/planner"},
|
|
)
|
|
|
|
|
|
@then("the taxonomy event should serialize to JSON and deserialize back identically")
|
|
def step_taxonomy_event_json_roundtrip(ctx: Context) -> None:
|
|
json_str = ctx.taxonomy_event.model_dump_json()
|
|
restored = DomainEvent.model_validate_json(json_str)
|
|
assert restored == ctx.taxonomy_event, (
|
|
f"Round-trip mismatch:\n original={ctx.taxonomy_event}\n restored={restored}"
|
|
)
|
|
|
|
|
|
@when("I create a DomainEvent for taxonomy with correlation fields")
|
|
def step_create_taxonomy_event_correlation(ctx: Context) -> None:
|
|
ctx.taxonomy_event = DomainEvent(
|
|
event_type=EventType.PLAN_CREATED,
|
|
plan_id=str(ULID()),
|
|
root_plan_id=str(ULID()),
|
|
session_id=str(ULID()),
|
|
)
|
|
|
|
|
|
@then("the taxonomy event plan_id should be set")
|
|
def step_taxonomy_plan_id_set(ctx: Context) -> None:
|
|
assert ctx.taxonomy_event.plan_id is not None
|
|
|
|
|
|
@then("the taxonomy event root_plan_id should be set")
|
|
def step_taxonomy_root_plan_id_set(ctx: Context) -> None:
|
|
assert ctx.taxonomy_event.root_plan_id is not None
|
|
|
|
|
|
@then("the taxonomy event session_id should be set")
|
|
def step_taxonomy_session_id_set(ctx: Context) -> None:
|
|
assert ctx.taxonomy_event.session_id is not None
|
|
|
|
|
|
@then("the taxonomy event user_identity should be None")
|
|
def step_taxonomy_event_user_identity_none(ctx: Context) -> None:
|
|
assert ctx.taxonomy_event.user_identity is None, (
|
|
f"Expected user_identity=None, got {ctx.taxonomy_event.user_identity!r}"
|
|
)
|
|
|
|
|
|
@when('I create a DomainEvent for taxonomy with user_identity "{uid}"')
|
|
def step_create_taxonomy_event_with_user_identity(ctx: Context, uid: str) -> None:
|
|
ctx.taxonomy_event = DomainEvent(
|
|
event_type=EventType.PLAN_CREATED,
|
|
user_identity=uid,
|
|
)
|
|
|
|
|
|
@then('the taxonomy event user_identity should be "{expected}"')
|
|
def step_taxonomy_event_user_identity_value(ctx: Context, expected: str) -> None:
|
|
assert ctx.taxonomy_event.user_identity == expected, (
|
|
f"Expected user_identity={expected!r}, got {ctx.taxonomy_event.user_identity!r}"
|
|
)
|
|
|
|
|
|
@when("I create a DomainEvent for taxonomy with typed details")
|
|
def step_create_taxonomy_event_with_details(ctx: Context) -> None:
|
|
ctx.taxonomy_event = DomainEvent(
|
|
event_type=EventType.PLAN_PHASE_CHANGED,
|
|
details={"old_phase": "strategize", "new_phase": "execute"},
|
|
)
|
|
|
|
|
|
@then("the taxonomy event details should contain the expected keys")
|
|
def step_taxonomy_event_details_keys(ctx: Context) -> None:
|
|
details = ctx.taxonomy_event.details
|
|
assert "old_phase" in details, "Missing 'old_phase' in details"
|
|
assert "new_phase" in details, "Missing 'new_phase' in details"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# EventBus Protocol
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then('the EventBus Protocol should define method "{method}"')
|
|
def step_protocol_has_method(ctx: Context, method: str) -> None:
|
|
assert hasattr(EventBus, method), f"EventBus Protocol missing method {method!r}"
|
|
|
|
|
|
@then("a ReactiveEventBus instance should satisfy the EventBus Protocol")
|
|
def step_reactive_satisfies_protocol(ctx: Context) -> None:
|
|
bus = ReactiveEventBus()
|
|
assert isinstance(bus, EventBus), "ReactiveEventBus does not satisfy EventBus"
|
|
|
|
|
|
@then("a LoggingEventBus instance should satisfy the EventBus Protocol")
|
|
def step_logging_satisfies_protocol(ctx: Context) -> None:
|
|
bus = LoggingEventBus()
|
|
assert isinstance(bus, EventBus), "LoggingEventBus does not satisfy EventBus"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# ReactiveEventBus — emit / subscribe / stream
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("a fresh ReactiveEventBus for taxonomy tests")
|
|
def step_given_fresh_bus(ctx: Context) -> None:
|
|
ctx.taxonomy_bus = ReactiveEventBus()
|
|
ctx.taxonomy_collectors = []
|
|
|
|
|
|
@given("a ReactiveEventBus for taxonomy tests with max_audit_log_size {n:d}")
|
|
def step_given_capped_bus(ctx: Context, n: int) -> None:
|
|
ctx.taxonomy_bus = ReactiveEventBus(max_audit_log_size=n)
|
|
ctx.taxonomy_collectors = []
|
|
|
|
|
|
@then(
|
|
"creating a ReactiveEventBus with max_audit_log_size {n:d} should raise ValueError"
|
|
)
|
|
def step_reactive_bus_invalid_audit_log_size(ctx: Context, n: int) -> None:
|
|
raised = False
|
|
try:
|
|
ReactiveEventBus(max_audit_log_size=n)
|
|
except ValueError:
|
|
raised = True
|
|
assert raised, f"Expected ValueError for max_audit_log_size={n}"
|
|
|
|
|
|
@when('I taxonomy-subscribe to "{et}" events')
|
|
def step_taxonomy_subscribe(ctx: Context, et: str) -> None:
|
|
collector = _TaxonomyCollector()
|
|
ctx.taxonomy_collectors.append(collector)
|
|
ctx.taxonomy_bus.subscribe(EventType(et), collector)
|
|
|
|
|
|
@when('I taxonomy-emit a "{et}" DomainEvent')
|
|
def step_taxonomy_emit(ctx: Context, et: str) -> None:
|
|
ctx.taxonomy_bus.emit(_make_event(et))
|
|
|
|
|
|
@then("the taxonomy handler should have received {n:d} event(s)")
|
|
def step_taxonomy_handler_received(ctx: Context, n: int) -> None:
|
|
assert ctx.taxonomy_collectors, "No taxonomy collectors registered"
|
|
count = len(ctx.taxonomy_collectors[0].received)
|
|
assert count == n, f"Expected {n} event(s), got {count}"
|
|
|
|
|
|
@when('I taxonomy-subscribe two handlers to "{et}" events')
|
|
def step_taxonomy_subscribe_two(ctx: Context, et: str) -> None:
|
|
for _ in range(2):
|
|
collector = _TaxonomyCollector()
|
|
ctx.taxonomy_collectors.append(collector)
|
|
ctx.taxonomy_bus.subscribe(EventType(et), collector)
|
|
|
|
|
|
@then("each taxonomy handler should have received {n:d} event")
|
|
def step_each_taxonomy_handler_received(ctx: Context, n: int) -> None:
|
|
for i, col in enumerate(ctx.taxonomy_collectors):
|
|
assert len(col.received) == n, (
|
|
f"Handler {i} received {len(col.received)}, expected {n}"
|
|
)
|
|
|
|
|
|
@then("the taxonomy bus stream should be an Observable")
|
|
def step_taxonomy_bus_stream_observable(ctx: Context) -> None:
|
|
stream = ctx.taxonomy_bus.stream
|
|
assert isinstance(stream, Observable), (
|
|
f"Expected Observable, got {type(stream).__name__}"
|
|
)
|
|
|
|
|
|
@then('the taxonomy bus stream should not expose method "{method}"')
|
|
def step_taxonomy_bus_stream_not_expose_method(ctx: Context, method: str) -> None:
|
|
stream = ctx.taxonomy_bus.stream
|
|
assert not hasattr(stream, method), (
|
|
f"Expected stream to hide {method!r}, but it is present"
|
|
)
|
|
|
|
|
|
@when('I taxonomy-subscribe a failing handler and a normal handler to "{et}" events')
|
|
def step_taxonomy_subscribe_failing_and_normal(ctx: Context, et: str) -> None:
|
|
normal_collector = _TaxonomyCollector()
|
|
ctx.taxonomy_collectors.append(normal_collector)
|
|
|
|
def _failing_handler(event: DomainEvent) -> None:
|
|
raise RuntimeError(f"failing taxonomy handler for {event.event_type.value}")
|
|
|
|
ctx.taxonomy_bus.subscribe(EventType(et), _failing_handler)
|
|
ctx.taxonomy_bus.subscribe(EventType(et), normal_collector)
|
|
|
|
|
|
@then("the taxonomy normal handler should have received {n:d} event(s)")
|
|
def step_taxonomy_normal_handler_received(ctx: Context, n: int) -> None:
|
|
assert ctx.taxonomy_collectors, "No taxonomy normal collector registered"
|
|
count = len(ctx.taxonomy_collectors[-1].received)
|
|
assert count == n, f"Expected normal handler to receive {n} event(s), got {count}"
|
|
|
|
|
|
@when('I observe the taxonomy bus stream and emit a "{et}" event')
|
|
def step_taxonomy_observe_and_emit(ctx: Context, et: str) -> None:
|
|
ctx.stream_events = []
|
|
ctx.taxonomy_bus.stream.subscribe(on_next=ctx.stream_events.append)
|
|
ctx.taxonomy_bus.emit(_make_event(et))
|
|
|
|
|
|
@when('I observe taxonomy stream with a failing observer and emit a "{et}" event')
|
|
def step_taxonomy_stream_failing_observer(ctx: Context, et: str) -> None:
|
|
def _failing_observer(event: DomainEvent) -> None:
|
|
raise RuntimeError(f"failing stream observer for {event.event_type.value}")
|
|
|
|
ctx.taxonomy_bus.stream.subscribe(on_next=_failing_observer)
|
|
ctx.taxonomy_bus.emit(_make_event(et))
|
|
|
|
|
|
@then("the stream observer should have captured the event")
|
|
def step_stream_observer_captured(ctx: Context) -> None:
|
|
assert len(ctx.stream_events) == 1, (
|
|
f"Expected 1 stream event, got {len(ctx.stream_events)}"
|
|
)
|
|
|
|
|
|
@then("taxonomy-emitting a non-DomainEvent should raise TypeError")
|
|
def step_taxonomy_emit_bad_type(ctx: Context) -> None:
|
|
raised = False
|
|
try:
|
|
ctx.taxonomy_bus.emit("not-an-event") # type: ignore[arg-type]
|
|
except TypeError:
|
|
raised = True
|
|
assert raised, "Expected TypeError for non-DomainEvent"
|
|
|
|
|
|
@then("taxonomy-subscribing with a non-EventType should raise TypeError")
|
|
def step_taxonomy_subscribe_bad_type(ctx: Context) -> None:
|
|
raised = False
|
|
try:
|
|
ctx.taxonomy_bus.subscribe("plan.created", lambda e: None) # type: ignore[arg-type]
|
|
except TypeError:
|
|
raised = True
|
|
assert raised, "Expected TypeError for non-EventType"
|
|
|
|
|
|
@then("taxonomy-subscribing with a non-callable should raise TypeError")
|
|
def step_taxonomy_subscribe_non_callable(ctx: Context) -> None:
|
|
raised = False
|
|
try:
|
|
ctx.taxonomy_bus.subscribe(EventType.PLAN_CREATED, "not-callable") # type: ignore[arg-type]
|
|
except TypeError:
|
|
raised = True
|
|
assert raised, "Expected TypeError for non-callable handler"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# In-memory event trail (audit_log)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then("the audit_log should contain {n:d} events")
|
|
def step_audit_log_count(ctx: Context, n: int) -> None:
|
|
log = ctx.taxonomy_bus.audit_log
|
|
assert len(log) == n, f"audit_log has {len(log)} events, expected {n}"
|
|
|
|
|
|
@then('the audit_log should include event types "{event_types}"')
|
|
def step_audit_log_contains_types(ctx: Context, event_types: str) -> None:
|
|
expected = [value.strip() for value in event_types.split(",")]
|
|
actual = [event.event_type.value for event in ctx.taxonomy_bus.audit_log]
|
|
assert actual == expected, f"audit_log types {actual} != expected {expected}"
|
|
|
|
|
|
@then('the audit_log events should be in order "{order}"')
|
|
def step_audit_log_order(ctx: Context, order: str) -> None:
|
|
expected = [value.strip() for value in order.split(",")]
|
|
log = ctx.taxonomy_bus.audit_log
|
|
actual = [e.event_type.value for e in log]
|
|
assert actual == expected, f"audit_log order {actual} != expected {expected}"
|
|
|
|
|
|
@then("mutating the returned audit_log should not affect the bus")
|
|
def step_audit_log_defensive_copy(ctx: Context) -> None:
|
|
log1 = ctx.taxonomy_bus.audit_log
|
|
original_len = len(log1)
|
|
log1.clear() # mutate the returned list
|
|
log2 = ctx.taxonomy_bus.audit_log
|
|
assert len(log2) == original_len, (
|
|
f"audit_log was mutated: expected {original_len}, got {len(log2)}"
|
|
)
|
|
|
|
|
|
@when("I clear the taxonomy bus audit_log")
|
|
def step_clear_taxonomy_audit_log(ctx: Context) -> None:
|
|
ctx.taxonomy_bus.clear_audit_log()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Log correlation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I emit two taxonomy events with the same correlation_id")
|
|
def step_emit_correlated_events(ctx: Context) -> None:
|
|
shared_cid = str(ULID())
|
|
ctx.taxonomy_bus.emit(
|
|
DomainEvent(
|
|
event_type=EventType.PLAN_CREATED,
|
|
correlation_id=shared_cid,
|
|
)
|
|
)
|
|
ctx.taxonomy_bus.emit(
|
|
DomainEvent(
|
|
event_type=EventType.PLAN_PHASE_CHANGED,
|
|
correlation_id=shared_cid,
|
|
)
|
|
)
|
|
ctx.shared_correlation_id = shared_cid
|
|
|
|
|
|
@then("both events in the audit_log should share the same correlation_id")
|
|
def step_correlated_events_in_log(ctx: Context) -> None:
|
|
log = ctx.taxonomy_bus.audit_log
|
|
assert len(log) == 2, f"Expected 2 events, got {len(log)}"
|
|
assert log[0].correlation_id == ctx.shared_correlation_id
|
|
assert log[1].correlation_id == ctx.shared_correlation_id
|
|
|
|
|
|
@when("I create two independent DomainEvents for taxonomy")
|
|
def step_create_two_independent_events(ctx: Context) -> None:
|
|
ctx.taxonomy_event_one = DomainEvent(event_type=EventType.PLAN_CREATED)
|
|
ctx.taxonomy_event_two = DomainEvent(event_type=EventType.DECISION_CREATED)
|
|
|
|
|
|
@then("the two taxonomy events should have different correlation_ids")
|
|
def step_taxonomy_events_have_different_correlation_ids(ctx: Context) -> None:
|
|
cid_one = ctx.taxonomy_event_one.correlation_id
|
|
cid_two = ctx.taxonomy_event_two.correlation_id
|
|
assert cid_one != cid_two, (
|
|
"Expected independently created DomainEvents to have unique "
|
|
f"correlation_ids, got {cid_one!r} and {cid_two!r}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# LoggingEventBus — emit / subscribe
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("a fresh LoggingEventBus for taxonomy tests")
|
|
def step_given_fresh_logging_bus(ctx: Context) -> None:
|
|
ctx.logging_taxonomy_bus = LoggingEventBus()
|
|
ctx.logging_taxonomy_collectors = []
|
|
|
|
|
|
@when('I logging-taxonomy-subscribe to "{et}" events')
|
|
def step_logging_taxonomy_subscribe(ctx: Context, et: str) -> None:
|
|
collector = _TaxonomyCollector()
|
|
ctx.logging_taxonomy_collectors.append(collector)
|
|
ctx.logging_taxonomy_bus.subscribe(EventType(et), collector)
|
|
|
|
|
|
@when('I logging-taxonomy-emit a "{et}" DomainEvent')
|
|
def step_logging_taxonomy_emit(ctx: Context, et: str) -> None:
|
|
ctx.logging_taxonomy_bus.emit(_make_event(et))
|
|
|
|
|
|
@then("the logging taxonomy handler should have received {n:d} event(s)")
|
|
def step_logging_taxonomy_handler_received(ctx: Context, n: int) -> None:
|
|
assert ctx.logging_taxonomy_collectors, "No logging taxonomy collectors registered"
|
|
count = len(ctx.logging_taxonomy_collectors[0].received)
|
|
assert count == n, f"Expected {n} event(s), got {count}"
|
|
|
|
|
|
@when(
|
|
'I logging-taxonomy-subscribe a failing handler and a normal handler to "{et}" events'
|
|
)
|
|
def step_logging_taxonomy_subscribe_failing_and_normal(ctx: Context, et: str) -> None:
|
|
normal_collector = _TaxonomyCollector()
|
|
ctx.logging_taxonomy_collectors.append(normal_collector)
|
|
|
|
def _failing_handler(event: DomainEvent) -> None:
|
|
raise RuntimeError(f"failing logging handler for {event.event_type.value}")
|
|
|
|
ctx.logging_taxonomy_bus.subscribe(EventType(et), _failing_handler)
|
|
ctx.logging_taxonomy_bus.subscribe(EventType(et), normal_collector)
|
|
|
|
|
|
@then("the logging taxonomy normal handler should have received {n:d} event(s)")
|
|
def step_logging_taxonomy_normal_handler_received(ctx: Context, n: int) -> None:
|
|
assert ctx.logging_taxonomy_collectors, (
|
|
"No logging taxonomy normal collector registered"
|
|
)
|
|
count = len(ctx.logging_taxonomy_collectors[-1].received)
|
|
assert count == n, f"Expected normal handler to receive {n} event(s), got {count}"
|
|
|
|
|
|
@then("logging-taxonomy-emitting a non-DomainEvent should raise TypeError")
|
|
def step_logging_taxonomy_emit_bad_type(ctx: Context) -> None:
|
|
raised = False
|
|
try:
|
|
ctx.logging_taxonomy_bus.emit("not-an-event") # type: ignore[arg-type]
|
|
except TypeError:
|
|
raised = True
|
|
assert raised, "Expected TypeError for non-DomainEvent"
|
|
|
|
|
|
@then("logging-taxonomy-subscribing with a non-EventType should raise TypeError")
|
|
def step_logging_taxonomy_subscribe_bad_type(ctx: Context) -> None:
|
|
raised = False
|
|
try:
|
|
ctx.logging_taxonomy_bus.subscribe(
|
|
"plan.created",
|
|
lambda e: None, # type: ignore[arg-type]
|
|
)
|
|
except TypeError:
|
|
raised = True
|
|
assert raised, "Expected TypeError for non-EventType"
|
|
|
|
|
|
@then("logging-taxonomy-subscribing with a non-callable should raise TypeError")
|
|
def step_logging_taxonomy_subscribe_non_callable(ctx: Context) -> None:
|
|
raised = False
|
|
try:
|
|
ctx.logging_taxonomy_bus.subscribe(
|
|
EventType.PLAN_CREATED,
|
|
"not-callable", # type: ignore[arg-type]
|
|
)
|
|
except TypeError:
|
|
raised = True
|
|
assert raised, "Expected TypeError for non-callable handler"
|