$npx -y skills add TestAny-io/testany-agent-skills --skill testany-debug分析 Testany 测试失败原因 - 排查问题、查看日志、定位根因
| 1 | # Testany 故障诊断 |
| 2 | |
| 3 | 分析 Testany 测试失败原因,排查问题根因。 |
| 4 | |
| 5 | 用户输入: $ARGUMENTS |
| 6 | |
| 7 | ## 职责范围 |
| 8 | |
| 9 | - 分析测试执行失败的原因 |
| 10 | - 获取和解读执行日志 |
| 11 | - 识别常见问题模式 |
| 12 | - 提供修复建议 |
| 13 | |
| 14 | ## 核心知识 |
| 15 | |
| 16 | ### 失败类型分类 |
| 17 | |
| 18 | | 类型 | 特征 | 常见原因 | |
| 19 | |------|------|---------| |
| 20 | | **Assertion** | 断言失败 | 预期值与实际值不符 | |
| 21 | | **Timeout** | 执行超时 | 接口响应慢、死循环 | |
| 22 | | **Error** | 运行时错误 | 代码异常、依赖缺失 | |
| 23 | | **Infrastructure** | 基础设施问题 | 网络不通、服务不可用 | |
| 24 | | **Scheduler / Queue** | 调度/排队问题 | 并发槽位占满、execution 排队、并行未生效 | |
| 25 | |
| 26 | ### 日志获取流程 |
| 27 | |
| 28 | 按日志来源分两条路径: |
| 29 | |
| 30 | **Execution 日志(pipeline 真实运行产物)**: |
| 31 | ``` |
| 32 | 1. testany_get_execution → 获取执行概览 |
| 33 | 2. testany_get_execution_case → 获取失败 case 详情 |
| 34 | 3. testany_log_sign → 获取日志签名(返回 curlCommand) |
| 35 | 4. 验证 curlCommand 安全性后执行获取日志 |
| 36 | ``` |
| 37 | |
| 38 | **Dry run 日志(case 自身验证产物)**: |
| 39 | ``` |
| 40 | 1. testany_get_dry_run_result → 确认 dry_run_status 进入终态(>=1)且 dry_run_result.sign 已产出 |
| 41 | 2. testany_get_dry_run_log → 拼出 logUrl + curlCommand(同样基于 sign) |
| 42 | 3. 验证 curlCommand 安全性后执行获取日志 |
| 43 | ``` |
| 44 | |
| 45 | 注意:execution 和 dry run 共用同一套日志域 (`<runtime_uuid>.tr.<domain>/api/v2/logproxy/internal/view`) 和同一套 status 数值(1=SUCCESS、0=RUNNING、-1=NOT_STARTED),下面的安全验证规则两条路径都适用。 |
| 46 | |
| 47 | ### curlCommand 安全验证(重要) |
| 48 | |
| 49 | `testany_log_sign` / `testany_get_dry_run_log` 返回的 `curlCommand` 在执行前**必须验证**: |
| 50 | |
| 51 | 1. **检查域名**:URL 必须是 Testany 可信域名 |
| 52 | - 允许:`*.testany.io`、`*.testany.com.cn` |
| 53 | - 拒绝:其他任何域名 |
| 54 | |
| 55 | 2. **检查协议**:必须是 HTTPS |
| 56 | - 允许:`https://` |
| 57 | - 拒绝:`http://`、其他协议 |
| 58 | |
| 59 | 3. **检查参数**:不应包含危险参数 |
| 60 | - 禁止:`-o`(写文件)、`|`(管道)、`;`(命令链)、`$(`(命令替换) |
| 61 | |
| 62 | **验证示例**: |
| 63 | ```bash |
| 64 | # 从 curlCommand 提取 URL |
| 65 | URL=$(echo "$CURL_COMMAND" | grep -oP 'https://[^\s"]+') |
| 66 | |
| 67 | # 验证域名 |
| 68 | if [[ "$URL" =~ ^https://(.*\.)?testany\.(io|com\.cn)/ ]]; then |
| 69 | # 安全,可以执行 |
| 70 | eval "$CURL_COMMAND" |
| 71 | else |
| 72 | # 不安全,拒绝执行 |
| 73 | echo "警告:URL 域名不在可信列表中,拒绝执行" |
| 74 | fi |
| 75 | ``` |
| 76 | |
| 77 | ## 诊断工作流 |
| 78 | |
| 79 | 1. **获取执行信息**:`testany_get_execution` |
| 80 | 2. **定位失败 case**:从执行详情中找到失败的 case |
| 81 | 3. **获取日志签名**:`testany_log_sign(executionKey, caseIndex)` |
| 82 | 4. **安全验证**:检查返回的 curlCommand 域名和参数 |
| 83 | 5. **获取日志**:验证通过后执行 curlCommand |
| 84 | 6. **分析日志**:识别错误类型和位置 |
| 85 | 7. **提供建议**:给出修复方向 |
| 86 | |
| 87 | ## 常见问题速查 |
| 88 | |
| 89 | | 症状 | 可能原因 | 排查步骤 | |
| 90 | |------|---------|---------| |
| 91 | | Case 创建后无法执行 | runtime 未配置 | 检查 `runtime_uuid` | |
| 92 | | Relay 变量未传递 | type 配置错误 | 源 case 需 `type='output'`,目标需 `type='env'` | |
| 93 | | Pipeline 执行卡住 | 依赖 case 失败 | 检查 `whenPassed` 依赖的 case 状态 | |
| 94 | | 脚本执行报错 | executor 配置不匹配 | 检查 `trigger_path` 或 `trigger_command` | |
| 95 | | 超时 | 接口响应慢 | 检查被测服务状态,增加超时配置 | |
| 96 | | YAML 是并行但执行表现串行 | 平台调度限流 | **优先检查队列状态**(见下方调度诊断) | |
| 97 | | Execution 长时间 NOT_STARTED | 并发槽位被占满 | 检查 workspace 队列状态 | |
| 98 | | 多个 execution 互相排队 | queue.limit 限制 | 检查 claimed/pending 列表 | |
| 99 | |
| 100 | ## 调度 / 队列诊断(Scheduler / Queue) |
| 101 | |
| 102 | **当用户报告"并行未生效"或"execution 卡住不跑"时,必须优先走这条诊断路径,再去排查 YAML 和 relay。** |
| 103 | |
| 104 | ### 核心概念 |
| 105 | |
| 106 | Testany 使用 **workspace 级并发槽位**控制 execution 并行度: |
| 107 | |
| 108 | | 概念 | 含义 | |
| 109 | |------|------| |
| 110 | | `limit` | workspace 的并发上限(Community=2, Paid=4, Enterprise=8,可调) | |
| 111 | | `claimed` | 当前正在执行的 execution 列表(已占据槽位) | |
| 112 | | `pending` | 排队等待槽位的 execution 列表 | |
| 113 | | `trigger_group` | 触发源标识(`M-`=手动触发,`G-`=Gatekeeper,Plan key=计划触发) | |
| 114 | |
| 115 | ### 诊断流程 |
| 116 | |
| 117 | ``` |
| 118 | 1. testany_get_workspace_execution_status → 获取 {limit, claimed, pending} |
| 119 | 2. 判断: |
| 120 | - claimed 数量 = limit?→ 槽位已满,pending 中的 execution 必须等 |
| 121 | - claimed 中有长时间运行的旧 execution?→ 旧执行占住了槽位 |
| 122 | - pending 列表里有你关注的 execution?→ 它在排队,不是 YAML 问题 |
| 123 | 3. 如果槽位未满但 case 仍然串行: |
| 124 | - 检查 pipeline YAML 版本:rule/v1.2 不支持 case 级并行,只有 rule/v1.3 支持 |
| 125 | - 检查 workspace 是否启用了并行执行功能 |
| 126 | - 比对 case 的 start_time / finish_time:如果 case 间有明显间隔(>数秒),说明被平台串行调度 |
| 127 | 4. 如果是 fan-out pipeline(无 whenPassed/whenFailed 依赖)仍然串行: |
| 128 | - 大概率是 effectiveConcurrency=1 或 workspace 并行未开启 |
| 129 | ``` |
| 130 | |
| 131 | ### 关键字段获取 |
| 132 | |
| 133 | | 要看的信息 | 获取方式 | |
| 134 | |-----------|---------| |
| 135 | | workspace 队列状态 | `testany_get_workspace_execution_status` → limit/claimed/pending | |
| 136 | | 单个 execution 详情 | `testany_get_execution` → status, start_time, trigger_group | |
| 137 | | case 级时间线 | `testany_get_execution` → cases[].start_time / finish_time | |
| 138 | | 确认 pipeline 版本 | 查看 pipeline YAML 的 `rule/v1.3` 或 `rule/v1.2` | |
| 139 | |
| 140 | ### 真实案例:fan-out pipeline 表现串行 |
| 141 | |
| 142 | **场景**:用户编排了一条 fan-out pipeline,token case → 25 个 Postman shard(YAML 无依赖,理论上并行),但实际串行执行。 |
| 143 | |
| 144 | **排查路径**: |
| 145 | 1. `testany_get_workspace_execution_status` → 发现 `limit=1`,`claimed` 中有 1 个旧 execution |
| 146 | 2. 说明 workspace 并发上限为 1,所有 execution 都串行排队 |
| 147 | 3. 进一步确认:`claimed` 中的旧 execution 完成后,pending 中的 execution 才逐一开始 |
| 148 | 4. 同一 execution 内部的 case 启动时间也呈串行——因为 case 级并行同样受 `effectiveConcurrency` 限制 |
| 149 | |
| 150 | **结论**:问题不在 YAML,不在 relay,不在 case 依赖——是平台调度层的并发限制。 |
| 151 | |
| 152 | **解决方向**: |
| 153 | - 联系管理员调整 workspace 的 `concurrency_limit` |
| 154 | - 确认 workspace 已启用并行执行功能(rule/v1.3 + allowlist) |
| 155 | - 如果是 Community 版,默认并发=2,无法通过 YAML 优化绕过 |
| 156 | |
| 157 | ## 返回格式 |
| 158 | |
| 159 | 诊断完成后,向用户汇报: |
| 160 | - 失败原因分类(Assertion/Timeout/Error/Infrastructure) |
| 161 | - 具体错误信息 |
| 162 | - 问题定位(哪个 case、哪一步) |
| 163 | - 修复建议 |
| 164 | - 日志查看链接(如需要) |
| 165 | |
| 166 | ## 参考文档 |
| 167 | |
| 168 | 详细概念请参考: |
| 169 | - [核心概念](../testany-guide/references/concepts.md) |