feat(api): parse endpoints pass EventBus into store writes

- api.parse_837 / parse_835: pass request.app.state.event_bus into store.add()
- conftest: autouse fixture wires a fresh EventBus onto app.state for every
  test, since TestClient does not invoke the FastAPI lifespan handler
  unless used as a context manager
- test_pubsub: split get_event_bus coverage into a raises-when-missing test
  and a returns-attached-bus test, both save/restore app.state.event_bus
  around the assertion so the autouse fixture's bus is preserved

Phase 2 complete: db init moved to lifespan, EventBus is the process-wide
publish point, and the two ingest endpoints publish claim_written /
remittance_written / activity_recorded events on every store.add().
This commit is contained in:
Tyler
2026-06-20 15:51:18 -06:00
parent 8d02ed3204
commit 8bcb23c78d
3 changed files with 49 additions and 12 deletions
+32 -7
View File
@@ -100,10 +100,35 @@ async def test_multiple_concurrent_subscribers_each_get_all_events():
def test_get_event_bus_raises_when_app_state_uninitialized():
"""get_event_bus() late-imports cyclone.api; without a running app,
accessing app.state.event_bus raises AttributeError."""
# Don't import cyclone.api at module level — that would force a load
# and could mask the real failure. Just call the helper and expect
# the AttributeError to surface from app.state.
with pytest.raises((AttributeError, RuntimeError)):
get_event_bus()
"""get_event_bus() late-imports cyclone.api; if the lifespan handler
hasn't set ``app.state.event_bus`` (or it's been cleared), the
AttributeError must surface so callers can fail loudly."""
from cyclone.api import app
# The autouse conftest fixture sets app.state.event_bus for every test
# so that endpoint code can call request.app.state.event_bus. We pop
# the key entirely here so __getattr__ raises AttributeError rather
# than returning None for an explicitly-None slot.
state_dict = app.state._state
saved = state_dict.pop("event_bus", None)
try:
with pytest.raises((AttributeError, RuntimeError)):
get_event_bus()
finally:
if saved is not None:
state_dict["event_bus"] = saved
def test_get_event_bus_returns_app_state_bus():
"""When the lifespan handler has initialised the bus, get_event_bus()
returns it. Validates the happy path under the same late-import."""
from cyclone.api import app
from cyclone.pubsub import EventBus
saved = getattr(app.state, "event_bus", None)
bus = EventBus()
app.state.event_bus = bus
try:
assert get_event_bus() is bus
finally:
app.state.event_bus = saved