| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- #!/usr/bin/env bash
- # R2-TIMEOUT-EVALUATOR-1/2 regression script (dev/test only, aidopdev).
- #
- # Drives the safe debug endpoint /api/aidop/s8/watch-debug/run-once twice and
- # verifies:
- # 1. only ONE active (status != CLOSED) exception per dedup_key;
- # 2. last_detected_at refreshed between runs;
- # 3. source_payload contains corrected __sourceObjectId metadata;
- # 4. G01_TEST_WATCH OUT_OF_RANGE compat path keeps producing duplicate skip
- # against id=34 (existing IN_PROGRESS exception).
- #
- # Requires:
- # - mysql client on PATH;
- # - python3 for JSON parsing;
- # - aidopdev backend running on http://localhost:5005;
- # - WatchScheduler:DebugEndpointEnabled=true (dev/test default);
- # - Web/tests/e2e/.auth/storage-state.json populated (run Playwright once).
- set -euo pipefail
- PROJECT_DIR="${PROJECT_DIR:-/home/yy968/work/New9S/AiDOPWarehouse}"
- STORAGE_STATE="${PROJECT_DIR}/Web/tests/e2e/.auth/storage-state.json"
- BACKEND_BASE="${BACKEND_BASE:-http://localhost:5005}"
- TENANT_ID="${TENANT_ID:-1}"
- FACTORY_ID="${FACTORY_ID:-1}"
- DB_HOST="${DB_HOST:-123.60.180.165}"
- DB_PORT="${DB_PORT:-3306}"
- DB_NAME="${DB_NAME:-aidopdev}"
- DB_USER="${DB_USER:-aidopremote}"
- DB_PASS="${DB_PASS:-1234567890aiDOP#}"
- RULE_CODE="${RULE_CODE:-G01_TEST_TIMEOUT}"
- fail() { echo "FAIL: $*" >&2; exit 1; }
- ok() { echo "OK: $*"; }
- [[ "${DB_NAME}" == "aidopdev" ]] || fail "DB_NAME must be aidopdev, got ${DB_NAME}"
- [[ -f "${STORAGE_STATE}" ]] || fail "storage state not found: ${STORAGE_STATE}"
- AT="$(python3 -c "
- import json,sys
- d=json.load(open(sys.argv[1]))
- for kv in d['origins'][0]['localStorage']:
- if kv['name']=='admin.net:access-token':
- print(kv['value'].strip('\"')); break
- " "${STORAGE_STATE}")"
- XAT="$(python3 -c "
- import json,sys
- d=json.load(open(sys.argv[1]))
- for kv in d['origins'][0]['localStorage']:
- if kv['name']=='admin.net:x-access-token':
- print(kv['value'].strip('\"')); break
- " "${STORAGE_STATE}")"
- [[ -n "${AT}" && "${AT}" != "null" ]] || fail "access-token missing in storage-state"
- [[ -n "${XAT}" && "${XAT}" != "null" ]] || fail "x-access-token missing in storage-state"
- mysql_query() {
- MYSQL_PWD="${DB_PASS}" mysql -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" "${DB_NAME}" \
- --default-character-set=utf8mb4 --connect-timeout=8 -N -B -e "$1" 2>/dev/null
- }
- run_once() {
- curl -fsS --max-time 15 -X POST \
- -H "Authorization: Bearer ${AT}" -H "X-Access-Token: ${XAT}" \
- "${BACKEND_BASE}/api/aidop/s8/watch-debug/run-once?tenantId=${TENANT_ID}&factoryId=${FACTORY_ID}"
- }
- precheck=$(mysql_query "SELECT COUNT(*) FROM ado_s8_watch_rule WHERE rule_code='${RULE_CODE}' AND enabled=1;")
- [[ "${precheck}" == "1" ]] || fail "${RULE_CODE} not present or disabled (got '${precheck}')"
- ok "${RULE_CODE} enabled"
- resp1="$(run_once)"
- count1=$(printf '%s' "${resp1}" | python3 -c "import json,sys;print(json.load(sys.stdin).get('count',0))")
- [[ "${count1}" -ge 1 ]] || fail "first run-once did not return any results"
- ok "first run-once HTTP 200, count=${count1}"
- # OUT_OF_RANGE compat: G01_TEST_WATCH (sourceRuleId=1) must remain duplicate-skip against id=34.
- g01ok=$(printf '%s' "${resp1}" | python3 -c "
- import json,sys
- d=json.load(sys.stdin)
- hit=any(r for r in d.get('results',[]) if str(r.get('sourceRuleId'))=='1' and r.get('skipped') and str(r.get('matchedExceptionId'))=='34')
- print('YES' if hit else 'NO')
- ")
- [[ "${g01ok}" == "YES" ]] || fail "OUT_OF_RANGE G01 regression: expected skipped duplicate for sourceRuleId=1 against id=34"
- ok "OUT_OF_RANGE G01 compat path: skipped duplicate id=34"
- before_ts=$(mysql_query "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;")
- ok "pre-second-run last_detected_at=${before_ts}"
- sleep 1
- resp2="$(run_once)"
- count2=$(printf '%s' "${resp2}" | python3 -c "import json,sys;print(json.load(sys.stdin).get('count',0))")
- ok "second run-once HTTP 200, count=${count2}"
- active_count=$(mysql_query "SELECT COUNT(*) FROM ado_s8_exception WHERE source_rule_code='${RULE_CODE}' AND status<>'CLOSED' AND is_deleted=0;")
- [[ "${active_count}" == "1" ]] || fail "expected exactly 1 active TIMEOUT exception, got ${active_count}"
- ok "active TIMEOUT exception count = 1 (no duplicate creation)"
- after_ts=$(mysql_query "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}" ]] || fail "last_detected_at did not refresh (before=${before_ts}, after=${after_ts})"
- ok "last_detected_at refreshed: ${before_ts} -> ${after_ts}"
- payload=$(mysql_query "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;")
- echo "${payload}" | grep -q '__sourceObjectId' || fail "source_payload missing __sourceObjectId metadata (R2-2 fix)"
- echo "${payload}" | grep -q '__sourceObjectType' || fail "source_payload missing __sourceObjectType metadata (R2-2 fix)"
- echo "${payload}" | grep -q '__ruleType' || fail "source_payload missing __ruleType metadata"
- ok "source_payload metadata fields present (__ruleType / __sourceObjectType / __sourceObjectId)"
- ok "R2-TIMEOUT regression PASSED"
|