94 lines
7.6 KiB
Bash
94 lines
7.6 KiB
Bash
# 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 ветки — не мутация" "$(t 'git checkout main')" ""
|
||
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')
|