Files
ISEP/hooks/tests/queue-drain-guard.test.sh
T
Leo 26aee6dffb queue-drain-guard:pick 自己 150s 收手,逾時當成讀不到(inkstone/ISEP#33)
09-13 本機實量 Gitea 同一分鐘 curl 60s 逾時、整趟 pick --all 約 15 分鐘;
hooks.json 給本閘 180s,被砍掉=非阻擋錯誤=放行收工。實跑真 Gitea:150s 逾時 → exit 2、log 寫入。
測試 26/26。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 12:41:43 +08:00

110 lines
5.7 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# queue-drain-guard.sh 的迴歸測試(inkstone/ISEP#33
#
# 全程離線:`ticket pick` 換成一支假的(ISEP_DRAIN_PICK_CMD),離開碼與輸出由測試指定。
# 兩個方向都測:該擋的(在接票、池子有票)與不該擋的(聊天 session、抓盡、鎖死保險)。
cd "$(dirname "$0")/../.." || exit 1
H=hooks/queue-drain-guard.sh
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
PASS=0; FAIL=0
unset ISEP_DRAIN CLAUDE_PLUGIN_ROOT
cat > "$TMP/pick.sh" <<'SH'
#!/bin/sh
cat "$STUB_DIR/out" 2>/dev/null
exit "$(cat "$STUB_DIR/rc")"
SH
export STUB_DIR="$TMP" ISEP_DRAIN_PICK_CMD="sh $TMP/pick.sh"
stub() { printf '%s' "$1" > "$TMP/rc"; printf '%s' "${2:-}" > "$TMP/out"; }
TICKET='{"ref":"inkstone/Arcrun#176","title":"查一份筆記關係的人","group":"主線"}'
# transcript <檔名> <Bash 指令或空> [純文字]
transcript() {
python3 - "$TMP/$1" "$2" "${3:-}" <<'PY'
import json, sys
path, cmd, text = sys.argv[1], sys.argv[2], sys.argv[3]
rows = [{"type": "user", "message": {"content": "開工"}}]
content = []
if text: content.append({"type": "text", "text": text})
if cmd: content.append({"type": "tool_use", "name": "Bash", "input": {"command": cmd}})
rows.append({"type": "assistant", "message": {"content": content}})
open(path, "w").write("\n".join(json.dumps(r, ensure_ascii=False) for r in rows))
PY
}
run() { # run <說明> <want exit> <session> <transcript 檔> [stop_hook_active]
export ISEP_DRAIN_STATE_DIR="$TMP/state-$3"; mkdir -p "$ISEP_DRAIN_STATE_DIR"
payload=$(printf '{"session_id":"%s","transcript_path":"%s","stop_hook_active":%s}' "$3" "$TMP/$4" "${5:-false}")
printf '%s' "$payload" | bash "$H" > "$TMP/stdout" 2> "$TMP/stderr"; got=$?
if [ "$got" = "$2" ]; then PASS=$((PASS+1)); printf ' ✅ '; else FAIL=$((FAIL+1)); printf ' ❌ '; fi
printf 'want=%s got=%s %s\n' "$2" "$got" "$1"
}
check() { # check <說明> <shell 條件>
if eval "$2"; then PASS=$((PASS+1)); printf ' ✅ '; else FAIL=$((FAIL+1)); printf ' ❌ '; fi
printf '%s\n' "$1"
}
transcript worker.jsonl 'python3 /x/scripts/ticket claim inkstone/Arcrun#175 --name cloud-worker'
transcript handback.jsonl 'python3 "$CLAUDE_PLUGIN_ROOT/scripts/ticket" handback inkstone/Arcrun#175 --to claude-code'
transcript chat.jsonl 'git status'
transcript textonly.jsonl '' '我等一下會跑 ticket pick 看看'
echo "── ① 該擋:在接票、池子還有票 ──"
stub 0 "$TICKET"
run "★ 跑過 ticket claim pick 有票 → 擋" 2 s1 worker.jsonl
check "訊息點名下一張票號" "grep -q 'inkstone/Arcrun#176' '$TMP/stderr'"
check "訊息給出認領指令(claim" "grep -q 'claim inkstone/Arcrun#176' '$TMP/stderr'"
check "訊息給出「做不了」的出路(寫回票+改欄位),不是叫它停" "grep -q '寫回那張票' '$TMP/stderr'"
run "引號包住路徑的 handback 也算在接票" 2 s2 handback.jsonl
run "★ stop_hook_active=true 仍擋(別的閘擋過一次就讓路,正是停工的來源)" 2 s3 worker.jsonl true
ISEP_DRAIN=1 run "ISEP_DRAIN=1:還沒跑過任何 ticket 指令也擋" 2 s4 chat.jsonl
check "log 寫在狀態目錄(不靠 CLAUDE_PROJECT_DIR" "grep -q '擋下' '$TMP/state-s4/queue-drain-guard.log'"
echo
echo "── ② 不該擋 ──"
stub 0 "$TICKET"
run "★ 沒跑過 ticket 指令的 session(跟 leo 聊天)→ 放行" 0 s5 chat.jsonl
run "文字裡提到 ticket pick、沒真的執行 → 放行(看動作不看措辭)" 0 s6 textonly.jsonl
stub 1 ""
run "★ pick 離開碼 1(真的抓盡)→ 放行" 0 s7 worker.jsonl
printf 'not json' | bash "$H" >/dev/null 2>&1; got=$?
if [ "$got" = 0 ]; then PASS=$((PASS+1)); printf ' ✅ '; else FAIL=$((FAIL+1)); printf ' ❌ '; fi
printf 'want=0 got=%s 壞掉的 payload → 放行\n' "$got"
echo
echo "── ③ 鎖死保險 ──"
stub 0 "$TICKET"
run "同一張第 1 次 → 擋" 2 s8 worker.jsonl
run "同一張第 2 次 → 擋" 2 s8 worker.jsonl
run "同一張第 3 次 → 擋" 2 s8 worker.jsonl
run "★ 同一張第 4 次仍沒被認領 → 放行(它可能真的做不了)" 0 s8 worker.jsonl
check "放行理由記進 log(那張要總管看)" "grep -q 'stuck-on:inkstone/Arcrun#176' '$TMP/state-s8/queue-drain-guard.log'"
stub 0 '{"ref":"inkstone/Arcrun#197","title":"另一張"}'
run "換了一張 → 計數重來,擋" 2 s8 worker.jsonl
stub 2 ""
run "讀不到佇列第 1 次 → 擋(讀不到 ≠ 沒票)" 2 s9 worker.jsonl
check "訊息叫它重跑 pick" "grep -q 'pick' '$TMP/stderr' && grep -q '讀不到' '$TMP/stderr'"
run "讀不到第 2 次 → 擋" 2 s9 worker.jsonl
run "★ 讀不到第 3 次 → 放行並留痕" 0 s9 worker.jsonl
check "log 有 unreachable" "grep -q 'unreachable' '$TMP/state-s9/queue-drain-guard.log'"
printf '0' > "$TMP/rc"; printf '%s' "$TICKET" > "$TMP/out"
ISEP_DRAIN_PICK_CMD="sleep 5; sh $TMP/pick.sh" ISEP_DRAIN_PICK_TIMEOUT=1 run "★ pick 超過時限(Gitea 慢)→ 當成讀不到而擋,不是被砍掉後放行" 2 s10 worker.jsonl
check "逾時那次訊息是「讀不到 ≠ 沒票」" "grep -q '讀不到' '$TMP/stderr'"
echo
echo "── ④ 真的 pick(不帶假指令)接得上 ──"
unset ISEP_DRAIN_PICK_CMD
export ISEP_DRAIN_STATE_DIR="$TMP/state-real"; mkdir -p "$ISEP_DRAIN_STATE_DIR"
printf '{"session_id":"r1","transcript_path":"%s"}' "$TMP/worker.jsonl" \
| TICKET_HOST=http://127.0.0.1:9 GITEA_TOKEN=x ISEP_API_RETRIES=0 ISEP_MAINLINE_FILE="$TMP/none.json" \
CLAUDE_PLUGIN_ROOT="$PWD" bash "$H" >/dev/null 2>"$TMP/stderr"; got=$?
if [ "$got" = 2 ]; then PASS=$((PASS+1)); printf ' ✅ '; else FAIL=$((FAIL+1)); printf ' ❌ '; fi
printf 'want=2 got=%s 真的 scripts/ticket pick 讀不到主線 → 當成讀不到(擋),不是當成抓盡\n' "$got"
echo
echo "通過 $PASS 條,失敗 $FAIL 條"
[ "$FAIL" = 0 ] || exit 1