Files
awoooi/apps/api/scripts/e2e_openclaw_test.py
OG T 6f049877fc fix(lint): ruff auto-fix + lewooogo-core src 加入 git
- Python: ruff --fix 修復 280 個 lint 錯誤
- lewooogo-core: src/ 目錄未追蹤,導致 CI eslint 失敗

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-03-23 23:51:37 +08:00

244 lines
8.6 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
#!/usr/bin/env python3
"""
Phase 5 E2E 點火測試 - OpenClaw 全鏈路驗證
==========================================
測試流程:
1. 發射模擬 K8s 告警到 Webhook
2. 驗證告警被正確處理
3. 驗證 ApprovalRecord 被建立
4. 模擬 Telegram 簽核回調
5. 驗證執行觸發
使用方式:
python scripts/e2e_openclaw_test.py
"""
import asyncio
import sys
from datetime import datetime
def print_header(title: str) -> None:
"""列印測試標題"""
print("\n" + "=" * 60)
print(f" {title}")
print("=" * 60)
def print_step(step: int, description: str) -> None:
"""列印測試步驟"""
print(f"\n🔹 Step {step}: {description}")
def print_success(message: str) -> None:
"""列印成功訊息"""
print(f"{message}")
def print_error(message: str) -> None:
"""列印錯誤訊息"""
print(f"{message}")
def print_info(message: str) -> None:
"""列印資訊訊息"""
print(f" {message}")
async def test_phase5_e2e():
"""Phase 5 E2E 測試"""
print_header("Phase 5 E2E 點火測試 - OpenClaw 全鏈路驗證")
print(f"執行時間: {datetime.now().isoformat()}")
# =========================================================================
# Step 1: 測試 LogLevelFilter (日誌清洗)
# =========================================================================
print_step(1, "日誌清洗模組 (LogLevelFilter)")
try:
from src.services.context_gatherer import LogLevelFilter
# 模擬 K8s 日誌
raw_logs = """
2024-03-21T10:15:23.456Z INFO [harbor.core] Starting Harbor Core
2024-03-21T10:15:24.789Z DEBUG [harbor.core.db] Initializing connection pool
2024-03-21T10:16:45.123Z ERROR [harbor.core.db] Connection lost to PostgreSQL
2024-03-21T10:16:45.456Z FATAL [harbor.core] Unrecoverable error
Traceback (most recent call last):
File "/harbor/core/db.py", line 234, in connect
raise DatabaseConnectionError("Max retries exceeded")
""".strip()
filtered = LogLevelFilter.filter_logs(raw_logs)
stats = LogLevelFilter.get_filter_stats(raw_logs, filtered)
# 驗證 DEBUG/INFO 被過濾
assert "DEBUG" not in filtered, "DEBUG should be filtered"
assert "INFO" not in filtered.replace("DatabaseConnectionError", ""), "INFO should be filtered"
assert "ERROR" in filtered, "ERROR should be preserved"
assert "FATAL" in filtered, "FATAL should be preserved"
assert "Traceback" in filtered, "Stacktrace should be preserved"
print_success(f"日誌清洗成功: {stats['original_lines']}{stats['filtered_lines']}")
print_success(f"雜訊移除率: {stats['removal_rate_percent']}%")
except Exception as e:
print_error(f"日誌清洗測試失敗: {e}")
return False
# =========================================================================
# Step 2: 測試 Security Interceptor (白名單 + Nonce)
# =========================================================================
print_step(2, "安全攔截器 (Security Interceptor)")
try:
from src.core.config import settings
from src.services.security_interceptor import (
TelegramSecurityInterceptor,
)
interceptor = TelegramSecurityInterceptor()
# 測試白名單 (假設統帥 ID: 5619078117)
test_user_id = 5619078117
# 檢查白名單配置
whitelist = settings.OPENCLAW_TG_USER_WHITELIST
print_info(f"白名單配置: {whitelist}")
if whitelist:
is_whitelisted = interceptor.is_whitelisted(test_user_id)
if is_whitelisted:
print_success(f"統帥 ID {test_user_id} 在白名單內")
else:
print_info(f"統帥 ID {test_user_id} 不在白名單 (需配置)")
else:
print_info("白名單為空 (需在環境變數中配置 OPENCLAW_TG_USER_WHITELIST)")
# 測試 Nonce 產生
nonce = interceptor.generate_callback_nonce("test-approval-123", "approve")
print_success(f"Nonce 產生成功: {nonce[:30]}...")
# 解析 Nonce
parsed = interceptor.parse_callback_data(nonce)
assert parsed["action"] == "approve"
assert parsed["approval_id"] == "test-approval-123"
print_success("Nonce 解析成功")
except Exception as e:
print_error(f"安全攔截器測試失敗: {e}")
return False
# =========================================================================
# Step 3: 測試 Telegram Gateway (訊息格式)
# =========================================================================
print_step(3, "Telegram Gateway (SOUL.md 訊息格式)")
try:
from src.services.telegram_gateway import RISK_EMOJI_MAP, TelegramMessage
# 建立測試訊息
message = TelegramMessage(
status_emoji=RISK_EMOJI_MAP["critical"],
risk_level="CRITICAL",
resource_name="harbor-core-7d4b8c9f5-xk2m3",
root_cause="OOMKilled",
suggested_action="DELETE_POD (重啟 Pod)",
estimated_downtime="~30s",
approval_id="test-approval-123",
)
formatted = message.format()
# 驗證 SOUL.md 格式
assert "🚨" in formatted, "Should have critical emoji"
assert "CRITICAL" in formatted, "Should have risk level"
assert "harbor-core" in formatted, "Should have resource name"
assert "OOMKilled" in formatted, "Should have root cause"
assert "建議" in formatted, "Should have suggestion"
assert "停機" in formatted, "Should have downtime"
assert len(formatted) <= 500, f"Should be <= 500 chars, got {len(formatted)}"
print_success("SOUL.md 訊息格式驗證通過")
print_info(f"訊息長度: {len(formatted)} / 500 字元")
print()
print(" 📱 訊息預覽:")
for line in formatted.split("\n"):
print(f" {line}")
except Exception as e:
print_error(f"Telegram Gateway 測試失敗: {e}")
return False
# =========================================================================
# Step 4: 測試 OpenClaw 模組載入
# =========================================================================
print_step(4, "OpenClaw AI 模組載入")
try:
from src.services.openclaw import OpenClawService, get_openclaw
openclaw = get_openclaw()
assert isinstance(openclaw, OpenClawService)
print_success("OpenClaw 服務載入成功")
# 檢查 AI Fallback 順序
from src.core.config import settings
print_info(f"AI Fallback 順序: {settings.AI_FALLBACK_ORDER}")
print_info(f"預設模型: {settings.OPENCLAW_DEFAULT_MODEL}")
except Exception as e:
print_error(f"OpenClaw 模組載入失敗: {e}")
return False
# =========================================================================
# Step 5: 測試 Signature 審計欄位
# =========================================================================
print_step(5, "Signature 審計欄位 (Telegram 擴充)")
try:
from src.models.approval import Signature, SignatureSource
# 建立 Telegram 簽核記錄
sig = Signature(
signer_id="tg_5619078117",
signer_name="統帥",
comment="Telegram 簽核測試",
source=SignatureSource.TELEGRAM,
telegram_user_id=5619078117,
telegram_message_id=12345,
)
assert sig.source == SignatureSource.TELEGRAM
assert sig.telegram_user_id == 5619078117
print_success("Telegram 審計欄位驗證通過")
print_info(f"簽核來源: {sig.source.value}")
print_info(f"Telegram User ID: {sig.telegram_user_id}")
except Exception as e:
print_error(f"Signature 審計欄位測試失敗: {e}")
return False
# =========================================================================
# 測試完成
# =========================================================================
print_header("E2E 測試結果")
print()
print(" ✅ Step 1: 日誌清洗 (LogLevelFilter) - PASSED")
print(" ✅ Step 2: 安全攔截器 (Security Interceptor) - PASSED")
print(" ✅ Step 3: Telegram Gateway (SOUL.md 格式) - PASSED")
print(" ✅ Step 4: OpenClaw AI 模組載入 - PASSED")
print(" ✅ Step 5: Signature 審計欄位 - PASSED")
print()
print("=" * 60)
print(" 🎉 Phase 5 E2E 點火測試 - 全數通過!")
print("=" * 60)
return True
if __name__ == "__main__":
success = asyncio.run(test_phase5_e2e())
sys.exit(0 if success else 1)