#!/usr/bin/env bats # # Tests for claude-templates/.ai/scripts/capture-guard — detects live # org-capture buffers visiting a target file before a workflow edits that # file on disk (the roam inbox, in inbox.org roam mode Phase D). Editing the file # underneath an indirect org-capture buffer wedges the capture (see emacs.md). # # Contract under test: # capture-guard [TARGET_FILE] (default TARGET_FILE = ~/org/roam/inbox.org) # exit 0 → safe to edit: emacsclient absent, daemon unreachable, or no # capture buffer visits TARGET_FILE. # exit 1 → a live capture buffer visits TARGET_FILE; its name(s) printed. # # Strategy: the emacsclient boundary is mocked with a PATH stub. The stub # answers the reachability probe (`-e t`) per STUB_REACHABLE and returns a # canned, real-emacsclient-shaped result (quoted string) for the buffer query # per STUB_BUFS. The script's own quote-stripping and exit logic is the code # under test; the file-equal-p precision is real-Emacs behavior we trust. SCRIPT="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)/capture-guard" BASH_BIN="$(command -v bash)" setup() { TEST_DIR="$(mktemp -d -t capture-guard-bats.XXXXXX)" STUB_DIR="$TEST_DIR/bin" mkdir -p "$STUB_DIR" cat > "$STUB_DIR/emacsclient" <<'STUB' #!/usr/bin/env bash # Mock emacsclient. `-e t` is the reachability probe; anything else is the # buffer query, answered with the real-emacsclient-shaped quoted string. expr="$2" if [ "$expr" = "t" ]; then [ "${STUB_REACHABLE:-1}" = "1" ] && { echo t; exit 0; } exit 1 fi printf '%s\n' "${STUB_BUFS:-\"\"}" exit 0 STUB chmod +x "$STUB_DIR/emacsclient" EMPTY_DIR="$TEST_DIR/empty" mkdir -p "$EMPTY_DIR" } teardown() { rm -rf "$TEST_DIR" } # ---- Safe-to-edit (exit 0) cases ------------------------------------ @test "capture-guard: emacsclient absent is safe (exit 0, no output)" { run env PATH="$EMPTY_DIR" "$BASH_BIN" "$SCRIPT" [ "$status" -eq 0 ] [ -z "$output" ] } @test "capture-guard: daemon unreachable is safe (exit 0)" { run env PATH="$STUB_DIR:$PATH" STUB_REACHABLE=0 "$BASH_BIN" "$SCRIPT" [ "$status" -eq 0 ] [ -z "$output" ] } @test "capture-guard: reachable with no capture buffers is safe (exit 0)" { run env PATH="$STUB_DIR:$PATH" STUB_REACHABLE=1 STUB_BUFS='""' "$BASH_BIN" "$SCRIPT" [ "$status" -eq 0 ] [ -z "$output" ] } # ---- Blocked (exit 1) cases ----------------------------------------- @test "capture-guard: one live capture buffer blocks (exit 1, name printed)" { run env PATH="$STUB_DIR:$PATH" STUB_REACHABLE=1 STUB_BUFS='"CAPTURE-inbox.org"' \ "$BASH_BIN" "$SCRIPT" [ "$status" -eq 1 ] [[ "$output" == *"CAPTURE-inbox.org"* ]] } @test "capture-guard: multiple live capture buffers all reported (exit 1)" { run env PATH="$STUB_DIR:$PATH" STUB_REACHABLE=1 \ STUB_BUFS='"CAPTURE-inbox.org,CAPTURE-2-inbox.org"' \ "$BASH_BIN" "$SCRIPT" [ "$status" -eq 1 ] [[ "$output" == *"CAPTURE-inbox.org"* ]] [[ "$output" == *"CAPTURE-2-inbox.org"* ]] } @test "capture-guard: blocked output does not contain stray surrounding quotes" { run env PATH="$STUB_DIR:$PATH" STUB_REACHABLE=1 STUB_BUFS='"CAPTURE-inbox.org"' \ "$BASH_BIN" "$SCRIPT" [ "$status" -eq 1 ] [[ "$output" != \"* ]] [[ "$output" != *\" ]] } # ---- Argument handling ---------------------------------------------- @test "capture-guard: accepts an explicit target-file argument" { run env PATH="$STUB_DIR:$PATH" STUB_REACHABLE=1 STUB_BUFS='""' \ "$BASH_BIN" "$SCRIPT" "$TEST_DIR/some-other-inbox.org" [ "$status" -eq 0 ] [ -z "$output" ] } # ---- --wait poll mode ----------------------------------------------- @test "capture-guard --wait: returns 0 instantly when already safe (no sleep)" { SECONDS=0 run env PATH="$STUB_DIR:$PATH" STUB_REACHABLE=1 STUB_BUFS='""' \ "$BASH_BIN" "$SCRIPT" --wait [ "$status" -eq 0 ] [ -z "$output" ] [ "$SECONDS" -lt 2 ] # didn't poll-sleep } @test "capture-guard --wait=1: times out to exit 1 when persistently blocked" { # Stub always reports the buffer, so it never clears — the short budget # forces a timeout. Capped sleep keeps this near 1s. run env PATH="$STUB_DIR:$PATH" STUB_REACHABLE=1 STUB_BUFS='"CAPTURE-inbox.org"' \ "$BASH_BIN" "$SCRIPT" --wait=1 [ "$status" -eq 1 ] [[ "$output" == *"CAPTURE-inbox.org"* ]] } @test "capture-guard --wait=N accepts a target after the flag" { run env PATH="$STUB_DIR:$PATH" STUB_REACHABLE=1 STUB_BUFS='""' \ "$BASH_BIN" "$SCRIPT" --wait=1 "$TEST_DIR/some-other-inbox.org" [ "$status" -eq 0 ] [ -z "$output" ] }