🐛 修復單元測試問題

This commit is contained in:
Minidoracat 2025-06-11 14:59:27 +08:00
parent 85ff83f037
commit 5150617abb
7 changed files with 134 additions and 40 deletions

View File

@ -83,25 +83,37 @@ class I18nManager:
if env_lang and env_lang in self._supported_languages:
return env_lang
# 3. 自動偵測系統語言
try:
# 獲取系統語言
system_locale = locale.getdefaultlocale()[0]
if system_locale:
if system_locale.startswith("zh_TW") or system_locale.startswith(
"zh_Hant"
):
# 3. 檢查其他環境變數LANG, LC_ALL 等)
for env_var in ["LANG", "LC_ALL", "LC_MESSAGES", "LANGUAGE"]:
env_value = os.getenv(env_var, "").strip()
if env_value:
if env_value.startswith("zh_TW") or env_value.startswith("zh_Hant"):
return "zh-TW"
if system_locale.startswith("zh_CN") or system_locale.startswith(
"zh_Hans"
):
if env_value.startswith("zh_CN") or env_value.startswith("zh_Hans"):
return "zh-CN"
if system_locale.startswith("en"):
if env_value.startswith("en"):
return "en"
except Exception:
pass
# 4. 回退到默認語言
# 4. 自動偵測系統語言(僅在非測試模式下)
if not os.getenv("MCP_TEST_MODE"):
try:
# 獲取系統語言
system_locale = locale.getdefaultlocale()[0]
if system_locale:
if system_locale.startswith("zh_TW") or system_locale.startswith(
"zh_Hant"
):
return "zh-TW"
if system_locale.startswith("zh_CN") or system_locale.startswith(
"zh_Hans"
):
return "zh-CN"
if system_locale.startswith("en"):
return "en"
except Exception:
pass
# 5. 回退到默認語言
return self._fallback_language
def _load_saved_language(self) -> str | None:

View File

