- Add Bash version (real-integration-test.sh) for Linux/macOS - Add PowerShell version (real-integration-test.ps1) for Windows - Tests cover: Gateway connection, Model config, Agent management, API Key validation, Chat functionality, Hands triggers, Memory persistence, Configuration validation - Update plan file to reflect P0 progress and completed P2 tasks These scripts enable validation against real OpenFang Kernel instead of mocks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
473 lines
16 KiB
PowerShell
473 lines
16 KiB
PowerShell
# ZCLAW 真实环境集成测试 (Windows PowerShell)
|
||
# 连接真实 OpenFang Kernel 验证完整数据流
|
||
#
|
||
# 使用方法:
|
||
# 1. 确保 OpenFang Kernel 正在运行: openfang start
|
||
# 2. 设置 API Key: $env:ZHIPU_API_KEY="your_key"
|
||
# 3. 运行测试: .\scripts\tests\real-integration-test.ps1
|
||
|
||
param(
|
||
[string]$GatewayHost = "127.0.0.1",
|
||
[int]$GatewayPort = 50051,
|
||
[switch]$Verbose
|
||
)
|
||
|
||
# 配置
|
||
$GatewayUrl = "http://${GatewayHost}:${GatewayPort}"
|
||
$Timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
|
||
$ResultsDir = "test-results/integration"
|
||
$LogFile = "$ResultsDir/integration_$Timestamp.log"
|
||
|
||
# 测试计数器
|
||
$script:TestsRun = 0
|
||
$script:TestsPassed = 0
|
||
$script:TestsFailed = 0
|
||
$script:TestsSkipped = 0
|
||
|
||
# 创建结果目录
|
||
if (-not (Test-Path $ResultsDir)) {
|
||
New-Item -ItemType Directory -Path $ResultsDir -Force | Out-Null
|
||
}
|
||
|
||
# 日志函数
|
||
function Write-Log {
|
||
param([string]$Message)
|
||
Write-Host $Message
|
||
Add-Content -Path $LogFile -Value $Message
|
||
}
|
||
|
||
function Write-TestResult {
|
||
param(
|
||
[string]$Id,
|
||
[string]$Description,
|
||
[string]$Status,
|
||
[string]$Details = ""
|
||
)
|
||
|
||
$script:TestsRun++
|
||
|
||
switch ($Status) {
|
||
"PASS" {
|
||
$script:TestsPassed++
|
||
Write-Log "${Green}✅ [$Id] $Description${Reset}"
|
||
}
|
||
"FAIL" {
|
||
$script:TestsFailed++
|
||
Write-Log "${Red}❌ [$Id] $Description${Reset}"
|
||
}
|
||
"SKIP" {
|
||
$script:TestsSkipped++
|
||
Write-Log "${Yellow}⏭️ [$Id] $Description (跳过)${Reset}"
|
||
}
|
||
}
|
||
|
||
if ($Details) {
|
||
Write-Log " $Details"
|
||
}
|
||
}
|
||
|
||
# 颜色定义
|
||
$Red = "`e[0;31m"
|
||
$Green = "`e[0;32m"
|
||
$Yellow = "`e[1;33m"
|
||
$Blue = "`e[0;34m"
|
||
$Reset = "`e[0m"
|
||
|
||
# =============================================================================
|
||
# 测试开始
|
||
# =============================================================================
|
||
|
||
Write-Log "${Blue}========================================${Reset}"
|
||
Write-Log "${Blue} ZCLAW 真实环境集成测试 (Windows)${Reset}"
|
||
Write-Log "${Blue}========================================${Reset}"
|
||
Write-Log ""
|
||
Write-Log "测试时间: $(Get-Date)"
|
||
Write-Log "Gateway URL: $GatewayUrl"
|
||
Write-Log "日志文件: $LogFile"
|
||
Write-Log ""
|
||
|
||
# =============================================================================
|
||
# 1. 环境检查
|
||
# =============================================================================
|
||
|
||
Write-Log "${Yellow}[1. 环境检查]${Reset}"
|
||
|
||
# RI-ENV-01: 检查 PowerShell 版本
|
||
$PSVersionTable.PSVersion
|
||
Write-TestResult "RI-ENV-01" "PowerShell 版本 $($PSVersionTable.PSVersion)" "PASS"
|
||
|
||
# RI-ENV-02: 检查 curl (Invoke-WebRequest)
|
||
try {
|
||
$null = Get-Command Invoke-WebRequest -ErrorAction Stop
|
||
Write-TestResult "RI-ENV-02" "Invoke-WebRequest 可用" "PASS"
|
||
} catch {
|
||
Write-TestResult "RI-ENV-02" "Invoke-WebRequest 可用" "FAIL"
|
||
exit 1
|
||
}
|
||
|
||
# RI-ENV-03: 检查 Node.js
|
||
try {
|
||
$nodeVersion = node -v
|
||
Write-TestResult "RI-ENV-03" "Node.js 可用 ($nodeVersion)" "PASS"
|
||
} catch {
|
||
Write-TestResult "RI-ENV-03" "Node.js 可用" "FAIL" "需要安装 Node.js"
|
||
exit 1
|
||
}
|
||
|
||
Write-Log ""
|
||
|
||
# =============================================================================
|
||
# 2. Gateway 连接测试
|
||
# =============================================================================
|
||
|
||
Write-Log "${Yellow}[2. Gateway 连接测试]${Reset}"
|
||
|
||
# RI-GW-01: 检查端口可达性
|
||
try {
|
||
$tcpClient = New-Object System.Net.Sockets.TcpClient
|
||
$connect = $tcpClient.BeginConnect($GatewayHost, $GatewayPort, $null, $null)
|
||
$wait = $connect.AsyncWaitHandle.WaitOne(5000)
|
||
|
||
if ($wait -and $tcpClient.Connected) {
|
||
Write-TestResult "RI-GW-01" "Gateway 端口 $GatewayPort 可达" "PASS"
|
||
$tcpClient.EndConnect($connect)
|
||
} else {
|
||
Write-TestResult "RI-GW-01" "Gateway 端口 $GatewayPort 可达" "FAIL" "请确保 OpenFang 正在运行: openfang start"
|
||
}
|
||
$tcpClient.Close()
|
||
} catch {
|
||
Write-TestResult "RI-GW-01" "Gateway 端口 $GatewayPort 可达" "FAIL" $_.Exception.Message
|
||
}
|
||
|
||
# RI-GW-02: Health 端点
|
||
try {
|
||
$response = Invoke-WebRequest -Uri "$GatewayUrl/api/health" -Method Get -TimeoutSec 10 -ErrorAction Stop
|
||
if ($response.StatusCode -eq 200) {
|
||
Write-TestResult "RI-GW-02" "Health 端点返回 200" "PASS" "响应: $($response.Content)"
|
||
} else {
|
||
Write-TestResult "RI-GW-02" "Health 端点返回 200" "FAIL" "HTTP $($response.StatusCode)"
|
||
}
|
||
} catch {
|
||
Write-TestResult "RI-GW-02" "Health 端点返回 200" "FAIL" $_.Exception.Message
|
||
}
|
||
|
||
# RI-GW-03: Health 响应结构
|
||
try {
|
||
$response = Invoke-WebRequest -Uri "$GatewayUrl/api/health" -Method Get -TimeoutSec 10 -ErrorAction Stop
|
||
$content = $response.Content | ConvertFrom-Json
|
||
if ($content.status) {
|
||
Write-TestResult "RI-GW-03" "Health 响应包含 status 字段" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-GW-03" "Health 响应包含 status 字段" "FAIL" "响应: $($response.Content)"
|
||
}
|
||
} catch {
|
||
Write-TestResult "RI-GW-03" "Health 响应包含 status 字段" "FAIL" $_.Exception.Message
|
||
}
|
||
|
||
Write-Log ""
|
||
|
||
# =============================================================================
|
||
# 3. 模型配置测试
|
||
# =============================================================================
|
||
|
||
Write-Log "${Yellow}[3. 模型配置测试]${Reset}"
|
||
|
||
# RI-MOD-01: 获取可用模型列表
|
||
try {
|
||
$response = Invoke-WebRequest -Uri "$GatewayUrl/api/models" -Method Get -TimeoutSec 10 -ErrorAction Stop
|
||
if ($response.StatusCode -eq 200) {
|
||
Write-TestResult "RI-MOD-01" "Models 端点返回 200" "PASS"
|
||
|
||
$models = $response.Content | ConvertFrom-Json
|
||
$modelCount = ($models | Measure-Object).Count
|
||
if ($modelCount -gt 0) {
|
||
Write-TestResult "RI-MOD-02" "检测到 $modelCount 个可用模型" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-MOD-02" "检测到可用模型" "FAIL" "响应: $($response.Content)"
|
||
}
|
||
} else {
|
||
Write-TestResult "RI-MOD-01" "Models 端点返回 200" "FAIL" "HTTP $($response.StatusCode)"
|
||
Write-TestResult "RI-MOD-02" "检测到可用模型" "SKIP"
|
||
}
|
||
} catch {
|
||
Write-TestResult "RI-MOD-01" "Models 端点返回 200" "FAIL" $_.Exception.Message
|
||
Write-TestResult "RI-MOD-02" "检测到可用模型" "SKIP"
|
||
}
|
||
|
||
# RI-MOD-03: 检查中文模型配置
|
||
if (Test-Path "config/chinese-providers.toml") {
|
||
Write-TestResult "RI-MOD-03" "中文模型配置文件存在" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-MOD-03" "中文模型配置文件存在" "FAIL" "缺少 config/chinese-providers.toml"
|
||
}
|
||
|
||
Write-Log ""
|
||
|
||
# =============================================================================
|
||
# 4. Agent 管理测试
|
||
# =============================================================================
|
||
|
||
Write-Log "${Yellow}[4. Agent 管理测试]${Reset}"
|
||
|
||
# RI-AGT-01: 获取 Agent 列表
|
||
try {
|
||
$response = Invoke-WebRequest -Uri "$GatewayUrl/api/agents" -Method Get -TimeoutSec 10 -ErrorAction Stop
|
||
if ($response.StatusCode -eq 200) {
|
||
Write-TestResult "RI-AGT-01" "Agents 端点返回 200" "PASS"
|
||
|
||
$agents = $response.Content | ConvertFrom-Json
|
||
if ($agents -and ($agents.id -or $agents.name)) {
|
||
Write-TestResult "RI-AGT-02" "检测到 Agent 配置" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-AGT-02" "检测到 Agent 配置" "FAIL" "响应: $($response.Content)"
|
||
}
|
||
} else {
|
||
Write-TestResult "RI-AGT-01" "Agents 端点返回 200" "FAIL" "HTTP $($response.StatusCode)"
|
||
}
|
||
} catch {
|
||
Write-TestResult "RI-AGT-01" "Agents 端点返回 200" "FAIL" $_.Exception.Message
|
||
}
|
||
|
||
Write-Log ""
|
||
|
||
# =============================================================================
|
||
# 5. API Key 验证测试
|
||
# =============================================================================
|
||
|
||
Write-Log "${Yellow}[5. API Key 验证测试]${Reset}"
|
||
|
||
# RI-KEY-01: 检查智谱 API Key
|
||
if ($env:ZHIPU_API_KEY) {
|
||
Write-TestResult "RI-KEY-01" "智谱 API Key 已设置" "PASS" "长度: $($env:ZHIPU_API_KEY.Length) 字符"
|
||
} else {
|
||
Write-TestResult "RI-KEY-01" "智谱 API Key 已设置" "FAIL" "请设置: `$env:ZHIPU_API_KEY='your_key'"
|
||
}
|
||
|
||
# RI-KEY-02: 检查通义千问 API Key
|
||
if ($env:DASHSCOPE_API_KEY) {
|
||
Write-TestResult "RI-KEY-02" "通义千问 API Key 已设置" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-KEY-02" "通义千问 API Key 已设置" "SKIP" "可选"
|
||
}
|
||
|
||
# RI-KEY-03: 检查 Kimi API Key
|
||
if ($env:MOONSHOT_API_KEY) {
|
||
Write-TestResult "RI-KEY-03" "Kimi API Key 已设置" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-KEY-03" "Kimi API Key 已设置" "SKIP" "可选"
|
||
}
|
||
|
||
Write-Log ""
|
||
|
||
# =============================================================================
|
||
# 6. 对话功能测试(需要 API Key)
|
||
# =============================================================================
|
||
|
||
Write-Log "${Yellow}[6. 对话功能测试]${Reset}"
|
||
|
||
if ($env:ZHIPU_API_KEY) {
|
||
# RI-CHAT-01: 发送测试消息
|
||
$chatPayload = @{
|
||
messages = @(
|
||
@{
|
||
role = "user"
|
||
content = "你好,这是一个测试消息。请简短回复。"
|
||
}
|
||
)
|
||
model = "glm-4-flash"
|
||
stream = $false
|
||
} | ConvertTo-Json -Depth 3
|
||
|
||
Write-Log "发送测试消息..."
|
||
|
||
try {
|
||
$response = Invoke-WebRequest -Uri "$GatewayUrl/api/chat" `
|
||
-Method Post `
|
||
-ContentType "application/json" `
|
||
-Body $chatPayload `
|
||
-TimeoutSec 60 `
|
||
-ErrorAction Stop
|
||
|
||
if ($response.StatusCode -eq 200) {
|
||
Write-TestResult "RI-CHAT-01" "对话请求成功" "PASS"
|
||
|
||
$chatResult = $response.Content | ConvertFrom-Json
|
||
if ($chatResult.content -or $chatResult.text -or $chatResult.message) {
|
||
Write-TestResult "RI-CHAT-02" "对话响应包含内容" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-CHAT-02" "对话响应包含内容" "FAIL" "响应: $($response.Content)"
|
||
}
|
||
} else {
|
||
Write-TestResult "RI-CHAT-01" "对话请求成功" "FAIL" "HTTP $($response.StatusCode)"
|
||
Write-TestResult "RI-CHAT-02" "对话响应包含内容" "SKIP"
|
||
}
|
||
} catch {
|
||
Write-TestResult "RI-CHAT-01" "对话请求成功" "FAIL" $_.Exception.Message
|
||
Write-TestResult "RI-CHAT-02" "对话响应包含内容" "SKIP"
|
||
}
|
||
} else {
|
||
Write-TestResult "RI-CHAT-01" "对话请求成功" "SKIP" "需要设置 ZHIPU_API_KEY"
|
||
Write-TestResult "RI-CHAT-02" "对话响应包含内容" "SKIP"
|
||
}
|
||
|
||
Write-Log ""
|
||
|
||
# =============================================================================
|
||
# 7. Hands 触发测试
|
||
# =============================================================================
|
||
|
||
Write-Log "${Yellow}[7. Hands 触发测试]${Reset}"
|
||
|
||
# RI-HAND-01: 获取可用 Hands
|
||
try {
|
||
$response = Invoke-WebRequest -Uri "$GatewayUrl/api/hands" -Method Get -TimeoutSec 10 -ErrorAction Stop
|
||
if ($response.StatusCode -eq 200) {
|
||
Write-TestResult "RI-HAND-01" "Hands 端点返回 200" "PASS"
|
||
|
||
$hands = $response.Content | ConvertFrom-Json
|
||
$handCount = ($hands | Measure-Object).Count
|
||
if ($handCount -gt 0) {
|
||
Write-TestResult "RI-HAND-02" "检测到 $handCount 个可用 Hands" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-HAND-02" "检测到可用 Hands" "SKIP" "无 Hands 配置"
|
||
}
|
||
} else {
|
||
Write-TestResult "RI-HAND-01" "Hands 端点返回 200" "FAIL" "HTTP $($response.StatusCode)"
|
||
Write-TestResult "RI-HAND-02" "检测到可用 Hands" "SKIP"
|
||
}
|
||
} catch {
|
||
Write-TestResult "RI-HAND-01" "Hands 端点返回 200" "SKIP" "端点可能未实现"
|
||
Write-TestResult "RI-HAND-02" "检测到可用 Hands" "SKIP"
|
||
}
|
||
|
||
Write-Log ""
|
||
|
||
# =============================================================================
|
||
# 8. 记忆持久化测试
|
||
# =============================================================================
|
||
|
||
Write-Log "${Yellow}[8. 记忆持久化测试]${Reset}"
|
||
|
||
$memoryDir = "$env:USERPROFILE\.openfang\data\memory"
|
||
|
||
# RI-MEM-01: 检查记忆存储目录
|
||
if (Test-Path $memoryDir) {
|
||
Write-TestResult "RI-MEM-01" "记忆存储目录存在" "PASS"
|
||
|
||
$memoryFiles = Get-ChildItem -Path $memoryDir -Filter "*.json" -ErrorAction SilentlyContinue
|
||
$memoryCount = ($memoryFiles | Measure-Object).Count
|
||
if ($memoryCount -gt 0) {
|
||
Write-TestResult "RI-MEM-02" "检测到 $memoryCount 个记忆文件" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-MEM-02" "检测到记忆文件" "SKIP" "尚无记忆数据"
|
||
}
|
||
} else {
|
||
Write-TestResult "RI-MEM-01" "记忆存储目录存在" "FAIL" "目录: $memoryDir"
|
||
Write-TestResult "RI-MEM-02" "检测到记忆文件" "SKIP"
|
||
}
|
||
|
||
Write-Log ""
|
||
|
||
# =============================================================================
|
||
# 9. 配置验证测试
|
||
# =============================================================================
|
||
|
||
Write-Log "${Yellow}[9. 配置验证测试]${Reset}"
|
||
|
||
# RI-CFG-01: 主配置文件
|
||
if (Test-Path "config/config.toml") {
|
||
Write-TestResult "RI-CFG-01" "主配置文件存在" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-CFG-01" "主配置文件存在" "FAIL"
|
||
}
|
||
|
||
# RI-CFG-02: OpenFang 配置文件
|
||
$openfangConfig = "$env:USERPROFILE\.openfang\config.toml"
|
||
if (Test-Path $openfangConfig) {
|
||
Write-TestResult "RI-CFG-02" "OpenFang 配置文件存在" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-CFG-02" "OpenFang 配置文件存在" "FAIL" "请运行: openfang init"
|
||
}
|
||
|
||
# RI-CFG-03: 检查前端依赖
|
||
if (Test-Path "desktop\node_modules") {
|
||
Write-TestResult "RI-CFG-03" "前端依赖已安装" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-CFG-03" "前端依赖已安装" "FAIL" "请运行: cd desktop; pnpm install"
|
||
}
|
||
|
||
Write-Log ""
|
||
|
||
# =============================================================================
|
||
# 10. 前端构建测试
|
||
# =============================================================================
|
||
|
||
Write-Log "${Yellow}[10. 前端构建测试]${Reset}"
|
||
|
||
# RI-UI-01: 检查 Tauri 配置
|
||
if (Test-Path "desktop\src-tauri\tauri.conf.json") {
|
||
Write-TestResult "RI-UI-01" "Tauri 配置文件存在" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-UI-01" "Tauri 配置文件存在" "FAIL"
|
||
}
|
||
|
||
# RI-UI-02: 检查 package.json
|
||
if (Test-Path "desktop\package.json") {
|
||
Write-TestResult "RI-UI-02" "前端 package.json 存在" "PASS"
|
||
} else {
|
||
Write-TestResult "RI-UI-02" "前端 package.json 存在" "FAIL"
|
||
}
|
||
|
||
Write-Log ""
|
||
|
||
# =============================================================================
|
||
# 测试总结
|
||
# =============================================================================
|
||
|
||
Write-Log "${Blue}========================================${Reset}"
|
||
Write-Log "${Blue} 测试总结${Reset}"
|
||
Write-Log "${Blue}========================================${Reset}"
|
||
Write-Log ""
|
||
Write-Log "总测试数: $script:TestsRun"
|
||
Write-Log "${Green}通过: $script:TestsPassed${Reset}"
|
||
Write-Log "${Red}失败: $script:TestsFailed${Reset}"
|
||
Write-Log "${Yellow}跳过: $script:TestsSkipped${Reset}"
|
||
Write-Log ""
|
||
|
||
# 计算成功率
|
||
if ($script:TestsRun -gt 0) {
|
||
$successRate = [math]::Round(($script:TestsPassed / $script:TestsRun) * 100, 2)
|
||
} else {
|
||
$successRate = 0
|
||
}
|
||
|
||
# 生成 JSON 报告
|
||
$reportFile = "$ResultsDir/report_$Timestamp.json"
|
||
$report = @{
|
||
timestamp = (Get-Date -Format "o")
|
||
gateway_url = $GatewayUrl
|
||
summary = @{
|
||
total = $script:TestsRun
|
||
passed = $script:TestsPassed
|
||
failed = $script:TestsFailed
|
||
skipped = $script:TestsSkipped
|
||
}
|
||
success_rate = $successRate
|
||
log_file = $LogFile
|
||
} | ConvertTo-Json -Depth 3
|
||
|
||
$report | Out-File -FilePath $reportFile -Encoding UTF8
|
||
|
||
Write-Log "报告已保存到: $reportFile"
|
||
Write-Log "日志已保存到: $LogFile"
|
||
Write-Log ""
|
||
|
||
# 退出码
|
||
if ($script:TestsFailed -gt 0) {
|
||
Write-Log "${Red}存在失败的测试,请检查上述详情${Reset}"
|
||
exit 1
|
||
} else {
|
||
Write-Log "${Green}所有测试通过!${Reset}"
|
||
exit 0
|
||
}
|