Files
archived-gitea-ai-assistant/e2e/test.sh
jeffusion 1e38a0e5e0 refactor: replace fixed review workflow with dynamic agent framework
- Add agent-kernel runtime (MainAgentRunner with while-true state machine,
  subagent spawning, tool loop, budget control)
- Add review-agent entrypoint with read_file/search_code/spawn_subagent tools
- Add deterministic publish adapter with cross-specialist finding dedup
- Delete old fixed workflow (orchestrator, triage, specialist, judge agents,
  4 domain agents, critic/debate/reflexion agents, learning/memory system)
- Remove legacy ModelRole (planner/specialist/judge) from LLM types, gateway,
  config schema, DB, and frontend RoleAssignment UI
- Replace RoleAssignment with AgentModelSettings for role-based model config
- Add agent config API endpoints (GET/PUT /admin/api/agents/config)
- Add review session detail page with observability/findings/logs tabs
- Add runtime contract tests and review adapter integration tests
- Add E2E mock LLM with scripted behavior support for deterministic testing
- Update E2E test script with subagent and finding assertions
- Add e2e/README.md with real PR review testing guide
- Fix seed.sh to run gitea admin commands as git user (not root)
- Update docs (configuration, review-engines, deployment, README)
- Remove unused feedback controller, qdrant k8s manifest, embedding migration
- Add .omo/ and .opencode/ to .gitignore
2026-05-27 07:34:59 +08:00

