mcp-feedback-enhanced/tests/integration/test_web_integration.py

306 lines
11 KiB
Python
Raw Normal View History

2025-06-10 08:40:47 +08:00
#!/usr/bin/env python3
"""
Web UI 集成測試
"""
import asyncio
import time
2025-06-11 03:25:08 +08:00
import pytest
2025-06-10 08:40:47 +08:00
from tests.fixtures.test_data import TestData
2025-06-11 03:25:08 +08:00
from tests.helpers.test_utils import TestUtils
2025-06-10 08:40:47 +08:00
class TestWebUIIntegration:
"""Web UI 集成測試"""
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@pytest.mark.asyncio
async def test_web_server_startup_and_routes(self, web_ui_manager):
"""測試 Web 服務器啟動和基本路由"""
# 啟動服務器
web_ui_manager.start_server()
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 等待服務器啟動
await asyncio.sleep(3)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 驗證服務器正在運行
assert web_ui_manager.server_thread is not None
assert web_ui_manager.server_thread.is_alive()
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 測試基本路由可訪問性
import aiohttp
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
base_url = f"http://{web_ui_manager.host}:{web_ui_manager.port}"
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
async with aiohttp.ClientSession() as session:
# 測試主頁
async with session.get(f"{base_url}/") as response:
assert response.status == 200
text = await response.text()
assert "MCP Feedback Enhanced" in text
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 測試靜態文件
async with session.get(f"{base_url}/static/css/style.css") as response:
# 可能返回 200 或 404但不應該是服務器錯誤
assert response.status in [200, 404]
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@pytest.mark.asyncio
async def test_session_api_integration(self, web_ui_manager, test_project_dir):
"""測試會話 API 集成"""
import aiohttp
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 創建會話
session_id = web_ui_manager.create_session(
2025-06-11 03:25:08 +08:00
str(test_project_dir), TestData.SAMPLE_SESSION["summary"]
2025-06-10 08:40:47 +08:00
)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 啟動服務器
web_ui_manager.start_server()
await asyncio.sleep(3)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
base_url = f"http://{web_ui_manager.host}:{web_ui_manager.port}"
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
async with aiohttp.ClientSession() as session:
# 測試當前會話 API
async with session.get(f"{base_url}/api/current-session") as response:
assert response.status == 200
data = await response.json()
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
assert data["session_id"] == session_id
assert data["project_directory"] == str(test_project_dir)
assert data["summary"] == TestData.SAMPLE_SESSION["summary"]
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@pytest.mark.asyncio
async def test_websocket_connection(self, web_ui_manager, test_project_dir):
"""測試 WebSocket 連接"""
import aiohttp
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 創建會話
web_ui_manager.create_session(
2025-06-11 03:25:08 +08:00
str(test_project_dir), TestData.SAMPLE_SESSION["summary"]
2025-06-10 08:40:47 +08:00
)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 啟動服務器
web_ui_manager.start_server()
await asyncio.sleep(3)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
ws_url = f"ws://{web_ui_manager.host}:{web_ui_manager.port}/ws"
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
async with aiohttp.ClientSession() as session:
try:
async with session.ws_connect(ws_url) as ws:
# 應該收到連接確認消息
msg = await asyncio.wait_for(ws.receive(), timeout=5)
assert msg.type == aiohttp.WSMsgType.TEXT
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
data = msg.json()
assert data["type"] == "connection_established"
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 測試發送心跳
heartbeat_msg = {
"type": "heartbeat",
"tabId": "test-tab-123",
2025-06-11 03:25:08 +08:00
"timestamp": time.time(),
2025-06-10 08:40:47 +08:00
}
await ws.send_str(str(heartbeat_msg).replace("'", '"'))
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 應該收到心跳回應
response = await asyncio.wait_for(ws.receive(), timeout=5)
if response.type == aiohttp.WSMsgType.TEXT:
response_data = response.json()
assert response_data["type"] == "heartbeat_response"
2025-06-11 03:25:08 +08:00
except TimeoutError:
2025-06-10 08:40:47 +08:00
pytest.fail("WebSocket 連接或通信超時")
except Exception as e:
pytest.fail(f"WebSocket 測試失敗: {e}")
class TestWebUISessionManagement:
"""Web UI 會話管理集成測試"""
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@pytest.mark.asyncio
async def test_session_lifecycle(self, web_ui_manager, test_project_dir):
"""測試會話生命週期"""
# 1. 創建會話
2025-06-11 03:25:08 +08:00
session_id = web_ui_manager.create_session(str(test_project_dir), "第一個會話")
2025-06-10 08:40:47 +08:00
current_session = web_ui_manager.get_current_session()
assert current_session is not None
assert current_session.session_id == session_id
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 2. 創建第二個會話(模擬第二次 MCP 調用)
session_id_2 = web_ui_manager.create_session(
2025-06-11 03:25:08 +08:00
str(test_project_dir), "第二個會話"
2025-06-10 08:40:47 +08:00
)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 當前會話應該切換到新會話
current_session = web_ui_manager.get_current_session()
assert current_session.session_id == session_id_2
assert current_session.summary == "第二個會話"
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 3. 測試會話狀態更新
from src.mcp_feedback_enhanced.web.models import SessionStatus
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
current_session.update_status(SessionStatus.FEEDBACK_SUBMITTED, "已提交回饋")
assert current_session.status == SessionStatus.FEEDBACK_SUBMITTED
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@pytest.mark.asyncio
async def test_session_feedback_flow(self, web_ui_manager, test_project_dir):
"""測試會話回饋流程"""
# 創建會話
session_id = web_ui_manager.create_session(
2025-06-11 03:25:08 +08:00
str(test_project_dir), TestData.SAMPLE_SESSION["summary"]
2025-06-10 08:40:47 +08:00
)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
session = web_ui_manager.get_current_session()
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 模擬提交回饋
await session.submit_feedback(
TestData.SAMPLE_FEEDBACK["feedback"],
TestData.SAMPLE_FEEDBACK["images"],
2025-06-11 03:25:08 +08:00
TestData.SAMPLE_FEEDBACK["settings"],
2025-06-10 08:40:47 +08:00
)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 驗證回饋已保存
assert session.feedback_result == TestData.SAMPLE_FEEDBACK["feedback"]
assert session.images == TestData.SAMPLE_FEEDBACK["images"]
assert session.settings == TestData.SAMPLE_FEEDBACK["settings"]
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 驗證狀態已更新
from src.mcp_feedback_enhanced.web.models import SessionStatus
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
assert session.status == SessionStatus.FEEDBACK_SUBMITTED
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@pytest.mark.asyncio
async def test_session_timeout_handling(self, web_ui_manager, test_project_dir):
"""測試會話超時處理"""
# 創建會話,設置短超時
session_id = web_ui_manager.create_session(
2025-06-11 03:25:08 +08:00
str(test_project_dir), TestData.SAMPLE_SESSION["summary"]
2025-06-10 08:40:47 +08:00
)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
session = web_ui_manager.get_current_session()
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 測試超時等待
try:
result = await asyncio.wait_for(
session.wait_for_feedback(timeout=1), # 1秒超時
2025-06-11 03:25:08 +08:00
timeout=2, # 外部超時保護
2025-06-10 08:40:47 +08:00
)
# 如果沒有超時,應該返回默認結果
assert TestUtils.validate_web_response(result)
2025-06-11 03:25:08 +08:00
except TimeoutError:
2025-06-10 08:40:47 +08:00
# 超時是預期的行為
pass
class TestWebUIErrorHandling:
"""Web UI 錯誤處理集成測試"""
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@pytest.mark.asyncio
async def test_no_session_handling(self, web_ui_manager):
"""測試無會話時的處理"""
import aiohttp
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 確保沒有活躍會話
web_ui_manager.clear_current_session()
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 啟動服務器
web_ui_manager.start_server()
await asyncio.sleep(3)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
base_url = f"http://{web_ui_manager.host}:{web_ui_manager.port}"
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
async with aiohttp.ClientSession() as session:
# 測試主頁應該顯示等待頁面
async with session.get(f"{base_url}/") as response:
assert response.status == 200
text = await response.text()
assert "MCP Feedback Enhanced" in text
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 測試當前會話 API 應該返回無會話狀態
async with session.get(f"{base_url}/api/current-session") as response:
assert response.status == 404 # 或其他適當的狀態碼
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@pytest.mark.asyncio
async def test_websocket_without_session(self, web_ui_manager):
"""測試無會話時的 WebSocket 連接"""
import aiohttp
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 確保沒有活躍會話
web_ui_manager.clear_current_session()
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 啟動服務器
web_ui_manager.start_server()
await asyncio.sleep(3)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
ws_url = f"ws://{web_ui_manager.host}:{web_ui_manager.port}/ws"
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
async with aiohttp.ClientSession() as session:
try:
async with session.ws_connect(ws_url) as ws:
# 連接應該被拒絕或立即關閉
msg = await asyncio.wait_for(ws.receive(), timeout=5)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
if msg.type == aiohttp.WSMsgType.CLOSE:
# 連接被關閉是預期的
assert True
2025-06-11 03:25:08 +08:00
# 如果收到消息,應該是錯誤消息
elif msg.type == aiohttp.WSMsgType.TEXT:
data = msg.json()
assert "error" in data or data.get("type") == "error"
2025-06-10 08:40:47 +08:00
except aiohttp.WSServerHandshakeError:
# WebSocket 握手失敗也是預期的
assert True
2025-06-11 03:25:08 +08:00
except TimeoutError:
2025-06-10 08:40:47 +08:00
# 超時也可能是預期的行為
assert True
class TestWebUIPerformance:
"""Web UI 性能集成測試"""
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@pytest.mark.asyncio
async def test_server_startup_time(self, web_ui_manager):
"""測試服務器啟動時間"""
from tests.helpers.test_utils import PerformanceTimer
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
with PerformanceTimer() as timer:
web_ui_manager.start_server()
await asyncio.sleep(3) # 等待啟動完成
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 啟動時間應該在合理範圍內
assert timer.duration < 10, f"Web 服務器啟動時間過長: {timer.duration:.2f}"
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 驗證服務器確實在運行
assert web_ui_manager.server_thread is not None
assert web_ui_manager.server_thread.is_alive()
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
@pytest.mark.asyncio
async def test_multiple_session_performance(self, web_ui_manager, test_project_dir):
"""測試多會話性能"""
from tests.helpers.test_utils import PerformanceTimer
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
session_ids = []
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
with PerformanceTimer() as timer:
# 創建多個會話
for i in range(10):
session_id = web_ui_manager.create_session(
2025-06-11 03:25:08 +08:00
str(test_project_dir), f"測試會話 {i + 1}"
2025-06-10 08:40:47 +08:00
)
session_ids.append(session_id)
2025-06-11 03:25:08 +08:00
2025-06-10 08:40:47 +08:00
# 創建會話的時間應該是線性的,不應該有明顯的性能下降
avg_time_per_session = timer.duration / 10
2025-06-11 03:25:08 +08:00
assert avg_time_per_session < 0.1, (
f"每個會話創建時間過長: {avg_time_per_session:.3f}"
)
2025-06-10 08:40:47 +08:00
# 驗證最後一個會話是當前活躍會話
current_session = web_ui_manager.get_current_session()
assert current_session.session_id == session_ids[-1]