blob: 5cc6d6a1bb539c946dc974b2ab62d211fe3509ed [file] [log] [blame] [raw]
"""
消息处理器核心功能单元测试
重点测试群聊参与管理、权限验证、消息处理等核心业务逻辑
"""
import pytest
import random
from unittest.mock import Mock, AsyncMock, patch
from telegram import Update, Message, User, Chat
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent / "src"))
from claude_agent.telegram.message_handler import MessageHandler, GroupParticipationManager
class TestGroupParticipationManager:
"""群聊参与管理器测试"""
def test_initialization_default(self):
"""测试默认初始化"""
manager = GroupParticipationManager()
# 检查默认bot名字
expected_names = [
'claude', 'Claude', 'CLAUDE',
'克劳德', 'AI', 'ai', 'bot', 'Bot', 'BOT',
'助手', '机器人'
]
assert manager._bot_names == expected_names
assert manager._participation_range == [1, 10]
assert manager._message_counters == {}
assert manager._trigger_thresholds == {}
def test_initialization_with_config(self):
"""测试使用配置初始化"""
bot_names = ['TestBot', 'MyBot']
participation_range = [5, 15]
manager = GroupParticipationManager(
bot_names=bot_names,
participation_range=participation_range
)
assert manager._bot_names == bot_names
assert manager._participation_range == participation_range
def test_reset_counter(self):
"""测试重置计数器"""
manager = GroupParticipationManager(participation_range=[3, 5])
chat_id = "-123456789"
with patch('random.randint', return_value=4) as mock_randint:
manager.reset_counter(chat_id)
assert manager._message_counters[chat_id] == 0
assert manager._trigger_thresholds[chat_id] == 4
mock_randint.assert_called_once_with(3, 5)
def test_should_participate_random_first_time(self):
"""测试首次检查随机参与"""
manager = GroupParticipationManager()
chat_id = "-123456789"
with patch.object(manager, 'reset_counter') as mock_reset:
result = manager.should_participate_random(chat_id)
assert result is False
mock_reset.assert_called_once_with(chat_id)
def test_should_participate_random_threshold_not_reached(self):
"""测试未达到参与阈值"""
manager = GroupParticipationManager()
chat_id = "-123456789"
# 手动设置状态
manager._message_counters[chat_id] = 2
manager._trigger_thresholds[chat_id] = 5
result = manager.should_participate_random(chat_id)
assert result is False
assert manager._message_counters[chat_id] == 3 # 计数器递增
def test_should_participate_random_threshold_reached(self):
"""测试达到参与阈值"""
manager = GroupParticipationManager()
chat_id = "-123456789"
# 手动设置状态
manager._message_counters[chat_id] = 4
manager._trigger_thresholds[chat_id] = 5
with patch.object(manager, 'reset_counter') as mock_reset:
result = manager.should_participate_random(chat_id)
assert result is True
mock_reset.assert_called_once_with(chat_id)
def test_is_name_mentioned_positive(self):
"""测试名字被提及的情况"""
manager = GroupParticipationManager()
test_cases = [
"Hello Claude, how are you?",
"CLAUDE please help",
"I need 助手 to assist",
"Call the AI for help",
"克劳德 你好"
]
for text in test_cases:
result = manager.is_name_mentioned(text)
assert result is True, f"Failed for text: {text}"
def test_is_name_mentioned_negative(self):
"""测试名字未被提及的情况"""
manager = GroupParticipationManager()
test_cases = [
"Hello world",
"This is a normal message",
"Just chatting with friends",
"No machines here" # 不包含任何默认bot名字的词
]
for text in test_cases:
result = manager.is_name_mentioned(text)
assert result is False, f"Failed for text: {text}"
def test_is_name_mentioned_case_insensitive(self):
"""测试大小写不敏感的名字检测"""
manager = GroupParticipationManager(bot_names=['TestBot'])
test_cases = [
"testbot help me",
"TESTBOT please",
"TestBot is here",
"I need testBot"
]
for text in test_cases:
result = manager.is_name_mentioned(text)
assert result is True, f"Failed for text: {text}"
class TestMessageHandler:
"""消息处理器测试"""
@pytest.fixture
def mock_components(self):
"""创建所有组件的Mock"""
return {
'telegram_client': Mock(),
'context_manager': Mock(),
'file_handler': Mock(),
'claude_agent': Mock(),
'stream_sender': Mock()
}
@pytest.fixture
def message_handler(self, mock_components):
"""创建消息处理器实例"""
return MessageHandler(
telegram_client=mock_components['telegram_client'],
context_manager=mock_components['context_manager'],
file_handler=mock_components['file_handler'],
claude_agent=mock_components['claude_agent'],
stream_sender=mock_components['stream_sender'],
allowed_users=[111111, 222222],
allowed_groups=[-333333, -444444]
)
def test_initialization_default_config(self, mock_components):
"""测试默认配置初始化"""
handler = MessageHandler(
allowed_users=[111],
allowed_groups=[-222],
**mock_components
)
assert handler.allowed_users == {111}
assert handler.allowed_groups == {-222}
assert handler.webhook_broadcast_callback is None
assert isinstance(handler.participation_manager, GroupParticipationManager)
def test_initialization_with_config(self, mock_components):
"""测试使用配置初始化"""
telegram_config = {
'group_participation': {
'bot_names': ['MyBot', 'TestBot'],
'random_participation_range': [8, 15]
}
}
webhook_callback = Mock()
handler = MessageHandler(
allowed_users=[111],
allowed_groups=[-222],
telegram_config=telegram_config,
webhook_broadcast_callback=webhook_callback,
**mock_components
)
assert handler.webhook_broadcast_callback == webhook_callback
assert handler.participation_manager._bot_names == ['MyBot', 'TestBot']
assert handler.participation_manager._participation_range == [8, 15]
def test_build_user_info(self, message_handler):
"""测试构建用户信息"""
mock_user = Mock(spec=User)
mock_user.id = 123456
mock_user.username = "test_user"
mock_user.first_name = "Test"
mock_user.last_name = "User"
mock_user.language_code = "en"
mock_chat = Mock(spec=Chat)
mock_chat.id = -789012
mock_chat.title = "Test Group"
mock_chat.type = "group"
user_info = message_handler._build_user_info(mock_user, mock_chat)
expected_user_info = {
'user_id': 123456,
'username': "test_user",
'first_name': "Test",
'last_name': "User",
'language_code': "en", # 根据实际实现添加
'chat_id': -789012,
'chat_title': "Test Group",
'chat_type': "group"
}
assert user_info == expected_user_info
def test_build_user_info_missing_optional_fields(self, message_handler):
"""测试构建用户信息(缺少可选字段)"""
mock_user = Mock(spec=User)
mock_user.id = 123456
mock_user.username = None # 可选字段
mock_user.first_name = "Test"
mock_user.last_name = None # 可选字段
mock_user.language_code = None
mock_chat = Mock(spec=Chat)
mock_chat.id = 111111 # 私聊
mock_chat.title = None # 私聊没有标题
mock_chat.type = "private"
user_info = message_handler._build_user_info(mock_user, mock_chat)
expected_user_info = {
'user_id': 123456,
'username': None,
'first_name': "Test",
'last_name': None,
'language_code': None,
'chat_id': 111111,
'chat_title': None,
'chat_type': "private"
}
assert user_info == expected_user_info
@pytest.mark.asyncio
async def test_save_message_to_agent(self, message_handler, mock_components):
"""测试保存消息到Agent"""
# Mock Claude Agent的内部方法
mock_agent = Mock()
mock_components['claude_agent']._get_or_create_agent = Mock(return_value=mock_agent)
user_info = {'user_id': 123, 'username': 'test'}
await message_handler._save_message_to_agent(
chat_id=-123456,
user_id=123,
message="Test message",
is_bot=False,
user_info=user_info
)
# 验证获取Agent被调用
mock_components['claude_agent']._get_or_create_agent.assert_called_once_with(-123456)
@pytest.mark.asyncio
async def test_is_authorized_user_private_chat_authorized(self, message_handler):
"""测试私聊授权用户"""
mock_user = Mock()
mock_user.id = 111111 # 在允许列表中
mock_chat = Mock()
mock_chat.type = "private"
mock_chat.id = 111111
mock_update = Mock()
mock_update.effective_user = mock_user
mock_update.effective_chat = mock_chat
result = await message_handler._is_authorized(mock_update)
assert result is True
@pytest.mark.asyncio
async def test_is_authorized_user_private_chat_unauthorized(self, message_handler):
"""测试私聊未授权用户"""
mock_user = Mock()
mock_user.id = 999999 # 不在允许列表中
mock_chat = Mock()
mock_chat.type = "private"
mock_chat.id = 999999
mock_update = Mock()
mock_update.effective_user = mock_user
mock_update.effective_chat = mock_chat
result = await message_handler._is_authorized(mock_update)
assert result is False
@pytest.mark.asyncio
async def test_is_authorized_group_chat_authorized(self, message_handler):
"""测试授权群组"""
mock_user = Mock()
mock_user.id = 123456
mock_chat = Mock()
mock_chat.type = "group"
mock_chat.id = -333333 # 在允许列表中
mock_update = Mock()
mock_update.effective_user = mock_user
mock_update.effective_chat = mock_chat
result = await message_handler._is_authorized(mock_update)
assert result is True
@pytest.mark.asyncio
async def test_is_authorized_group_chat_unauthorized(self, message_handler):
"""测试未授权群组"""
mock_user = Mock()
mock_user.id = 123456
mock_chat = Mock()
mock_chat.type = "supergroup"
mock_chat.id = -999999 # 不在允许列表中
mock_update = Mock()
mock_update.effective_user = mock_user
mock_update.effective_chat = mock_chat
result = await message_handler._is_authorized(mock_update)
assert result is False
@pytest.mark.asyncio
async def test_is_authorized_unknown_chat_type(self, message_handler):
"""测试未知聊天类型"""
mock_user = Mock()
mock_user.id = 123456
mock_chat = Mock()
mock_chat.type = "channel" # 未处理的类型
mock_chat.id = -123456
mock_update = Mock()
mock_update.effective_user = mock_user
mock_update.effective_chat = mock_chat
result = await message_handler._is_authorized(mock_update)
assert result is False
def test_extract_reply_info_with_text(self, message_handler):
"""测试提取文本回复信息"""
# 创建回复的消息
mock_reply_user = Mock(spec=User)
mock_reply_user.id = 12345
mock_reply_user.username = "replied_user"
mock_reply_user.first_name = "Replied"
mock_reply_user.last_name = None
mock_reply_user.is_bot = False
mock_reply_message = Mock(spec=Message)
mock_reply_message.from_user = mock_reply_user
mock_reply_message.text = "This is the replied message"
mock_reply_message.photo = None
mock_reply_message.document = None
mock_reply_message.message_id = 100
# Set date to None to avoid mock astimezone() calls
mock_reply_message.configure_mock(date=None)
# 创建当前消息
mock_message = Mock(spec=Message)
mock_message.reply_to_message = mock_reply_message
reply_info = message_handler._extract_reply_info(mock_message)
expected_reply_info = {
'user_info': {
'user_id': 12345,
'username': "replied_user",
'first_name': "Replied",
'last_name': None,
'is_bot': False
},
'timestamp': None, # No date set on mock
'content': "This is the replied message"
}
assert reply_info == expected_reply_info
def test_extract_reply_info_with_photo(self, message_handler):
"""测试提取图片回复信息"""
mock_reply_user = Mock(spec=User)
mock_reply_user.id = 12346
mock_reply_user.username = "photo_user"
mock_reply_user.first_name = "Photo"
mock_reply_user.last_name = None
mock_reply_user.is_bot = False
mock_reply_message = Mock(spec=Message)
mock_reply_message.from_user = mock_reply_user
mock_reply_message.text = None
mock_reply_message.photo = [Mock()] # 有图片
mock_reply_message.document = None
mock_reply_message.caption = "A beautiful photo"
mock_reply_message.message_id = 200
# Set date to None to avoid mock astimezone() calls
mock_reply_message.configure_mock(date=None)
mock_message = Mock(spec=Message)
mock_message.reply_to_message = mock_reply_message
reply_info = message_handler._extract_reply_info(mock_message)
expected_reply_info = {
'user_info': {
'user_id': 12346,
'username': "photo_user",
'first_name': "Photo",
'last_name': None,
'is_bot': False
},
'timestamp': None, # No date set on mock
'content': "A beautiful photo"
}
assert reply_info == expected_reply_info
def test_extract_reply_info_with_document(self, message_handler):
"""测试提取文档回复信息"""
mock_reply_user = Mock(spec=User)
mock_reply_user.id = 12347
mock_reply_user.username = "doc_user"
mock_reply_user.first_name = "Document"
mock_reply_user.last_name = None
mock_reply_user.is_bot = False
mock_document = Mock()
mock_document.file_name = "test_file.pdf"
mock_reply_message = Mock(spec=Message)
mock_reply_message.from_user = mock_reply_user
mock_reply_message.text = None
mock_reply_message.photo = None
mock_reply_message.document = mock_document
mock_reply_message.caption = "Important document"
mock_reply_message.message_id = 300
# Set date to None to avoid mock astimezone() calls
mock_reply_message.configure_mock(date=None)
mock_message = Mock(spec=Message)
mock_message.reply_to_message = mock_reply_message
reply_info = message_handler._extract_reply_info(mock_message)
expected_reply_info = {
'user_info': {
'user_id': 12347,
'username': "doc_user",
'first_name': "Document",
'last_name': None,
'is_bot': False
},
'timestamp': None, # No date set on mock
'content': "Important document"
}
assert reply_info == expected_reply_info
def test_extract_reply_info_no_caption(self, message_handler):
"""测试提取无说明的回复信息"""
mock_reply_user = Mock(spec=User)
mock_reply_user.id = 12348
mock_reply_user.username = "user"
mock_reply_user.first_name = "User"
mock_reply_user.last_name = None
mock_reply_user.is_bot = False
mock_reply_message = Mock(spec=Message)
mock_reply_message.from_user = mock_reply_user
mock_reply_message.text = None
mock_reply_message.photo = [Mock()]
mock_reply_message.document = None
mock_reply_message.caption = None # 无说明
mock_reply_message.message_id = 400
mock_reply_message.date = None # No date
mock_message = Mock(spec=Message)
mock_message.reply_to_message = mock_reply_message
reply_info = message_handler._extract_reply_info(mock_message)
# When there's no caption but there are photos, the method returns "[图片]"
assert reply_info['content'] == "[图片]"
@pytest.mark.asyncio
async def test_get_reply_info_private_chat_authorized(self, message_handler):
"""测试私聊授权用户的回复信息"""
mock_user = Mock(spec=User)
mock_user.id = 111111 # 授权用户
mock_chat = Mock(spec=Chat)
mock_chat.type = "private"
mock_chat.id = 111111
mock_update = Mock(spec=Update)
mock_update.effective_user = mock_user
mock_update.effective_chat = mock_chat
should_reply, is_random = await message_handler._get_reply_info(mock_update, "Hello")
assert should_reply is True
assert is_random is False
@pytest.mark.asyncio
async def test_get_reply_info_private_chat_unauthorized(self, message_handler):
"""测试私聊未授权用户的回复信息"""
mock_user = Mock(spec=User)
mock_user.id = 999999 # 未授权用户
mock_chat = Mock(spec=Chat)
mock_chat.type = "private"
mock_chat.id = 999999
mock_update = Mock(spec=Update)
mock_update.effective_user = mock_user
mock_update.effective_chat = mock_chat
should_reply, is_random = await message_handler._get_reply_info(mock_update, "Hello")
assert should_reply is False
assert is_random is False
@pytest.mark.asyncio
async def test_get_reply_info_group_mentioned(self, message_handler):
"""测试群聊中被提及的情况"""
mock_user = Mock(spec=User)
mock_user.id = 123456
mock_chat = Mock(spec=Chat)
mock_chat.type = "group"
mock_chat.id = -333333 # 授权群组
mock_update = Mock(spec=Update)
mock_update.effective_user = mock_user
mock_update.effective_chat = mock_chat
with patch.object(message_handler.participation_manager, 'is_name_mentioned', return_value=True):
should_reply, is_random = await message_handler._get_reply_info(mock_update, "Hello Claude")
assert should_reply is True
assert is_random is False
@pytest.mark.asyncio
async def test_get_reply_info_group_random_participation(self, message_handler):
"""测试群聊随机参与"""
mock_user = Mock(spec=User)
mock_user.id = 123456
mock_chat = Mock(spec=Chat)
mock_chat.type = "supergroup"
mock_chat.id = -444444 # 授权群组
mock_update = Mock(spec=Update)
mock_update.effective_user = mock_user
mock_update.effective_chat = mock_chat
with patch.object(message_handler.participation_manager, 'is_name_mentioned', return_value=False):
with patch.object(message_handler.participation_manager, 'should_participate_random', return_value=True):
should_reply, is_random = await message_handler._get_reply_info(mock_update, "Normal message")
assert should_reply is True
assert is_random is True
@pytest.mark.asyncio
async def test_get_reply_info_group_no_participation(self, message_handler):
"""测试群聊不参与"""
mock_user = Mock(spec=User)
mock_user.id = 123456
mock_chat = Mock(spec=Chat)
mock_chat.type = "group"
mock_chat.id = -333333
mock_update = Mock(spec=Update)
mock_update.effective_user = mock_user
mock_update.effective_chat = mock_chat
with patch.object(message_handler.participation_manager, 'is_name_mentioned', return_value=False):
with patch.object(message_handler.participation_manager, 'should_participate_random', return_value=False):
should_reply, is_random = await message_handler._get_reply_info(mock_update, "Normal message")
assert should_reply is False
assert is_random is False
@pytest.mark.asyncio
async def test_get_reply_info_unauthorized_group(self, message_handler):
"""测试未授权群组"""
mock_user = Mock(spec=User)
mock_user.id = 123456
mock_chat = Mock(spec=Chat)
mock_chat.type = "group"
mock_chat.id = -999999 # 未授权群组
mock_update = Mock(spec=Update)
mock_update.effective_user = mock_user
mock_update.effective_chat = mock_chat
should_reply, is_random = await message_handler._get_reply_info(mock_update, "Hello Claude")
assert should_reply is False
assert is_random is False
class TestMessageHandlerEdgeCases:
"""消息处理器边界情况测试"""
@pytest.fixture
def minimal_handler(self):
"""创建最小配置的消息处理器"""
mock_components = {
'telegram_client': Mock(),
'context_manager': Mock(),
'file_handler': Mock(),
'claude_agent': Mock(),
'stream_sender': Mock()
}
return MessageHandler(
allowed_users=[],
allowed_groups=[],
**mock_components
)
@pytest.mark.asyncio
async def test_empty_allowed_lists(self, minimal_handler):
"""测试空的授权列表"""
mock_user = Mock()
mock_user.id = 123456
mock_chat = Mock()
mock_chat.type = "private"
mock_chat.id = 123456
mock_update = Mock()
mock_update.effective_user = mock_user
mock_update.effective_chat = mock_chat
result = await minimal_handler._is_authorized(mock_update)
assert result is False
def test_participation_manager_with_empty_config(self):
"""测试空配置的参与管理器"""
manager = GroupParticipationManager(bot_names=[], participation_range=[1, 1])
# 空的bot名字列表
result = manager.is_name_mentioned("Claude help me")
assert result is False
# 范围为[1,1]意味着每次都会参与
chat_id = "-123"
manager.reset_counter(chat_id)
result = manager.should_participate_random(chat_id)
assert result is True
def test_extract_reply_info_no_user(self, minimal_handler):
"""测试回复消息没有用户信息的情况"""
mock_reply_message = Mock(spec=Message)
mock_reply_message.from_user = None # 没有用户信息
mock_reply_message.text = "Message without user"
mock_reply_message.photo = None
mock_reply_message.document = None
mock_reply_message.message_id = 500
mock_message = Mock(spec=Message)
mock_message.reply_to_message = mock_reply_message
reply_info = minimal_handler._extract_reply_info(mock_message)
# 应该仍然能提取信息,但用户信息包含默认值
expected_user_info = {
'user_id': 'unknown',
'username': None,
'first_name': 'system',
'last_name': None,
'is_bot': False
}
assert reply_info['user_info'] == expected_user_info
assert reply_info['content'] == "Message without user"
def test_build_user_info_extreme_values(self, minimal_handler):
"""测试极值情况的用户信息构建"""
mock_user = Mock(spec=User)
mock_user.id = 2**63 - 1 # 最大整数
mock_user.username = "a" * 100 # 很长的用户名
mock_user.first_name = "" # 空字符串
mock_user.last_name = " " # 空白字符串
mock_user.is_bot = True
mock_chat = Mock(spec=Chat)
mock_chat.id = -(2**63) # 最小整数
mock_chat.title = "群组" * 100 # 很长的群组名
mock_chat.type = "supergroup"
user_info = minimal_handler._build_user_info(mock_user, mock_chat)
# 验证所有值都被正确处理
assert user_info['user_id'] == 2**63 - 1
assert user_info['username'] == "a" * 100
assert user_info['first_name'] == ""
assert user_info['last_name'] == " "
# The actual _build_user_info method doesn't include is_bot field
# assert user_info['is_bot'] is True # This field is not included in actual implementation
assert user_info['chat_id'] == -(2**63)
assert user_info['chat_title'] == "群组" * 100
assert user_info['chat_type'] == "supergroup"
class TestBotInteractionFunctionality:
"""Bot交互功能测试(从test_bot_interaction_simple.py合并)"""
@pytest.fixture
def mock_components(self):
"""创建模拟组件"""
return {
'telegram_client': Mock(),
'context_manager': Mock(),
'file_handler': Mock(),
'claude_agent': Mock(),
'stream_sender': Mock()
}
@pytest.fixture
def message_handler(self, mock_components):
"""创建消息处理器"""
return MessageHandler(
telegram_client=mock_components['telegram_client'],
context_manager=mock_components['context_manager'],
file_handler=mock_components['file_handler'],
claude_agent=mock_components['claude_agent'],
stream_sender=mock_components['stream_sender'],
allowed_users=[123456],
allowed_groups=[789012]
)
def test_message_handler_initialization_with_lists(self, message_handler):
"""测试消息处理器初始化(使用列表参数)"""
assert message_handler.allowed_users == {123456}
assert message_handler.allowed_groups == {789012}
assert message_handler.participation_manager is not None
def test_bot_mention_detection_webhook(self, message_handler):
"""测试Webhook Bot提及检测"""
# 设置bot用户名
message_handler.bot_username = "TestBot"
# 测试@mention检测
result = message_handler._check_mention_in_text_webhook("Hello @TestBot, how are you?")
assert result is True
# 测试无@mention
result = message_handler._check_mention_in_text_webhook("Hello everyone!")
assert result is False
def test_build_user_info_comprehensive(self, message_handler):
"""测试构建用户信息(全面测试)"""
from telegram import User, Chat
user = Mock(spec=User)
user.id = 123456
user.username = "testuser"
user.first_name = "Test"
user.last_name = "User"
user.language_code = "en"
chat = Mock(spec=Chat)
chat.id = 789012
chat.type = "group"
chat.title = "Test Group"
user_info = message_handler._build_user_info(user, chat)
assert user_info['user_id'] == 123456
assert user_info['username'] == "testuser"
assert user_info['first_name'] == "Test"
assert user_info['chat_id'] == 789012
assert user_info['chat_type'] == "group"
def test_format_reply_context_user(self, message_handler):
"""测试回复上下文格式化(用户)"""
from datetime import datetime
reply_info = {
'user_info': {
'user_id': 999,
'username': 'testuser',
'first_name': 'Test',
'is_bot': False
},
'timestamp': datetime(2025, 10, 4, 10, 30),
'content': 'Hello world'
}
result = message_handler._format_reply_context(reply_info)
expected = "↳ 回复 [@testuser 10:30]: Hello world"
assert result == expected
def test_format_reply_context_bot_user(self, message_handler):
"""测试Bot回复上下文格式化"""
from datetime import datetime
reply_info = {
'user_info': {
'user_id': 888,
'username': 'testbot',
'first_name': 'TestBot',
'is_bot': True
},
'timestamp': datetime(2025, 10, 4, 10, 30),
'content': 'Bot response'
}
result = message_handler._format_reply_context(reply_info)
expected = "↳ 回复 [🤖 @testbot 10:30]: Bot response"
assert result == expected
def test_build_user_display_name_variations(self, message_handler):
"""测试构建用户显示名(各种情况)"""
# 有用户名的情况
user_info = {'username': 'testuser'}
result = message_handler._build_user_display_name(123, user_info)
assert result == "@testuser"
# 只有名字的情况
user_info = {'first_name': 'Test', 'last_name': 'User'}
result = message_handler._build_user_display_name(123, user_info)
assert result == "Test User"
# 只有first_name的情况
user_info = {'first_name': 'Test'}
result = message_handler._build_user_display_name(123, user_info)
assert result == "Test"
# 没有任何信息的情况
user_info = {}
result = message_handler._build_user_display_name(123, user_info)
assert result == "User123"