#!/usr/bin/env bash # S8-REGRESSION-FIXTURE-1 统一回归 driver(dev/test only, aidopdev)。 # # 顺序运行子脚本,统一汇总 PASS / SKIP / FAIL: # 1. sched-exec-regression.sh — 调度执行 + antiflap + DetectionLog 主链 # 2. r2-timeout-regression.sh — TIMEOUT 主链(默认复用 demo rule 10) # 3. r3-shortage-regression.sh — SHORTAGE 主链(当前 dev 无 demo SHORTAGE,SKIP) # 4. r3-out-of-range-regression.sh — OUT_OF_RANGE 主链(默认复用 demo rule 11) # 5. r6-detection-log-edge-regression.sh — NO_HIT/EVALUATE_FAILED edge # 6. recovered-edge-regression.sh — RECOVERED 主链 + recover_count_required 抗抖 # 7. active-exception-id-trigger1-regression.sh — trigger=1 首 tick state.active_exception_id 回填 # # 退出码: # - 仅 FAIL>0 时 exit 1; # - 全 PASS 或 PASS+SKIP 混合(FAIL=0)→ exit 0; # - 致命错误(mysql/auth)→ exit 2。 set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=./s8-regression-common.sh source "${SCRIPT_DIR}/s8-regression-common.sh" TOTAL_PASS=0 TOTAL_FAIL=0 TOTAL_SKIP=0 declare -a CHILD_RESULTS # 入场前快照 demo rule 初态(disabled,CTO 决策默认值);末态比对此快照而不是硬编码 enabled=1。 DEMO_INITIAL_SNAPSHOT=$(snapshot_demo_rule_state) baseline_before=$(read_baseline) echo "============================================================" echo "S8 Regression Driver" echo " baseline_before=${baseline_before}" echo " demo_initial_snapshot=${DEMO_INITIAL_SNAPSHOT}" echo "============================================================" # trap:异常退出(child fail / Ctrl+C / SIGTERM)也必须恢复 demo rule disabled 并清 ghost。 # 正常路径会在 trap 触发前显式调用 restore,trap 二次调用是幂等的。 RESTORED_DEMO=0 on_exit() { if [[ "${RESTORED_DEMO}" == "0" ]]; then cleanup_temp_sched_approval_ghost_tasks 2>/dev/null || true restore_demo_rules_disabled_after_regression 2>/dev/null || true RESTORED_DEMO=1 fi } trap on_exit EXIT INT TERM # 入场 arm:CTO 决策"demo rule 默认关闭",回归期间临时启用以驱动 r2/r3-oor 等子链。 arm_demo_rules_for_regression # 每个 child 结束后调用: # 1. 关闭 TEMP_SCHED_% 残留(防跨 child 状态污染); # 2. 软删 TEMP_SCHED_% 的活跃异常; # 3. cleanup_temp_sched_approval_ghost_tasks:取消 TEMP 软删后遗留的 Pending 审批任务。 # 不清除 demo rule 10/11/12、DEMO-S2/S3 数据、或真实业务 exception。 # 不动 detection_state:避免与 Job scheduler 并发竞态破坏 active_exception_id 回填。 # 各 child 自身负责 setup 阶段清自己的 detection_state。 cleanup_between_children() { local label="$1" mysql_run "UPDATE ado_s8_watch_rule SET enabled=0, lock_token=NULL, locked_by=NULL, lock_until=NULL, updated_at=NOW() WHERE rule_code LIKE 'TEMP_SCHED_%' AND tenant_id=${TENANT_ID} AND factory_id=${FACTORY_ID} AND enabled=1;" 2>/dev/null || true mysql_run "UPDATE ado_s8_exception SET is_deleted=1, updated_at=NOW() WHERE source_rule_code LIKE 'TEMP_SCHED_%' AND is_deleted=0;" 2>/dev/null || true cleanup_temp_sched_approval_ghost_tasks 2>/dev/null || true echo "[teardown] after ${label}: TEMP_SCHED_% disabled + active exceptions soft-deleted + ghost approval cancelled" } run_child() { local label="$1" local script="$2" if [[ ! -x "${script}" ]]; then echo "[${label}] SKIP — script not executable: ${script}" TOTAL_SKIP=$((TOTAL_SKIP + 1)) CHILD_RESULTS+=("${label}: SKIP (not executable)") return 0 fi echo echo "===> [${label}] ${script}" local out local rc=0 set +e out=$(bash "${script}" 2>&1) rc=$? set -e echo "${out}" local p f s p=$(printf '%s\n' "${out}" | grep -c '^PASS:' || true) f=$(printf '%s\n' "${out}" | grep -c '^FAIL:' || true) s=$(printf '%s\n' "${out}" | grep -c '^SKIP:' || true) TOTAL_PASS=$((TOTAL_PASS + p)) TOTAL_FAIL=$((TOTAL_FAIL + f)) TOTAL_SKIP=$((TOTAL_SKIP + s)) local status if [[ "${rc}" == "0" && "${f}" == "0" ]]; then if [[ "${p}" == "0" && "${s}" -gt 0 ]]; then status="SKIP" else status="PASS" fi else status="FAIL" fi CHILD_RESULTS+=("${label}: ${status} (pass=${p} fail=${f} skip=${s} rc=${rc})") echo "[${label}] result: ${status} pass=${p} fail=${f} skip=${s} rc=${rc}" cleanup_between_children "${label}" } run_child "sched-exec" "${SCRIPT_DIR}/sched-exec-regression.sh" run_child "r2-timeout" "${SCRIPT_DIR}/r2-timeout-regression.sh" run_child "r3-shortage" "${SCRIPT_DIR}/r3-shortage-regression.sh" run_child "r3-oor" "${SCRIPT_DIR}/r3-out-of-range-regression.sh" run_child "r6-edge" "${SCRIPT_DIR}/r6-detection-log-edge-regression.sh" run_child "recovered" "${SCRIPT_DIR}/recovered-edge-regression.sh" run_child "trigger1-active" "${SCRIPT_DIR}/active-exception-id-trigger1-regression.sh" run_child "oor-shortage-edge" "${SCRIPT_DIR}/oor-shortage-edge-regression.sh" # 显式 restore(trap 是兜底,正常路径走这里;幂等)。 cleanup_temp_sched_approval_ghost_tasks restore_demo_rules_disabled_after_regression RESTORED_DEMO=1 baseline_after=$(read_baseline) echo echo "============================================================" echo "Driver Summary" echo "============================================================" echo "baseline_before=${baseline_before}" echo "baseline_after=${baseline_after}" if [[ "${baseline_before}" == "${baseline_after}" ]]; then echo "baseline: UNCHANGED" else echo "baseline: DRIFTED (${baseline_before} -> ${baseline_after})" TOTAL_FAIL=$((TOTAL_FAIL + 1)) fi # 验证 demo rule 10/11/12 末态 = 入场前快照(业务侧 enabled=0 不漂移)。 demo_state_after=$(snapshot_demo_rule_state) echo "demo_rule_state_initial=${DEMO_INITIAL_SNAPSHOT}" echo "demo_rule_state_final =${demo_state_after}" if [[ "${demo_state_after}" == "${DEMO_INITIAL_SNAPSHOT}" ]]; then echo "demo rule 10/11/12: RESTORED to initial state (CTO policy: default off)" else echo "demo rule 10/11/12: DRIFTED from initial (initial=${DEMO_INITIAL_SNAPSHOT} final=${demo_state_after})" TOTAL_FAIL=$((TOTAL_FAIL + 1)) fi # 验证 TEMP_SCHED_TIMEOUT_ANTIFLAP 收尾态 temp_state=$(mysql_run "SELECT IFNULL(GROUP_CONCAT(CONCAT(id,':',enabled)),'absent') FROM ado_s8_watch_rule WHERE rule_code='TEMP_SCHED_TIMEOUT_ANTIFLAP';") temp_visible=$(mysql_run "SELECT COUNT(*) FROM ado_s8_exception WHERE source_rule_code='TEMP_SCHED_TIMEOUT_ANTIFLAP' AND is_deleted=0;") echo "temp_rule_state=${temp_state} temp_active_in_default_list=${temp_visible}" echo echo "Per-child:" for r in "${CHILD_RESULTS[@]}"; do echo " - ${r}" done echo echo "Used fixtures:" echo " - DEMO_ORDER_DELIVERY_TIMEOUT (rule id=10)" echo " - DEMO_ORDER_DIMENSION_OOR (rule id=11)" echo " - TEMP_SCHED_TIMEOUT_ANTIFLAP (TEMP fixture, post-test enabled=0)" echo " - SHORTAGE / G01_TEST_* (absent → SKIP)" echo echo "Totals: passed=${TOTAL_PASS} failed=${TOTAL_FAIL} skipped=${TOTAL_SKIP}" if (( TOTAL_FAIL > 0 )); then echo "Driver result: FAIL" exit 1 fi echo "Driver result: PASS (FAIL=0)" exit 0