#!/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 ]