diff --git a/tests/run-bats.sh b/tests/run-bats.sh index e0b0b4e..9e374ad 100755 --- a/tests/run-bats.sh +++ b/tests/run-bats.sh @@ -45,11 +45,17 @@ fi # land at .claude/skills//tests/ and be discovered and run a second time, # which is ADR-0024 consequence 2 arriving here. Keeping the exclusion now is # what stops that landing as a mystery double-run on the merge that enables it. +# +# The walk runs from inside REPO_ROOT so the exclusions match paths RELATIVE to +# it, the same universe the `git ls-files` grep below sees. Matched against +# absolute paths, `*/.claude/worktrees/*` excluded every file whenever the +# checkout itself was a Claude worktree (/.claude/worktrees//), and +# the run failed with "N of N tracked .bats file(s) were not discovered". TEST_FILES=() while IFS= read -r f; do - TEST_FILES+=("$f") + TEST_FILES+=("$REPO_ROOT/${f#./}") done < <( - find "$REPO_ROOT" -name "*.bats" \ + cd "$REPO_ROOT" && find . -name "*.bats" \ -not -path "*/tests/bats/*" \ -not -path "*/test_helper/*" \ -not -path "*/.claude/worktrees/*" \ diff --git a/tests/run-tests.sh b/tests/run-tests.sh index ff93451..1080bd2 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -151,11 +151,17 @@ run_bats # such file exists today -- the skill suites are all .bats, where run-bats.sh # already hit this -- so the exclusion is symmetry with its sibling, kept here # so the first shell suite added under a skill does not reintroduce it. +# +# The walk runs from inside SEARCH_ROOT so the exclusions match paths RELATIVE to +# it. Matched against absolute paths, `*/.claude/worktrees/*` excluded every +# suite whenever the checkout itself was a Claude worktree +# (/.claude/worktrees//); relative, it still skips worktrees nested +# below the root. The `./` prefix is swapped back for SEARCH_ROOT afterwards. SCRIPTS=() while IFS= read -r script; do - SCRIPTS+=("$script") + SCRIPTS+=("$SEARCH_ROOT/${script#./}") done < <( - find "$SEARCH_ROOT" -name "test-*.sh" \ + cd "$SEARCH_ROOT" && find . -name "test-*.sh" \ -not -path "*/.git/*" \ -not -path "*/.claude/worktrees/*" \ -not -path "*/apm_modules/*" \ diff --git a/tests/test-run-bats.sh b/tests/test-run-bats.sh index a2c87ef..0f637b4 100644 --- a/tests/test-run-bats.sh +++ b/tests/test-run-bats.sh @@ -43,8 +43,8 @@ trap cleanup EXIT # invokes it as `$(make_fake_repo)`, and an append made in here would land in the # command substitution's subshell and be lost. Registration is the caller's job. make_fake_repo() { - local dir - dir="$(mktemp -d)" + local dir="${1:-}" + [[ -n "$dir" ]] || dir="$(mktemp -d)" mkdir -p "$dir/tests" "$dir/scripts/lib" cp "$REPO_ROOT/scripts/lib/batch-run.sh" "$dir/scripts/lib/batch-run.sh" cp "$RUN_BATS" "$dir/tests/run-bats.sh" @@ -410,6 +410,40 @@ else fail "the run failed but not with the zero-files message: $FAKE_OUT" fi +# --- 10. A repo root that itself sits under .claude/worktrees/ still runs. The +# worktree exclusion used to match the absolute path, so a Claude worktree -- +# which lives at /.claude/worktrees// -- excluded every file of its +# own and failed with "N of N tracked .bats file(s) were not discovered". The +# exclusion is now relative to the root, so a worktree nested BELOW the root is +# still skipped. +echo "" +echo "--- a root under .claude/worktrees/ discovers its own files and still skips nested worktrees ---" +WT_PARENT="$(mktemp -d)" +FIXTURES+=("$WT_PARENT") +DIR10="$WT_PARENT/repo/.claude/worktrees/agent-x" +make_fake_repo "$DIR10" >/dev/null +git -C "$DIR10" init -q +seed_bats_files "$DIR10" +mkdir -p "$DIR10/.claude/worktrees/nested/tests" +printf '@test "n" { false; }\n' > "$DIR10/.claude/worktrees/nested/tests/n.bats" +install_stub_bats "$DIR10" <<'EOF' +#!/usr/bin/env bash +echo "1..1" +echo "ok 1 first" +exit 0 +EOF +git -C "$DIR10" add tests/a.bats tests/b.bats +run_fake "$DIR10" +if [[ $FAKE_RC -ne 0 ]]; then + fail "a root under .claude/worktrees/ failed — its own files were excluded: $FAKE_OUT" +elif ! grep -q "^=== tests/a.bats ===$" <<< "$FAKE_OUT"; then + fail "a root under .claude/worktrees/ did not run its own tests/a.bats: $FAKE_OUT" +elif grep -q "nested/tests/n.bats" <<< "$FAKE_OUT"; then + fail "a worktree nested below the root was discovered and run: $FAKE_OUT" +else + pass "a root under .claude/worktrees/ runs its own files and skips nested worktrees" +fi + echo "" echo "Results: $PASS passed, $FAIL failed" [[ $FAIL -eq 0 ]] diff --git a/tests/test-run-tests.sh b/tests/test-run-tests.sh index 7d84cbd..96987e8 100644 --- a/tests/test-run-tests.sh +++ b/tests/test-run-tests.sh @@ -45,8 +45,8 @@ trap cleanup EXIT # substitution's subshell and be lost. Registration is the caller's job. Same # convention as tests/test-run-bats.sh. make_fake_repo() { - local dir - dir="$(mktemp -d)" + local dir="${1:-}" + [[ -n "$dir" ]] || dir="$(mktemp -d)" mkdir -p "$dir/tests" "$dir/scripts/lib" "$dir/cases" cp "$REPO_ROOT/scripts/lib/batch-run.sh" "$dir/scripts/lib/batch-run.sh" cp "$RUN_TESTS" "$dir/tests/run-tests.sh" @@ -670,6 +670,39 @@ else pass "--strict and RUN_TESTS_STRICT=1 both dispatch children with RUN_TESTS_STRICT absent" fi +# --- A search root that itself sits under .claude/worktrees/ still runs. The +# worktree exclusion used to match the absolute path, so every suite in a Claude +# worktree (/.claude/worktrees//) was filtered out. It is now +# relative to the search root, so a worktree nested BELOW the root is still +# skipped. +echo "" +echo "--- a root under .claude/worktrees/ discovers its own suites and still skips nested worktrees ---" +WT_PARENT="$(mktemp -d)" +FIXTURES+=("$WT_PARENT") +WT_DIR="$WT_PARENT/repo/.claude/worktrees/agent-x" +make_fake_repo "$WT_DIR" >/dev/null +install_healthy_bats_runner "$WT_DIR" +add_case "$WT_DIR" test-own.sh <<'EOF' +#!/usr/bin/env bash +echo "own suite ran" +EOF +mkdir -p "$WT_DIR/cases/.claude/worktrees/nested" +cat > "$WT_DIR/cases/.claude/worktrees/nested/test-nested.sh" <<'EOF' +#!/usr/bin/env bash +echo "nested suite ran" +exit 1 +EOF +run_fake "$WT_DIR" +if [[ $FAKE_RC -ne 0 ]]; then + fail "a root under .claude/worktrees/ failed: $FAKE_OUT" +elif ! grep -q "own suite ran" <<< "$FAKE_OUT"; then + fail "a root under .claude/worktrees/ did not run its own suite: $FAKE_OUT" +elif grep -q "nested suite ran" <<< "$FAKE_OUT"; then + fail "a worktree nested below the root was discovered and run: $FAKE_OUT" +else + pass "a root under .claude/worktrees/ runs its own suites and skips nested worktrees" +fi + echo "" echo "Results: $PASS passed, $FAIL failed" [[ $FAIL -eq 0 ]]