Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions bin/fm-backend.sh
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,13 @@ fm_backend_expected_label_of_selector() { # <raw-target> <state-dir>
fm_backend_source() { # <name>
local name=$1
fm_backend_validate "$name" || return 1
# A missing adapter file must be a guardable refusal, never a fatal source
# error: under `set -e` (fm-teardown.sh and other lifecycle consumers) a
# failed `.` on a nonexistent file exits the shell even inside `||`/`if`
# guards on bash 3.2, which silently reports success instead of reaching the
# caller's "prerequisites unavailable" refusal. Precheck existence so the
# source only ever runs on a present file.
[ -f "$FM_BACKEND_LIB_DIR/backends/$name.sh" ] || return 1
case "$name" in
tmux)
if [ -z "${_FM_BACKEND_TMUX_SOURCED:-}" ]; then
Expand Down
24 changes: 24 additions & 0 deletions tests/fm-backend.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,29 @@ test_backend_source_shell_portable() {
pass "bash: fm_backend_source recognizes known backends and rejects unknown ones"
}

test_backend_source_missing_adapter_refuses_guardably() {
local out rc tmpbin
# The teardown preflight relies on fm_backend_source returning 1 when an
# adapter file is absent. Under `set -e`, a failed `.` on a nonexistent file
# is a fatal special-builtin error on bash 3.2, even inside `||`/`if` guards,
# so the refusal must come from an existence precheck instead of the source
# itself failing - otherwise lifecycle callers silently exit as if they
# succeeded. This mirrors the missing-adapter teardown fixture at the
# library boundary for every backend and caller.
tmpbin="$TMP_ROOT/missing-adapter-lib/backends"
mkdir -p "$tmpbin"
rc=0
out=$(set -eu; cd "$ROOT" \
&& . bin/fm-backend.sh \
&& FM_BACKEND_LIB_DIR="$tmpbin" \
&& if fm_backend_source herdr; then echo sourced; else echo refused; fi) || rc=$?
[ "$rc" -eq 0 ] \
|| fail "fm_backend_source missing adapter: the caller shell died (rc=$rc) instead of refusing"
[ "$out" = "refused" ] \
|| fail "fm_backend_source missing adapter: expected 'refused', got: $out"
pass "fm_backend_source refuses a missing adapter guardably under set -e instead of exiting the caller"
}

test_backend_validate_spawn_accepts_orca() {
local out
fm_backend_validate_spawn tmux 2>/dev/null || fail "fm_backend_validate_spawn should accept tmux"
Expand Down Expand Up @@ -1126,6 +1149,7 @@ test_backend_name_autodetect_notice
test_backend_name_explicit_beats_detection
test_backend_validate_refuses_unknown
test_backend_source_shell_portable
test_backend_source_missing_adapter_refuses_guardably
test_backend_validate_spawn_accepts_orca
test_meta_get_and_backend_of_meta
test_resolve_selector_three_forms
Expand Down
Loading