86 lines
5.3 KiB
Bash
86 lines
5.3 KiB
Bash
#!/usr/bin/env bash
|
||
# milestone-account-guard.sh 的迴歸測試(inkstone/ISEP#85)
|
||
#
|
||
# A 群「不該擋」——純讀取/建里程碑/改別的欄位/只是談論它/heredoc 內文/
|
||
# 走正門 `milestone-account close`/已經記過帳/解析不出來
|
||
# B 群「該擋」 ——真的用 curl/python 把某一個里程碑 PATCH 成 closed,而帳本裡沒有它
|
||
# C 群「訊息承諾的出路真的走得通」——記過帳之後立刻不再擋;加了豁免字樣就放行
|
||
#
|
||
# 🔴 全程離線:帳本走 MILESTONE_ACCOUNT_LEDGER 指到 mktemp 的檔,不打 Gitea。
|
||
# **誤攔比漏擋更該修**——A 群任何一條紅,就是這支閘在懲罰謹慎。
|
||
set -u
|
||
HOOK="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/milestone-account-guard.sh}"
|
||
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT
|
||
LED="$TMP/ledger.jsonl"; : > "$LED"
|
||
PASS=0; FAIL=0; N=0
|
||
|
||
fire() {
|
||
want="$1"; desc="$2"; cmd="$3"
|
||
N=$((N+1))
|
||
payload=$(python3 -c 'import json,sys; print(json.dumps({"tool_input":{"command":sys.argv[1]}}))' "$cmd")
|
||
out=$(printf '%s' "$payload" | MILESTONE_ACCOUNT_LEDGER="$LED" bash "$HOOK" 2>&1 >/dev/null); rc=$?
|
||
if [ "$rc" -eq "$want" ]; then printf ' ✅ %s\n' "$desc"; PASS=$((PASS+1))
|
||
else
|
||
printf ' ❌ %s —— 期望 exit=%s,實得 exit=%s\n' "$desc" "$want" "$rc"
|
||
printf '%s\n' "$out" | sed -n '1,6p' | sed 's/^/ /'; FAIL=$((FAIL+1))
|
||
fi
|
||
}
|
||
|
||
CLOSE='curl -s -X PATCH -H "Authorization: token X" -d {"state":"closed"} https://git.uncle6.me/api/v1/repos/inkstone/ISEP/milestones/12'
|
||
|
||
echo "── A 群:不該擋(誤攔比漏擋更該修)─────────────────────────"
|
||
fire 0 "① 空指令" ""
|
||
fire 0 "② 純 GET 撈里程碑" \
|
||
'curl -s https://git.uncle6.me/api/v1/repos/inkstone/ISEP/milestones/12'
|
||
fire 0 "③ 列出所有里程碑" \
|
||
'curl -s "https://git.uncle6.me/api/v1/repos/inkstone/ISEP/milestones?state=open"'
|
||
fire 0 "④ 建一個新里程碑(那是 milestone-due-guard 管的,不是這支)" \
|
||
'curl -X POST -d {"title":"x","due_on":"2026-09-05T23:59:59Z"} https://git.uncle6.me/api/v1/repos/inkstone/ISEP/milestones'
|
||
fire 0 "⑤ PATCH 但只是改期限,沒有要關它" \
|
||
'curl -X PATCH -d {"due_on":"2026-09-05T23:59:59Z"} https://git.uncle6.me/api/v1/repos/inkstone/ISEP/milestones/12'
|
||
fire 0 "⑥ 只是談論它(echo 在指令位置,curl 在字串裡)" \
|
||
"echo '$CLOSE'"
|
||
fire 0 "⑦ 讀這支閘自己的原始碼" 'cat hooks/milestone-account-guard.sh'
|
||
fire 0 "⑧ commit 訊息裡出現「關掉 milestone」" \
|
||
'git commit -m "關掉 milestone 12,state closed"'
|
||
fire 0 "⑨ 走正門:scripts/milestone-account close" \
|
||
'python3 scripts/milestone-account close inkstone/ISEP#12 --reason idle'
|
||
fire 0 "⑩ 正門的 --dry-run" \
|
||
'python3 scripts/milestone-account close inkstone/ISEP#12 --reason idle --dry-run'
|
||
fire 0 "⑪ 把那段指令寫進文件(heredoc 內文是資料不是指令)" \
|
||
"$(printf 'cat > docs/x.md <<%s\n%s\nEOD\n' "'EOD'" "$CLOSE")"
|
||
fire 0 "⑫ 網址沒有里程碑編號(抽不出目標 ⇒ 不擋)" \
|
||
'curl -X PATCH -d {"state":"closed"} https://git.uncle6.me/api/v1/repos/inkstone/ISEP/milestones'
|
||
fire 0 "⑬ 關的是一張票不是里程碑" \
|
||
'curl -X PATCH -d {"state":"closed"} https://git.uncle6.me/api/v1/repos/inkstone/ISEP/issues/12'
|
||
|
||
echo "── B 群:該擋 ──────────────────────────────────────────────"
|
||
fire 2 "⑭ curl PATCH state=closed,帳本裡沒有它" "$CLOSE"
|
||
fire 2 "⑮ 換成 python urllib(method=\"PATCH\")一樣擋" \
|
||
'python3 -c "import urllib.request,json; urllib.request.urlopen(urllib.request.Request(\"https://git.uncle6.me/api/v1/repos/inkstone/ISEP/milestones/12\", data=json.dumps({\"state\":\"closed\"}).encode(), method=\"PATCH\"))"'
|
||
fire 2 "⑯ 前面串一個無害指令也擋(不是靠前綴判斷)" "ls && $CLOSE"
|
||
fire 2 "⑰ --request PATCH 的長寫法" \
|
||
'curl --request PATCH --data {"state":"closed"} https://git.uncle6.me/api/v1/repos/inkstone/ISEP/milestones/12'
|
||
fire 2 "⑱ 別的 repo 的里程碑一樣管" \
|
||
'curl -X PATCH -d {"state":"closed"} https://git.uncle6.me/api/v1/repos/inkstone/arcrun-rag/milestones/47'
|
||
|
||
echo "── C 群:訊息承諾的出路真的走得通 ──────────────────────────"
|
||
N=$((N+1))
|
||
out=$(python3 -c 'import json,sys; print(json.dumps({"tool_input":{"command":sys.argv[1]}}))' "$CLOSE" \
|
||
| MILESTONE_ACCOUNT_LEDGER="$LED" bash "$HOOK" 2>&1 >/dev/null)
|
||
if printf '%s' "$out" | grep -q "scripts/milestone-account audit inkstone/ISEP#12"; then
|
||
printf ' ✅ ⑲ 訊息直接給出可以照打的那一行(含正確的 owner/repo#編號)\n'; PASS=$((PASS+1))
|
||
else
|
||
printf ' ❌ ⑲ 訊息沒有點名目標\n'; FAIL=$((FAIL+1))
|
||
fi
|
||
|
||
echo '{"ref": "inkstone/ISEP#12", "reason": "idle"}' >> "$LED"
|
||
fire 0 "⑳ 記過帳之後立刻放行(正門是先記帳再關)" "$CLOSE"
|
||
fire 2 "㉑ 但別的里程碑還是照擋(不是一記帳就全開)" \
|
||
'curl -X PATCH -d {"state":"closed"} https://git.uncle6.me/api/v1/repos/inkstone/ISEP/milestones/13'
|
||
fire 0 "㉒ 明講的豁免字樣(留痕)" "$CLOSE # milestone-account-ok"
|
||
|
||
echo
|
||
echo "通過 $PASS 條,失敗 $FAIL 條(共 $N 條)"
|
||
[ "$FAIL" -eq 0 ]
|