claude-harness/tests/test-hook-parse.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

105 lines
8.8 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.

# harness_target.py targets: разбор команды Bash на цели записи, без файловой системы (спека hook-guards).
P="$ROOT/hooks/harness_target.py"
assert_file "harness_target.py существует" "$P"
t(){ python3 "$P" targets "$1" /r 2>&1; }
NL=$'\n'
# чтение и дескрипторы
assert_eq "grep с текстом «npm i» — целей нет" "$(t 'grep -rn "npm i\|npm install" docs | head')" ""
assert_eq "2>/dev/null и 2>&1 — не цели" "$(t 'ls x 2>/dev/null; git fetch origin --prune 2>&1 | tail -3')" ""
assert_eq ">&2 и 1>&- — не цели" "$(t 'echo x >&2; echo y 1>&-')" ""
assert_eq "регулярка с > в кавычках" "$(t "python3 -c 're.search(r\"\\d>{1,2}\", s); mkdir'")" ""
assert_eq "комментарий" "$(t 'echo x # > f')" ""
# редиректы
assert_eq "редирект" "$(t 'echo x > notes.md')" "file /r/notes.md >"
assert_eq "дозапись по абсолютному пути" "$(t 'echo x >> /tmp/a.log')" "file /tmp/a.log >>"
assert_eq "stderr в файл" "$(t 'make 2>build.log')" "file /r/build.log 2>"
assert_eq "echo 2 > f — 2 это аргумент" "$(t 'echo 2 > f')" "file /r/f >"
assert_eq "&> в файл" "$(t 'cmd &> out.log')" "file /r/out.log &>"
assert_eq "heredoc: цель — файл, тело не разбирается" "$(t "cat > CLAUDE.md <<'EOF'${NL}a > b${NL}rm -rf x${NL}EOF${NL}echo ok")" "file /r/CLAUDE.md >"
# команды записи
assert_eq "tee -a" "$(t 'echo x | tee -a a.log b.log')" "file /r/a.log tee${NL}file /r/b.log tee"
assert_eq "cp — назначение" "$(t 'cp a b dst/')" "file /r/dst cp"
assert_eq "cp -t" "$(t 'cp -t out a b')" "file /r/out cp"
assert_eq "mv — источник и назначение" "$(t 'mv a b')" "file /r/a mv${NL}file /r/b mv"
assert_eq "rm -rf" "$(t 'rm -rf build')" "file /r/build rm"
assert_eq "chmod — без режима" "$(t 'chmod +x run.sh')" "file /r/run.sh chmod"
assert_eq "mkdir -p .wt/" "$(t 'mkdir -p .wt/6-x')" "file /r/.wt/6-x mkdir"
assert_eq "glob — каталог до метасимвола" "$(t 'rm docs/*.md')" "file /r/docs rm"
assert_eq "sed -i" "$(t "sed -i 's/a/b/' x.md")" "file /r/x.md sed -i"
assert_eq "sed -i '' (BSD)" "$(t "sed -i '' 's/a/b/' x.md")" "file /r/x.md sed -i"
assert_eq "sed -i -e, два файла" "$(t 'sed -i -e s/a/b/ x.md y.md')" "file /r/x.md sed -i${NL}file /r/y.md sed -i"
assert_eq "sed -n — чтение" "$(t 'sed -n 1,5p x.md')" ""
assert_eq "perl -pi -e" "$(t "perl -pi -e 's/a/b/' x")" "file /r/x perl -i"
assert_eq "dd of=" "$(t 'dd if=a of=/r/b.img')" "file /r/b.img dd"
assert_eq "tar -x -C" "$(t 'tar -xzf a.tgz -C out')" "file /r/out tar -x"
assert_eq "tar c — не запись" "$(t 'tar czf a.tgz src')" ""
assert_eq "unzip -d" "$(t 'unzip a.zip -d out')" "file /r/out unzip"
assert_eq "unzip -l — чтение" "$(t 'unzip -l a.zip')" ""
assert_eq "npm install — текущий каталог" "$(t 'npm install')" "file /r npm install"
assert_eq "npm -g install — не репозиторий" "$(t 'npm -g install x')" ""
assert_eq "pip install — не репозиторий" "$(t 'pip install requests')" ""
assert_eq "openspec archive" "$(t 'openspec archive x')" "file /r openspec archive"
assert_eq "openspec validate — чтение" "$(t 'openspec validate x --strict')" ""
assert_eq "docker … sh -c — не локальная запись" "$(t "docker compose exec -T db sh -c 'psql > /tmp/x'")" ""
assert_eq "префиксы sudo/env/присваивания" "$(t 'sudo rm /etc/x; env A=1 touch f; A=1 B=2 touch g')" "file /etc/x rm${NL}file /r/f touch${NL}file /r/g touch"
assert_eq "xargs rm — цели неизвестны" "$(t 'xargs rm < list')" "file - rm"
assert_eq "if/then" "$(t 'if [ -f x ]; then rm x; fi')" "file /r/x rm"
# каталог
assert_eq "cd && запись" "$(t 'cd /w && echo x > f')" "file /w/f >"
assert_eq "cd относительный ;" "$(t 'cd sub; touch f')" "file /r/sub/f touch"
assert_eq "( cd ) не протекает" "$(t '(cd /w; touch a); touch b')" "file /w/a touch${NL}file /r/b touch"
assert_eq "cd в конвейере не протекает" "$(t 'cd /w | cat; touch b')" "file /r/b touch"
assert_eq "pushd/popd" "$(t 'pushd /w && touch a && popd && touch b')" "file /w/a touch${NL}file /r/b touch"
assert_eq "cd - — каталог неизвестен" "$(t 'cd - && touch a')" "file - touch"
assert_eq "bash -c разбирается" "$(t "bash -c 'echo x > f'")" "file /r/f >"
assert_eq "eval разбирается" "$(t 'eval "rm x"')" "file /r/x rm"
# переменные и HOME
assert_eq "цель в переменной" "$(t 'echo x > "$OUT"')" "file - >"
assert_eq "\$HOME раскрывается" "$(t 'echo x > $HOME/f')" "file $HOME/f >"
assert_eq "~ раскрывается" "$(t 'echo x > ~/f')" "file $HOME/f >"
assert_eq "ln в переменную" "$(t 'ln -sfn ../../fixtures "$WT/fixtures"')" "file - ln"
# git
assert_eq "git add && commit" "$(t 'git add -A && git commit -m x')" "git /r add${NL}git /r commit"
assert_eq "git -C" "$(t 'git -C /w commit -m x')" "git /w commit"
assert_eq "git -C в переменной" "$(t 'for r in a b; do git -C "$r" commit -m x; done')" "git - commit"
assert_eq "git --git-dir" "$(t 'git --git-dir=/x/.git commit -m x')" "git - commit"
assert_eq "worktree, pull, fetch, log — не мутации" "$(t 'git worktree add ../w -b f; git pull --ff-only; git fetch; git log')" ""
assert_eq "stash list/show — чтение" "$(t 'git stash list; git stash show')" ""
assert_eq "stash — мутация" "$(t 'git stash')" "git /r stash"
assert_eq "checkout -b — смена ветки" "$(t 'git checkout -b f')" "switch /r f"
assert_eq "checkout -- пути" "$(t 'git checkout -- a.txt')" "git /r checkout --"
assert_eq "checkout main — смена ветки (I-1)" "$(t 'git checkout main && git merge x')" "switch /r main${NL}git /r merge"
assert_eq "checkout ветки фичи — не событие" "$(t 'git checkout feat/x')" ""
assert_eq "switch - — ветка неизвестна (I-1)" "$(t 'git switch - && git merge x')" "switch /r -${NL}git /r merge"
assert_eq "timeout 60 прячет голову (I-2)" "$(t 'timeout 60 git push origin main')" "git /r push>main"
assert_eq "timeout -s KILL 5 (I-2)" "$(t 'timeout -s KILL 5 rm x')" "file /r/x rm"
assert_eq "nice -n 10 (I-2)" "$(t 'nice -n 10 git commit -m x')" "git /r commit"
assert_eq "sudo -u bob (I-2)" "$(t 'sudo -u bob touch f')" "file /r/f touch"
assert_eq "env -C меняет каталог команды (I-2)" "$(t 'env -C /w git commit -m x')" "git /w commit"
assert_eq "xargs -n 1 rm (I-2)" "$(t 'xargs -n 1 rm < list')" "file - rm"
assert_eq "push --tags — не ветка (M-5)" "$(t 'git push --tags')" ""
assert_eq "push текущей ветки через \$(git branch --show-current) (M-5)" "$(t 'git push -u origin "$(git branch --show-current)"')" "git /r push"
assert_eq "push текущей ветки через rev-parse (M-5)" "$(t 'git push origin $(git rev-parse --abbrev-ref HEAD)')" "git /r push"
assert_eq "switch -c && commit" "$(t 'git switch -q -c feat/x && git commit -m y')" "switch /r feat/x${NL}git /r commit"
assert_eq "apply --check — чтение" "$(t 'git apply --check p.diff')" ""
assert_eq "apply — мутация" "$(t 'git apply p.diff')" "git /r apply"
assert_eq "commit с heredoc в подстановке" "$(t "git commit -m \"\$(cat <<'EOF'${NL}fix: it's (done)${NL}EOF${NL})\"")" "git /r commit"
# push
assert_eq "push без refspec" "$(t 'git push')" "git /r push"
assert_eq "push origin HEAD" "$(t 'git push -u origin HEAD')" "git /r push"
assert_eq "push ветки фичи" "$(t 'git push -u origin feat/x')" ""
assert_eq "push --delete ветки фичи" "$(t 'git push origin --delete feat/x')" ""
assert_eq "push HEAD:main" "$(t 'git push origin HEAD:main')" "git /r push>main"
assert_eq "push --delete main" "$(t 'git push origin --delete main')" "git /r push>main"
assert_eq "push --all" "$(t 'git push --all')" "git /r push --all"
assert_eq "push в переменную" "$(t 'git push origin "$BR"')" "git /r push?"
assert_eq "push feat/\$ID — префикс фичи" "$(t 'git push origin feat/$ID-x')" ""
# граничные случаи (Review Focus)
assert_eq "путь с пробелом в кавычках" "$(t 'touch "a b.md"')" "file /r/a b.md touch"
assert_eq "арифметика \$((…)) не ломает разбор" "$(t 'echo $((1+2)) > f')" "file /r/f >"
assert_eq "cd ~/x" "$(t 'cd ~/x && touch f')" "file $HOME/x/f touch"
assert_eq "git -C относительный после cd" "$(t 'cd /w && git -C sub commit -m x')" "git /w/sub commit"
assert_eq "перенос строки обратным слешем" "$(t "git add a \\${NL} b && git commit -m x")" "git /r add${NL}git /r commit"
# сбой разбора
assert_grep "незакрытая кавычка — error" '^error' <(t 'echo "x')