#!/bin/bash # 「戳記要證明看過,不是跑過」的測試(inkstone/ISEP#72 → comment 4873) # # 這支測的是 scripts/ticket 的閘一之二:搜尋跑了,但輸出被丟進 /dev/null 時, # `ticket new` 要擋下來,並把那份被丟掉的候選清單交到眼前。 # # 怎麼在「不真的開票」的前提下分辨擋/放行: # TICKET_HOST 指到一個連不上的位址(127.0.0.1:9) # 離開碼 2 = 閘擋下(die) # 離開碼 1 = 閘全部放行,走到網路那一層才炸(Connection refused) # 全程離線、不打真實 Gitea、不產生任何票。 cd "$(dirname "$0")/.." || exit 1 T=scripts/ticket STAMP=/tmp/.ticket-where-ok TMP=$(mktemp -d) trap 'rm -rf "$TMP"' EXIT PASS=0; FAIL=0 SAVED=""; [ -f "$STAMP" ] && SAVED=$(cat "$STAMP") rm -f "$STAMP" cat > "$TMP/body.md" <<'MD' ## 目標 測試用內文。 ## 驗收條件 離開碼 2 = 被擋;離開碼 1 = 放行(走到網路才炸)。 ## deliverable 類型 code MD TITLE="身為要開票的人,我要先看過命中的舊票,我才不會又開一張一樣的" # stamp stamp() { python3 - "$1" "$2" "$3" <<'PY' import json, sys, time n, shown, age = int(sys.argv[1]), sys.argv[2], float(sys.argv[3]) st = {"at": time.time() - age, "kws": ["ingest", "queue"], "n": n, "top": ["inkstone/arcrun-rag#104"], "top_detail": [{"ref": "inkstone/arcrun-rag#104", "score": 2, "title": "它把我整個 repo 的一萬多個檔案排進佇列", "labels": ["Human"], "excerpt": "這些庫都早就萃好了,所以它要辨識這個庫已經有 wiki"}]} if shown != "none": st["shown"] = (shown == "true") json.dump(st, open("/tmp/.ticket-where-ok", "w")) PY } # run <說明> [額外參數...] run() { want=$1; what=$2; shift 2 out=$(TICKET_HOST=http://127.0.0.1:9 GITEA_TOKEN=x \ python3 "$T" new ISEP -F "$TMP/body.md" --title "$TITLE" "$@" 2>&1) got=$? LAST_OUT="$out" if [ "$got" = "$want" ]; then PASS=$((PASS+1)); printf ' ✅ '; else FAIL=$((FAIL+1)); printf ' ❌ '; fi printf 'want=%s got=%s %s\n' "$want" "$got" "$what" } has() { # has <字串> <說明> if printf '%s' "$LAST_OUT" | grep -q "$1"; then PASS=$((PASS+1)); printf ' ✅ ' else FAIL=$((FAIL+1)); printf ' ❌ '; fi printf '訊息裡有「%s」 %s\n' "$1" "$2" } echo "── 該擋 ──" rm -f "$STAMP" run 2 "完全沒搜過(既有閘一,回歸)" stamp 3 true 3600 run 2 "戳記過期(既有閘一,回歸)" stamp 72 false 10 run 2 "★ 今天的重演:where 的輸出被丟進 /dev/null,命中 72 張一眼沒看" has "沒有到任何人眼前" "擋下的理由講的是「沒看」不是「沒搜」" has "arcrun-rag#104" "擋下的訊息把被丟掉的候選清單交到眼前" has "這些庫都早就萃好了" "連內文摘要都給了(光看標題看不出是同一條線)" stamp 72 false 10 run 2 "★ 閉著眼睛先寫好理由也繞不過(--not-a-comment 在前面就被擋)" --not-a-comment "這是新的一條線" stamp 72 none 10 run 2 "舊格式戳記(沒有 shown 欄位)→ fail-closed,當成沒看過" --not-a-comment "x" stamp 72 true 10 run 2 "命中卻沒給理由(既有閘二,回歸)" echo echo "── 不該擋(誤攔比漏擋更該修)──" stamp 72 true 10 run 1 "看過了、也給了理由 → 放行(走到網路才炸)" --not-a-comment "這是新的一條線" stamp 0 false 10 run 1 "沒有命中任何票 → 沒東西可看,不要吵" stamp 0 none 10 run 1 "沒命中 + 舊格式戳記 → 一樣不吵" echo echo "── 擋完之後:那一次的擋本身就是「把清單交到眼前」 ──" stamp 72 false 10 run 2 "第一次:被擋" --not-a-comment "這是新的一條線" run 1 "第二次下一模一樣的指令 → 放行(不必多打任何字)" --not-a-comment "這是新的一條線" echo echo "── fd_is_devnull:判準是 fstat 問出來的事實,不是猜文字 ──" u() { # u <說明> got=$(eval "$3") if [ "$got" = "$2" ]; then PASS=$((PASS+1)); printf ' ✅ '; else FAIL=$((FAIL+1)); printf ' ❌ '; fi printf 'want=%s got=%s %s\n' "$2" "$got" "$1" } PROBE="import importlib.util as u,sys; from importlib.machinery import SourceFileLoader as L; s=u.spec_from_loader('t',L('t','scripts/ticket')); m=u.module_from_spec(s); s.loader.exec_module(m); print(m.fd_is_devnull(1), file=sys.stderr)" u "stdout 丟進 /dev/null → True" "True" "python3 -c \"\$PROBE\" >/dev/null 2>&1; python3 -c \"\$PROBE\" 2>&1 >/dev/null" u "stdout 接到 pipe(Claude Code 就是這種)→ False" "False" "python3 -c \"\$PROBE\" 2>&1 | cat" u "stdout 寫進一般檔案(讀得回來)→ False" "False" "python3 -c \"\$PROBE\" 2>&1 >\"$TMP/o.txt\"" rm -f "$STAMP" [ -n "$SAVED" ] && printf '%s' "$SAVED" > "$STAMP" echo echo "通過 $PASS 條,失敗 $FAIL 條" [ "$FAIL" = 0 ] || exit 1