修復項目: 1. 新增 conftest.py 確保環境變數在 settings 前載入 2. test_github_webhook.py 移除重複的 os.environ 設定 (E402) 3. test_smart_router.py 排序 import (I001) 4. github_webhook.py 修正 send_message → send_notification Phase 13.1 首席架構師審查修復 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
194 lines
6.4 KiB
Python
194 lines
6.4 KiB
Python
"""
|
|
Smart Router Tests - Phase 13.3
|
|
===============================
|
|
測試意圖分類、複雜度評分、AI 路由
|
|
"""
|
|
|
|
from src.services.ai_router import (
|
|
AIRouter,
|
|
get_ai_router,
|
|
)
|
|
from src.services.complexity_scorer import (
|
|
ComplexityScorer,
|
|
get_complexity_scorer,
|
|
)
|
|
from src.services.intent_classifier import (
|
|
IntentClassifier,
|
|
IntentType,
|
|
get_intent_classifier,
|
|
)
|
|
|
|
|
|
class TestIntentClassifier:
|
|
"""測試意圖分類器"""
|
|
|
|
def test_alert_keywords(self):
|
|
"""測試告警關鍵字匹配"""
|
|
classifier = IntentClassifier()
|
|
|
|
# 中文告警
|
|
assert classifier.classify_sync("高負載警報") == IntentType.ALERT_TRIAGE
|
|
assert classifier.classify_sync("CPU 異常告警") == IntentType.ALERT_TRIAGE
|
|
assert classifier.classify_sync("OOM error detected") == IntentType.ALERT_TRIAGE
|
|
|
|
def test_deployment_keywords(self):
|
|
"""測試部署關鍵字匹配"""
|
|
classifier = IntentClassifier()
|
|
|
|
assert classifier.classify_sync("部署新版本") == IntentType.DEPLOYMENT
|
|
assert classifier.classify_sync("kubectl apply -f manifest.yaml") == IntentType.DEPLOYMENT
|
|
assert classifier.classify_sync("rollout deployment api") == IntentType.DEPLOYMENT
|
|
|
|
def test_query_keywords(self):
|
|
"""測試查詢關鍵字匹配"""
|
|
classifier = IntentClassifier()
|
|
|
|
assert classifier.classify_sync("查詢 Pod 狀態") == IntentType.QUERY
|
|
assert classifier.classify_sync("kubectl get pods") == IntentType.QUERY
|
|
assert classifier.classify_sync("現在有多少 replicas") == IntentType.QUERY
|
|
|
|
def test_maintenance_keywords(self):
|
|
"""測試維運關鍵字匹配"""
|
|
classifier = IntentClassifier()
|
|
|
|
assert classifier.classify_sync("重啟服務") == IntentType.MAINTENANCE
|
|
assert classifier.classify_sync("scale deployment to 5") == IntentType.MAINTENANCE
|
|
assert classifier.classify_sync("回滾到上一版") == IntentType.MAINTENANCE
|
|
|
|
def test_code_review_keywords(self):
|
|
"""測試程式碼審查關鍵字匹配"""
|
|
classifier = IntentClassifier()
|
|
|
|
assert classifier.classify_sync("review this PR") == IntentType.CODE_REVIEW
|
|
assert classifier.classify_sync("審查這個 commit") == IntentType.CODE_REVIEW
|
|
|
|
def test_unknown_intent(self):
|
|
"""測試未知意圖"""
|
|
classifier = IntentClassifier()
|
|
|
|
assert classifier.classify_sync("hello world") == IntentType.UNKNOWN
|
|
assert classifier.classify_sync("今天天氣如何") == IntentType.UNKNOWN
|
|
|
|
|
|
class TestComplexityScorer:
|
|
"""測試複雜度評分器"""
|
|
|
|
def test_simple_context(self):
|
|
"""測試簡單上下文"""
|
|
scorer = ComplexityScorer()
|
|
|
|
result = scorer.score({})
|
|
assert result.score == 1
|
|
assert result.recommended_model == "llama3.2:3b"
|
|
|
|
def test_multi_service_context(self):
|
|
"""測試多服務上下文"""
|
|
scorer = ComplexityScorer()
|
|
|
|
result = scorer.score({
|
|
"affected_services": ["api", "worker", "redis"],
|
|
})
|
|
assert result.score >= 2
|
|
assert "service_count" in result.features
|
|
|
|
def test_code_analysis_context(self):
|
|
"""測試需要程式碼分析"""
|
|
scorer = ComplexityScorer()
|
|
|
|
result = scorer.score({
|
|
"requires_code_analysis": True,
|
|
})
|
|
assert result.score >= 2
|
|
assert result.features.get("code_analysis") == 1
|
|
|
|
def test_critical_severity(self):
|
|
"""測試 CRITICAL 嚴重程度"""
|
|
scorer = ComplexityScorer()
|
|
|
|
result = scorer.score({
|
|
"severity": "CRITICAL",
|
|
})
|
|
assert result.score >= 2
|
|
assert result.features.get("severity") == 4
|
|
|
|
def test_complex_context(self):
|
|
"""測試複雜上下文"""
|
|
scorer = ComplexityScorer()
|
|
|
|
result = scorer.score({
|
|
"affected_services": ["api", "worker", "redis", "postgres"],
|
|
"metrics": ["cpu", "memory", "latency", "error_rate", "rps"],
|
|
"cross_system": True,
|
|
"severity": "CRITICAL",
|
|
})
|
|
assert result.score >= 4
|
|
# 複雜情況應該用雲端模型
|
|
assert result.recommended_model in ["gemini", "claude"]
|
|
|
|
|
|
class TestAIRouter:
|
|
"""測試 AI 路由器"""
|
|
|
|
def test_query_routes_to_fast_model(self):
|
|
"""測試查詢路由到快速模型"""
|
|
router = AIRouter()
|
|
|
|
decision = router.route_sync("查詢 Pod 狀態", {})
|
|
assert decision.model == "llama3.2:3b"
|
|
assert decision.intent == IntentType.QUERY
|
|
|
|
def test_code_review_routes_to_strong_model(self):
|
|
"""測試程式碼審查路由到強模型"""
|
|
router = AIRouter()
|
|
|
|
decision = router.route_sync("review this PR", {})
|
|
assert decision.model == "qwen2.5:7b-instruct"
|
|
assert decision.intent == IntentType.CODE_REVIEW
|
|
|
|
def test_complex_alert_routes_to_cloud(self):
|
|
"""測試複雜告警路由到雲端"""
|
|
router = AIRouter()
|
|
|
|
decision = router.route_sync("高負載告警", {
|
|
"affected_services": ["api", "worker", "redis", "postgres"],
|
|
"metrics": ["cpu", "memory", "latency", "error_rate"],
|
|
"cross_system": True,
|
|
"severity": "CRITICAL",
|
|
})
|
|
assert decision.intent == IntentType.ALERT_TRIAGE
|
|
assert decision.complexity.score >= 4
|
|
# 高複雜度告警應該用雲端
|
|
assert decision.model in ["gemini", "claude", "qwen2.5:7b-instruct"]
|
|
|
|
def test_fallback_list(self):
|
|
"""測試 Fallback 列表"""
|
|
router = AIRouter()
|
|
|
|
decision = router.route_sync("查詢 Pod 狀態", {})
|
|
# Fallback 不應包含已選模型
|
|
assert decision.model not in decision.fallback_models
|
|
# 應該有備援
|
|
assert len(decision.fallback_models) >= 2
|
|
|
|
|
|
class TestSingletons:
|
|
"""測試單例"""
|
|
|
|
def test_intent_classifier_singleton(self):
|
|
"""測試 IntentClassifier 單例"""
|
|
c1 = get_intent_classifier()
|
|
c2 = get_intent_classifier()
|
|
assert c1 is c2
|
|
|
|
def test_complexity_scorer_singleton(self):
|
|
"""測試 ComplexityScorer 單例"""
|
|
s1 = get_complexity_scorer()
|
|
s2 = get_complexity_scorer()
|
|
assert s1 is s2
|
|
|
|
def test_ai_router_singleton(self):
|
|
"""測試 AIRouter 單例"""
|
|
r1 = get_ai_router()
|
|
r2 = get_ai_router()
|
|
assert r1 is r2
|