210 lines
7.3 KiB
Bash
Executable File
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.
#!/usr/bin/env bash
set -euo pipefail
# E2E Test Script
#
# 前置条件:
# 1. docker compose -f docker-compose.e2e.yml up -d
# 2. ./e2e/seed.sh
# 3. E2E_GITEA_TOKEN=xxx docker compose -f docker-compose.e2e.yml up -d assistant
ENV_FILE="e2e/.env.e2e"
if [ ! -f "${ENV_FILE}" ]; then
echo "ERROR: ${ENV_FILE} 不存在,请先运行 ./e2e/seed.sh"
exit 1
fi
source "${ENV_FILE}"
MAX_WAIT=240
POLL_INTERVAL=5
PASS=0
FAIL=0
RUN_ID=""
LATEST_DETAIL='{}'
echo "=== E2E 测试开始 ==="
echo " Gitea: ${GITEA_URL}"
echo " Repo: ${ADMIN_USER}/${REPO_NAME}"
echo " PR: #${PR_NUMBER}"
echo ""
# ─── 测试 1: Assistant 服务健康检查 ───
echo "[TEST 1] Assistant 服务健康检查"
if curl -sf "${ASSISTANT_URL}/" > /dev/null 2>&1; then
echo " ✅ PASS: Assistant 服务正常"
PASS=$((PASS + 1))
else
echo " ❌ FAIL: Assistant 服务不可达"
FAIL=$((FAIL + 1))
fi
if [ "${E2E_MOCK_LLM:-}" = "1" ]; then
echo " E2E_MOCK_LLM=1 (shell env)"
else
echo " E2E_MOCK_LLM 由 assistant 容器环境决定docker-compose.e2e.yml 已配置)"
fi
# ─── 测试 2: Gitea API 可用 ───
echo "[TEST 2] Gitea API 可用性"
VERSION=$(curl -sf "${GITEA_URL}/api/v1/version" | python3 -c "import sys,json; print(json.load(sys.stdin).get('version','unknown'))" 2>/dev/null || echo "unknown")
if [ "${VERSION}" != "unknown" ]; then
echo " ✅ PASS: Gitea v${VERSION}"
PASS=$((PASS + 1))
else
echo " ❌ FAIL: Gitea API 不可用"
FAIL=$((FAIL + 1))
fi
# ─── 测试 3: PR 存在 ───
echo "[TEST 3] PR 存在性"
PR_STATE=$(curl -sf "${GITEA_URL}/api/v1/repos/${ADMIN_USER}/${REPO_NAME}/pulls/${PR_NUMBER}" \
-H "Authorization: token ${GITEA_TOKEN}" | \
python3 -c "import sys,json; print(json.load(sys.stdin).get('state',''))" 2>/dev/null || echo "")
if [ "${PR_STATE}" = "open" ]; then
echo " ✅ PASS: PR #${PR_NUMBER} 状态为 open"
PASS=$((PASS + 1))
else
echo " ❌ FAIL: PR #${PR_NUMBER} 状态异常 (${PR_STATE})"
FAIL=$((FAIL + 1))
fi
echo "[TEST 4] Admin 登录"
ADMIN_JWT=$(curl -sf -X POST "${ASSISTANT_URL}/admin/api/login" \
-H "Content-Type: application/json" \
-d '{"password":"password"}' | python3 -c "import sys,json; print(json.load(sys.stdin).get('token',''))" 2>/dev/null || echo "")
if [ -n "${ADMIN_JWT}" ]; then
echo " ✅ PASS: Admin JWT 获取成功"
PASS=$((PASS + 1))
else
echo " ❌ FAIL: Admin JWT 获取失败"
FAIL=$((FAIL + 1))
fi
echo "[TEST 5] 等待 review run 产出并成功(最多等待 ${MAX_WAIT}s"
RUNS=$(curl -sf "${ASSISTANT_URL}/admin/api/review/runs" \
-H "Authorization: Bearer ${ADMIN_JWT}" 2>/dev/null || echo "[]")
RUN_COUNT=$(echo "${RUNS}" | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d.get('data',d if isinstance(d,list) else [])))" 2>/dev/null || echo "0")
WAITED=0
while [ ${WAITED} -lt ${MAX_WAIT} ]; do
RUNS=$(curl -sf "${ASSISTANT_URL}/admin/api/review/runs" \
-H "Authorization: Bearer ${ADMIN_JWT}" 2>/dev/null || echo "{}")
RUN_ID=$(echo "${RUNS}" | python3 -c "import sys,json; d=json.load(sys.stdin); runs=d.get('data', d if isinstance(d,list) else []); print(runs[0]['id'] if runs else '')" 2>/dev/null || echo "")
RUN_STATUS=$(echo "${RUNS}" | python3 -c "import sys,json; d=json.load(sys.stdin); runs=d.get('data', d if isinstance(d,list) else []); print(runs[0].get('status','') if runs else '')" 2>/dev/null || echo "")
if [ -n "${RUN_ID}" ] && [ "${RUN_STATUS}" = "succeeded" ]; then
echo " ✅ PASS: run=${RUN_ID} status=succeeded (${WAITED}s)"
PASS=$((PASS + 1))
break
fi
echo " ⏳ 等待 run... (${WAITED}/${MAX_WAIT}s, run=${RUN_ID:-none}, status=${RUN_STATUS:-none})"
sleep ${POLL_INTERVAL}
WAITED=$((WAITED + POLL_INTERVAL))
done
if [ -z "${RUN_ID}" ]; then
echo " ❌ FAIL: 未发现 review run"
FAIL=$((FAIL + 1))
fi
if [ -n "${RUN_ID}" ]; then
LATEST_DETAIL=$(curl -sf "${ASSISTANT_URL}/admin/api/review/runs/${RUN_ID}" \
-H "Authorization: Bearer ${ADMIN_JWT}" 2>/dev/null || echo '{}')
fi
echo "[TEST 6] 会话树包含主/子 Agent 与工具调用"
TREE_ASSERT=$(echo "${LATEST_DETAIL}" | python3 -c '
import json,sys
d=json.load(sys.stdin)
t=d.get("sessionTree") or {}
main_type=t.get("agentType")
main_tools=[x.get("toolName") for x in t.get("toolCalls",[])]
inv=t.get("invocations",[])
has_spawn="spawn_subagent" in main_tools
child_ok=False
if inv:
child=inv[0].get("childSession") or {}
child_tools=[x.get("toolName") for x in child.get("toolCalls",[])]
child_ok=("search_code" in child_tools and "read_file" in child_tools)
print("ok" if (main_type=="review-main-agent" and has_spawn and len(inv)>0 and child_ok) else "bad")
' 2>/dev/null || echo "bad")
if [ "${TREE_ASSERT}" = "ok" ]; then
echo " ✅ PASS: 主会话与子代理调用链存在(含 search_code/read_file"
PASS=$((PASS + 1))
else
echo " ❌ FAIL: sessionTree 未满足动态子代理断言"
echo "${LATEST_DETAIL}" | python3 -m json.tool 2>/dev/null || true
FAIL=$((FAIL + 1))
fi
echo "[TEST 7] run details 包含 findings 与评论记录"
DETAIL_ASSERT=$(echo "${LATEST_DETAIL}" | python3 -c '
import json,sys
d=json.load(sys.stdin)
findings=d.get("findings",[])
comments=d.get("comments",[])
ok=(len(findings) > 0 and len(comments) > 0)
print("ok" if ok else "bad")
' 2>/dev/null || echo "bad")
if [ "${DETAIL_ASSERT}" = "ok" ]; then
echo " ✅ PASS: run details 存在 findings/comments"
PASS=$((PASS + 1))
else
echo " ❌ FAIL: run details 缺少 findings 或 comments"
FAIL=$((FAIL + 1))
fi
echo "[TEST 8] Gitea 评论产物summary + line comments"
ISSUE_COMMENTS=$(curl -sf "${GITEA_URL}/api/v1/repos/${ADMIN_USER}/${REPO_NAME}/issues/${PR_NUMBER}/comments" \
-H "Authorization: token ${GITEA_TOKEN}" 2>/dev/null || echo "[]")
LINE_COMMENTS=$(curl -sf "${GITEA_URL}/api/v1/repos/${ADMIN_USER}/${REPO_NAME}/pulls/${PR_NUMBER}/comments" \
-H "Authorization: token ${GITEA_TOKEN}" 2>/dev/null || echo "[]")
SUMMARY_COUNT=$(echo "${ISSUE_COMMENTS}" | python3 -c '
import json,sys
arr=json.load(sys.stdin)
cnt=0
for c in arr:
body=c.get("body") or ""
if "审查" in body or "review" in body.lower() or "AI" in body:
cnt += 1
print(cnt)
' 2>/dev/null || echo "0")
LINE_COUNT=$(echo "${LINE_COMMENTS}" | python3 -c 'import json,sys; print(len(json.load(sys.stdin)))' 2>/dev/null || echo "0")
if [ "${SUMMARY_COUNT}" -gt "0" ] && [ "${LINE_COUNT}" -gt "0" ]; then
echo " ✅ PASS: summary=${SUMMARY_COUNT}, line_comments=${LINE_COUNT}"
PASS=$((PASS + 1))
else
echo " ❌ FAIL: Gitea 评论产物不足summary=${SUMMARY_COUNT}, line_comments=${LINE_COUNT}"
echo " --- issue comments ---"
echo "${ISSUE_COMMENTS}" | python3 -m json.tool 2>/dev/null || true
echo " --- line comments ---"
echo "${LINE_COMMENTS}" | python3 -m json.tool 2>/dev/null || true
FAIL=$((FAIL + 1))
fi
# ─── 结果汇总 ───
echo ""
echo "=== E2E 测试结果 ==="
TOTAL=$((PASS + FAIL))
echo " 通过: ${PASS}/${TOTAL}"
echo " 失败: ${FAIL}/${TOTAL}"
if [ ${FAIL} -gt 0 ]; then
echo ""
echo "⚠️ 部分测试失败。请检查:"
echo " 1. docker compose e2e 容器均 healthy"
echo " 2. assistant 容器环境含 E2E_MOCK_LLM=1 与正确 GITEA_ACCESS_TOKEN"
echo " 3. webhook 已触发且 run details 可见 sessionTree/findings/comments"
exit 1
else
echo ""
echo "🎉 所有 E2E 测试通过!"
exit 0
fi