156 lines
4.6 KiB
Python
Raw Permalink Normal View History

2025-06-10 08:40:47 +08:00
#!/usr/bin/env python3
"""
測試工具函數
"""
import asyncio
import socket
2025-06-11 03:25:08 +08:00
import time
from typing import Any
2025-06-10 08:40:47 +08:00
class TestUtils:
"""測試工具類"""
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@staticmethod
def find_free_port(start_port: int = 8000, max_attempts: int = 100) -> int:
"""尋找可用端口"""
for port in range(start_port, start_port + max_attempts):
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
2025-06-11 03:25:08 +08:00
s.bind(("127.0.0.1", port))
2025-06-10 08:40:47 +08:00
return port
except OSError:
continue
2025-06-11 03:25:08 +08:00
raise RuntimeError(
f"無法找到可用端口 (嘗試範圍: {start_port}-{start_port + max_attempts})"
)
2025-06-10 08:40:47 +08:00
@staticmethod
async def wait_for_condition(
2025-06-11 03:25:08 +08:00
condition_func, timeout: float = 10.0, check_interval: float = 0.1
2025-06-10 08:40:47 +08:00
) -> bool:
"""等待條件滿足"""
start_time = time.time()
while time.time() - start_time < timeout:
2025-06-11 03:25:08 +08:00
if (
await condition_func()
if asyncio.iscoroutinefunction(condition_func)
else condition_func()
):
2025-06-10 08:40:47 +08:00
return True
await asyncio.sleep(check_interval)
return False
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@staticmethod
def create_test_session_data(
session_id: str = "test-session-123",
project_directory: str = "/test/project",
2025-06-11 03:25:08 +08:00
summary: str = "測試摘要",
) -> dict[str, Any]:
2025-06-10 08:40:47 +08:00
"""創建測試會話數據"""
return {
"session_id": session_id,
"project_directory": project_directory,
"summary": summary,
"status": "waiting",
"created_at": time.time(),
2025-06-11 03:25:08 +08:00
"last_activity": time.time(),
2025-06-10 08:40:47 +08:00
}
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@staticmethod
def create_test_feedback_data(
2025-06-11 03:25:08 +08:00
feedback: str = "測試回饋", images: list[dict] | None = None
) -> dict[str, Any]:
2025-06-10 08:40:47 +08:00
"""創建測試回饋數據"""
return {
"feedback": feedback,
"images": images or [],
"settings": {
"image_size_limit": 1024 * 1024, # 1MB
2025-06-11 03:25:08 +08:00
"enable_base64_detail": True,
},
2025-06-10 08:40:47 +08:00
}
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@staticmethod
2025-06-11 03:25:08 +08:00
def validate_web_response(response_data: dict[str, Any]) -> bool:
2025-06-10 08:40:47 +08:00
"""驗證 Web 回應格式"""
required_fields = ["command_logs", "interactive_feedback", "images"]
return all(field in response_data for field in required_fields)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@staticmethod
2025-06-11 03:25:08 +08:00
def validate_session_info(session_info: dict[str, Any]) -> bool:
2025-06-10 08:40:47 +08:00
"""驗證會話信息格式"""
required_fields = ["session_id", "project_directory", "summary", "status"]
return all(field in session_info for field in required_fields)
class MockWebSocketClient:
"""模擬 WebSocket 客戶端"""
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
def __init__(self):
self.connected = False
self.messages = []
self.responses = []
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
async def connect(self, url: str) -> bool:
"""模擬連接"""
self.connected = True
return True
2025-06-11 03:25:08 +08:00
async def send_json(self, data: dict[str, Any]):
2025-06-10 08:40:47 +08:00
"""模擬發送 JSON 數據"""
if not self.connected:
raise RuntimeError("WebSocket 未連接")
self.messages.append(data)
2025-06-11 03:25:08 +08:00
async def receive_json(self) -> dict[str, Any]:
2025-06-10 08:40:47 +08:00
"""模擬接收 JSON 數據"""
if not self.connected:
raise RuntimeError("WebSocket 未連接")
if self.responses:
2025-06-11 06:11:29 +08:00
response = self.responses.pop(0)
# 修復 no-any-return 錯誤 - 確保返回明確類型
return dict(response) # 明確返回 dict[str, Any] 類型
2025-06-10 08:40:47 +08:00
# 返回默認回應
return {"type": "connection_established", "message": "連接成功"}
2025-06-11 03:25:08 +08:00
def add_response(self, response: dict[str, Any]):
2025-06-10 08:40:47 +08:00
"""添加模擬回應"""
self.responses.append(response)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
async def close(self):
"""關閉連接"""
self.connected = False
class PerformanceTimer:
"""性能計時器"""
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
def __init__(self):
2025-06-11 06:11:29 +08:00
self.start_time: float | None = None
self.end_time: float | None = None
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
def start(self):
"""開始計時"""
self.start_time = time.time()
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
def stop(self):
"""停止計時"""
self.end_time = time.time()
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@property
def duration(self) -> float:
"""獲取持續時間"""
if self.start_time is None:
return 0.0
end = self.end_time or time.time()
return end - self.start_time
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
def __enter__(self):
self.start()
return self
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
def __exit__(self, exc_type, exc_val, exc_tb):
self.stop()