""" Neural Sync - Event Bus Unit Tests """ import pytest import asyncio from datetime import datetime from backend.app.events import ( EventType, Event, EventBus, get_event_bus, event_bus_context, publish_event, subscribe_event, ) @pytest.fixture async def event_bus(): """创建事件总线 fixture""" bus = EventBus() await bus.start() yield bus await bus.stop() @pytest.fixture async def event_bus_context_fixture(): """创建事件总线上下文 fixture""" async with event_bus_context() as bus: yield bus class TestEvent: """测试 Event 数据类""" def test_create_event(self): """测试创建事件""" event = Event( type=EventType.SIGNAL_INGESTED, data={"signal_id": "123", "content": "Test signal"}, source="test", ) assert event.type == EventType.SIGNAL_INGESTED assert event.data["signal_id"] == "123" assert event.source == "test" assert isinstance(event.timestamp, float) def test_event_to_dict(self): """测试事件转字典""" event = Event( type=EventType.SIGNAL_ANALYZED, data={"score": 0.95}, correlation_id="test-123", ) event_dict = event.to_dict() assert event_dict["type"] == "signal.analyzed" assert event_dict["data"]["score"] == 0.95 assert event_dict["correlation_id"] == "test-123" class TestEventBus: """测试 EventBus""" @pytest.mark.asyncio async def test_start_stop(self, event_bus): """测试启动和停止""" assert event_bus._running is True await event_bus.stop() assert event_bus._running is False @pytest.mark.asyncio async def test_subscribe_and_publish(self, event_bus): """测试订阅和发布""" received_events = [] async def handler(event: Event): received_events.append(event) # 订阅事件 unsubscribe = event_bus.subscribe(EventType.SIGNAL_INGESTED, handler) # 发布事件 event = Event( type=EventType.SIGNAL_INGESTED, data={"signal_id": "123"} ) await event_bus.publish(event) # 等待异步处理 await asyncio.sleep(0.1) assert len(received_events) == 1 assert received_events[0].data["signal_id"] == "123" # 取消订阅 unsubscribe() # 发布另一个事件 await event_bus.publish(event) await asyncio.sleep(0.1) # 应该没有新事件 assert len(received_events) == 1 @pytest.mark.asyncio async def test_wildcard_subscription(self, event_bus): """测试通配符订阅""" received_events = [] async def handler(event: Event): received_events.append(event.type) event_bus.subscribe_wildcard(handler) # 发布不同类型的事件 await event_bus.publish( Event(type=EventType.SIGNAL_INGESTED, data={}) ) await event_bus.publish( Event(type=EventType.TASK_ASSIGNED, data={}) ) await asyncio.sleep(0.1) assert len(received_events) == 2 assert EventType.SIGNAL_INGESTED in received_events assert EventType.TASK_ASSIGNED in received_events @pytest.mark.asyncio async def test_event_filter(self, event_bus): """测试事件过滤""" received_events = [] async def handler(event: Event): received_events.append(event) # 只接收 signal_id > 100 的事件 def filter_func(event: Event): return event.data.get("signal_id", 0) > 100 event_bus.subscribe( EventType.SIGNAL_INGESTED, handler, filter_func=filter_func ) # 发布事件 await event_bus.publish( Event(type=EventType.SIGNAL_INGESTED, data={"signal_id": 50}) ) await event_bus.publish( Event(type=EventType.SIGNAL_INGESTED, data={"signal_id": 150}) ) await asyncio.sleep(0.1) # 只应该收到一个事件 assert len(received_events) == 1 assert received_events[0].data["signal_id"] == 150 @pytest.mark.asyncio async def test_publish_and_wait(self, event_bus): """测试发布并等待处理完成""" results = [] async def handler1(event: Event): results.append("handler1") await asyncio.sleep(0.1) return "result1" async def handler2(event: Event): results.append("handler2") return "result2" event_bus.subscribe(EventType.SIGNAL_INGESTED, handler1) event_bus.subscribe(EventType.SIGNAL_INGESTED, handler2) event = Event(type=EventType.SIGNAL_INGESTED, data={}) handler_results = await event_bus.publish_and_wait(event, timeout=1.0) assert len(results) == 2 assert len(handler_results) == 2 @pytest.mark.asyncio async def test_metrics(self, event_bus): """测试事件指标""" async def handler(event: Event): pass event_bus.subscribe(EventType.SIGNAL_INGESTED, handler) await event_bus.publish( Event(type=EventType.SIGNAL_INGESTED, data={}) ) await asyncio.sleep(0.1) metrics = event_bus.metrics assert metrics["published"] >= 1 assert metrics["delivered"] >= 1 @pytest.mark.asyncio async def test_event_history(self, event_bus): """测试事件历史""" event_bus._max_history = 5 # 发布超过历史限制的事件 for i in range(10): await event_bus.publish( Event(type=EventType.SIGNAL_INGESTED, data={"index": i}) ) history = event_bus.history assert len(history) == 5 # 最新的事件应该在历史中 assert history[-1]["data"]["index"] == 9 @pytest.mark.asyncio async def test_subscriber_count(self, event_bus): """测试订阅者计数""" async def handler1(event: Event): pass async def handler2(event: Event): pass # 特定事件订阅 event_bus.subscribe(EventType.SIGNAL_INGESTED, handler1) event_bus.subscribe(EventType.SIGNAL_INGESTED, handler2) # 通配符订阅 event_bus.subscribe_wildcard(handler1) assert event_bus.get_subscriber_count(EventType.SIGNAL_INGESTED) == 2 assert event_bus.get_subscriber_count() == 3 # 包括通配符订阅 @pytest.mark.asyncio async def test_handler_error_handling(self, event_bus): """测试处理器错误处理""" received_normal = [] async def failing_handler(event: Event): raise ValueError("Test error") async def normal_handler(event: Event): received_normal.append(event) event_bus.subscribe(EventType.SIGNAL_INGESTED, failing_handler) event_bus.subscribe(EventType.SIGNAL_INGESTED, normal_handler) await event_bus.publish( Event(type=EventType.SIGNAL_INGESTED, data={}) ) await asyncio.sleep(0.1) # 正常处理器应该仍然被调用 assert len(received_normal) == 1 # 错误应该被记录 assert event_bus.metrics["failed"] >= 1 class TestGlobalEventBus: """测试全局事件总线""" @pytest.mark.asyncio async def test_get_event_bus_singleton(self): """测试获取单例""" bus1 = get_event_bus() bus2 = get_event_bus() assert bus1 is bus2 @pytest.mark.asyncio async def test_convenience_functions(self): """测试便捷函数""" received = [] async def handler(event: Event): received.append(event) # 使用便捷函数订阅 unsubscribe = subscribe_event(EventType.SIGNAL_INGESTED, handler) # 使用便捷函数发布 await publish_event(EventType.SIGNAL_INGESTED, {"test": True}) await asyncio.sleep(0.1) assert len(received) == 1 # 取消订阅 unsubscribe() @pytest.mark.asyncio async def test_event_bus_context(self): """测试事件总线上下文管理器""" async with event_bus_context() as bus: assert bus._running is True received = [] async def handler(event: Event): received.append(event) bus.subscribe(EventType.SIGNAL_INGESTED, handler) await bus.publish(Event(type=EventType.SIGNAL_INGESTED, data={})) await asyncio.sleep(0.1) assert len(received) == 1 # 退出上下文后应该停止 assert bus._running is False @pytest.mark.asyncio async def test_multiple_event_types(): """测试多种事件类型""" bus = EventBus() await bus.start() try: counters = {event_type: 0 for event_type in EventType} async def count_handler(event: Event): counters[event.type] += 1 # 订阅所有事件类型 for event_type in EventType: bus.subscribe(event_type, count_handler) # 发布各种事件 await bus.publish(Event(type=EventType.SIGNAL_INGESTED, data={})) await bus.publish(Event(type=EventType.TASK_ASSIGNED, data={})) await bus.publish(Event(type=EventType.CODE_COMMIT, data={})) await asyncio.sleep(0.1) assert counters[EventType.SIGNAL_INGESTED] == 1 assert counters[EventType.TASK_ASSIGNED] == 1 assert counters[EventType.CODE_COMMIT] == 1 finally: await bus.stop() if __name__ == "__main__": pytest.main([__file__, "-v"])