Эх сурвалжийг харах

fix(interface): 修复闭环脚本三处长期失真/漏测问题

1) 报告 E2E 指标恒为 0:.last-run.json 不含 passed 字段,旧实现永远取不到,
   报告长期显示「API e2e 末次 0 通过」。改为优先解析 playwright_smoke.log 汇总行。
2) NOT_FOUND 计数按行统计(6 行)而非唯一端点(2 个),报告前后矛盾。
   改为按 method+path 去重,并修正措辞(真实成因是契约路径不一致,非「前端未部署」)。
3) run_interface_tests_cron.sh 补全用例截断 Bug:': > $EXTRA_CASES' 每轮清空文件
   只写本轮新增,导致历史自动补全用例被静默丢弃(持续性漏测)。改为并集去重。

另:Playwright 执行器异常中断时不再回落成「0 passed/0 failed」(看似全绿实为未跑),
显式标注「未执行」并给出失败特征。
E2E Test Bot 1 долоо хоног өмнө
parent
commit
753b569583
1 өөрчлөгдсөн 64 нэмэгдсэн , 15 устгасан
  1. 64 15
      run_interface_tests_cron.sh

+ 64 - 15
run_interface_tests_cron.sh

@@ -90,14 +90,23 @@ if [ -f "$BASELINE_EP" ]; then
   n_rmv=$(echo "$RMVEP" | grep -c . || true)
   log "接口面变化:新增 $n_new 个,移除 $n_rmv 个"
   if [ "$n_new" -gt 0 ]; then
-    : > "$EXTRA_CASES"
+    # 合并而非截断:旧实现 `: > "$EXTRA_CASES"` 会先清空整个文件再只写「本轮新增」,
+    # 导致上一轮自动补全的用例被静默丢弃(每出现 1 个新接口就抹掉上一批,持续性漏测)。
+    # 改为与既有内容取并集去重后写回。
+    _EXTRA_TMP="$EXTRA_CASES.tmp.$$"
+    : > "$_EXTRA_TMP"
+    [ -f "$EXTRA_CASES" ] && cat "$EXTRA_CASES" >> "$_EXTRA_TMP"
     while IFS= read -r line; do
       [ -z "$line" ] && continue
       m=$(echo "$line" | awk '{print $1}')
       p=$(echo "$line" | awk '{print $2}')
-      printf 'auto\tauto\t%s\t%s\tfalse\tfalse\n' "$m" "$p" >> "$EXTRA_CASES"
+      printf 'auto\tauto\t%s\t%s\tfalse\tfalse\n' "$m" "$p" >> "$_EXTRA_TMP"
     done <<< "$NEWEP"
-    log "已为新增接口生成补全用例 -> $EXTRA_CASES"
+    # 按 method+path 去重排序后回写
+    sort -u -t$'\t' -k3,4 "$_EXTRA_TMP" > "$EXTRA_CASES"
+    rm -f "$_EXTRA_TMP"
+    _extra_n=$(grep -c . "$EXTRA_CASES" 2>/dev/null || echo 0)
+    log "已为新增接口生成补全用例(并集去重后共 $_extra_n 条)-> $EXTRA_CASES"
   fi
 else
   log "首次运行:建立接口基线(不触发补全)"
@@ -324,24 +333,47 @@ for f in specs:
 completed_runnable = e2e_total - skip
 
 # 3) API e2e 末次实跑(.last-run.json)
+#    注意:.last-run.json 只含 {"status","failedTests"},不含 passed 数,
+#    旧实现用 j.get('passed', ...) 永远取不到,导致报告长期显示「末次 0 通过」。
+#    改为优先解析 playwright_smoke.log 的汇总行("<n> passed"),并以 .last-run.json 兜底。
 api_pass = api_fixme = 0
