Files
ISEP/hooks/tests/hook-sandbox.test.sh
T
Leo f1b2ed909f 測試沙盒傳錯參數時,會把整個 tech_projects 複製進暫存區(inkstone/ISEP#122)
本輪跑迴歸測試時實撞兩次,兩次都把這台機器的磁碟寫滿:

  $ bash hooks/tests/main-and-prod-push-guard.test.sh "$PWD"
      REAL="$1" 要的是「那支 hook 的檔案路徑」,我傳了 repo 根目錄
    ⇒ hook_sandbox 不驗參數,直接 cp -R "$(dirname "$1")"
    ⇒ dirname 變成 ~/Documents/tech_projects(上一層)
    ⇒ 整個 tech_projects(所有 repo、所有 worktree)被搬進 mktemp

  實測:第一次 13 GB + 10 GB,第二次 23 GB。
        磁碟可用 25 GB → 462 MB,cp 一路吐 "No space left on device"。
        兩次都是我手動 rm -rf 才回來的(27 GB)。

🔴 而它印出來的只有一句「 沙盒建不起來」——
   沒說是參數傳錯,也沒說它已經把磁碟寫滿了。
   ⇒ 這跟本票在講的是同一句話:**閘/工具給的下一步,沒有人照著打過一次**,
     差別只在這次壞的不是逃生門,是「它壞掉時說的話」。

改法(判準是「要求某個東西在場」,不是關鍵字比對):
  ① $1 要指到一個真的檔案(空字串、目錄、不存在的路徑都不算)
  ② 它的上一層目錄名要叫 hooks(沙盒的前提就是複製一整個 hooks/)
  任一不成立 ⇒ 在 mktemp 之前 return 1,並印出走得通的那一行。

順手補上 docs/TESTING.md 之前沒寫過的一件事:**有五支測試要傳參數**,
而不傳的後果不是報錯是假綠——prod-write-guard.test.sh 的 HOOK="$1" 空掉時
每一條都執行空指令回 0 ⇒「該擋」全變成「實得 pass」,19 條假紅
(傳對參數:通過 37 / 失敗 0)。

測試:hooks/tests/hook-sandbox.test.sh 10 條(A30),通過 10 失敗 0
      ①③⑤⑥ 驗的是「收手在複製之前」,不是「訊息好不好看」
      ⑧⑨⑩ 驗正常用法沒被弄壞、複本裡沒混進 hooks/ 以外的東西
既有兩支沙盒測試複驗:
      scripts/test-main-and-prod-push-guard.sh                    13/13
      hooks/tests/main-and-prod-push-guard.test.sh          通過 10 / 失敗 0
      hooks/tests/main-and-prod-push-guard-cross-repo.test.sh 通過 19 / 失敗 0

📌 這顆跟 0.22.0 的三件事無關,是本輪路上撞到的。要拆票或丟掉這顆都行,
   它獨立於前面兩顆,而且只動 hooks/tests/(沒有任何 hook 的執行行為改變)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 11:54:50 +08:00

75 lines
4.2 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.
#!/usr/bin/env bash
# hooks/tests/lib/hook-sandbox.sh 的迴歸測試(inkstone/ISEP#122
#
# 🔴 為什麼有這支(2026-09-02 實撞,證據在票上):
# `hook_sandbox` 的第一個參數是「那支 hook 的檔案路徑」,傳錯時舊版不檢查就
# `cp -R "$(dirname "$1")"` ⇒ 複製的是上一層。傳 repo 根目錄進去,
# 複製的就是**整個 tech_projects**:兩次吃掉 23 GB,磁碟剩 462 MB
# 而它只印一句「沙盒建不起來」——**沒說參數錯,也沒說它已經把磁碟寫滿**。
#
# 所以這支測的不是「錯誤訊息好不好看」,是**收手的時機**:
# ④⑤ 兩條驗「複製之前就 return 1」——暫存區裡不准留下任何東西。
#
# 用法:bash hooks/tests/hook-sandbox.test.sh
set -u
HERE=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
ROOT=$(CDPATH= cd -- "$HERE/.." && pwd) # …/hooks
REPO=$(CDPATH= cd -- "$ROOT/.." && pwd)
PASS=0; FAIL=0
ok(){ printf ' ✅ %s\n' "$1"; PASS=$((PASS+1)); }
no(){ printf ' ❌ %s —— %s\n' "$1" "$2"; FAIL=$((FAIL+1)); }
# 每一條都在自己的子殼裡跑:hook_sandbox 會設全域變數,不隔離會互相污染。
run(){ # run <參數> → 印 "rc|沙盒路徑|訊息"
( . "$HERE/lib/hook-sandbox.sh"
msg=$(hook_sandbox "$1" 2>&1); rc=$?
printf '%s|%s|%s' "$rc" "${HOOK_SANDBOX:-}" "$(printf '%s' "$msg" | tr '\n' ' ')"
)
}
echo "── 該收手(傳錯參數)──────────────────────────────────────"
r=$(run "$REPO"); rc=${r%%|*}; rest=${r#*|}; sb=${rest%%|*}; msg=${rest#*|}
[ "$rc" = "1" ] && ok "① 傳 repo 根目錄(本票的實撞)→ return 1" \
|| no "① 傳 repo 根目錄(本票的實撞)→ return 1" "實得 rc=$rc"
case "$msg" in *"hooks/main-and-prod-push-guard.sh"*)
ok "② 訊息給得出一行**真的跑得起來**的用法" ;;
*) no "② 訊息給得出一行**真的跑得起來**的用法" "訊息:$msg" ;;
esac
if [ -z "$sb" ] || [ ! -d "$sb" ]; then ok "③ 收手在 mktemp 之前,暫存區沒有殘骸"
else no "③ 收手在 mktemp 之前,暫存區沒有殘骸" "留下了 $sb"; fi
r=$(run ""); [ "${r%%|*}" = "1" ] && ok "④ 空參數 → return 1(不是拿空字串去 dirname" \
|| no "④ 空參數 → return 1" "實得 rc=${r%%|*}"
r=$(run "$REPO/scripts/ticket"); rc=${r%%|*}; rest=${r#*|}; sb=${rest%%|*}
if [ "$rc" = "1" ]; then ok "⑤ 檔案存在但上一層不是 hooks/ → 一樣收手"
else no "⑤ 檔案存在但上一層不是 hooks/ → 一樣收手" "實得 rc=$rc"; fi
if [ -z "$sb" ] || [ ! -d "$sb" ]; then ok "⑥ ⑤ 這條也沒把 scripts/ 複製出去"
else no "⑥ ⑤ 這條也沒把 scripts/ 複製出去" "留下了 $sb"; fi
r=$(run "$ROOT/沒有這支.sh"); [ "${r%%|*}" = "1" ] && ok "⑦ 路徑不存在 → return 1" \
|| no "⑦ 路徑不存在 → return 1" "實得 rc=${r%%|*}"
echo "── 不准把本來會過的弄壞(誤攔比漏擋嚴重)────────────────────"
( . "$HERE/lib/hook-sandbox.sh"
hook_sandbox "$ROOT/main-and-prod-push-guard.sh"; rc=$?
if [ "$rc" != "0" ]; then echo "RC=$rc"; exit 0; fi
[ -f "$HOOK_SANDBOX_HOOK" ] && echo "HOOKOK"
[ -d "$HOOK_SANDBOX/hooks/lib" ] && echo "LIBOK"
# 只複製 hooks/,不該把 repo 的其他目錄帶進來
[ -d "$HOOK_SANDBOX/hooks/scripts" ] && echo "LEAK"
hook_sandbox_cleanup
) > /tmp/.hs-ok.$$ 2>&1
grep -q HOOKOK /tmp/.hs-ok.$$ && ok "⑧ 正常用法照樣建得起來,複本裡有那支 hook" \
|| no "⑧ 正常用法照樣建得起來" "$(cat /tmp/.hs-ok.$$)"
grep -q LIBOK /tmp/.hs-ok.$$ && ok "⑨ 整個 hooks/(含 lib/)都在複本裡" \
|| no "⑨ 整個 hooks/(含 lib/)都在複本裡" "$(cat /tmp/.hs-ok.$$)"
grep -q LEAK /tmp/.hs-ok.$$ && no "⑩ 只複製 hooks/,沒有把 repo 其他目錄帶進去" "複本裡出現 scripts/" \
|| ok "⑩ 只複製 hooks/,沒有把 repo 其他目錄帶進去"
rm -f /tmp/.hs-ok.$$
echo
echo "通過 $PASS 條,失敗 $FAIL 條(共 $((PASS+FAIL)) 條)"
[ "$FAIL" -eq 0 ]