Files
cc-web/docs/gitea-workflow/TESTING.md

8.1 KiB
Raw Permalink Blame History

Gitea Webhook Agent 测试设计

本文与 PRD.md 的 FR/AC 以及 ARCHITECTURE.md 的字段、状态和错误矩阵对齐。独立协议 回归入口为:

timeout 60s node scripts/gitea-webhook-regression.js

脚本只使用 Node 内置模块和 fixtures/gitea-workflow/*.json,不启动 server.js、不访问网络、不写业务 Store适合实现落地前后的离线验收。

1. 测试金字塔

层级 目标 本轮入口
契约/纯函数 HMAC、事件规范化、mention/Bot 过滤、幂等键 gitea-webhook-regression.js
状态模型 queued/running/retry_wait/终态恢复和有限重试 同上 + 实现方状态机单测
协议集成 HTTP header、Gitea REST、Codex JSON-RPC、MCP stdio mock server/adapter 集成测试
端到端 真实 Gitea 测试仓库到最终评论 staging 手工/CI smoke不纳入离线脚本
运维验收 重启、备份恢复、pause/disable/abort、告警脱敏 staging runbook 演练

离线脚本是最小门禁;当前仓库已提供以下专项门禁:

node scripts/gitea-webhook-regression.js
node scripts/gitea-webhook-workspace-unit.js
node scripts/gitea-workflow-codex-unit.js
node scripts/gitea-workflow-core-unit.js
node scripts/gitea-workflow-management-unit.js

仍需在 staging 用真实 Gitea 验证出站回帖确认;纯函数通过不能替代真实凭据和 网络链路验收。

2. Fixtures 约定

所有 fixture 均为 JSON放在 fixtures/gitea-workflow/,不含真实 Secret、Token、 源码或生产 URL。字段约定

{
  "name": "valid-issue-comment",
  "headers": {
    "x-gitea-event": "issue_comment",
    "x-gitea-delivery": "delivery-001"
  },
  "payload": {
    "repository": {"owner": "acme", "name": "widget"},
    "issue": {"number": 42},
    "comment": {"id": 101, "body": "@ccweb-bot 请修复"},
    "sender": {"login": "alice", "is_bot": false}
  },
  "expected": {"accepted": true, "resourceKind": "issue"}
}

脚本运行时用固定测试 Secret 计算签名;签名不落盘。需要拒绝签名时复制 payload 后篡改 raw body 或签名头。fixture 变更必须同步场景 ID 和预期结果。

3. 需求覆盖矩阵

场景 ID 验证内容 Fixture/断言 对应 AC
WH-01 合法 Issue 评论 mention 触发,规范化 owner/repo/number/comment valid-issue-comment.json AC-01
WH-02 合法 PR 评论进入 pull_request 资源,保留 base/head valid-pr-comment.json AC-01/03
WH-03 HMAC 缺失/错误不写业务任务,常量时间比较 valid-issue-comment.json 篡改签名 AC-02
WH-04 相同 instanceId:deliveryId 重放返回 duplicate不增 task valid fixture 两次提交 AC-02
WH-05 无 mention、非评论事件、Bot 自评论、停用仓库均 ignored/rejected ignored-events.jsonbot-self-comment.json AC-01/10
Q-01 重启后 queued 保留running 变 retry_wait(interrupted_by_restart) queue-recovery.json AC-09
Q-02 已有重试次数达到上限后进入 failedwaiting_user/终态不重跑 同上 AC-09
Q-03 同仓库运行锁互斥;跨仓库可以并行,不设置额外全局活动上限 脚本内最小状态快照 AC-04
R-00 合法触发仅产生一条 giteabot: 已收到 起始回执;成功路径不产生运行中/等待/已完成状态评论 server.js 回执策略断言 AC-07
R-01 MCP 回帖成功但查询缺失只创建一个 reply_retry reply-failure.json AC-08
R-02 补发仍失败才 REST 兜底,结果为 succeeded_with_rest_fallback 同上 AC-08
MCP-01 thread/start.config.mcp_servers.gitea 为线程级 stdio 配置 mcp-thread-config.json AC-06
MCP-02 collaborationMode 字段放 settings顶层无 model/effort 重复字段 同上 AC-06
MCP-03 gitea-mcp 命令缺失、进程启动即退、initialize 返回 error、握手超时分别映射稳定错误码 gitea-mcp-probe-unit.js + mock stdio fixture AC-06/07
MCP-04 预检失败后任务只产生一条 giteabot: 失败终态回执;线程级注入仍保留且不自动安装二进制 gitea-mcp-probe-unit.js 源码守门 AC-06/07/11
SEC-01 Secret/token 不进入日志、评论、clone URL 或 fixture 全部 fixture + 输出扫描 AC-02/11
SEC-02 路径穿越、任意命令、越权控制参数被拒绝 实现方安全单测 AC-03/10/11

4. 状态、恢复与并发测试

实现方 Store 测试必须验证以下不变量:

  1. deliveryKeysessionKeytaskId+turnIdcommentKey 唯一;重复请求 返回原记录且不创建新 turn/comment。
  2. 同一 repoKey 同时最多一个 preparing/running/aborting;不同 repoKey 可以并行pause 只阻止领取,不强制终止运行中任务。
  3. 重启扫描用 stateVersion/leaseId CAS旧 worker 不能覆盖新租约; running 最多自动恢复一次,超过上限进入 failed
  4. waiting_user 保留 thread新评论创建新 task 但复用 sessionKey/threadId 所有终态不再自动执行。
  5. blocked_workspace 不执行 reset/clean管理员解除后才可重新入队。

故障注入至少覆盖进程在“intent 已落库、外部调用未完成”和“外部返回成功、 结果未确认”两个窗口崩溃,恢复后必须通过幂等键继续,而非盲目重复修改。

5. 回帖重试测试

按以下顺序模拟:

  1. MCP comment 返回 accepted但 Gitea 查询无匹配标记:状态进入 verifying_reply,只允许一个 reply_retry turn。
  2. reply_retry 仍无评论:调用一次 REST 兜底body 复用 taskId/turnId/resourceKey 标记;查询成功为 succeeded_with_rest_fallback
  3. REST 返回 2xx 但查询仍为空:状态必须是 failed_reply,不能虚报成功,也不得 无限重试。
  4. 原 MCP 评论已确认后重复 webhook/重启:不得再发送起始、状态、最终或兜底评论。

测试日志只输出 taskId/turnId/commentId/errorCode,不得输出正文中的 Secret 或 完整 Authorization。

6. MCP/JSON-RPC 协议测试

mock app-server 必须断言初始化顺序为:initializeinitialized → best-effort experimentalFeature/enablement/setcollaborationMode/listthread/start。 能力探测失败只记录降级,不阻断启动。

另需运行 node scripts/gitea-mcp-probe-unit.js,它会以独立 mock stdio 进程验证 gitea-mcp 的 initialize 预检,不需要真实 Gitea、Token 或 Docker。

thread/start 断言:

  • config.mcp_servers.gitea 每个线程单独携带 host/token 引用和 stdio command
  • CC_WEB_SOURCE_SESSION_ID 等来源上下文不能只存在进程环境;
  • 使用 collaboration mode 时,modelreasoning_effortdeveloper_instructions 只在 collaborationMode.settings,顶层不存在重复 model/effort;拒绝 collaborationMode 时可降级普通 turn。

7. 安全与运维验收

  • 验签使用 raw body + HMAC-SHA256 + 常量时间比较;缺失/错误签名 401 且无业务写入。
  • 事件正文、Issue 描述、代码和 PR 内容均视为不可信 prompt不能改变系统约束、 读取 Secret、关闭审计或注入任意命令。
  • Bot 自评论、停用仓库、非评论事件和无 mention 事件不启动 Agent每个决定有 审计 action/errorCode。
  • clone/fetch 使用不含 Token 的 remote URL工作区路径必须在允许根目录内 路径穿越和脏目录覆盖均拒绝。
  • 控制 API 需要管理员身份、reason 和 idempotency key取消/中止幂等。
  • 日志和审计脱敏检查覆盖 Secret、Bot Token、Authorization、MCP args/env。

8. 执行清单

node --check scripts/gitea-webhook-regression.js
timeout 60s node scripts/gitea-webhook-regression.js
git diff --check

CI 失败时保留 stdout/stderr、fixture 名称和场景 ID不得重试隐藏不稳定失败。 真实 Gitea staging 验收完成后,应将 delivery/task/thread/turn/comment 关联链和 最终状态写入审计报告,作为发布批准依据。