-lr = os.path.join(tests_dir, 'test-results', '.last-run.json')
-if os.path.exists(lr):
+api_pass_src = '未取到结果'
+_pw_log = os.path.join(repo_dir, 'playwright_smoke.log')
+if os.path.exists(_pw_log):
     try:
-        j = json.load(open(lr, encoding='utf-8'))
-        if isinstance(j, dict):
-            api_pass = int(j.get('passed', j.get('stats', {}).get('expected', 0) or 0))
-            api_fixme = int(j.get('fixme', j.get('stats', {}).get('skipped', 0) or 0))
+        _txt = open(_pw_log, encoding='utf-8', errors='ignore').read()
+        _m = re.findall(r'(\d+)\s+passed', _txt)
+        _s = re.findall(r'(\d+)\s+skipped', _txt)
+        if _m:
+            api_pass = int(_m[-1])
+            api_fixme = int(_s[-1]) if _s else 0
+            api_pass_src = 'playwright_smoke.log'
     except Exception:
         pass
-
-# 4) NOT_FOUND 端点数(从本轮 CUR_TSV 直接统计,避免依赖 env 未导出字段)
+if api_pass_src != 'playwright_smoke.log':
+    lr = os.path.join(tests_dir, 'test-results', '.last-run.json')
+    if os.path.exists(lr):
+        try:
+            j = json.load(open(lr, encoding='utf-8'))
+            if isinstance(j, dict):
+                api_pass = int(j.get('passed', j.get('stats', {}).get('expected', 0) or 0))
+                api_fixme = int(j.get('fixme', j.get('stats', {}).get('skipped', 0) or 0))
+                api_pass_src = '.last-run.json'
+        except Exception:
+            pass
+
+# 3b) NOT_FOUND 唯一端点数(按 method+path 去重)
+#     CUR_TSV 中同一端点会因多调用点重复出现(历史实测 6 行 -> 实际 2 个唯一端点),
+#     旧实现按行数统计,导致报告写「6 个 NOT_FOUND 端点」而摘要写 2,前后矛盾。
 nf = 0
+nf_unique = set()
 try:
     for line in open(cur_tsv, encoding='utf-8', errors='ignore'):
         c = line.rstrip('\n').split('\t')
         if len(c) >= 7 and c[6].strip() == 'NOT_FOUND':
             nf += 1
+            nf_unique.add((c[2].strip(), c[3].strip()))
+    nf = len(nf_unique)
 except Exception:
     pass
 
@@ -354,7 +386,7 @@ if dom > 0:
 if skip > 0:
     reasons.append((skip, f"已知缺陷/待上线导致跳过:{skip} 个用例被 test.skip/test.fixme 标记(能量发放 500-CFC-EN-001、昵称未持久化-CFC-FM-001 等),待后端修复后启用。"))
 if nf > 0:
-    reasons.append((nf, f"新功能待部署后验证:接口冒烟发现 {nf} 个 NOT_FOUND 端点,对应前端页面尚未部署到测试环境,相关 E2E 暂挂起。"))
+    reasons.append((nf, f"接口冒烟发现 {nf} 个唯一端点返回 404(NOT_FOUND):前后端契约路径不一致(如前端 child/* 与后端 member/*),需开发对齐路径或补别名后重部署,相关 E2E 暂挂起。"))
 if TOTAL_DOC > 0:
     reasons.append((TOTAL_DOC, f"文档化用例未全量自动化:{TOTAL_DOC} 条设计用例(TC-xxxx),核心主链路已落地 e2e,边缘/异常流仍以文档形式待补自动化。"))
 
@@ -363,7 +395,7 @@ section.append("\n## 测试用例完成情况")
 section.append(f"- 自动化用例总数(可执行 `test()`):**{e2e_total}**(api 层 {api} / 角色分包 {roles} / DOM 层 {dom})")
 section.append(f"- 文档化用例(设计层 TC-xxxx):**{TOTAL_DOC}**")
 section.append(f"- 已完成(自动化且未被跳过):**{completed_runnable}/{e2e_total} = {pct(completed_runnable, e2e_total)}**")