@ -47,12 +47,16 @@ class WebUIManager:
if env_port:
try:
custom_port = int(env_port)
if 1024 <= custom_port <= 65535:
if custom_port == 0:
# 特殊值 0 表示使用系統自動分配的端口
preferred_port = 0
debug_log("使用環境變數指定的自動端口分配 (0)")
elif 1024 <= custom_port <= 65535:
preferred_port = custom_port
debug_log(f"使用環境變數指定的端口: {preferred_port}")
else:
debug_log(
f"MCP_WEB_PORT 值無效 ({custom_port}),必須在 1024-65535 範圍內,使用預設端口 8765"
f"MCP_WEB_PORT 值無效 ({custom_port}),必須在 1024-65535 範圍內或為 0,使用預設端口 8765"
)
except ValueError:
debug_log(
@ -63,9 +67,23 @@ class WebUIManager:
# 使用增強的端口管理,測試模式下禁用自動清理避免權限問題
auto_cleanup = os.environ.get("MCP_TEST_MODE", "").lower() != "true"
self.port = port or PortManager.find_free_port_enhanced(
preferred_port=preferred_port, auto_cleanup=auto_cleanup, host=self.host
)
if port is not None:
# 如果明確指定了端口,使用指定的端口
self.port = port
elif preferred_port == 0:
# 如果偏好端口為 0使用系統自動分配
import socket
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((self.host, 0))
self.port = s.getsockname()[1]
debug_log(f"系統自動分配端口: {self.port}")
else:
# 使用增強的端口管理
self.port = PortManager.find_free_port_enhanced(
preferred_port=preferred_port, auto_cleanup=auto_cleanup, host=self.host
)
self.app = FastAPI(title="MCP Feedback Enhanced")
# 設置壓縮和緩存中間件

View File

@ -50,13 +50,35 @@ def test_project_dir(temp_dir: Path) -> Path:
@pytest.fixture
def web_ui_manager() -> Generator[WebUIManager, None, None]:
"""創建 WebUIManager fixture"""
manager = WebUIManager(host="127.0.0.1", port=0) # 使用隨機端口
yield manager
import os
# 清理
if manager.server_thread and manager.server_thread.is_alive():
# 這裡可以添加服務器停止邏輯
pass
# 設置測試模式環境變數
original_test_mode = os.environ.get("MCP_TEST_MODE")
original_web_port = os.environ.get("MCP_WEB_PORT")
os.environ["MCP_TEST_MODE"] = "true"
# 使用動態端口範圍避免衝突
os.environ["MCP_WEB_PORT"] = "0" # 讓系統自動分配端口
try:
manager = WebUIManager(host="127.0.0.1") # 使用環境變數控制端口
yield manager
finally:
# 恢復原始環境變數
if original_test_mode is not None:
os.environ["MCP_TEST_MODE"] = original_test_mode
else:
os.environ.pop("MCP_TEST_MODE", None)
if original_web_port is not None:
os.environ["MCP_WEB_PORT"] = original_web_port
else:
os.environ.pop("MCP_WEB_PORT", None)
# 清理
if manager.server_thread and manager.server_thread.is_alive():
# 這裡可以添加服務器停止邏輯
pass
@pytest.fixture

View File

@ -26,8 +26,8 @@ class SimpleMCPClient:
async def start_server(self) -> bool:
"""啟動 MCP 服務器"""
try:
# 使用當前專案的 MCP 服務器
cmd = ["python", "-m", "mcp_feedback_enhanced.server"]
# 使用正確的 uv run 命令啟動 MCP 服務器
cmd = ["uv", "run", "python", "-m", "mcp_feedback_enhanced"]
self.server_process = subprocess.Popen(
cmd,
@ -37,6 +37,8 @@ class SimpleMCPClient:
text=True,
bufsize=0,
cwd=Path.cwd(),
encoding="utf-8", # 明確指定 UTF-8 編碼
errors="replace", # 處理編碼錯誤
)
self.stdin = self.server_process.stdin

View File

@ -106,9 +106,10 @@ class TestI18NFileSystemIntegration:
assert locales_dir.exists(), f"翻譯目錄不存在: {locales_dir}"
# 檢查每種支援語言的翻譯文件
# 檢查每種支援語言的翻譯文件(使用正確的路徑結構)
for lang in TestData.SUPPORTED_LANGUAGES:
lang_file = locales_dir / f"{lang}.json"
lang_dir = locales_dir / lang
lang_file = lang_dir / "translation.json"
assert lang_file.exists(), f"翻譯文件不存在: {lang_file}"
# 檢查文件內容
@ -132,7 +133,8 @@ class TestI18NFileSystemIntegration:
locales_dir = manager._locales_dir
for lang in TestData.SUPPORTED_LANGUAGES:
lang_file = locales_dir / f"{lang}.json"
lang_dir = locales_dir / lang
lang_file = lang_dir / "translation.json"
if lang_file.exists():
# 測試 UTF-8 編碼
@ -160,6 +162,9 @@ class TestI18NEnvironmentIntegration:
try:
# 測試不同的環境設置
test_cases = [
{"MCP_LANGUAGE": "zh-TW", "expected": "zh-TW"},
{"MCP_LANGUAGE": "zh-CN", "expected": "zh-CN"},
{"MCP_LANGUAGE": "en", "expected": "en"},
{"LANG": "zh_TW.UTF-8", "expected": "zh-TW"},
{"LANG": "zh_CN.UTF-8", "expected": "zh-CN"},
{"LANG": "en_US.UTF-8", "expected": "en"},
@ -170,22 +175,34 @@ class TestI18NEnvironmentIntegration:
# 清理環境變數
for var in env_vars:
os.environ.pop(var, None)
# 也清理 MCP_LANGUAGE
os.environ.pop("MCP_LANGUAGE", None)
# 設置測試模式,禁用系統語言檢測
os.environ["MCP_TEST_MODE"] = "true"
# 設置測試環境
for key, value in test_case.items():
if key != "expected":
os.environ[key] = value
# 創建新的管理器實例
manager = I18nManager()
# 修復 attr-defined 錯誤 - 使用正確的方法名
detected = manager._detect_language()
# 創建新的管理器實例,並清理可能的保存設定
import tempfile
from pathlib import Path
# 驗證檢測結果
expected = test_case["expected"]
assert detected == expected, (
f"環境 {test_case} 檢測到 {detected},預期 {expected}"
)
with tempfile.TemporaryDirectory() as temp_dir:
# 臨時修改配置文件路徑,避免使用真實的用戶配置
manager = I18nManager()
manager._config_file = Path(temp_dir) / "test_language.json"
# 修復 attr-defined 錯誤 - 使用正確的方法名
detected = manager._detect_language()
# 驗證檢測結果
expected = test_case["expected"]
assert detected == expected, (
f"環境 {test_case} 檢測到 {detected},預期 {expected}"
)
finally:
# 恢復原始環境變數

View File

@ -97,6 +97,24 @@ class TestWebUIIntegration:
data = msg.json()
assert data["type"] == "connection_established"
# 可能會收到額外的消息session_updated 或 status_update先處理掉
try:
while True:
extra_msg = await asyncio.wait_for(ws.receive(), timeout=1)
if extra_msg.type == aiohttp.WSMsgType.TEXT:
extra_data = extra_msg.json()
if extra_data["type"] in [
"session_updated",
"status_update",
]:
continue
# 如果是其他類型的消息,可能是我們要的回應,先保存
break
break
except TimeoutError:
# 沒有額外消息,繼續測試
pass
# 測試發送心跳
heartbeat_msg = {
"type": "heartbeat",

View File

@ -33,6 +33,11 @@ class TestResourceManager:
# 重置單例實例
ResourceManager._instance = None
# 重置全局資源管理器實例
import mcp_feedback_enhanced.utils.resource_manager as rm_module
rm_module._resource_manager = None
def test_singleton_pattern(self):
"""測試單例模式"""
rm1 = ResourceManager()