305 lines
11 KiB
Python
305 lines
11 KiB
Python
|
#!/usr/bin/env python
|
|||
|
# -*- coding: utf-8 -*-
|
|||
|
|
|||
|
"""
|
|||
|
WebSocket模块测试
|
|||
|
测试WebSocket内置函数的基本功能,包括模拟WebSocket服务和客户端
|
|||
|
"""
|
|||
|
|
|||
|
import asyncio
|
|||
|
import json
|
|||
|
import time
|
|||
|
import threading
|
|||
|
import sys
|
|||
|
import os
|
|||
|
from unittest.mock import Mock, AsyncMock, patch
|
|||
|
from typing import Dict, Any, Set
|
|||
|
|
|||
|
# 添加项目根目录到Python路径
|
|||
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|||
|
parent_dir = os.path.dirname(current_dir)
|
|||
|
if parent_dir not in sys.path:
|
|||
|
sys.path.insert(0, parent_dir)
|
|||
|
|
|||
|
# 导入被测试的模块
|
|||
|
try:
|
|||
|
from services.online_script.built_in_modules.websocket_module import VWEDWebSocketModule
|
|||
|
from utils.logger import get_logger
|
|||
|
except ImportError as e:
|
|||
|
print(f"导入项目模块失败: {e}")
|
|||
|
print("请确保从项目根目录运行此脚本")
|
|||
|
sys.exit(1)
|
|||
|
|
|||
|
logger = get_logger("tests.websocket_module")
|
|||
|
|
|||
|
|
|||
|
class MockWebSocket:
|
|||
|
"""模拟WebSocket连接"""
|
|||
|
|
|||
|
def __init__(self, client_ip: str, client_id: str = None):
|
|||
|
self.client = Mock()
|
|||
|
self.client.host = client_ip
|
|||
|
self.client_id = client_id or f"client_{client_ip.replace('.', '_')}"
|
|||
|
self.messages = []
|
|||
|
self.closed = False
|
|||
|
|
|||
|
async def send_text(self, message: str):
|
|||
|
"""模拟发送文本消息"""
|
|||
|
if self.closed:
|
|||
|
raise Exception("WebSocket已关闭")
|
|||
|
self.messages.append(message)
|
|||
|
logger.info(f"模拟WebSocket发送消息: {message} 到 {self.client.host}")
|
|||
|
|
|||
|
def close(self):
|
|||
|
"""关闭连接"""
|
|||
|
self.closed = True
|
|||
|
|
|||
|
|
|||
|
class MockConnectionManager:
|
|||
|
"""模拟WebSocket连接管理器"""
|
|||
|
|
|||
|
def __init__(self):
|
|||
|
self.active_connections: Dict[str, Set[MockWebSocket]] = {}
|
|||
|
self.storage_location_connections: Dict[str, Set[MockWebSocket]] = {}
|
|||
|
|
|||
|
def add_connection(self, task_record_id: str, websocket: MockWebSocket):
|
|||
|
"""添加活跃连接"""
|
|||
|
if task_record_id not in self.active_connections:
|
|||
|
self.active_connections[task_record_id] = set()
|
|||
|
self.active_connections[task_record_id].add(websocket)
|
|||
|
|
|||
|
def add_storage_connection(self, scene_id: str, websocket: MockWebSocket):
|
|||
|
"""添加库位状态连接"""
|
|||
|
if scene_id not in self.storage_location_connections:
|
|||
|
self.storage_location_connections[scene_id] = set()
|
|||
|
self.storage_location_connections[scene_id].add(websocket)
|
|||
|
|
|||
|
async def send_personal_message(self, message: str, websocket: MockWebSocket):
|
|||
|
"""发送个人消息"""
|
|||
|
await websocket.send_text(message)
|
|||
|
|
|||
|
|
|||
|
class WebSocketModuleTest:
|
|||
|
"""WebSocket模块测试类"""
|
|||
|
|
|||
|
def __init__(self):
|
|||
|
self.mock_manager = MockConnectionManager()
|
|||
|
self.module = VWEDWebSocketModule("test_script_001")
|
|||
|
|
|||
|
# 设置模拟管理器
|
|||
|
self.module._manager = self.mock_manager
|
|||
|
|
|||
|
def setup_test_connections(self):
|
|||
|
"""设置测试连接"""
|
|||
|
# 创建模拟WebSocket连接
|
|||
|
self.ws1 = MockWebSocket("192.168.1.100", "client1")
|
|||
|
self.ws2 = MockWebSocket("192.168.1.101", "client2")
|
|||
|
self.ws3 = MockWebSocket("192.168.1.102", "client3")
|
|||
|
|
|||
|
# 添加到连接管理器
|
|||
|
self.mock_manager.add_connection("task_001", self.ws1)
|
|||
|
self.mock_manager.add_connection("task_002", self.ws2)
|
|||
|
self.mock_manager.add_storage_connection("scene_001", self.ws3)
|
|||
|
|
|||
|
logger.info("测试连接设置完成")
|
|||
|
|
|||
|
async def test_send_msg_by_ip(self):
|
|||
|
"""测试根据IP发送消息"""
|
|||
|
logger.info("=== 测试根据IP发送消息 ===")
|
|||
|
|
|||
|
try:
|
|||
|
# 测试发送消息到存在的IP
|
|||
|
self.module.send_msg_to_wsc_by_client_ip("测试消息1", "192.168.1.100")
|
|||
|
await asyncio.sleep(0.1) # 等待异步操作完成
|
|||
|
|
|||
|
# 检查消息是否发送
|
|||
|
assert len(self.ws1.messages) > 0, "消息未发送到正确的WebSocket连接"
|
|||
|
assert "测试消息1" in self.ws1.messages, "发送的消息内容不正确"
|
|||
|
|
|||
|
logger.info(f"✓ 成功发送消息到IP 192.168.1.100,收到消息: {self.ws1.messages}")
|
|||
|
|
|||
|
# 测试发送消息到不存在的IP
|
|||
|
try:
|
|||
|
self.module.send_msg_to_wsc_by_client_ip("测试消息2", "192.168.1.999")
|
|||
|
assert False, "应该抛出异常但没有抛出"
|
|||
|
except Exception as e:
|
|||
|
logger.info(f"✓ 正确处理不存在的IP: {e}")
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"✗ 测试根据IP发送消息失败: {e}")
|
|||
|
raise
|
|||
|
|
|||
|
async def test_send_msg_by_name(self):
|
|||
|
"""测试根据客户端名称发送消息"""
|
|||
|
logger.info("=== 测试根据客户端名称发送消息 ===")
|
|||
|
|
|||
|
try:
|
|||
|
# 测试发送消息到存在的客户端名称
|
|||
|
self.module.send_msg_to_wsc_by_client_name("测试消息3", "task_001")
|
|||
|
await asyncio.sleep(0.1) # 等待异步操作完成
|
|||
|
|
|||
|
# 检查消息是否发送
|
|||
|
assert len(self.ws1.messages) > 1, "消息未发送到正确的WebSocket连接"
|
|||
|
assert "测试消息3" in self.ws1.messages, "发送的消息内容不正确"
|
|||
|
|
|||
|
logger.info(f"✓ 成功发送消息到客户端 task_001,收到消息: {self.ws1.messages}")
|
|||
|
|
|||
|
# 测试发送消息到不存在的客户端名称
|
|||
|
try:
|
|||
|
self.module.send_msg_to_wsc_by_client_name("测试消息4", "task_999")
|
|||
|
assert False, "应该抛出异常但没有抛出"
|
|||
|
except Exception as e:
|
|||
|
logger.info(f"✓ 正确处理不存在的客户端名称: {e}")
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"✗ 测试根据客户端名称发送消息失败: {e}")
|
|||
|
raise
|
|||
|
|
|||
|
def test_get_client_ips(self):
|
|||
|
"""测试获取客户端IP列表"""
|
|||
|
logger.info("=== 测试获取客户端IP列表 ===")
|
|||
|
|
|||
|
try:
|
|||
|
client_ips = self.module.get_websocket_client_ip()
|
|||
|
|
|||
|
# 验证结果
|
|||
|
expected_ips = ["192.168.1.100", "192.168.1.101", "192.168.1.102"]
|
|||
|
for ip in expected_ips:
|
|||
|
assert ip in client_ips, f"IP {ip} 不在返回的列表中"
|
|||
|
|
|||
|
logger.info(f"✓ 成功获取客户端IP列表: {client_ips}")
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"✗ 测试获取客户端IP列表失败: {e}")
|
|||
|
raise
|
|||
|
|
|||
|
def test_get_client_names(self):
|
|||
|
"""测试获取客户端名称列表"""
|
|||
|
logger.info("=== 测试获取客户端名称列表 ===")
|
|||
|
|
|||
|
try:
|
|||
|
client_names = self.module.get_websocket_client_name()
|
|||
|
|
|||
|
# 验证结果
|
|||
|
expected_names = ["task_001", "task_002", "scene_001"]
|
|||
|
for name in expected_names:
|
|||
|
assert name in client_names, f"客户端名称 {name} 不在返回的列表中"
|
|||
|
|
|||
|
logger.info(f"✓ 成功获取客户端名称列表: {client_names}")
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"✗ 测试获取客户端名称列表失败: {e}")
|
|||
|
raise
|
|||
|
|
|||
|
async def test_error_handling(self):
|
|||
|
"""测试错误处理"""
|
|||
|
logger.info("=== 测试错误处理 ===")
|
|||
|
|
|||
|
try:
|
|||
|
# 测试WebSocket连接异常
|
|||
|
self.ws1.close()
|
|||
|
|
|||
|
try:
|
|||
|
self.module.send_msg_to_wsc_by_client_ip("测试消息5", "192.168.1.100")
|
|||
|
await asyncio.sleep(0.1)
|
|||
|
logger.info("✓ 处理了WebSocket连接异常")
|
|||
|
except Exception as e:
|
|||
|
logger.info(f"✓ 正确捕获WebSocket异常: {e}")
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"✗ 测试错误处理失败: {e}")
|
|||
|
raise
|
|||
|
|
|||
|
async def run_all_tests(self):
|
|||
|
"""运行所有测试"""
|
|||
|
logger.info("开始WebSocket模块测试")
|
|||
|
|
|||
|
try:
|
|||
|
# 设置测试环境
|
|||
|
self.setup_test_connections()
|
|||
|
|
|||
|
# 运行测试
|
|||
|
await self.test_send_msg_by_ip()
|
|||
|
await self.test_send_msg_by_name()
|
|||
|
self.test_get_client_ips()
|
|||
|
self.test_get_client_names()
|
|||
|
await self.test_error_handling()
|
|||
|
|
|||
|
logger.info("✓ 所有WebSocket模块测试通过")
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"✗ WebSocket模块测试失败: {e}")
|
|||
|
raise
|
|||
|
|
|||
|
|
|||
|
def run_mock_websocket_server():
|
|||
|
"""运行模拟WebSocket服务器"""
|
|||
|
logger.info("=== 启动模拟WebSocket服务器 ===")
|
|||
|
|
|||
|
async def mock_server():
|
|||
|
try:
|
|||
|
# 创建测试实例
|
|||
|
test = WebSocketModuleTest()
|
|||
|
|
|||
|
# 运行测试
|
|||
|
await test.run_all_tests()
|
|||
|
|
|||
|
logger.info("模拟WebSocket服务器测试完成")
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"模拟WebSocket服务器测试失败: {e}")
|
|||
|
raise
|
|||
|
|
|||
|
# 运行异步测试
|
|||
|
asyncio.run(mock_server())
|
|||
|
|
|||
|
|
|||
|
def test_websocket_integration():
|
|||
|
"""集成测试 - 模拟完整的WebSocket通信场景"""
|
|||
|
logger.info("=== WebSocket集成测试 ===")
|
|||
|
|
|||
|
async def integration_test():
|
|||
|
# 创建测试环境
|
|||
|
test = WebSocketModuleTest()
|
|||
|
test.setup_test_connections()
|
|||
|
|
|||
|
# 模拟实际使用场景
|
|||
|
logger.info("模拟实际使用场景...")
|
|||
|
|
|||
|
# 场景1:广播消息给所有客户端
|
|||
|
all_ips = test.module.get_websocket_client_ip()
|
|||
|
for ip in all_ips:
|
|||
|
try:
|
|||
|
test.module.send_msg_to_wsc_by_client_ip(f"广播消息给 {ip}", ip)
|
|||
|
await asyncio.sleep(0.05)
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"发送广播消息失败: {e}")
|
|||
|
|
|||
|
# 场景2:向特定任务发送状态更新
|
|||
|
test.module.send_msg_to_wsc_by_client_name("任务状态: 执行中", "task_001")
|
|||
|
await asyncio.sleep(0.05)
|
|||
|
|
|||
|
# 场景3:获取连接统计信息
|
|||
|
client_count = len(test.module.get_websocket_client_name())
|
|||
|
logger.info(f"当前连接客户端数量: {client_count}")
|
|||
|
|
|||
|
logger.info("✓ WebSocket集成测试完成")
|
|||
|
|
|||
|
asyncio.run(integration_test())
|
|||
|
|
|||
|
|
|||
|
if __name__ == "__main__":
|
|||
|
logger.info("开始WebSocket模块完整测试")
|
|||
|
|
|||
|
try:
|
|||
|
# 运行基本功能测试
|
|||
|
run_mock_websocket_server()
|
|||
|
|
|||
|
# 运行集成测试
|
|||
|
test_websocket_integration()
|
|||
|
|
|||
|
logger.info("✅ 所有WebSocket模块测试成功完成")
|
|||
|
|
|||
|
except Exception as e:
|
|||
|
logger.error(f"❌ WebSocket模块测试失败: {e}")
|
|||
|
raise
|