-section.append(f"- 已实跑通过:接口冒烟 BIZ_OK **{OK_COUNT}** 端点 + API e2e 末次 **{api_pass}** 通过 / {api_fixme} fixme")
+section.append(f"- 已实跑通过:接口冒烟 BIZ_OK **{OK_COUNT}** 端点 + API e2e 末次 **{api_pass}** 通过 / {api_fixme} skipped(来源 {api_pass_src})")
 section.append("")
 section.append(f"### 未完成 / 未开始的原因(共 {len(reasons)} 类)")
 if reasons:
@@ -438,16 +470,33 @@ if [ "${#PW_RUN[@]}" -gt 0 ] && [ -f "$PW_CFG" ]; then
   PW_PASS=$(grep -oE '[0-9]+ passed' "$PW_LOG" | grep -oE '[0-9]+' | head -1)
   PW_FAIL=$(grep -oE '[0-9]+ failed' "$PW_LOG" | grep -oE '[0-9]+' | head -1)
   PW_SKIP=$(grep -oE '[0-9]+ skipped' "$PW_LOG" | grep -oE '[0-9]+' | head -1)
+  # 崩溃保护:若日志里没有任何用例汇总行(如被沙箱 safe-delete/ETIMEDOUT 打断),
+  # 旧实现会因 grep 取空而回落成「0 passed / 0 failed」,看上去像「全绿」,实为「根本没跑」。
+  # 此时显式标记为「未执行」,避免误报。
+  PW_CRASHED=0
+  if ! grep -qE '[0-9]+ (passed|failed|skipped)' "$PW_LOG"; then
+    PW_CRASHED=1
+  fi
   {
     echo ""
     echo "## Playwright E2E 冒烟(api 层,独立,不阻断主闭环)"
     echo "- 执行器: ${PW_RUN[*]} test -c playwright.config.api.js --project=desktop"
-    echo "- 结果: **${PW_PASS:-0} passed / ${PW_FAIL:-0} failed / ${PW_SKIP:-0} skipped**"
+    if [ "$PW_CRASHED" = "1" ]; then
+      echo "- 结果: **未执行(执行器异常中断,日志无用例汇总行)**"
+      echo "- ⚠️ 本轮 E2E 未产出结果,**不可**解读为 0 failed / 全绿;需人工重跑确认。"
+      echo "- 失败特征: $(grep -oE 'Error: [^\\n]*' "$PW_LOG" | head -1)"
+    else
+      echo "- 结果: **${PW_PASS:-0} passed / ${PW_FAIL:-0} failed / ${PW_SKIP:-0} skipped**"
+    fi
     echo "- 覆盖: tests/e2e/api/*.spec.js(含 nonfunc-security 安全契约、nonfunc-perf-compat 性能兼容)"
     echo "- 说明: 红用例=已知后端缺口(权限提升/开放端点/上传未校验/缺鉴权过滤器),作持续告警;失败不阻断本闭环。"
     echo "- 明细: $PW_LOG"
   } > "$PW_RPT"
-  log "Playwright E2E 冒烟完成: ${PW_PASS:-0} passed / ${PW_FAIL:-0} failed / ${PW_SKIP:-0} skipped(报告 $PW_RPT)"
+  if [ "$PW_CRASHED" = "1" ]; then
+    log "⚠️ Playwright E2E 冒烟未产出结果(执行器异常中断,非 0 failed)—— 报告已标注「未执行」,不可解读为全绿"
+  else
+    log "Playwright E2E 冒烟完成: ${PW_PASS:-0} passed / ${PW_FAIL:-0} failed / ${PW_SKIP:-0} skipped(报告 $PW_RPT)"
+  fi
 else
   log "⚠️ 未找到 Playwright 执行器或配置,跳过 E2E 冒烟"
 fi