forked from Rockachopa/Timmy-time-dashboard
committed by
GitHub
parent
11ba21418a
commit
82fb2417e3
@@ -1,6 +1,6 @@
|
||||
"""Tests for the async event bus (infrastructure.events.bus)."""
|
||||
|
||||
import asyncio
|
||||
import sqlite3
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -231,3 +231,121 @@ class TestConvenienceFunctions:
|
||||
# Cleanup
|
||||
event_bus._subscribers.clear()
|
||||
event_bus.clear_history()
|
||||
|
||||
|
||||
# ── Persistence ──────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
class TestEventBusPersistence:
|
||||
"""Test that EventBus persists events to SQLite."""
|
||||
|
||||
@pytest.fixture
|
||||
def persistent_bus(self, tmp_path):
|
||||
"""Create an EventBus with persistence enabled."""
|
||||
db_path = tmp_path / "events.db"
|
||||
bus = EventBus()
|
||||
bus.enable_persistence(db_path)
|
||||
return bus
|
||||
|
||||
async def test_publish_persists_event(self, persistent_bus):
|
||||
"""Published events should be written to SQLite."""
|
||||
await persistent_bus.publish(
|
||||
Event(type="task.created", source="test", data={"task_id": "t1"})
|
||||
)
|
||||
events = persistent_bus.replay(event_type="task.created")
|
||||
assert len(events) >= 1
|
||||
assert events[0].type == "task.created"
|
||||
assert events[0].data["task_id"] == "t1"
|
||||
|
||||
async def test_replay_returns_persisted_events(self, persistent_bus):
|
||||
"""Replay should return events from SQLite, not just in-memory history."""
|
||||
for i in range(5):
|
||||
await persistent_bus.publish(Event(type="task.created", source="test", data={"i": i}))
|
||||
|
||||
# Create a fresh bus pointing at the same DB to prove persistence
|
||||
bus2 = EventBus()
|
||||
bus2.enable_persistence(persistent_bus._persistence_db_path)
|
||||
events = bus2.replay(event_type="task.created")
|
||||
assert len(events) == 5
|
||||
|
||||
async def test_replay_filters_by_type(self, persistent_bus):
|
||||
"""Replay should filter by event type."""
|
||||
await persistent_bus.publish(Event(type="task.created", source="s"))
|
||||
await persistent_bus.publish(Event(type="agent.joined", source="s"))
|
||||
|
||||
tasks = persistent_bus.replay(event_type="task.created")
|
||||
agents = persistent_bus.replay(event_type="agent.joined")
|
||||
assert len(tasks) == 1
|
||||
assert len(agents) == 1
|
||||
|
||||
async def test_replay_filters_by_source(self, persistent_bus):
|
||||
"""Replay should filter by source."""
|
||||
await persistent_bus.publish(Event(type="x", source="alpha"))
|
||||
await persistent_bus.publish(Event(type="x", source="beta"))
|
||||
|
||||
alpha_events = persistent_bus.replay(source="alpha")
|
||||
assert len(alpha_events) == 1
|
||||
assert alpha_events[0].source == "alpha"
|
||||
|
||||
async def test_replay_filters_by_task_id(self, persistent_bus):
|
||||
"""Replay should filter by task_id in data."""
|
||||
await persistent_bus.publish(
|
||||
Event(type="task.started", source="s", data={"task_id": "abc"})
|
||||
)
|
||||
await persistent_bus.publish(
|
||||
Event(type="task.started", source="s", data={"task_id": "xyz"})
|
||||
)
|
||||
|
||||
events = persistent_bus.replay(task_id="abc")
|
||||
assert len(events) == 1
|
||||
assert events[0].data["task_id"] == "abc"
|
||||
|
||||
async def test_replay_respects_limit(self, persistent_bus):
|
||||
"""Replay should respect the limit parameter."""
|
||||
for i in range(10):
|
||||
await persistent_bus.publish(Event(type="x", source="s"))
|
||||
|
||||
events = persistent_bus.replay(limit=3)
|
||||
assert len(events) == 3
|
||||
|
||||
async def test_persistence_failure_does_not_crash(self, tmp_path):
|
||||
"""If persistence fails, publish should still work (graceful degradation)."""
|
||||
bus = EventBus()
|
||||
# Enable persistence to a read-only path to simulate failure
|
||||
bus.enable_persistence(tmp_path / "events.db")
|
||||
|
||||
received = []
|
||||
|
||||
@bus.subscribe("test.event")
|
||||
async def handler(event):
|
||||
received.append(event)
|
||||
|
||||
# Should not raise even if persistence has issues
|
||||
count = await bus.publish(Event(type="test.event", source="test"))
|
||||
assert count == 1
|
||||
assert len(received) == 1
|
||||
|
||||
async def test_bus_without_persistence_still_works(self):
|
||||
"""EventBus should work fine without persistence enabled."""
|
||||
bus = EventBus()
|
||||
received = []
|
||||
|
||||
@bus.subscribe("x")
|
||||
async def handler(event):
|
||||
received.append(event)
|
||||
|
||||
await bus.publish(Event(type="x", source="s"))
|
||||
assert len(received) == 1
|
||||
|
||||
# replay returns empty when no persistence
|
||||
events = bus.replay()
|
||||
assert events == []
|
||||
|
||||
async def test_wal_mode_on_persistence_db(self, persistent_bus):
|
||||
"""Persistence database should use WAL mode."""
|
||||
conn = sqlite3.connect(str(persistent_bus._persistence_db_path))
|
||||
try:
|
||||
mode = conn.execute("PRAGMA journal_mode").fetchone()[0]
|
||||
assert mode == "wal"
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
Reference in New Issue
Block a user