claude-harness/tests/test-hook-require-plan.sh
andrey.epifantsev 2f96a13861 fix(hooks): финальное ревью — checkout main, префиксы с опциями, ссылки, литеральные пути, push текущей ветки (#6)
I-1, I-2, M-3, M-4, M-5 (переоценены в Important), M-8; спека и design.md синхронизированы (M-7).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 02:24:07 +04:00

36 lines
3.3 KiB
Bash
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.

# require-plan.sh: корень, ветка, code-paths, .bounded и планы — из репозитория правимого файла (спека hook-guards).
H="$ROOT/hooks/require-plan.sh"
S="$TMP/rp"; mkdir -p "$S/out"
mk(){ git init -q "$1" && git -C "$1" symbolic-ref HEAD "refs/heads/${2:-main}" && git -C "$1" commit -q --allow-empty -m init; }
mk "$S/a"; touch "$S/a/.bounded" # A: есть .bounded
mk "$S/b" # B: ни плана, ни .bounded
mk "$S/f"; git -C "$S/f" worktree add -q "$S/wt" -b feat/6-x; touch "$S/wt/.bounded"
mk "$S/c"; mkdir -p "$S/c/.claude"; printf 'hooks/ # код\n\nskills/\n' > "$S/c/.claude/code-paths"
mk "$S/p2" feat/6-x; mkdir -p "$S/p2/docs/superpowers/plans"
echo "Ветка: feat/6-x" > "$S/p2/docs/superpowers/plans/2026-10-16-a.md"; echo "другое" > "$S/p2/docs/superpowers/plans/2026-10-11-b.md"
mk "$S/p0" feat/7-y; mkdir -p "$S/p0/docs/superpowers/plans"
echo "Ветка: feat/6-x" > "$S/p0/docs/superpowers/plans/a.md"; echo "другое" > "$S/p0/docs/superpowers/plans/b.md"
mk "$S/p1" feat/7-y; mkdir -p "$S/p1/docs/superpowers/plans"; echo "x" > "$S/p1/docs/superpowers/plans/a.md"
mk "$S/pd"; mkdir -p "$S/pd/docs/superpowers/plans"; echo a > "$S/pd/docs/superpowers/plans/a.md"; echo b > "$S/pd/docs/superpowers/plans/b.md"
git -C "$S/pd" checkout -q --detach
j(){ python3 -c 'import json,sys; print(json.dumps({"cwd":sys.argv[1],"tool_name":"Edit","tool_input":{"file_path":sys.argv[2]}}))' "$@"; }
rc(){ j "$@" | bash "$H" 2>/dev/null; echo $?; }
assert_eq "файл в B без плана при cwd в A с .bounded" "$(rc "$S/a" "$S/b/src/a.py")" 2
assert_eq "файл в worktree с .bounded при cwd на main" "$(rc "$S/f" "$S/wt/src/a.py")" 0
assert_eq "src на глубине без плана" "$(rc "$S/b" "$S/b/api/src/a.py")" 2
assert_eq "имя файла с [скобками] в src без плана (M-4)" "$(rc "$S/b" "$S/b/src/[slug].ts")" 2
assert_eq "не код — пропуск" "$(rc "$S/b" "$S/b/docs/a.md")" 0
assert_eq "вне репозитория — пропуск" "$(rc "$S/b" "$S/out/src/a.py")" 0
assert_eq "code-paths: hooks/ без плана" "$(rc "$S/a" "$S/c/hooks/x.sh")" 2
assert_eq "code-paths: src/ не защищён" "$(rc "$S/c" "$S/c/src/a.py")" 0
assert_eq "два плана, один с веткой" "$(rc "$S/p2" "$S/p2/src/a.py")" 0
assert_eq "два плана без ветки" "$(rc "$S/p0" "$S/p0/src/a.py")" 2
assert_eq "один план — пропуск" "$(rc "$S/p1" "$S/p1/src/a.py")" 0
assert_eq "detached HEAD при планах — пропуск" "$(rc "$S/pd" "$S/pd/src/a.py")" 0
assert_eq "не JSON — пропуск" "$(printf 'not json' | bash "$H" 2>/dev/null; echo $?)" 0
assert_eq "без file_path — пропуск" "$(printf '{"cwd":"%s","tool_input":{}}' "$S/b" | bash "$H" 2>/dev/null; echo $?)" 0
m=$(j "$S/a" "$S/b/src/a.py" | bash "$H" 2>&1 >/dev/null)
assert_grep "сообщение: префикс и путь" '^BLOCKED \[require-plan\]: правка src/a.py' <(printf '%s' "$m")
assert_grep "сообщение: .bounded репозитория файла" "$(basename "$S")/b/\.bounded" <(printf '%s' "$m")
assert_grep "settings.hooks.json регистрирует require-plan" 'require-plan\.sh' "$ROOT/hooks/settings.hooks.json"