# P2P Network Communication - Integration Tests """ 客户端-服务器集成测试 测试端到端消息传递、文件传输和语音通话流程 需求: 全部 """ import asyncio import pytest import time import uuid import os import tempfile from unittest.mock import AsyncMock, MagicMock, patch from datetime import datetime from client.app import P2PClientApp, create_client, quick_connect from client.connection_manager import ConnectionManager, ConnectionState, Connection from server.relay_server import RelayServer, ClientConnection from shared.models import ( Message, MessageType, UserInfo, UserStatus, ChatMessage, ConnectionMode, TransferProgress ) from shared.message_handler import MessageHandler from config import ClientConfig, ServerConfig class TestP2PClientAppInit: """测试客户端应用程序初始化""" def test_init_with_default_config(self): """测试使用默认配置初始化""" app = P2PClientApp() assert app.config is not None assert app.user_info is None assert app.is_connected is False assert app.connection_state == ConnectionState.DISCONNECTED def test_init_with_custom_config(self): """测试使用自定义配置初始化""" config = ClientConfig( server_host="192.168.1.100", server_port=9999 ) app = P2PClientApp(config) assert app.config.server_host == "192.168.1.100" assert app.config.server_port == 9999 def test_components_initialized(self): """测试组件初始化""" app = P2PClientApp() assert app.connection_manager is not None assert app.file_transfer is not None assert app.image_processor is not None assert app.media_player is not None # voice_chat 在 start() 后初始化 assert app.voice_chat is None class TestCallbackRegistration: """测试回调注册""" def test_message_callback_registration(self): """测试消息回调注册""" app = P2PClientApp() callbacks_called = [] def callback(msg): callbacks_called.append(msg) app.add_message_callback(callback) assert callback in app._message_callbacks app.remove_message_callback(callback) assert callback not in app._message_callbacks def test_state_callback_registration(self): """测试状态回调注册""" app = P2PClientApp() callbacks_called = [] def callback(state, reason): callbacks_called.append((state, reason)) app.add_state_callback(callback) assert callback in app._state_callbacks app.remove_state_callback(callback) assert callback not in app._state_callbacks def test_user_list_callback_registration(self): """测试用户列表回调注册""" app = P2PClientApp() callbacks_called = [] def callback(users): callbacks_called.append(users) app.add_user_list_callback(callback) assert callback in app._user_list_callbacks app.remove_user_list_callback(callback) assert callback not in app._user_list_callbacks class TestChatHistory: """测试聊天历史管理""" def test_add_to_history(self): """测试添加消息到历史""" app = P2PClientApp() msg = ChatMessage( message_id="msg1", sender_id="user1", receiver_id="user2", content_type=MessageType.TEXT, content="Hello", timestamp=datetime.now() ) app._add_to_history("user1", msg) history = app.get_chat_history("user1") assert len(history) == 1 assert history[0].content == "Hello" def test_history_sorted_by_time(self): """测试历史按时间排序""" app = P2PClientApp() msg1 = ChatMessage( message_id="msg1", sender_id="user1", receiver_id="user2", content_type=MessageType.TEXT, content="First", timestamp=datetime(2024, 1, 1, 10, 0, 0) ) msg2 = ChatMessage( message_id="msg2", sender_id="user1", receiver_id="user2", content_type=MessageType.TEXT, content="Second", timestamp=datetime(2024, 1, 1, 9, 0, 0) # 更早的时间 ) app._add_to_history("user1", msg1) app._add_to_history("user1", msg2) history = app.get_chat_history("user1") assert len(history) == 2 assert history[0].content == "Second" # 更早的消息在前 assert history[1].content == "First" def test_clear_history(self): """测试清除历史""" app = P2PClientApp() msg = ChatMessage( message_id="msg1", sender_id="user1", receiver_id="user2", content_type=MessageType.TEXT, content="Hello", timestamp=datetime.now() ) app._add_to_history("user1", msg) assert len(app.get_chat_history("user1")) == 1 app.clear_chat_history("user1") assert len(app.get_chat_history("user1")) == 0 def test_get_empty_history(self): """测试获取空历史""" app = P2PClientApp() history = app.get_chat_history("nonexistent") assert history == [] class TestMessageHandling: """测试消息处理""" def test_handle_text_message(self): """测试处理文本消息""" app = P2PClientApp() app._user_info = UserInfo( user_id="user2", username="user2", display_name="User 2" ) message = Message( msg_type=MessageType.TEXT, sender_id="user1", receiver_id="user2", timestamp=time.time(), payload=b"Hello, World!" ) app._handle_text_message(message) history = app.get_chat_history("user1") assert len(history) == 1 assert history[0].content == "Hello, World!" assert history[0].sender_id == "user1" def test_handle_user_list_response(self): """测试处理用户列表响应""" import json app = P2PClientApp() users_data = [ { "user_id": "user1", "username": "user1", "display_name": "User 1", "status": "online" }, { "user_id": "user2", "username": "user2", "display_name": "User 2", "status": "online" } ] message = Message( msg_type=MessageType.USER_LIST_RESPONSE, sender_id="server", receiver_id="me", timestamp=time.time(), payload=json.dumps(users_data).encode('utf-8') ) app._handle_user_list_response(message) assert len(app._online_users) == 2 assert "user1" in app._online_users assert "user2" in app._online_users def test_handle_ack_message(self): """测试处理确认消息""" app = P2PClientApp() message = Message( msg_type=MessageType.ACK, sender_id="server", receiver_id="me", timestamp=time.time(), payload=b"Message delivered" ) # 不应该抛出异常 app._handle_ack_message(message) def test_handle_error_message(self): """测试处理错误消息""" app = P2PClientApp() message = Message( msg_type=MessageType.ERROR, sender_id="server", receiver_id="me", timestamp=time.time(), payload=b"User not found" ) # 不应该抛出异常 app._handle_error_message(message) class TestOnlineUsers: """测试在线用户管理""" def test_get_online_users_empty(self): """测试获取空的在线用户列表""" app = P2PClientApp() users = app.get_online_users() assert users == [] def test_get_user_info(self): """测试获取用户信息""" app = P2PClientApp() user = UserInfo( user_id="user1", username="user1", display_name="User 1" ) app._online_users["user1"] = user info = app.get_user_info("user1") assert info is not None assert info.username == "user1" info = app.get_user_info("nonexistent") assert info is None class TestConnectionMode: """测试连接模式""" def test_get_connection_mode(self): """测试获取连接模式""" app = P2PClientApp() # 默认应该是中转模式 mode = app.get_connection_mode("unknown_peer") assert mode == ConnectionMode.RELAY class TestStats: """测试统计信息""" def test_get_stats_initial(self): """测试获取初始统计信息""" app = P2PClientApp() stats = app.get_stats() assert stats["user"] is None assert stats["online_users"] == 0 assert stats["chat_sessions"] == 0 assert stats["total_messages"] == 0 assert stats["voice_call_active"] is False def test_get_stats_with_data(self): """测试获取有数据的统计信息""" app = P2PClientApp() app._user_info = UserInfo( user_id="user1", username="testuser", display_name="Test User" ) # 添加一些聊天历史 msg = ChatMessage( message_id="msg1", sender_id="user1", receiver_id="user2", content_type=MessageType.TEXT, content="Hello", timestamp=datetime.now() ) app._add_to_history("user2", msg) stats = app.get_stats() assert stats["user"] == "testuser" assert stats["chat_sessions"] == 1 assert stats["total_messages"] == 1 class TestCreateClient: """测试便捷函数""" def test_create_client_default(self): """测试使用默认参数创建客户端""" client = create_client() assert client is not None assert client.config.server_host == "127.0.0.1" assert client.config.server_port == 8888 def test_create_client_custom(self): """测试使用自定义参数创建客户端""" client = create_client( server_host="192.168.1.100", server_port=9999 ) assert client.config.server_host == "192.168.1.100" assert client.config.server_port == 9999 class TestMessageFlow: """测试完整消息流程""" @pytest.fixture def mock_connection_manager(self): """创建模拟的连接管理器""" cm = MagicMock(spec=ConnectionManager) cm.is_connected = True cm.state = ConnectionState.CONNECTED cm.send_message = AsyncMock(return_value=True) cm.get_connection_mode = MagicMock(return_value=ConnectionMode.RELAY) return cm @pytest.mark.asyncio async def test_send_text_message_flow(self, mock_connection_manager): """测试发送文本消息流程""" app = P2PClientApp() app._connection_manager = mock_connection_manager app._user_info = UserInfo( user_id="sender", username="sender", display_name="Sender" ) success = await app.send_text_message("receiver", "Hello!") assert success mock_connection_manager.send_message.assert_called_once() # 检查消息被添加到历史 history = app.get_chat_history("receiver") assert len(history) == 1 assert history[0].content == "Hello!" @pytest.mark.asyncio async def test_send_text_message_not_logged_in(self): """测试未登录时发送消息""" app = P2PClientApp() app._user_info = None success = await app.send_text_message("receiver", "Hello!") assert not success def test_receive_text_message_flow(self): """测试接收文本消息流程""" app = P2PClientApp() app._user_info = UserInfo( user_id="receiver", username="receiver", display_name="Receiver" ) # 模拟接收消息 message = Message( msg_type=MessageType.TEXT, sender_id="sender", receiver_id="receiver", timestamp=time.time(), payload=b"Hello from sender!" ) # 触发消息处理 app._on_message_received(message) # 检查消息被添加到历史 history = app.get_chat_history("sender") assert len(history) == 1 assert history[0].content == "Hello from sender!" def test_message_callback_triggered(self): """测试消息回调被触发""" app = P2PClientApp() received_messages = [] def callback(msg): received_messages.append(msg) app.add_message_callback(callback) message = Message( msg_type=MessageType.TEXT, sender_id="sender", receiver_id="receiver", timestamp=time.time(), payload=b"Test" ) app._on_message_received(message) assert len(received_messages) == 1 assert received_messages[0].msg_type == MessageType.TEXT class TestStateManagement: """测试状态管理""" def test_state_callback_triggered(self): """测试状态回调被触发""" app = P2PClientApp() state_changes = [] def callback(state, reason): state_changes.append((state, reason)) app.add_state_callback(callback) # 模拟状态变化 app._on_connection_state_changed(ConnectionState.CONNECTING, "Test") assert len(state_changes) == 1 assert state_changes[0][0] == ConnectionState.CONNECTING assert state_changes[0][1] == "Test" class TestMediaPlayback: """测试媒体播放""" def test_pause_media(self): """测试暂停媒体""" app = P2PClientApp() # 不应该抛出异常 app.pause_media() def test_stop_media(self): """测试停止媒体""" app = P2PClientApp() # 不应该抛出异常 app.stop_media() def test_set_volume(self): """测试设置音量""" app = P2PClientApp() # 不应该抛出异常 app.set_volume(0.5) class TestVoiceCall: """测试语音通话""" def test_voice_call_not_initialized(self): """测试语音模块未初始化时的操作""" app = P2PClientApp() # voice_chat 在 start() 后才初始化 assert app.voice_chat is None # 这些操作不应该抛出异常 app.reject_voice_call("peer") app.end_voice_call() app.mute_voice_call(True) class TestEndToEndIntegration: """端到端集成测试""" @pytest.fixture def server_config(self): """服务器配置""" return ServerConfig( host="127.0.0.1", port=18888, # 使用不同端口避免冲突 max_connections=10 ) @pytest.fixture def client_config(self, server_config): """客户端配置""" return ClientConfig( server_host=server_config.host, server_port=server_config.port ) @pytest.mark.asyncio async def test_client_server_message_relay(self, server_config, client_config): """测试客户端-服务器消息中转""" # 这是一个集成测试框架,实际测试需要启动真实服务器 # 这里只验证组件可以正确初始化 server = RelayServer(server_config) client1 = P2PClientApp(client_config) client2 = P2PClientApp(client_config) assert server is not None assert client1 is not None assert client2 is not None # 验证配置正确 assert server.port == client_config.server_port assert client1.config.server_host == server_config.host class TestComponentIntegration: """组件集成测试""" def test_file_transfer_integration(self): """测试文件传输模块集成""" app = P2PClientApp() # 验证文件传输模块已集成 assert app.file_transfer is not None assert app.file_transfer._send_message is not None def test_image_processor_integration(self): """测试图片处理器集成""" app = P2PClientApp() # 验证图片处理器已集成 assert app.image_processor is not None def test_media_player_integration(self): """测试媒体播放器集成""" app = P2PClientApp() # 验证媒体播放器已集成 assert app.media_player is not None class TestUserListCallback: """测试用户列表回调""" def test_user_list_callback_triggered(self): """测试用户列表回调被触发""" import json app = P2PClientApp() received_users = [] def callback(users): received_users.extend(users) app.add_user_list_callback(callback) users_data = [ { "user_id": "user1", "username": "user1", "display_name": "User 1", "status": "online" } ] message = Message( msg_type=MessageType.USER_LIST_RESPONSE, sender_id="server", receiver_id="me", timestamp=time.time(), payload=json.dumps(users_data).encode('utf-8') ) app._handle_user_list_response(message) assert len(received_users) == 1 assert received_users[0].user_id == "user1"