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

153 lines
5.1 KiB
Python

from __future__ import annotations
import asyncio
import pytest
from yuxi.channels.services.context import ChatAbortEntry, ChatRunBuffer, GatewayRequestContext
class TestChatAbortEntry:
def test_abort_cancels_task(self):
async def dummy():
await asyncio.sleep(10)
loop = asyncio.new_event_loop()
task = loop.create_task(dummy())
entry = ChatAbortEntry(task=task)
entry.abort()
loop.run_until_complete(asyncio.sleep(0))
assert task.cancelled()
loop.close()
def test_abort_with_none_task_does_not_raise(self):
entry = ChatAbortEntry(task=None)
entry.abort()
class TestChatRunBuffer:
def test_append_and_get_full_text(self):
buffer = ChatRunBuffer(run_id="test-1")
buffer.append_chunk("Hello ")
buffer.append_chunk("World")
assert buffer.get_full_text() == "Hello World"
def test_is_finished_defaults_false(self):
buffer = ChatRunBuffer(run_id="test-2")
assert not buffer.is_finished()
def test_mark_finished(self):
buffer = ChatRunBuffer(run_id="test-3")
buffer.mark_finished()
assert buffer.is_finished()
def test_empty_buffer_returns_empty_string(self):
buffer = ChatRunBuffer(run_id="test-4")
assert buffer.get_full_text() == ""
class TestGatewayRequestContext:
def test_default_values(self):
ctx = GatewayRequestContext()
assert ctx.runtime_config == {}
assert ctx.start_channel is None
assert ctx.stop_channel is None
assert ctx.broadcast_fn is None
assert ctx.node_send_to_session_fn is None
assert ctx.get_snapshot is None
assert ctx.chat_abort_controllers == {}
assert ctx.chat_run_buffers == {}
assert ctx.runtime_snapshot is None
assert ctx.dedupe is None
assert ctx.mark_channel_logged_out is None
def test_get_runtime_config(self):
ctx = GatewayRequestContext(runtime_config={"key": "value"})
assert ctx.get_runtime_config("key") == "value"
assert ctx.get_runtime_config("missing", "default") == "default"
def test_set_runtime_config(self):
ctx = GatewayRequestContext()
ctx.set_runtime_config("new_key", "new_value")
assert ctx.get_runtime_config("new_key") == "new_value"
def test_chat_abort_controllers_mutable(self):
ctx = GatewayRequestContext()
entry = ChatAbortEntry()
ctx.chat_abort_controllers["run-1"] = entry
assert ctx.chat_abort_controllers["run-1"] is entry
def test_chat_run_buffers_mutable(self):
ctx = GatewayRequestContext()
buffer = ChatRunBuffer(run_id="run-1")
ctx.chat_run_buffers["run-1"] = buffer
assert ctx.chat_run_buffers["run-1"] is buffer
def test_add_chat_run(self):
ctx = GatewayRequestContext()
entry = ChatAbortEntry()
buffer = ChatRunBuffer(run_id="run-1")
ctx.add_chat_run("run-1", entry, buffer)
assert ctx.chat_abort_controllers["run-1"] is entry
assert ctx.chat_run_buffers["run-1"] is buffer
def test_remove_chat_run(self):
ctx = GatewayRequestContext()
entry = ChatAbortEntry()
ctx.chat_abort_controllers["run-1"] = entry
ctx.chat_run_buffers["run-1"] = ChatRunBuffer(run_id="run-1")
ctx.remove_chat_run("run-1")
assert "run-1" not in ctx.chat_abort_controllers
assert "run-1" not in ctx.chat_run_buffers
def test_abort_chat_run(self):
ctx = GatewayRequestContext()
entry = ChatAbortEntry()
ctx.chat_abort_controllers["run-1"] = entry
assert ctx.abort_chat_run("run-1") is True
assert entry.is_aborted()
def test_abort_chat_run_unknown(self):
ctx = GatewayRequestContext()
assert ctx.abort_chat_run("nonexistent") is False
def test_get_chat_buffer(self):
ctx = GatewayRequestContext()
buffer = ChatRunBuffer(run_id="run-1")
ctx.chat_run_buffers["run-1"] = buffer
assert ctx.get_chat_buffer("run-1") is buffer
def test_get_chat_buffer_unknown(self):
ctx = GatewayRequestContext()
assert ctx.get_chat_buffer("nonexistent") is None
@pytest.mark.asyncio
async def test_callbacks_are_awaitable(self):
called = False
async def demo_start(channel_id: str, config: dict):
nonlocal called
called = True
ctx = GatewayRequestContext(start_channel=demo_start)
await ctx.start_channel("test", {})
assert called
@pytest.mark.asyncio
async def test_broadcast_method_delegates(self):
received = []
async def mock_broadcast(event: str, payload):
received.append((event, payload))
ctx = GatewayRequestContext(broadcast_fn=mock_broadcast)
await ctx.broadcast("test.event", {"data": 1})
assert received == [("test.event", {"data": 1})]
@pytest.mark.asyncio
async def test_broadcast_without_fn_does_not_raise(self):
ctx = GatewayRequestContext()
await ctx.broadcast("test.event", {})
await ctx.node_send_to_session("s1", "test", {})