|
|
@@ -0,0 +1,202 @@
|
|
|
+#!/usr/bin/env bash
|
|
|
+# =============================================================================
|
|
|
+# CFC 接口测试闭环(定时任务主体)
|
|
|
+# 流程:git pull 最新 -> 检测新增/变更需求 -> 跑冒烟(含回归对比) ->
|
|
|
+# 为新增接口补全用例并跑新测试 -> 提交测试结果 -> push 到远端(与开发团队闭环)
|
|
|
+#
|
|
|
+# 网络说明:测试目标 192.168.16.251 只能在「前台/宿主机」网络可达。
|
|
|
+# 本脚本设计为在宿主机用 Windows 任务计划程序每 30 分钟调用,
|
|
|
+# 不要在受限沙箱后台运行(后台网络被拦截)。
|
|
|
+# =============================================================================
|
|
|
+set -uo pipefail
|
|
|
+
|
|
|
+REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
+cd "$REPO_DIR" || exit 1
|
|
|
+
|
|
|
+BRANCH="${CFC_BRANCH:-feature/ui-token-u5-color-tokens}"
|
|
|
+BASE_URL="${CFC_BASE:-http://192.168.16.251}"
|
|
|
+HOST_HDR="${CFC_HOST:-cfc.iwintrue.com}"
|
|
|
+TIMEOUT="${CFC_TIMEOUT:-3}"
|
|
|
+TEST_SCRIPT="ops_interface_smoke.py"
|
|
|
+SCANNER="scan_frontend_endpoints.py"
|
|
|
+
|
|
|
+LOG_DIR="$REPO_DIR/logs"
|
|
|
+mkdir -p "$LOG_DIR"
|
|
|
+
|
|
|
+TS="$(date +%Y%m%d_%H%M%S)"
|
|
|
+RUN_DIR="$LOG_DIR/run_$TS"
|
|
|
+mkdir -p "$RUN_DIR"
|
|
|
+
|
|
|
+CUR_TSV="$RUN_DIR/results_$TS.tsv"
|
|
|
+ENDPOINTS_CUR="$RUN_DIR/endpoints_$TS.txt"
|
|
|
+EXTRA_CASES="$REPO_DIR/interface_test_extra_cases.tsv"
|
|
|
+
|
|
|
+BASELINE_EP="$REPO_DIR/interface_test_baseline_endpoints.txt"
|
|
|
+PREV_TSV="$REPO_DIR/interface_test_prev.tsv"
|
|
|
+TREND="$REPO_DIR/interface_test_trend.log"
|
|
|
+
|
|
|
+MANAGED="INTERFACE_TEST_REPORT.md interface_test_report.tsv cfc-frontend/INTERFACE_TEST_MATRIX.md ops_interface_smoke.py"
|
|
|
+
|
|
|
+log() { echo "[$(date +%H:%M:%S)] $*" | tee -a "$LOG_DIR/interface_cron.log"; }
|
|
|
+
|
|
|
+log "===== CFC 接口测试闭环 开始 $TS ====="
|
|
|
+log "REPO=$REPO_DIR BRANCH=$BRANCH BASE=$BASE_URL HOST=$HOST_HDR"
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 1) 拉取远端最新(含开发团队的修复/新需求)
|
|
|
+# 仅丢弃我们生成的报告类文件本地改动(每次都会重新生成),其余未提交改动用
|
|
|
+# --autostash 处理,避免破坏用户其他在途工作。
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+git checkout -- INTERFACE_TEST_REPORT.md interface_test_report.tsv cfc-frontend/INTERFACE_TEST_MATRIX.md 2>/dev/null
|
|
|
+if git pull --rebase --autostash origin "$BRANCH" >>"$LOG_DIR/interface_cron.log" 2>&1; then
|
|
|
+ log "git pull 成功"
|
|
|
+else
|
|
|
+ log "git pull 失败(可能本地有冲突或凭证问题),用当前代码继续"
|
|
|
+fi
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 2) 检测新增/变更需求(前端接口面变化)
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+python "$SCANNER" --out "$ENDPOINTS_CUR" 2>/dev/null
|
|
|
+NEWEP=""; RMVEP=""
|
|
|
+if [ -f "$BASELINE_EP" ]; then
|
|
|
+ NEWEP=$(comm -13 <(sort "$BASELINE_EP") <(sort "$ENDPOINTS_CUR"))
|
|
|
+ RMVEP=$(comm -23 <(sort "$BASELINE_EP") <(sort "$ENDPOINTS_CUR"))
|
|
|
+ n_new=$(echo "$NEWEP" | grep -c . || true)
|
|
|
+ n_rmv=$(echo "$RMVEP" | grep -c . || true)
|
|
|
+ log "接口面变化:新增 $n_new 个,移除 $n_rmv 个"
|
|
|
+ if [ "$n_new" -gt 0 ]; then
|
|
|
+ : > "$EXTRA_CASES"
|
|
|
+ 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"
|
|
|
+ done <<< "$NEWEP"
|
|
|
+ log "已为新增接口生成补全用例 -> $EXTRA_CASES"
|
|
|
+ fi
|
|
|
+else
|
|
|
+ log "首次运行:建立接口基线(不触发补全)"
|
|
|
+fi
|
|
|
+cp "$ENDPOINTS_CUR" "$BASELINE_EP"
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 3) 跑冒烟测试(前台调用,含登录态;新增接口通过 --extra 一并测试)
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+EXTRA_FLAG=""
|
|
|
+[ -s "$EXTRA_CASES" ] && EXTRA_FLAG="--extra $EXTRA_CASES"
|
|
|
+log "开始执行冒烟测试 $EXTRA_FLAG"
|
|
|
+python "$TEST_SCRIPT" --base "$BASE_URL" --host "$HOST_HDR" \
|
|
|
+ --timeout "$TIMEOUT" --auto-login --authed $EXTRA_FLAG \
|
|
|
+ --report "$CUR_TSV" > "$RUN_DIR/smoke_stdout.txt" 2>&1
|
|
|
+log "冒烟测试完成,结果 -> $CUR_TSV"
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 4) 汇总 + 回归对比 + 生成报告
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+python - "$CUR_TSV" "$PREV_TSV" "$RUN_DIR/report_$TS.md" "$TS" "$TREND" <<'PY'
|
|
|
+import sys, collections
|
|
|
+cur_tsv, prev_tsv, rpt, ts, trend = sys.argv[1:6]
|
|
|
+
|
|
|
+def load(path):
|
|
|
+ d = {}
|
|
|
+ try:
|
|
|
+ with open(path, encoding='utf-8') as f:
|
|
|
+ next(f, None)
|
|
|
+ for line in f:
|
|
|
+ c = line.rstrip('\n').split('\t')
|
|
|
+ if len(c) < 7: continue
|
|
|
+ page, fn, method, path, http, biz, cat = c
|
|
|
+ d[(method, path)] = (page, fn, http, biz, cat)
|
|
|
+ except FileNotFoundError:
|
|
|
+ pass
|
|
|
+ return d
|
|
|
+
|
|
|
+cur = load(cur_tsv)
|
|
|
+prev = load(prev_tsv) if prev_tsv and os.path.exists(prev_tsv) else {}
|
|
|
+
|
|
|
+cats = collections.Counter(v[4] for v in cur.values())
|
|
|
+total = len(cur)
|
|
|
+ok = cats.get('BIZ_OK', 0)
|
|
|
+rate = ok * 100 // total if total else 0
|
|
|
+
|
|
|
+# 回归对比
|
|
|
+fixed = [] # 之前非 OK -> 现在 OK
|
|
|
+regressed = [] # 之前 OK -> 现在非 OK
|
|
|
+for k, v in cur.items():
|
|
|
+ cat = v[4]
|
|
|
+ if k in prev:
|
|
|
+ pcat = prev[k][4]
|
|
|
+ if pcat != 'BIZ_OK' and cat == 'BIZ_OK':
|
|
|
+ fixed.append((k, pcat, cat))
|
|
|
+ elif pcat == 'BIZ_OK' and cat != 'BIZ_OK':
|
|
|
+ regressed.append((k, pcat, cat))
|
|
|
+
|
|
|
+def top(cat, n=20):
|
|
|
+ return [(k[0], k[1], v[3], v[4]) for k, v in cur.items() if v[4] == cat][:n]
|
|
|
+
|
|
|
+lines = []
|
|
|
+lines.append(f"# CFC 接口冒烟测试报告 ({ts})\n")
|
|
|
+lines.append(f"- 目标: {os.environ.get('CFC_HOST', 'cfc.iwintrue.com')} 用例总数: **{total}**")
|
|
|
+lines.append(f"- 业务成功率(BIZ_OK): **{ok}/{total} = {rate}%**\n")
|
|
|
+lines.append("## 分类汇总")
|
|
|
+for k in ('BIZ_OK','BIZ_FAIL','NOT_FOUND','SERVER_ERR','NON_JSON','CONN_ERR'):
|
|
|
+ if cats.get(k): lines.append(f"- {k}: {cats[k]}")
|
|
|
+lines.append("")
|
|
|
+if fixed:
|
|
|
+ lines.append(f"## ✅ 回归修复({len(fixed)} 个,之前失败现已通过)")
|
|
|
+ for (m,p),pc,_ in fixed[:30]:
|
|
|
+ lines.append(f"- `{m} {p}` (原 {pc})")
|
|
|
+ lines.append("")
|
|
|
+if regressed:
|
|
|
+ lines.append(f"## ⚠️ 回归劣化({len(regressed)} 个,之前通过现失败)")
|
|
|
+ for (m,p),_,c in regressed[:30]:
|
|
|
+ lines.append(f"- `{m} {p}` (现 {c})")
|
|
|
+ lines.append("")
|
|
|
+lines.append(f"## 需开发团队关注的契约缺口 (NOT_FOUND={cats.get('NOT_FOUND',0)})")
|
|
|
+for m,p,b,c in top('NOT_FOUND'):
|
|
|
+ lines.append(f"- `{m} {p}` (biz={b}, {c})")
|
|
|
+lines.append("")
|
|
|
+lines.append(f"## 健壮性风险 (SERVER_ERR={cats.get('SERVER_ERR',0)})")
|
|
|
+for m,p,b,c in top('SERVER_ERR'):
|
|
|
+ lines.append(f"- `{m} {p}` (http={b}, {c})")
|
|
|
+lines.append("")
|
|
|
+lines.append("> 说明:冒烟为空体请求,部分 500/400 属预期(缺必填参数)。NOT_FOUND 与回归劣化才需优先处理。")
|
|
|
+
|
|
|
+report = "\n".join(lines) + "\n"
|
|
|
+with open(rpt, 'w', encoding='utf-8') as f:
|
|
|
+ f.write(report)
|
|
|
+
|
|
|
+# 趋势日志
|
|
|
+with open(trend, 'a', encoding='utf-8') as f:
|
|
|
+ f.write(f"{ts}\ttotal={total}\tBIZ_OK={ok}\trate={rate}%\t"
|
|
|
+ f"fixed={len(fixed)}\tregressed={len(regressed)}\t"
|
|
|
+ f"NOT_FOUND={cats.get('NOT_FOUND',0)}\tSERVER_ERR={cats.get('SERVER_ERR',0)}\n")
|
|
|
+print(report)
|
|
|
+print(f"\n[趋势] 已写入 {trend}")
|
|
|
+PY
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 5) 提交测试结果 + 补全用例
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+git add -f ops_interface_smoke.py
|
|
|
+git add INTERFACE_TEST_REPORT.md interface_test_report.tsv cfc-frontend/INTERFACE_TEST_MATRIX.md "$EXTRA_CASES" 2>/dev/null
|
|
|
+if git diff --cached --quiet; then
|
|
|
+ log "无测试结果变更,跳过提交"
|
|
|
+else
|
|
|
+ git commit -m "test(interface): 接口冒烟闭环 $(date '+%Y-%m-%d %H:%M') — 通过率 ${rate:-?}% (fix=$(echo $fixed|wc -l) reg=$(echo $regressed|wc -l))" \
|
|
|
+ >>"$LOG_DIR/interface_cron.log" 2>&1 && log "已提交测试结果"
|
|
|
+fi
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 6) 推送远端,与开发团队形成闭环
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+if git push origin "$BRANCH" >>"$LOG_DIR/interface_cron.log" 2>&1; then
|
|
|
+ log "git push 成功 — 测试闭环已同步远端"
|
|
|
+else
|
|
|
+ log "git push 失败(凭证/网络),下次运行将重试"
|
|
|
+fi
|
|
|
+
|
|
|
+# 更新回归基线
|
|
|
+cp "$CUR_TSV" "$PREV_TSV"
|
|
|
+log "===== 本轮结束 ====="
|