dc4a5c5b5e
- tests/helpers/common.bash: hide_command hides python3/jq/rg via a per-directory-batched PATH shim (not per-file — that took minutes), making the fail-open/grep-fallback branches testable. - 20 new tests across all 7 .bats files: unknown flags, invalid --settings-scope, missing python3/jq, grep fallback when rg is unavailable, empty/multi-vault config edge cases, a vault path that stops existing or isn't a directory, and the previously-untested tool_name != Bash branch of force-ask-on-raw-git.sh. 53 -> 73 tests. - hooks.bats: deploy the hooks once in setup_file() (BATS_FILE_TMPDIR) instead of per-test (BATS_TEST_TMPDIR) -- faster, and gives kcov one stable path per hook to aggregate coverage against instead of a fragmented copy per test. - .gitignore (first in this repo) + tests/README.md Coverage section documenting the kcov invocation and why --include-pattern needs three entries, not two. Real, tool-measured coverage via kcov: 86.15% (255/296 lines) across all 10 wrapper/setup/hook scripts in both plugins, replacing an earlier manual ~68% branch-coverage estimate.
106 lines
3.8 KiB
Bash
106 lines
3.8 KiB
Bash
#!/usr/bin/env bats
|
|
# Tests for the two optional PreToolUse hooks. Run against the *deployed*
|
|
# copies (via a real setup.sh run into a sandboxed $HOME) rather than the
|
|
# repo source under hooks/ directly, because force-ask-on-raw-git.sh only
|
|
# gets its companion _lib.sh (for the "already supports" wording) once
|
|
# deployed alongside it — exactly like real usage.
|
|
#
|
|
# Deployed once in setup_file() (bats-core: runs once per file, not per
|
|
# test) into $BATS_FILE_TMPDIR rather than a fresh $BATS_TEST_TMPDIR/home
|
|
# per test: besides being faster (one setup.sh run instead of one per
|
|
# test), it gives every test in this file the *same* absolute script path,
|
|
# which matters for coverage tooling (kcov) to aggregate hits against one
|
|
# file instead of splintering across a differently-pathed copy per test.
|
|
|
|
load '../helpers/common'
|
|
|
|
setup_file() {
|
|
FILE_HOME="$BATS_FILE_TMPDIR/home"
|
|
mkdir -p "$FILE_HOME"
|
|
HOME="$FILE_HOME" "$REPO_ROOT/vendor/claude-code/plugins/git-manager/scripts/setup.sh" \
|
|
--settings-scope user --install-hook --install-raw-git-hook > /dev/null
|
|
}
|
|
|
|
setup() {
|
|
CHAIN_HOOK="$BATS_FILE_TMPDIR/home/.agent-skills/git-manager/bin/force-ask-on-chaining.sh"
|
|
RAWGIT_HOOK="$BATS_FILE_TMPDIR/home/.agent-skills/git-manager/bin/force-ask-on-raw-git.sh"
|
|
}
|
|
|
|
# --- force-ask-on-chaining.sh ---
|
|
|
|
@test "chaining hook: && in the command forces an ask" {
|
|
input='{"tool_name":"Bash","tool_input":{"command":"git add -A && git commit -m x"}}'
|
|
run "$CHAIN_HOOK" <<< "$input"
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *'"permissionDecision": "ask"'* ]]
|
|
}
|
|
|
|
@test "chaining hook: a clean command produces no output" {
|
|
input='{"tool_name":"Bash","tool_input":{"command":"git status"}}'
|
|
run "$CHAIN_HOOK" <<< "$input"
|
|
[ "$status" -eq 0 ]
|
|
[ -z "$output" ]
|
|
}
|
|
|
|
@test "chaining hook: non-Bash tool_name produces no output even with chaining chars" {
|
|
input='{"tool_name":"Read","tool_input":{"command":"a && b"}}'
|
|
run "$CHAIN_HOOK" <<< "$input"
|
|
[ "$status" -eq 0 ]
|
|
[ -z "$output" ]
|
|
}
|
|
|
|
@test "chaining hook: missing jq fails open instead of blocking every Bash call" {
|
|
input='{"tool_name":"Bash","tool_input":{"command":"a && b"}}'
|
|
hide_command jq
|
|
run "$CHAIN_HOOK" <<< "$input"
|
|
[ "$status" -eq 0 ]
|
|
[ -z "$output" ]
|
|
}
|
|
|
|
# --- force-ask-on-raw-git.sh ---
|
|
|
|
@test "raw-git hook: git_cmd.sh invocations are exempted" {
|
|
input='{"tool_name":"Bash","tool_input":{"command":"~/.agent-skills/git-manager/bin/git_cmd.sh status"}}'
|
|
run "$RAWGIT_HOOK" <<< "$input"
|
|
[ "$status" -eq 0 ]
|
|
[ -z "$output" ]
|
|
}
|
|
|
|
@test "raw-git hook: raw git on a wrapper-supported subcommand asks with 'already supports' wording" {
|
|
input='{"tool_name":"Bash","tool_input":{"command":"git status"}}'
|
|
run "$RAWGIT_HOOK" <<< "$input"
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *'"permissionDecision": "ask"'* ]]
|
|
[[ "$output" == *"already supports"* ]]
|
|
}
|
|
|
|
@test "raw-git hook: raw git on an unsupported subcommand asks with 'accepted fallback' wording" {
|
|
input='{"tool_name":"Bash","tool_input":{"command":"git stash"}}'
|
|
run "$RAWGIT_HOOK" <<< "$input"
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *'"permissionDecision": "ask"'* ]]
|
|
[[ "$output" == *"accepted fallback"* ]]
|
|
}
|
|
|
|
@test "raw-git hook: a non-git command produces no output" {
|
|
input='{"tool_name":"Bash","tool_input":{"command":"ls -la"}}'
|
|
run "$RAWGIT_HOOK" <<< "$input"
|
|
[ "$status" -eq 0 ]
|
|
[ -z "$output" ]
|
|
}
|
|
|
|
@test "raw-git hook: non-Bash tool_name produces no output even for raw git" {
|
|
input='{"tool_name":"Read","tool_input":{"command":"git status"}}'
|
|
run "$RAWGIT_HOOK" <<< "$input"
|
|
[ "$status" -eq 0 ]
|
|
[ -z "$output" ]
|
|
}
|
|
|
|
@test "raw-git hook: missing jq fails open instead of blocking every Bash call" {
|
|
input='{"tool_name":"Bash","tool_input":{"command":"git status"}}'
|
|
hide_command jq
|
|
run "$RAWGIT_HOOK" <<< "$input"
|
|
[ "$status" -eq 0 ]
|
|
[ -z "$output" ]
|
|
}
|