ForcePilot/backend/test/unit/channels/test_broadcast.py
Kris 3264900bc9 test: 新增多渠道单元测试用例并配置测试环境变量
新增了Twitch、Telegram、Discord、Slack、Mattermost、WeChat、Zalo等多渠道的单元测试用例,覆盖了令牌处理、速率限制、消息去重、会话解析、格式转换、安全策略等模块
同时在测试配置中添加了测试用的OpenAI API密钥环境变量
2026-05-12 00:56:47 +08:00

180 lines
6.1 KiB
Python

from __future__ import annotations
import asyncio
import pytest
from yuxi.channels.infra.broadcast import EventBroadcaster
class TestEventBroadcaster:
@pytest.mark.asyncio
async def test_broadcast_delivers_to_subscriber(self):
broadcaster = EventBroadcaster()
queue = broadcaster.subscribe("test.event")
await broadcaster.broadcast("test.event", {"key": "value"})
received = await asyncio.wait_for(queue.get(), timeout=1.0)
assert received == {"event": "test.event", "payload": {"key": "value"}}
@pytest.mark.asyncio
async def test_broadcast_does_not_deliver_to_other_events(self):
broadcaster = EventBroadcaster()
queue = broadcaster.subscribe("event.a")
await broadcaster.broadcast("event.b", {"key": "value"})
with pytest.raises(asyncio.TimeoutError):
await asyncio.wait_for(queue.get(), timeout=0.1)
@pytest.mark.asyncio
async def test_wildcard_subscriber_receives_all(self):
broadcaster = EventBroadcaster()
queue = broadcaster.subscribe("*")
await broadcaster.broadcast("any.event", {"key": "value"})
received = await asyncio.wait_for(queue.get(), timeout=1.0)
assert received == {"event": "any.event", "payload": {"key": "value"}}
@pytest.mark.asyncio
async def test_unsubscribe_stops_delivery(self):
broadcaster = EventBroadcaster()
queue = broadcaster.subscribe("test.event")
broadcaster.unsubscribe("test.event", queue)
await broadcaster.broadcast("test.event", {})
with pytest.raises(asyncio.TimeoutError):
await asyncio.wait_for(queue.get(), timeout=0.1)
@pytest.mark.asyncio
async def test_unsubscribe_nonexistent_does_not_raise(self):
broadcaster = EventBroadcaster()
queue: asyncio.Queue = asyncio.Queue()
broadcaster.unsubscribe("nonexistent", queue)
@pytest.mark.asyncio
async def test_node_send_to_session(self):
broadcaster = EventBroadcaster()
queue = broadcaster.subscribe("session:sess-1")
await broadcaster.node_send_to_session("sess-1", "custom.event", {"data": 1})
received = await asyncio.wait_for(queue.get(), timeout=1.0)
assert received == {"event": "custom.event", "payload": {"data": 1}}
@pytest.mark.asyncio
async def test_multiple_subscribers_same_event(self):
broadcaster = EventBroadcaster()
q1 = broadcaster.subscribe("multi")
q2 = broadcaster.subscribe("multi")
await broadcaster.broadcast("multi", {"x": 1})
r1 = await asyncio.wait_for(q1.get(), timeout=1.0)
r2 = await asyncio.wait_for(q2.get(), timeout=1.0)
assert r1["payload"] == {"x": 1}
assert r2["payload"] == {"x": 1}
def test_subscriber_count(self):
broadcaster = EventBroadcaster()
assert broadcaster.subscriber_count() == 0
broadcaster.subscribe("e1")
broadcaster.subscribe("e1")
broadcaster.subscribe("e2")
assert broadcaster.subscriber_count("e1") == 2
assert broadcaster.subscriber_count("e2") == 1
assert broadcaster.subscriber_count() == 3
@pytest.mark.asyncio
async def test_callback_subscription_receives_broadcast(self):
broadcaster = EventBroadcaster()
received = []
def handler(event, payload):
received.append((event, payload))
broadcaster.subscribe_callback("test.event", handler)
await broadcaster.broadcast("test.event", {"data": 1})
assert received == [("test.event", {"data": 1})]
@pytest.mark.asyncio
async def test_async_callback_subscription(self):
broadcaster = EventBroadcaster()
received = []
async def handler(event, payload):
received.append((event, payload))
broadcaster.subscribe_callback("test.event", handler)
await broadcaster.broadcast("test.event", {"data": 1})
assert received == [("test.event", {"data": 1})]
@pytest.mark.asyncio
async def test_wildcard_callback_receives_all(self):
broadcaster = EventBroadcaster()
received = []
def handler(event, payload):
received.append(event)
broadcaster.subscribe_callback("*", handler)
await broadcaster.broadcast("event.a", {})
await broadcaster.broadcast("event.b", {})
assert received == ["event.a", "event.b"]
@pytest.mark.asyncio
async def test_unsubscribe_callback_stops_delivery(self):
broadcaster = EventBroadcaster()
received = []
def handler(event, payload):
received.append(event)
broadcaster.subscribe_callback("test.event", handler)
broadcaster.unsubscribe_callback("test.event", handler)
await broadcaster.broadcast("test.event", {})
assert received == []
def test_subscriber_count_includes_callbacks(self):
broadcaster = EventBroadcaster()
broadcaster.subscribe("e1")
broadcaster.subscribe_callback("e1", lambda e, p: None)
broadcaster.subscribe_callback("e2", lambda e, p: None)
assert broadcaster.subscriber_count("e1") == 2
assert broadcaster.subscriber_count("e2") == 1
assert broadcaster.subscriber_count() == 3
@pytest.mark.asyncio
async def test_callback_error_does_not_block_broadcast(self):
broadcaster = EventBroadcaster()
received = []
def good_handler(event, payload):
received.append(event)
def bad_handler(event, payload):
raise RuntimeError("boom")
broadcaster.subscribe_callback("test.event", bad_handler)
broadcaster.subscribe_callback("test.event", good_handler)
await broadcaster.broadcast("test.event", {})
assert received == ["test.event"]
@pytest.mark.asyncio
async def test_broadcast_without_payload(self):
broadcaster = EventBroadcaster()
queue = broadcaster.subscribe("test.event")
await broadcaster.broadcast("test.event")
received = await asyncio.wait_for(queue.get(), timeout=1.0)
assert received == {"event": "test.event", "payload": None}