|
|
@@ -8,11 +8,19 @@
|
|
|
#
|
|
|
# 验证项(demo rule 10 模式):
|
|
|
# 1. 双 run-once HTTP 200;
|
|
|
-# 2. 同一 dedup_key 下仅 1 条活动 exception;
|
|
|
-# 3. last_detected_at 在两次运行之间推进;
|
|
|
+# 2. 同一 dedup_key 下仅 1 条活动 exception(断言作用域 = TARGET_OBJECT_CODE);
|
|
|
+# 3. last_detected_at 在两次运行之间推进(同样按 TARGET_OBJECT_CODE 取窗);
|
|
|
# 4. source_payload 含 __ruleType / __sourceObjectType / __sourceObjectId 元数据;
|
|
|
# 5. demo rule 10 守恒:enabled=1 / paused_until=NULL / trigger=1 / recover=1 /
|
|
|
# params_json / rule_type / expression 不变(运行态字段 last_run_at/next_run_at 等允许刷新)。
|
|
|
+#
|
|
|
+# 作用域设计(S8-REGRESSION-R2-TIMEOUT-SCOPE-FIX-1):
|
|
|
+# demo rule 10 的 expression 是 `SELECT ... FROM demo_test_order` 无 WHERE,
|
|
|
+# 因此 demo_test_order 中所有已超期的行(TEST-ORDER-001 + DEMO-S2-001 +
|
|
|
+# DEMO-S3-001 等演示资产)都会命中产生独立 active exception。
|
|
|
+# 旧版用全局 active TIMEOUT count==1 作硬断言会与演示数据并存事实冲突。
|
|
|
+# 新版仅校验 TARGET_OBJECT_CODE(默认 TEST-ORDER-001)对应的 active exception
|
|
|
+# 存在且唯一;全局 active TIMEOUT 数量仅作 INFO 输出,不参与 PASS/FAIL 判定。
|
|
|
|
|
|
set -uo pipefail
|
|
|
|
|
|
@@ -23,9 +31,12 @@ source "${SCRIPT_DIR}/s8-regression-common.sh"
|
|
|
auth_load
|
|
|
|
|
|
RULE_CODE="${RULE_CODE:-DEMO_ORDER_DELIVERY_TIMEOUT}"
|
|
|
+TARGET_OBJECT_CODE="${R2_TARGET_OBJECT_CODE:-TEST-ORDER-001}"
|
|
|
+# 目标对象作用域过滤:覆盖 dedup_key / related_object_code / source_object_id 三类匹配。
|
|
|
+SCOPE_FILTER="(related_object_code='${TARGET_OBJECT_CODE}' OR source_object_id='${TARGET_OBJECT_CODE}' OR dedup_key LIKE '%:${TARGET_OBJECT_CODE}')"
|
|
|
|
|
|
baseline_before=$(read_baseline)
|
|
|
-echo "==== r2-timeout-regression RULE_CODE=${RULE_CODE} baseline_before=${baseline_before} ===="
|
|
|
+echo "==== r2-timeout-regression RULE_CODE=${RULE_CODE} TARGET_OBJECT_CODE=${TARGET_OBJECT_CODE} baseline_before=${baseline_before} ===="
|
|
|
|
|
|
if ! require_demo_rule "${RULE_CODE}"; then
|
|
|
record_skip "TIMEOUT fixture '${RULE_CODE}' not present or disabled — historic G01_TEST_TIMEOUT removed from dev"
|
|
|
@@ -48,21 +59,26 @@ resp1=$(run_once_endpoint)
|
|
|
count1=$(printf '%s' "${resp1}" | python3 -c "import json,sys;print(json.load(sys.stdin).get('count',0))")
|
|
|
[[ "${count1}" -ge 1 ]] && record_pass "first run-once HTTP 200, count=${count1}" || record_fail "first run-once empty (${count1})"
|
|
|
|
|
|
-before_ts=$(mysql_run "SELECT IFNULL(MAX(last_detected_at), '1970-01-01') FROM ado_s8_exception WHERE source_rule_code='${RULE_CODE}' AND status<>'CLOSED' AND is_deleted=0;")
|
|
|
-echo "pre-second-run last_detected_at=${before_ts}"
|
|
|
+before_ts=$(mysql_run "SELECT IFNULL(MAX(last_detected_at), '1970-01-01') FROM ado_s8_exception WHERE source_rule_code='${RULE_CODE}' AND status<>'CLOSED' AND is_deleted=0 AND ${SCOPE_FILTER};")
|
|
|
+echo "pre-second-run last_detected_at(target=${TARGET_OBJECT_CODE})=${before_ts}"
|
|
|
|
|
|
sleep 1
|
|
|
resp2=$(run_once_endpoint)
|
|
|
count2=$(printf '%s' "${resp2}" | python3 -c "import json,sys;print(json.load(sys.stdin).get('count',0))")
|
|
|
[[ "${count2}" -ge 1 ]] && record_pass "second run-once HTTP 200, count=${count2}" || record_fail "second run-once empty (${count2})"
|
|
|
|
|
|
-active_count=$(mysql_run "SELECT COUNT(*) FROM ado_s8_exception WHERE source_rule_code='${RULE_CODE}' AND status<>'CLOSED' AND is_deleted=0;")
|
|
|
-[[ "${active_count}" == "1" ]] && record_pass "active TIMEOUT exception count = 1 (no duplicate creation)" || record_fail "expected exactly 1 active TIMEOUT exception, got ${active_count}"
|
|
|
+global_active=$(mysql_run "SELECT COUNT(*) FROM ado_s8_exception WHERE source_rule_code='${RULE_CODE}' AND status<>'CLOSED' AND is_deleted=0;")
|
|
|
+echo "INFO: global active TIMEOUT exceptions (rule=${RULE_CODE}) = ${global_active}; scoped target=${TARGET_OBJECT_CODE}"
|
|
|
+
|
|
|
+scoped_active=$(mysql_run "SELECT COUNT(*) FROM ado_s8_exception WHERE source_rule_code='${RULE_CODE}' AND status<>'CLOSED' AND is_deleted=0 AND ${SCOPE_FILTER};")
|
|
|
+[[ "${scoped_active}" == "1" ]] \
|
|
|
+ && record_pass "scoped active TIMEOUT exception count = 1 for target ${TARGET_OBJECT_CODE} (no duplicate creation; global=${global_active})" \
|
|
|
+ || record_fail "expected exactly 1 active TIMEOUT exception for target ${TARGET_OBJECT_CODE}, got ${scoped_active} (global=${global_active})"
|
|
|
|
|
|
-after_ts=$(mysql_run "SELECT MAX(last_detected_at) FROM ado_s8_exception WHERE source_rule_code='${RULE_CODE}' AND status<>'CLOSED' AND is_deleted=0;")
|
|
|
-[[ "${after_ts}" > "${before_ts}" ]] && record_pass "last_detected_at refreshed: ${before_ts} -> ${after_ts}" || record_fail "last_detected_at did not refresh (before=${before_ts}, after=${after_ts})"
|
|
|
+after_ts=$(mysql_run "SELECT MAX(last_detected_at) FROM ado_s8_exception WHERE source_rule_code='${RULE_CODE}' AND status<>'CLOSED' AND is_deleted=0 AND ${SCOPE_FILTER};")
|
|
|
+[[ "${after_ts}" > "${before_ts}" ]] && record_pass "last_detected_at refreshed for target ${TARGET_OBJECT_CODE}: ${before_ts} -> ${after_ts}" || record_fail "last_detected_at did not refresh for target ${TARGET_OBJECT_CODE} (before=${before_ts}, after=${after_ts})"
|
|
|
|
|
|
-payload=$(mysql_run "SELECT source_payload FROM ado_s8_exception WHERE source_rule_code='${RULE_CODE}' AND status<>'CLOSED' AND is_deleted=0 ORDER BY id DESC LIMIT 1;")
|
|
|
+payload=$(mysql_run "SELECT source_payload FROM ado_s8_exception WHERE source_rule_code='${RULE_CODE}' AND status<>'CLOSED' AND is_deleted=0 AND ${SCOPE_FILTER} ORDER BY id DESC LIMIT 1;")
|
|
|
metadata_ok=1
|
|
|
for field in __sourceObjectId __sourceObjectType __ruleType; do
|
|
|
echo "${payload}" | grep -q "${field}" || { record_fail "source_payload missing ${field}"; metadata_ok=0; }
|