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

179 lines
6.0 KiB
Python

from __future__ import annotations
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from yuxi.channels.adapters.telegram.adapter import TelegramAdapter
from yuxi.channels.models import ChannelStatus
class TestMessageReactionCount:
@pytest.fixture
def adapter(self):
return TelegramAdapter(config={"bot_token": "123:abc"})
def test_reaction_count_passthrough(self, adapter):
mr_chat = MagicMock()
mr_chat.id = -10012345
mr_chat.type = "supergroup"
mrc = MagicMock()
mrc.chat = mr_chat
mrc.message_id = 42
rc1 = MagicMock()
rc1.type = MagicMock()
rc1.type.__str__ = lambda s: "ReactionTypeEmoji"
rc1.total_count = 5
rc1.emoji = "\U0001f44d"
rc2 = MagicMock()
rc2.type = MagicMock()
rc2.type.__str__ = lambda s: "ReactionTypeEmoji"
rc2.total_count = 3
rc2.emoji = "\u2764\ufe0f"
mrc.reactions = [rc1, rc2]
update = MagicMock()
update.update_id = 1
update.message = None
update.edited_message = None
update.callback_query = None
update.my_chat_member = None
update.chat_member = None
update.poll = None
update.poll_answer = None
update.message_reaction = None
update.message_reaction_count = mrc
result = adapter._make_passthrough_event(update)
assert result.content == "(reaction count update)"
assert result.metadata["message_id"] == "42"
assert len(result.metadata["reactions"]) == 2
assert result.metadata["reactions"][0]["count"] == 5
assert result.metadata["reactions"][1]["count"] == 3
class TestStartPollingError:
@pytest.fixture
def adapter(self):
return TelegramAdapter(config={"bot_token": "123:abc"})
@pytest.mark.asyncio
async def test_polling_unexpected_error_sets_error_status(self, adapter):
adapter._application = MagicMock()
adapter._application.bot = AsyncMock()
adapter._application.updater = MagicMock()
adapter._application.updater.start_polling = AsyncMock(
side_effect=ConnectionError("network gone")
)
adapter._status = ChannelStatus.CONNECTED
try:
await adapter._application.updater.start_polling(
poll_interval=1.0, timeout=10
)
except Exception:
adapter._status = ChannelStatus.ERROR
assert adapter._status == ChannelStatus.ERROR
@pytest.mark.asyncio
async def test_polling_cancelled_error_preserved(self, adapter):
adapter._application = MagicMock()
adapter._application.bot = AsyncMock()
adapter._application.updater = MagicMock()
adapter._application.updater.start_polling = AsyncMock(
side_effect=ConnectionError("network gone")
)
adapter._status = ChannelStatus.CONNECTED
try:
await adapter._application.updater.start_polling(
poll_interval=1.0, timeout=10
)
except Exception:
adapter._status = ChannelStatus.ERROR
assert adapter._status == ChannelStatus.ERROR
@pytest.mark.asyncio
async def test_cancelled_error_does_not_change_status(self):
import asyncio
adapter = TelegramAdapter(config={"bot_token": "123:abc"})
adapter._status = ChannelStatus.CONNECTED
try:
raise asyncio.CancelledError()
except asyncio.CancelledError:
pass
assert adapter._status == ChannelStatus.CONNECTED
class TestDownloadMediaProtection:
@pytest.fixture
def adapter(self):
return TelegramAdapter(config={"bot_token": "123:abc"})
@pytest.mark.asyncio
async def test_download_large_file_raises_error(self, adapter):
mock_file = MagicMock()
mock_file.file_size = 200 * 1024 * 1024
adapter._application = MagicMock()
adapter._application.bot = AsyncMock()
adapter._application.bot.get_file = AsyncMock(return_value=mock_file)
with pytest.raises(ValueError, match="209715200"):
await adapter.download_media("file_id_123")
@pytest.mark.asyncio
async def test_download_small_file_allowed(self, adapter):
mock_file = MagicMock()
mock_file.file_size = 5 * 1024 * 1024
mock_file.download_as_bytearray = AsyncMock(return_value=b"data")
adapter._application = MagicMock()
adapter._application.bot = AsyncMock()
adapter._application.bot.get_file = AsyncMock(return_value=mock_file)
result = await adapter.download_media("file_id_123")
assert result == b"data"
@pytest.mark.asyncio
async def test_download_no_size_info_allowed(self, adapter):
mock_file = MagicMock()
mock_file.file_size = 0
mock_file.download_as_bytearray = AsyncMock(return_value=b"ok")
adapter._application = MagicMock()
adapter._application.bot = AsyncMock()
adapter._application.bot.get_file = AsyncMock(return_value=mock_file)
result = await adapter.download_media("file_id_123")
assert result == b"ok"
@pytest.mark.asyncio
async def test_download_none_size_allowed(self, adapter):
mock_file = MagicMock()
mock_file.file_size = None
mock_file.download_as_bytearray = AsyncMock(return_value=b"ok")
adapter._application = MagicMock()
adapter._application.bot = AsyncMock()
adapter._application.bot.get_file = AsyncMock(return_value=mock_file)
result = await adapter.download_media("file_id_123")
assert result == b"ok"
class TestLeaseFallback:
@pytest.mark.asyncio
async def test_lease_acquire_without_redis_still_acquires(self):
from yuxi.channels.adapters.telegram.lease import PollingLease
with patch("yuxi.channels.adapters.telegram.lease.PollingLease.acquire",
new=AsyncMock(return_value=True)):
lease = PollingLease({"bot_token": "test123"})
lease._acquired = True
assert lease.is_acquired is True