#!/usr/bin/env bats setup() { REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../.." && pwd)" GATE="$REPO_ROOT/claude-templates/bin/git-worktree-gate" WORK="$(mktemp -d)" REPO="$WORK/repo" git init -q "$REPO" git -C "$REPO" config user.email test@example.com git -C "$REPO" config user.name tester printf 'base\n' >"$REPO/tracked" git -C "$REPO" add tracked git -C "$REPO" commit -qm init } teardown() { rm -rf "$WORK" } @test "strict accepts a completely clean worktree" { run bash "$GATE" strict "$REPO" [ "$status" -eq 0 ] [ -z "$output" ] } @test "strict rejects unstaged, staged, and untracked changes with paths" { printf 'changed\n' >>"$REPO/tracked" printf 'new\n' >"$REPO/staged" git -C "$REPO" add staged printf 'loose\n' >"$REPO/loose" run bash "$GATE" strict "$REPO" [ "$status" -eq 1 ] [[ "$output" == *"wrap blocked"* ]] [[ "$output" == *"tracked"* ]] [[ "$output" == *"staged"* ]] [[ "$output" == *"loose"* ]] } @test "sync-safe permits untracked inbox deliveries and strict still rejects them" { mkdir -p "$REPO/inbox/nested" printf 'handoff\n' >"$REPO/inbox/nested/from-home.org" run bash "$GATE" sync-safe "$REPO" [ "$status" -eq 0 ] run bash "$GATE" strict "$REPO" [ "$status" -eq 1 ] [[ "$output" == *"inbox/nested/from-home.org"* ]] } @test "sync-safe rejects tracked changes even inside inbox" { mkdir -p "$REPO/inbox" printf 'tracked\n' >"$REPO/inbox/tracked.org" git -C "$REPO" add inbox/tracked.org git -C "$REPO" commit -qm inbox printf 'changed\n' >>"$REPO/inbox/tracked.org" run bash "$GATE" sync-safe "$REPO" [ "$status" -eq 1 ] [[ "$output" == *"inbox/tracked.org"* ]] } @test "sync-safe rejects untracked files outside inbox" { printf 'scratch\n' >"$REPO/scratch" run bash "$GATE" sync-safe "$REPO" [ "$status" -eq 1 ] [[ "$output" == *"scratch"* ]] } @test "ignored files do not block either policy" { printf 'cache/\n' >"$REPO/.gitignore" git -C "$REPO" add .gitignore git -C "$REPO" commit -qm ignore mkdir -p "$REPO/cache" printf 'generated\n' >"$REPO/cache/output" run bash "$GATE" strict "$REPO" [ "$status" -eq 0 ] run bash "$GATE" sync-safe "$REPO" [ "$status" -eq 0 ] } @test "reports unusual filenames without losing the entry" { odd=$'line break\nname' printf 'odd\n' >"$REPO/$odd" run bash "$GATE" strict "$REPO" [ "$status" -eq 1 ] [[ "$output" == *"line"* ]] [[ "$output" == *"name"* ]] } @test "certify and verify bind a clean worktree to its current HEAD" { run bash "$GATE" certify "$REPO" [ "$status" -eq 0 ] run bash "$GATE" verify "$REPO" [ "$status" -eq 0 ] git -C "$REPO" commit -q --allow-empty -m later run bash "$GATE" verify "$REPO" [ "$status" -eq 1 ] [[ "$output" == *"HEAD changed"* ]] } @test "verify rejects changes made after certification" { run bash "$GATE" certify "$REPO" [ "$status" -eq 0 ] printf 'late\n' >>"$REPO/tracked" run bash "$GATE" verify "$REPO" [ "$status" -eq 1 ] [[ "$output" == *"tracked"* ]] } @test "dirty submodule blocks strict and sync-safe" { CHILD="$WORK/child" git init -q "$CHILD" git -C "$CHILD" config user.email test@example.com git -C "$CHILD" config user.name tester printf 'child\n' >"$CHILD/file" git -C "$CHILD" add file git -C "$CHILD" commit -qm init git -C "$REPO" -c protocol.file.allow=always submodule add -q "$CHILD" sub git -C "$REPO" commit -qam submodule printf 'dirty\n' >>"$REPO/sub/file" run bash "$GATE" strict "$REPO" [ "$status" -eq 1 ] [[ "$output" == *"sub"* ]] run bash "$GATE" sync-safe "$REPO" [ "$status" -eq 1 ] } @test "a low-level git status failure blocks instead of looking clean" { printf 'not an index\n' >"$WORK/bad-index" run env GIT_INDEX_FILE="$WORK/bad-index" bash "$GATE" strict "$REPO" [ "$status" -eq 1 ] [[ "$output" == *"git status failed"* ]] } @test "an in-progress sequencer operation blocks a clean-looking tree" { gitdir="$(git -C "$REPO" rev-parse --absolute-git-dir)" mkdir -p "$gitdir/sequencer" run bash "$GATE" strict "$REPO" [ "$status" -eq 1 ] [[ "$output" == *"sequencer"* ]] }