Files
Henner M. Kruse dc4a5c5b5e Close bats coverage gaps, add hide_command helper, wire up kcov
- 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.
2026-08-04 21:09:07 +00:00

126 lines
4.3 KiB
Bash

#!/usr/bin/env bats
# Tests for vendor/claude-code/plugins/git-manager/scripts/setup.sh.
load '../helpers/common'
setup() {
sandbox_home
SETUP="$REPO_ROOT/vendor/claude-code/plugins/git-manager/scripts/setup.sh"
SETTINGS_FILE="$HOME/.claude/settings.json"
CONFIG_FILE="$HOME/.agent-skills/git-manager/config.json"
REPO="$BATS_TEST_TMPDIR/repo"
make_test_repo "$REPO"
}
@test "writes the two expected tilde-form permission rules" {
run "$SETUP" --settings-scope user
[ "$status" -eq 0 ]
allow=$(json_get "$SETTINGS_FILE" "permissions.allow")
[[ "$allow" == *'Bash(~/.agent-skills/git-manager/bin/git_cmd.sh:*)'* ]]
[[ "$allow" == *'Read(~/.agent-skills/git-manager/config.json)'* ]]
}
@test "merges into an existing settings.json without disturbing unrelated keys" {
mkdir -p "$(dirname "$SETTINGS_FILE")"
cat > "$SETTINGS_FILE" << 'EOF'
{"permissions": {"allow": ["Bash(ls:*)"]}, "unrelatedTopLevelKey": "keep-me"}
EOF
run "$SETUP" --settings-scope user
[ "$status" -eq 0 ]
allow=$(json_get "$SETTINGS_FILE" "permissions.allow")
[[ "$allow" == *'Bash(ls:*)'* ]]
[[ "$allow" == *'Bash(~/.agent-skills/git-manager/bin/git_cmd.sh:*)'* ]]
kept=$(json_get "$SETTINGS_FILE" "unrelatedTopLevelKey")
[ "$kept" = '"keep-me"' ]
}
@test "re-running setup is idempotent: no duplicate allow entries" {
"$SETUP" --settings-scope user > /dev/null
run "$SETUP" --settings-scope user
[ "$status" -eq 0 ]
count=$(python3 -c "import json; print(json.load(open('$SETTINGS_FILE'))['permissions']['allow'].count('Bash(~/.agent-skills/git-manager/bin/git_cmd.sh:*)'))")
[ "$count" -eq 1 ]
}
@test "--repo registers a new repo in config.json" {
run "$SETUP" --settings-scope user --repo myrepo="$REPO"
[ "$status" -eq 0 ]
path=$(json_get "$CONFIG_FILE" "repos.0.path")
[ "$path" = "\"$REPO\"" ]
}
@test "registering the same repo name twice updates the path instead of duplicating" {
REPO2="$BATS_TEST_TMPDIR/repo2"
make_test_repo "$REPO2"
"$SETUP" --settings-scope user --repo myrepo="$REPO" > /dev/null
run "$SETUP" --settings-scope user --repo myrepo="$REPO2"
[ "$status" -eq 0 ]
count=$(python3 -c "import json; print(len(json.load(open('$CONFIG_FILE'))['repos']))")
[ "$count" -eq 1 ]
path=$(json_get "$CONFIG_FILE" "repos.0.path")
[ "$path" = "\"$REPO2\"" ]
}
@test "without --install-hook or --install-raw-git-hook, no PreToolUse hooks are installed" {
run "$SETUP" --settings-scope user
[ "$status" -eq 0 ]
run python3 -c "import json; print('hooks' in json.load(open('$SETTINGS_FILE')))"
[ "$output" = "False" ]
}
@test "--install-hook installs only the anti-chaining hook" {
run "$SETUP" --settings-scope user --install-hook
[ "$status" -eq 0 ]
run python3 -c "
import json
h = json.load(open('$SETTINGS_FILE'))['hooks']['PreToolUse']
cmds = [c['command'] for e in h for c in e['hooks']]
print(len(cmds))
print(any('force-ask-on-chaining.sh' in c for c in cmds))
print(any('force-ask-on-raw-git.sh' in c for c in cmds))
"
[[ "$output" == $'1\nTrue\nFalse' ]]
}
@test "--install-raw-git-hook installs only the raw-git nudge hook" {
run "$SETUP" --settings-scope user --install-raw-git-hook
[ "$status" -eq 0 ]
run python3 -c "
import json
h = json.load(open('$SETTINGS_FILE'))['hooks']['PreToolUse']
cmds = [c['command'] for e in h for c in e['hooks']]
print(len(cmds))
print(any('force-ask-on-chaining.sh' in c for c in cmds))
print(any('force-ask-on-raw-git.sh' in c for c in cmds))
"
[[ "$output" == $'1\nFalse\nTrue' ]]
}
@test "--settings-scope project writes under <project-dir>/.claude/settings.json" {
PROJECT_DIR="$BATS_TEST_TMPDIR/someproject"
mkdir -p "$PROJECT_DIR"
run "$SETUP" --settings-scope project --project-dir "$PROJECT_DIR"
[ "$status" -eq 0 ]
[ -f "$PROJECT_DIR/.claude/settings.json" ]
[ ! -e "$HOME/.claude/settings.json" ]
}
@test "an unknown flag errors" {
run "$SETUP" --settings-scope user --bogus-flag
[ "$status" -eq 1 ]
[[ "$output" == *"unknown argument '--bogus-flag'"* ]]
}
@test "an invalid --settings-scope value errors" {
run "$SETUP" --settings-scope nonsense
[ "$status" -eq 1 ]
[[ "$output" == *"--settings-scope must be 'project' or 'user'"* ]]
}
@test "missing python3 fails with a clear error instead of a silent crash" {
hide_command python3
run "$SETUP" --settings-scope user
[ "$status" -eq 1 ]
[[ "$output" == *"python3 is required"* ]]
}