mcp-feedback-enhanced/tests/test_error_handler.py

254 lines
9.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
錯誤處理框架測試模組
測試 ErrorHandler 類的各項功能,包括:
- 錯誤類型自動分類
- 用戶友好錯誤信息生成
- 國際化支持
- 錯誤上下文記錄
"""
import pytest
import sys
import os
from unittest.mock import patch, MagicMock
# 添加 src 目錄到 Python 路徑
sys.path.insert(0, 'src')
from mcp_feedback_enhanced.utils.error_handler import ErrorHandler, ErrorType, ErrorSeverity
class TestErrorHandler:
"""錯誤處理器測試類"""
def test_classify_error_network(self):
"""測試網絡錯誤分類"""
# 測試 ConnectionError
error = ConnectionError("Connection failed")
assert ErrorHandler.classify_error(error) == ErrorType.NETWORK
# 測試包含網絡關鍵字的錯誤(不包含 timeout
error = Exception("socket connection failed")
assert ErrorHandler.classify_error(error) == ErrorType.NETWORK
def test_classify_error_file_io(self):
"""測試文件 I/O 錯誤分類"""
# 測試 FileNotFoundError
error = FileNotFoundError("No such file or directory")
assert ErrorHandler.classify_error(error) == ErrorType.FILE_IO
# 測試包含文件關鍵字的錯誤(不包含權限關鍵字)
error = Exception("file not found")
assert ErrorHandler.classify_error(error) == ErrorType.FILE_IO
def test_classify_error_timeout(self):
"""測試超時錯誤分類"""
error = TimeoutError("Operation timed out")
assert ErrorHandler.classify_error(error) == ErrorType.TIMEOUT
error = Exception("timeout occurred")
assert ErrorHandler.classify_error(error) == ErrorType.TIMEOUT
def test_classify_error_permission(self):
"""測試權限錯誤分類"""
error = PermissionError("Access denied")
assert ErrorHandler.classify_error(error) == ErrorType.PERMISSION
error = Exception("access denied")
assert ErrorHandler.classify_error(error) == ErrorType.PERMISSION
def test_classify_error_validation(self):
"""測試驗證錯誤分類"""
error = ValueError("Invalid value")
assert ErrorHandler.classify_error(error) == ErrorType.VALIDATION
error = TypeError("Wrong type")
assert ErrorHandler.classify_error(error) == ErrorType.VALIDATION
def test_classify_error_default_system(self):
"""測試默認系統錯誤分類"""
error = Exception("Some completely unknown issue")
assert ErrorHandler.classify_error(error) == ErrorType.SYSTEM
def test_format_user_error_basic(self):
"""測試基本用戶友好錯誤信息生成"""
error = ConnectionError("Connection failed")
result = ErrorHandler.format_user_error(error)
assert "" in result
assert "網絡連接出現問題" in result or "网络连接出现问题" in result or "Network connection issue" in result
def test_format_user_error_with_context(self):
"""測試帶上下文的錯誤信息生成"""
error = FileNotFoundError("File not found")
context = {
"operation": "文件讀取",
"file_path": "/path/to/file.txt"
}
result = ErrorHandler.format_user_error(error, context=context)
assert "" in result
assert "文件讀取" in result or "文件读取" in result or "文件讀取" in result
assert "/path/to/file.txt" in result
def test_format_user_error_with_technical_details(self):
"""測試包含技術細節的錯誤信息"""
error = ValueError("Invalid input")
result = ErrorHandler.format_user_error(error, include_technical=True)
assert "" in result
assert "ValueError" in result
assert "Invalid input" in result
def test_get_error_solutions(self):
"""測試獲取錯誤解決方案"""
solutions = ErrorHandler.get_error_solutions(ErrorType.NETWORK)
assert isinstance(solutions, list)
assert len(solutions) > 0
# 應該包含網絡相關的解決方案
solutions_text = " ".join(solutions).lower()
assert any(keyword in solutions_text for keyword in ["網絡", "网络", "network", "連接", "连接", "connection"])
def test_log_error_with_context(self):
"""測試帶上下文的錯誤記錄"""
error = Exception("Test error")
context = {"operation": "測試操作", "user": "test_user"}
error_id = ErrorHandler.log_error_with_context(error, context=context)
assert isinstance(error_id, str)
assert error_id.startswith("ERR_")
assert len(error_id.split("_")) == 3 # ERR_timestamp_id
def test_create_error_response(self):
"""測試創建標準化錯誤響應"""
error = ConnectionError("Network error")
context = {"operation": "網絡請求"}
response = ErrorHandler.create_error_response(error, context=context)
assert isinstance(response, dict)
assert response["success"] is False
assert "error_id" in response
assert "error_type" in response
assert "message" in response
assert response["error_type"] == ErrorType.NETWORK.value
assert "solutions" in response
def test_create_error_response_for_user(self):
"""測試為用戶界面創建錯誤響應"""
error = FileNotFoundError("File not found")
response = ErrorHandler.create_error_response(error, for_user=True)
assert response["success"] is False
assert "context" not in response # 用戶界面不應包含技術上下文
assert "" in response["message"] # 應該包含用戶友好的格式
@patch('mcp_feedback_enhanced.utils.error_handler.ErrorHandler.get_i18n_error_message')
def test_language_support(self, mock_get_message):
"""測試多語言支持"""
error = ConnectionError("Network error")
# 測試繁體中文
mock_get_message.return_value = "網絡連接出現問題"
result = ErrorHandler.format_user_error(error)
assert "網絡連接出現問題" in result
# 測試簡體中文
mock_get_message.return_value = "网络连接出现问题"
result = ErrorHandler.format_user_error(error)
assert "网络连接出现问题" in result
# 測試英文
mock_get_message.return_value = "Network connection issue"
result = ErrorHandler.format_user_error(error)
assert "Network connection issue" in result
def test_error_severity_logging(self):
"""測試錯誤嚴重程度記錄"""
error = Exception("Critical system error")
# 測試高嚴重程度錯誤
error_id = ErrorHandler.log_error_with_context(
error,
severity=ErrorSeverity.CRITICAL
)
assert isinstance(error_id, str)
assert error_id.startswith("ERR_")
def test_get_current_language_fallback(self):
"""測試語言獲取回退機制"""
# 由於 i18n 系統可能會覆蓋環境變數,我們主要測試函數不會拋出異常
language = ErrorHandler.get_current_language()
assert isinstance(language, str)
assert len(language) > 0
# 測試語言代碼格式
assert language in ["zh-TW", "zh-CN", "en"] or "-" in language
def test_i18n_integration(self):
"""測試國際化系統集成"""
# 測試當 i18n 系統不可用時的回退
error_type = ErrorType.NETWORK
# 測試獲取錯誤信息
message = ErrorHandler.get_i18n_error_message(error_type)
assert isinstance(message, str)
assert len(message) > 0
# 測試獲取解決方案
solutions = ErrorHandler.get_i18n_error_solutions(error_type)
assert isinstance(solutions, list)
def test_error_context_preservation(self):
"""測試錯誤上下文保存"""
error = Exception("Test error")
context = {
"operation": "測試操作",
"file_path": "/test/path",
"user_id": "test_user",
"timestamp": "2025-01-05"
}
error_id = ErrorHandler.log_error_with_context(error, context=context)
# 驗證錯誤 ID 格式
assert isinstance(error_id, str)
assert error_id.startswith("ERR_")
# 上下文應該被記錄到調試日誌中(通過 debug_log
# 這裡我們主要驗證函數不會拋出異常
def test_json_rpc_safety(self):
"""測試不影響 JSON RPC 通信"""
# 錯誤處理應該只記錄到 stderr通過 debug_log
# 不應該影響 stdout 或 JSON RPC 響應
error = Exception("Test error for JSON RPC safety")
context = {"operation": "JSON RPC 測試"}
# 這些操作不應該影響 stdout
error_id = ErrorHandler.log_error_with_context(error, context=context)
user_message = ErrorHandler.format_user_error(error)
response = ErrorHandler.create_error_response(error)
# 驗證返回值類型正確
assert isinstance(error_id, str)
assert isinstance(user_message, str)
assert isinstance(response, dict)
# 驗證不會拋出異常
assert error_id.startswith("ERR_")
assert "" in user_message
assert response["success"] is False
if __name__ == '__main__':
# 運行測試
pytest.main([__file__, '-v'])