diff --git a/lib/core/sudo.sh b/lib/core/sudo.sh index 483497d..23f421f 100644 --- a/lib/core/sudo.sh +++ b/lib/core/sudo.sh @@ -108,6 +108,11 @@ request_sudo_access() { return 0 fi + # Tests must never trigger real password or Touch ID prompts. + if [[ "${MOLE_TEST_MODE:-0}" == "1" || "${MOLE_TEST_NO_AUTH:-0}" == "1" ]]; then + return 1 + fi + # Detect if running in TTY environment local tty_path="/dev/tty" local is_gui_mode=false @@ -299,6 +304,11 @@ ensure_sudo_session() { return 0 fi + if [[ "${MOLE_TEST_MODE:-0}" == "1" || "${MOLE_TEST_NO_AUTH:-0}" == "1" ]]; then + MOLE_SUDO_ESTABLISHED="false" + return 1 + fi + # Stop old keepalive if exists if [[ -n "$MOLE_SUDO_KEEPALIVE_PID" ]]; then _stop_sudo_keepalive "$MOLE_SUDO_KEEPALIVE_PID" diff --git a/mole b/mole index 6f99026..b1f374e 100755 --- a/mole +++ b/mole @@ -494,6 +494,10 @@ update_mole() { # Remove flow (Homebrew + manual + config/cache). remove_mole() { local dry_run_mode="${1:-false}" + local test_mode=false + if [[ "${MOLE_TEST_MODE:-0}" == "1" ]]; then + test_mode=true + fi if [[ -t 1 ]]; then start_inline_spinner "Detecting Mole installations..." @@ -507,37 +511,47 @@ remove_mole() { local -a manual_installs=() local -a alias_installs=() - if command -v brew > /dev/null 2>&1; then - brew_cmd="brew" - elif [[ -x "/opt/homebrew/bin/brew" ]]; then - brew_cmd="/opt/homebrew/bin/brew" - elif [[ -x "/usr/local/bin/brew" ]]; then - brew_cmd="/usr/local/bin/brew" - fi - - if [[ -n "$brew_cmd" ]]; then - if "$brew_cmd" list mole > /dev/null 2>&1; then - brew_has_mole="true" + if [[ "$test_mode" != "true" ]]; then + if command -v brew > /dev/null 2>&1; then + brew_cmd="brew" + elif [[ -x "/opt/homebrew/bin/brew" ]]; then + brew_cmd="/opt/homebrew/bin/brew" + elif [[ -x "/usr/local/bin/brew" ]]; then + brew_cmd="/usr/local/bin/brew" fi - fi - if [[ "$brew_has_mole" == "true" ]] || is_homebrew_install; then - is_homebrew=true + if [[ -n "$brew_cmd" ]]; then + if "$brew_cmd" list mole > /dev/null 2>&1; then + brew_has_mole="true" + fi + fi + + if [[ "$brew_has_mole" == "true" ]] || is_homebrew_install; then + is_homebrew=true + fi fi local found_mole - found_mole=$(command -v mole 2> /dev/null || true) - if [[ -n "$found_mole" && -f "$found_mole" ]]; then - if [[ ! -L "$found_mole" ]] || ! readlink "$found_mole" | grep -q "Cellar/mole"; then - manual_installs+=("$found_mole") + found_mole="" + if [[ "$test_mode" != "true" ]]; then + found_mole=$(command -v mole 2> /dev/null || true) + if [[ -n "$found_mole" && -f "$found_mole" ]]; then + if [[ ! -L "$found_mole" ]] || ! readlink "$found_mole" | grep -q "Cellar/mole"; then + manual_installs+=("$found_mole") + fi fi fi - local -a fallback_paths=( - "/usr/local/bin/mole" - "$HOME/.local/bin/mole" - "/opt/local/bin/mole" - ) + local -a fallback_paths=() + if [[ "$test_mode" == "true" ]]; then + fallback_paths=("$HOME/.local/bin/mole") + else + fallback_paths=( + "/usr/local/bin/mole" + "$HOME/.local/bin/mole" + "/opt/local/bin/mole" + ) + fi for path in "${fallback_paths[@]}"; do if [[ -f "$path" && "$path" != "$found_mole" ]]; then @@ -548,18 +562,26 @@ remove_mole() { done local found_mo - found_mo=$(command -v mo 2> /dev/null || true) - if [[ -n "$found_mo" && -f "$found_mo" ]]; then - if [[ ! -L "$found_mo" ]] || ! readlink "$found_mo" | grep -q "Cellar/mole"; then - alias_installs+=("$found_mo") + found_mo="" + if [[ "$test_mode" != "true" ]]; then + found_mo=$(command -v mo 2> /dev/null || true) + if [[ -n "$found_mo" && -f "$found_mo" ]]; then + if [[ ! -L "$found_mo" ]] || ! readlink "$found_mo" | grep -q "Cellar/mole"; then + alias_installs+=("$found_mo") + fi fi fi - local -a alias_fallback=( - "/usr/local/bin/mo" - "$HOME/.local/bin/mo" - "/opt/local/bin/mo" - ) + local -a alias_fallback=() + if [[ "$test_mode" == "true" ]]; then + alias_fallback=("$HOME/.local/bin/mo") + else + alias_fallback=( + "/usr/local/bin/mo" + "$HOME/.local/bin/mo" + "/opt/local/bin/mo" + ) + fi for alias in "${alias_fallback[@]}"; do if [[ -f "$alias" && "$alias" != "$found_mo" ]]; then diff --git a/scripts/test.sh b/scripts/test.sh index eed0352..7da5bfb 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -10,6 +10,9 @@ PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_ROOT" +# Never allow the scripted test run to trigger real sudo or Touch ID prompts. +export MOLE_TEST_NO_AUTH=1 + # shellcheck source=lib/core/file_ops.sh source "$PROJECT_ROOT/lib/core/file_ops.sh" diff --git a/tests/uninstall.bats b/tests/uninstall.bats index e6bfe27..0c55367 100644 --- a/tests/uninstall.bats +++ b/tests/uninstall.bats @@ -362,7 +362,7 @@ EOF touch "$HOME/.local/bin/mo" mkdir -p "$HOME/.config/mole" "$HOME/.cache/mole" - run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="/usr/bin:/bin" bash --noprofile --norc <<'EOF' + run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="/usr/bin:/bin" MOLE_TEST_MODE=1 bash --noprofile --norc <<'EOF' set -euo pipefail start_inline_spinner() { :; } stop_inline_spinner() { :; } @@ -410,7 +410,7 @@ EOF touch "$HOME/.local/bin/mo" mkdir -p "$HOME/.config/mole" "$HOME/.cache/mole" - run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="/usr/bin:/bin" bash --noprofile --norc <<'EOF' + run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="/usr/bin:/bin" MOLE_TEST_MODE=1 bash --noprofile --norc <<'EOF' set -euo pipefail start_inline_spinner() { :; } stop_inline_spinner() { :; } @@ -425,3 +425,35 @@ EOF [ -d "$HOME/.config/mole" ] [ -d "$HOME/.cache/mole" ] } + +@test "remove_mole test mode ignores PATH installs outside test HOME" { + mkdir -p "$HOME/.local/bin" "$HOME/.config/mole" "$HOME/.cache/mole" + touch "$HOME/.local/bin/mole" + touch "$HOME/.local/bin/mo" + + fake_global_bin="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-remove-path.XXXXXX")" + touch "$fake_global_bin/mole" + touch "$fake_global_bin/mo" + cat > "$fake_global_bin/brew" <<'EOF' +#!/bin/bash +exit 0 +EOF + chmod +x "$fake_global_bin/brew" + + run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="$fake_global_bin:/usr/bin:/bin" MOLE_TEST_MODE=1 bash --noprofile --norc <<'EOF' +set -euo pipefail +start_inline_spinner() { :; } +stop_inline_spinner() { :; } +export -f start_inline_spinner stop_inline_spinner +printf '\n' | "$PROJECT_ROOT/mole" remove --dry-run +EOF + + rm -rf "$fake_global_bin" + + [ "$status" -eq 0 ] + [[ "$output" == *"$HOME/.local/bin/mole"* ]] + [[ "$output" == *"$HOME/.local/bin/mo"* ]] + [[ "$output" != *"$fake_global_bin/mole"* ]] + [[ "$output" != *"$fake_global_bin/mo"* ]] + [[ "$output" != *"brew uninstall --force mole"* ]] +}