12.3 测试与调试:把扩展做成可回放的工程完整流程
最后更新于
openclaw doctor
openclaw gateway status --deep --require-rpc
openclaw plugins doctor
openclaw plugins inspect my-plugin --runtime --json# 生成当前状态快照(先用当前版本稳定支持的文本或 JSON 输出)
openclaw status --deep > snapshots/status-baseline.txt
openclaw plugins list > snapshots/plugins-baseline.txt
openclaw plugins doctor > snapshots/plugins-doctor-baseline.txt# 从日志中提取最近的工具调用事件作为行为基线
openclaw logs --json | jq -c 'select(.type=="log") | (.raw | fromjson? // {}) as $raw | select(($raw|tostring)|contains("tool")) | {time, level, subsystem, raw:$raw}' \
| tail -20 > snapshots/behavior-baseline.json#!/bin/bash
# scripts/snapshot-diff.sh
# 用法:bash scripts/snapshot-diff.sh ./snapshots ./snapshots-new
set -u
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <baseline-dir> <current-dir>" >&2
exit 2
fi
BASELINE=$1
CURRENT=$2
failed=0
compare_snapshot() {
local label=$1
local file=$2
echo "=== ${label} Diff ==="
if diff -u "$BASELINE/$file" "$CURRENT/$file"; then
echo "PASS: ${label} unchanged"
else
echo "FAIL: ${label} changed"
failed=1
fi
}
compare_snapshot "Config Snapshot" "status-baseline.txt"
compare_snapshot "Plugin Snapshot" "plugins-baseline.txt"
compare_snapshot "Plugin Doctor" "plugins-doctor-baseline.txt"
exit "$failed"openclaw logs --follow --json | jq -c 'select(.type=="log") | (.raw | fromjson? // {}) as $raw | select(($raw|tostring)|contains("<TRACE_ID>")) | {time, level, subsystem, message, raw:$raw}'name: Extension Verification
on: [push, pull_request]
jobs:
verify:
runs-on: ubuntu-latest
steps:
# 步骤 1: 配置校验(使用官方稳定命令)
- name: Validate Plugin Configuration
run: |
openclaw doctor
openclaw plugins doctor
openclaw gateway status --deep --require-rpc
openclaw plugins inspect my-plugin --runtime --json
# 步骤 2: 快照对比
- name: Run Snapshot Tests
run: |
openclaw status --deep > /tmp/status-baseline.txt
openclaw plugins list > /tmp/plugins-baseline.txt
openclaw plugins doctor > /tmp/plugins-doctor-baseline.txt
bash scripts/snapshot-diff.sh ./snapshots /tmp
# 步骤 3: 单元测试(插件 SDK 注册契约)
- name: Unit Tests
run: |
pnpm test -- plugins/my-plugin/**/*.test.ts
# 步骤 4: loader-backed smoke test(验证 manifest + runtime 注册)
- name: Integration Tests
run: |
pnpm test -- plugins/my-plugin/loader-smoke.test.ts
# 步骤 5: 灰度模拟
- name: Canary Simulation
run: |
bash tests/canary/simulate-rollout.sh --dry-run#!/bin/bash
# tests/integration/test-tool-chains.sh
# 通过日志验证工具调用行为,而非直接调用 CLI 子命令
set -euo pipefail
TMP_DIR="$(mktemp -d)"
LOG_FILE="$TMP_DIR/openclaw-test.log"
LOG_PID=""
cleanup() {
if [ -n "$LOG_PID" ]; then
kill "$LOG_PID" 2>/dev/null || true
wait "$LOG_PID" 2>/dev/null || true
fi
rm -rf "$TMP_DIR"
}
trap cleanup EXIT
echo "=== Test 1: 启动 Gateway 并收集日志 ==="
openclaw logs --follow --json > "$LOG_FILE" &
LOG_PID=$!
for _ in {1..10}; do
[ -s "$LOG_FILE" ] && break
sleep 1
done
echo "=== Test 2: 验证工具策略加载 ==="
# 从 status 输出中确认 deny 规则已生效
openclaw status --deep | grep -q "deny" || {
echo "FAIL: Tool deny rules not found in status"
exit 1
}
echo "PASS: Tool deny rules loaded"
echo "=== Test 3: 验证插件加载状态 ==="
openclaw plugins doctor || {
echo "FAIL: Plugin doctor reported issues"
exit 1
}
echo "PASS: All plugins healthy"
echo "=== Test 4: 检查日志中的工具拒绝事件格式 ==="
# 在运行本检查前,先通过策略层探针或手工交互触发一次已知会被拒绝的工具调用;
# 这里仅验证拒绝事件是否包含必要字段(traceId、spanId、tool、reason)。
echo "请在 30 秒内触发一次预期会被拒绝的工具调用..."
deadline=$((SECONDS + 30))
found=0
while [ "$SECONDS" -lt "$deadline" ]; do
if jq -e 'select(.type=="log") | (.raw | fromjson? // {}) | select((.event=="blocked_tool_call" or .event=="tool_call_blocked") and .traceId and .spanId and .tool and .reason)' "$LOG_FILE" >/dev/null 2>&1; then
found=1
break
fi
sleep 1
done
if [ "$found" -ne 1 ]; then
echo "FAIL: no structured tool blocked event found"
exit 1
fi
echo "PASS: Tool denial events are structured"
echo "All integration tests passed."