1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-04 12:41:46 +00:00

refactor: Consolidate and refactor test suite by removing redundant files and simplifying test execution.

This commit is contained in:
Tw93
2026-01-03 12:04:57 +08:00
parent f5a8adb97e
commit 4aef2de0fc
18 changed files with 391 additions and 546 deletions

View File

@@ -25,6 +25,7 @@ jobs:
env: env:
MOLE_PERF_BYTES_TO_HUMAN_LIMIT_MS: "6000" MOLE_PERF_BYTES_TO_HUMAN_LIMIT_MS: "6000"
MOLE_PERF_GET_FILE_SIZE_LIMIT_MS: "3000" MOLE_PERF_GET_FILE_SIZE_LIMIT_MS: "3000"
BATS_FORMATTER: pretty
run: ./scripts/test.sh run: ./scripts/test.sh
compatibility: compatibility:

View File

@@ -49,20 +49,11 @@ if command -v bats > /dev/null 2>&1 && [ -d "tests" ]; then
if [[ $# -eq 0 ]]; then if [[ $# -eq 0 ]]; then
set -- tests set -- tests
fi fi
if [[ -t 1 ]]; then if TERM="${TERM:-xterm-256color}" bats --formatter "${BATS_FORMATTER:-pretty}" "$@"; then
if bats -p "$@" | sed -e 's/^ok /OK /' -e 's/^not ok /FAIL /'; then printf "${GREEN}${ICON_SUCCESS} Unit tests passed${NC}\n"
printf "${GREEN}${ICON_SUCCESS} Unit tests passed${NC}\n"
else
printf "${RED}${ICON_ERROR} Unit tests failed${NC}\n"
((FAILED++))
fi
else else
if TERM="${TERM:-xterm-256color}" bats --tap "$@" | sed -e 's/^ok /OK /' -e 's/^not ok /FAIL /'; then printf "${RED}${ICON_ERROR} Unit tests failed${NC}\n"
printf "${GREEN}${ICON_SUCCESS} Unit tests passed${NC}\n" ((FAILED++))
else
printf "${RED}${ICON_ERROR} Unit tests failed${NC}\n"
((FAILED++))
fi
fi fi
else else
printf "${YELLOW}${ICON_WARNING} bats not installed or no tests found, skipping${NC}\n" printf "${YELLOW}${ICON_WARNING} bats not installed or no tests found, skipping${NC}\n"

View File

@@ -1,32 +0,0 @@
#!/usr/bin/env bats
setup_file() {
PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)"
export PROJECT_ROOT
}
@test "is_critical_system_component matches known system services" {
run bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/core/app_protection.sh"
is_critical_system_component "backgroundtaskmanagement" && echo "yes"
is_critical_system_component "SystemSettings" && echo "yes"
EOF
[ "$status" -eq 0 ]
[[ "${lines[0]}" == "yes" ]]
[[ "${lines[1]}" == "yes" ]]
}
@test "is_critical_system_component ignores non-system names" {
run bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/core/app_protection.sh"
if is_critical_system_component "myapp"; then
echo "bad"
else
echo "ok"
fi
EOF
[ "$status" -eq 0 ]
[[ "$output" == "ok" ]]
}

View File

@@ -88,3 +88,29 @@ EOF
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[[ "$output" == *"Skipped: No permission"* ]] [[ "$output" == *"Skipped: No permission"* ]]
} }
@test "is_critical_system_component matches known system services" {
run bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/core/app_protection.sh"
is_critical_system_component "backgroundtaskmanagement" && echo "yes"
is_critical_system_component "SystemSettings" && echo "yes"
EOF
[ "$status" -eq 0 ]
[[ "${lines[0]}" == "yes" ]]
[[ "${lines[1]}" == "yes" ]]
}
@test "is_critical_system_component ignores non-system names" {
run bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/core/app_protection.sh"
if is_critical_system_component "myapp"; then
echo "bad"
else
echo "ok"
fi
EOF
[ "$status" -eq 0 ]
[[ "$output" == "ok" ]]
}

View File

@@ -20,6 +20,30 @@ teardown_file() {
fi fi
} }
create_fake_utils() {
local dir="$1"
mkdir -p "$dir"
cat > "$dir/sudo" <<'SCRIPT'
#!/usr/bin/env bash
if [[ "$1" == "-n" || "$1" == "-v" ]]; then
exit 0
fi
exec "$@"
SCRIPT
chmod +x "$dir/sudo"
cat > "$dir/bioutil" <<'SCRIPT'
#!/usr/bin/env bash
if [[ "$1" == "-r" ]]; then
echo "Touch ID: 1"
exit 0
fi
exit 0
SCRIPT
chmod +x "$dir/bioutil"
}
setup() { setup() {
rm -rf "$HOME/.config" rm -rf "$HOME/.config"
mkdir -p "$HOME" mkdir -p "$HOME"
@@ -65,3 +89,90 @@ setup() {
skip "analyze-go binary not built" skip "analyze-go binary not built"
fi fi
} }
@test "mo clean --debug creates debug log file" {
mkdir -p "$HOME/.config/mole"
run env HOME="$HOME" TERM="xterm-256color" MOLE_TEST_MODE=1 MO_DEBUG=1 "$PROJECT_ROOT/mole" clean --dry-run
[ "$status" -eq 0 ]
MOLE_OUTPUT="$output"
DEBUG_LOG="$HOME/.config/mole/mole_debug_session.log"
[ -f "$DEBUG_LOG" ]
run grep "Mole Debug Session" "$DEBUG_LOG"
[ "$status" -eq 0 ]
[[ "$MOLE_OUTPUT" =~ "Debug session log saved to" ]]
}
@test "mo clean without debug does not show debug log path" {
mkdir -p "$HOME/.config/mole"
run env HOME="$HOME" TERM="xterm-256color" MOLE_TEST_MODE=1 MO_DEBUG=0 "$PROJECT_ROOT/mole" clean --dry-run
[ "$status" -eq 0 ]
[[ "$output" != *"Debug session log saved to"* ]]
}
@test "mo clean --debug logs system info" {
mkdir -p "$HOME/.config/mole"
run env HOME="$HOME" TERM="xterm-256color" MOLE_TEST_MODE=1 MO_DEBUG=1 "$PROJECT_ROOT/mole" clean --dry-run
[ "$status" -eq 0 ]
DEBUG_LOG="$HOME/.config/mole/mole_debug_session.log"
run grep "User:" "$DEBUG_LOG"
[ "$status" -eq 0 ]
run grep "Architecture:" "$DEBUG_LOG"
[ "$status" -eq 0 ]
}
@test "touchid status reflects pam file contents" {
pam_file="$HOME/pam_test"
cat > "$pam_file" <<'EOF'
auth sufficient pam_opendirectory.so
EOF
run env MOLE_PAM_SUDO_FILE="$pam_file" "$PROJECT_ROOT/bin/touchid.sh" status
[ "$status" -eq 0 ]
[[ "$output" == *"not configured"* ]]
cat > "$pam_file" <<'EOF'
auth sufficient pam_tid.so
EOF
run env MOLE_PAM_SUDO_FILE="$pam_file" "$PROJECT_ROOT/bin/touchid.sh" status
[ "$status" -eq 0 ]
[[ "$output" == *"enabled"* ]]
}
@test "enable_touchid inserts pam_tid line in pam file" {
pam_file="$HOME/pam_enable"
cat > "$pam_file" <<'EOF'
auth sufficient pam_opendirectory.so
EOF
fake_bin="$HOME/fake-bin"
create_fake_utils "$fake_bin"
run env PATH="$fake_bin:$PATH" MOLE_PAM_SUDO_FILE="$pam_file" "$PROJECT_ROOT/bin/touchid.sh" enable
[ "$status" -eq 0 ]
grep -q "pam_tid.so" "$pam_file"
[[ -f "${pam_file}.mole-backup" ]]
}
@test "disable_touchid removes pam_tid line" {
pam_file="$HOME/pam_disable"
cat > "$pam_file" <<'EOF'
auth sufficient pam_tid.so
auth sufficient pam_opendirectory.so
EOF
fake_bin="$HOME/fake-bin-disable"
create_fake_utils "$fake_bin"
run env PATH="$fake_bin:$PATH" MOLE_PAM_SUDO_FILE="$pam_file" "$PROJECT_ROOT/bin/touchid.sh" disable
[ "$status" -eq 0 ]
run grep "pam_tid.so" "$pam_file"
[ "$status" -ne 0 ]
}

View File

@@ -174,3 +174,30 @@ EOF
) )
[[ "$result" == *"done"* ]] [[ "$result" == *"done"* ]]
} }
@test "read_key maps j/k/h/l to navigation" {
run bash -c "export MOLE_BASE_LOADED=1; source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'j' | read_key"
[ "$output" = "DOWN" ]
run bash -c "export MOLE_BASE_LOADED=1; source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'k' | read_key"
[ "$output" = "UP" ]
run bash -c "export MOLE_BASE_LOADED=1; source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'h' | read_key"
[ "$output" = "LEFT" ]
run bash -c "export MOLE_BASE_LOADED=1; source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'l' | read_key"
[ "$output" = "RIGHT" ]
}
@test "read_key maps uppercase J/K/H/L to navigation" {
run bash -c "export MOLE_BASE_LOADED=1; source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'J' | read_key"
[ "$output" = "DOWN" ]
run bash -c "export MOLE_BASE_LOADED=1; source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'K' | read_key"
[ "$output" = "UP" ]
}
@test "read_key respects MOLE_READ_KEY_FORCE_CHAR" {
run bash -c "export MOLE_BASE_LOADED=1; export MOLE_READ_KEY_FORCE_CHAR=1; source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'j' | read_key"
[ "$output" = "CHAR:j" ]
}

View File

@@ -1,61 +0,0 @@
#!/usr/bin/env bats
setup_file() {
PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)"
export PROJECT_ROOT
ORIGINAL_HOME="${HOME:-}"
export ORIGINAL_HOME
HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-debug-logging.XXXXXX")"
export HOME
mkdir -p "$HOME"
}
teardown_file() {
rm -rf "$HOME"
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
export HOME="$ORIGINAL_HOME"
fi
}
setup() {
export TERM="xterm-256color"
rm -rf "${HOME:?}"/*
mkdir -p "$HOME/.config/mole"
}
@test "mo clean --debug creates debug log file" {
run env HOME="$HOME" MOLE_TEST_MODE=1 MO_DEBUG=1 "$PROJECT_ROOT/mole" clean --dry-run
[ "$status" -eq 0 ]
MOLE_OUTPUT="$output"
DEBUG_LOG="$HOME/.config/mole/mole_debug_session.log"
[ -f "$DEBUG_LOG" ]
run grep "Mole Debug Session" "$DEBUG_LOG"
[ "$status" -eq 0 ]
[[ "$MOLE_OUTPUT" =~ "Debug session log saved to" ]]
}
@test "mo clean without debug does not show debug log path" {
run env HOME="$HOME" MOLE_TEST_MODE=1 MO_DEBUG=0 "$PROJECT_ROOT/mole" clean --dry-run
[ "$status" -eq 0 ]
[[ "$output" != *"Debug session log saved to"* ]]
}
@test "mo clean --debug logs system info" {
run env HOME="$HOME" MOLE_TEST_MODE=1 MO_DEBUG=1 "$PROJECT_ROOT/mole" clean --dry-run
[ "$status" -eq 0 ]
DEBUG_LOG="$HOME/.config/mole/mole_debug_session.log"
run grep "User:" "$DEBUG_LOG"
[ "$status" -eq 0 ]
run grep "Architecture:" "$DEBUG_LOG"
[ "$status" -eq 0 ]
}

View File

@@ -98,3 +98,28 @@ EOF
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
[[ "$output" == *"LaunchServices repaired"* ]] [[ "$output" == *"LaunchServices repaired"* ]]
} }
@test "opt_msg uses dry-run output" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" MOLE_DRY_RUN=1 bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/core/common.sh"
source "$PROJECT_ROOT/lib/optimize/tasks.sh"
opt_msg "dry"
EOF
[ "$status" -eq 0 ]
[[ "$output" == *"dry"* ]]
}
@test "run_launchctl_unload skips in dry-run" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" MOLE_DRY_RUN=1 bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/optimize/tasks.sh"
launchctl() { echo "called"; }
export -f launchctl
run_launchctl_unload "/tmp/test.plist" false
EOF
[ "$status" -eq 0 ]
[[ "$output" != *"called"* ]]
}

View File

@@ -1,46 +0,0 @@
#!/usr/bin/env bats
setup_file() {
PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)"
export PROJECT_ROOT
ORIGINAL_HOME="${HOME:-}"
export ORIGINAL_HOME
HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-optimize-helpers.XXXXXX")"
export HOME
mkdir -p "$HOME"
}
teardown_file() {
rm -rf "$HOME"
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
export HOME="$ORIGINAL_HOME"
fi
}
@test "opt_msg uses dry-run output" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" MOLE_DRY_RUN=1 bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/core/common.sh"
source "$PROJECT_ROOT/lib/optimize/tasks.sh"
opt_msg "dry"
EOF
[ "$status" -eq 0 ]
[[ "$output" == *"dry"* ]]
}
@test "run_launchctl_unload skips in dry-run" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" MOLE_DRY_RUN=1 bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/optimize/tasks.sh"
launchctl() { echo "called"; }
export -f launchctl
run_launchctl_unload "/tmp/test.plist" false
EOF
[ "$status" -eq 0 ]
[[ "$output" != *"called"* ]]
}

View File

@@ -1,84 +0,0 @@
#!/usr/bin/env bats
setup_file() {
PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)"
export PROJECT_ROOT
ORIGINAL_HOME="${HOME:-}"
export ORIGINAL_HOME
HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-purge-extra.XXXXXX")"
export HOME
mkdir -p "$HOME"
}
teardown_file() {
rm -rf "$HOME"
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
export HOME="$ORIGINAL_HOME"
fi
}
@test "is_project_container detects project indicators" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/clean/project.sh"
mkdir -p "$HOME/Workspace2/project"
touch "$HOME/Workspace2/project/package.json"
if is_project_container "$HOME/Workspace2" 2; then
echo "yes"
fi
EOF
[ "$status" -eq 0 ]
[[ "$output" == *"yes"* ]]
}
@test "discover_project_dirs includes detected containers" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/clean/project.sh"
mkdir -p "$HOME/CustomProjects/app"
touch "$HOME/CustomProjects/app/go.mod"
discover_project_dirs | grep -q "$HOME/CustomProjects"
EOF
[ "$status" -eq 0 ]
}
@test "save_discovered_paths writes config with tilde" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/clean/project.sh"
save_discovered_paths "$HOME/Projects"
grep -q "^~/" "$HOME/.config/mole/purge_paths"
EOF
[ "$status" -eq 0 ]
}
@test "scan_purge_targets finds artifacts via find path" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" MOLE_PURGE_MIN_DEPTH=1 MOLE_PURGE_MAX_DEPTH=2 bash --noprofile --norc <<'EOF'
set -euo pipefail
PATH="/usr/bin:/bin"
source "$PROJECT_ROOT/lib/clean/project.sh"
mkdir -p "$HOME/dev/app/node_modules"
scan_purge_targets "$HOME/dev" "$HOME/results.txt"
grep -q "node_modules" "$HOME/results.txt"
EOF
[ "$status" -eq 0 ]
}
@test "select_purge_categories returns failure on empty input" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/clean/project.sh"
if select_purge_categories; then
exit 1
fi
EOF
[ "$status" -eq 0 ]
}

View File

@@ -204,6 +204,69 @@ setup() {
[[ "$result" == "NOT_PROTECTED" ]] [[ "$result" == "NOT_PROTECTED" ]]
} }
@test "is_project_container detects project indicators" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/clean/project.sh"
mkdir -p "$HOME/Workspace2/project"
touch "$HOME/Workspace2/project/package.json"
if is_project_container "$HOME/Workspace2" 2; then
echo "yes"
fi
EOF
[ "$status" -eq 0 ]
[[ "$output" == *"yes"* ]]
}
@test "discover_project_dirs includes detected containers" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/clean/project.sh"
mkdir -p "$HOME/CustomProjects/app"
touch "$HOME/CustomProjects/app/go.mod"
discover_project_dirs | grep -q "$HOME/CustomProjects"
EOF
[ "$status" -eq 0 ]
}
@test "save_discovered_paths writes config with tilde" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/clean/project.sh"
save_discovered_paths "$HOME/Projects"
grep -q "^~/" "$HOME/.config/mole/purge_paths"
EOF
[ "$status" -eq 0 ]
}
@test "scan_purge_targets finds artifacts via find path" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" MOLE_PURGE_MIN_DEPTH=1 MOLE_PURGE_MAX_DEPTH=2 bash --noprofile --norc <<'EOF'
set -euo pipefail
PATH="/usr/bin:/bin"
source "$PROJECT_ROOT/lib/clean/project.sh"
mkdir -p "$HOME/dev/app/node_modules"
scan_purge_targets "$HOME/dev" "$HOME/results.txt"
grep -q "node_modules" "$HOME/results.txt"
EOF
[ "$status" -eq 0 ]
}
@test "select_purge_categories returns failure on empty input" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/clean/project.sh"
if select_purge_categories; then
exit 1
fi
EOF
[ "$status" -eq 0 ]
}
@test "is_protected_vendor_dir: protects Go vendor" { @test "is_protected_vendor_dir: protects Go vendor" {
mkdir -p "$HOME/www/go-app/vendor" mkdir -p "$HOME/www/go-app/vendor"
touch "$HOME/www/go-app/go.mod" touch "$HOME/www/go-app/go.mod"

View File

@@ -1,95 +0,0 @@
#!/usr/bin/env bats
setup_file() {
PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)"
export PROJECT_ROOT
ORIGINAL_HOME="${HOME:-}"
export ORIGINAL_HOME
HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-touchid.XXXXXX")"
export HOME
mkdir -p "$HOME"
}
teardown_file() {
rm -rf "$HOME"
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
export HOME="$ORIGINAL_HOME"
fi
}
create_fake_utils() {
local dir="$1"
mkdir -p "$dir"
cat > "$dir/sudo" <<'SCRIPT'
#!/usr/bin/env bash
if [[ "$1" == "-n" || "$1" == "-v" ]]; then
exit 0
fi
exec "$@"
SCRIPT
chmod +x "$dir/sudo"
cat > "$dir/bioutil" <<'SCRIPT'
#!/usr/bin/env bash
if [[ "$1" == "-r" ]]; then
echo "Touch ID: 1"
exit 0
fi
exit 0
SCRIPT
chmod +x "$dir/bioutil"
}
@test "touchid status reflects pam file contents" {
pam_file="$HOME/pam_test"
cat > "$pam_file" <<'EOF'
auth sufficient pam_opendirectory.so
EOF
run env MOLE_PAM_SUDO_FILE="$pam_file" "$PROJECT_ROOT/bin/touchid.sh" status
[ "$status" -eq 0 ]
[[ "$output" == *"not configured"* ]]
cat > "$pam_file" <<'EOF'
auth sufficient pam_tid.so
EOF
run env MOLE_PAM_SUDO_FILE="$pam_file" "$PROJECT_ROOT/bin/touchid.sh" status
[ "$status" -eq 0 ]
[[ "$output" == *"enabled"* ]]
}
@test "enable_touchid inserts pam_tid line in pam file" {
pam_file="$HOME/pam_enable"
cat > "$pam_file" <<'EOF'
auth sufficient pam_opendirectory.so
EOF
fake_bin="$HOME/fake-bin"
create_fake_utils "$fake_bin"
run env PATH="$fake_bin:$PATH" MOLE_PAM_SUDO_FILE="$pam_file" "$PROJECT_ROOT/bin/touchid.sh" enable
[ "$status" -eq 0 ]
grep -q "pam_tid.so" "$pam_file"
[[ -f "${pam_file}.mole-backup" ]]
}
@test "disable_touchid removes pam_tid line" {
pam_file="$HOME/pam_disable"
cat > "$pam_file" <<'EOF'
auth sufficient pam_tid.so
auth sufficient pam_opendirectory.so
EOF
fake_bin="$HOME/fake-bin-disable"
create_fake_utils "$fake_bin"
run env PATH="$fake_bin:$PATH" MOLE_PAM_SUDO_FILE="$pam_file" "$PROJECT_ROOT/bin/touchid.sh" disable
[ "$status" -eq 0 ]
run grep "pam_tid.so" "$pam_file"
[ "$status" -ne 0 ]
}

View File

@@ -1,39 +0,0 @@
#!/usr/bin/env bats
setup_file() {
PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)"
export PROJECT_ROOT
}
setup() {
export MOLE_BASE_LOADED=1 # mock base loaded
}
@test "read_key maps j/k/h/l to navigation" {
source "$PROJECT_ROOT/lib/core/ui.sh"
run bash -c "source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'j' | read_key"
[ "$output" = "DOWN" ]
run bash -c "source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'k' | read_key"
[ "$output" = "UP" ]
run bash -c "source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'h' | read_key"
[ "$output" = "LEFT" ]
run bash -c "source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'l' | read_key"
[ "$output" = "RIGHT" ]
}
@test "read_key maps uppercase J/K/H/L to navigation" {
run bash -c "source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'J' | read_key"
[ "$output" = "DOWN" ]
run bash -c "source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'K' | read_key"
[ "$output" = "UP" ]
}
@test "read_key respects MOLE_READ_KEY_FORCE_CHAR" {
run bash -c "export MOLE_READ_KEY_FORCE_CHAR=1; source '$PROJECT_ROOT/lib/core/ui.sh'; echo -n 'j' | read_key"
[ "$output" = "CHAR:j" ]
}

View File

@@ -216,4 +216,25 @@ result=$(decode_file_list "$encoded_data" "TestApp")
EOF EOF
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
} }
@test "remove_mole deletes manual binaries and caches" {
mkdir -p "$HOME/.local/bin"
touch "$HOME/.local/bin/mole"
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'
set -euo pipefail
start_inline_spinner() { :; }
stop_inline_spinner() { :; }
export -f start_inline_spinner stop_inline_spinner
printf '\n' | "$PROJECT_ROOT/mole" remove
EOF
[ "$status" -eq 0 ]
[ ! -f "$HOME/.local/bin/mole" ]
[ ! -f "$HOME/.local/bin/mo" ]
[ ! -d "$HOME/.config/mole" ]
[ ! -d "$HOME/.cache/mole" ]
}

View File

@@ -4,6 +4,9 @@ setup_file() {
PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)" PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)"
export PROJECT_ROOT export PROJECT_ROOT
CURRENT_VERSION="$(grep '^VERSION=' "$PROJECT_ROOT/mole" | head -1 | sed 's/VERSION=\"\\(.*\\)\"/\\1/')"
export CURRENT_VERSION
ORIGINAL_HOME="${HOME:-}" ORIGINAL_HOME="${HOME:-}"
export ORIGINAL_HOME export ORIGINAL_HOME
@@ -161,3 +164,72 @@ EOF
[[ "$output" == *"MOLE_CACHE_RESET"* ]] [[ "$output" == *"MOLE_CACHE_RESET"* ]]
[[ "$output" == *"All updates completed"* ]] [[ "$output" == *"All updates completed"* ]]
} }
@test "update_via_homebrew reports already on latest version" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc << 'EOF'
set -euo pipefail
MOLE_TEST_BREW_UPDATE_OUTPUT="Updated 0 formulae"
MOLE_TEST_BREW_UPGRADE_OUTPUT="Warning: mole 1.7.9 already installed"
MOLE_TEST_BREW_LIST_OUTPUT="mole 1.7.9"
start_inline_spinner() { :; }
stop_inline_spinner() { :; }
brew() {
case "$1" in
update) echo "$MOLE_TEST_BREW_UPDATE_OUTPUT";;
upgrade) echo "$MOLE_TEST_BREW_UPGRADE_OUTPUT";;
list) if [[ "$2" == "--versions" ]]; then echo "$MOLE_TEST_BREW_LIST_OUTPUT"; fi ;;
esac
}
export -f brew start_inline_spinner stop_inline_spinner
source "$PROJECT_ROOT/lib/core/common.sh"
update_via_homebrew "1.7.9"
EOF
[ "$status" -eq 0 ]
[[ "$output" == *"Already on latest version"* ]]
}
@test "update_mole skips download when already latest" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" CURRENT_VERSION="$CURRENT_VERSION" PATH="$HOME/fake-bin:/usr/bin:/bin" TERM="dumb" bash --noprofile --norc << 'EOF'
set -euo pipefail
curl() {
local out=""
local url=""
while [[ $# -gt 0 ]]; do
case "$1" in
-o)
out="$2"
shift 2
;;
http*://*)
url="$1"
shift
;;
*)
shift
;;
esac
done
if [[ -n "$out" ]]; then
echo "Installer executed" > "$out"
return 0
fi
if [[ "$url" == *"api.github.com"* ]]; then
echo "{\"tag_name\":\"$CURRENT_VERSION\"}"
else
echo "VERSION=\"$CURRENT_VERSION\""
fi
}
export -f curl
brew() { exit 1; }
export -f brew
"$PROJECT_ROOT/mole" update
EOF
[ "$status" -eq 0 ]
[[ "$output" == *"Already on latest version"* ]]
}

View File

@@ -1,114 +0,0 @@
#!/usr/bin/env bats
setup_file() {
PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)"
export PROJECT_ROOT
CURRENT_VERSION="$(grep '^VERSION=' "$PROJECT_ROOT/mole" | head -1 | sed 's/VERSION="\(.*\)"/\1/')"
export CURRENT_VERSION
ORIGINAL_HOME="${HOME:-}"
export ORIGINAL_HOME
HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-update-home.XXXXXX")"
export HOME
}
teardown_file() {
rm -rf "$HOME"
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
export HOME="$ORIGINAL_HOME"
fi
}
setup() {
export TERM="dumb"
rm -rf "${HOME:?}"/*
mkdir -p "$HOME"
}
@test "update_via_homebrew reports already on latest version" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc << 'EOF'
set -euo pipefail
MOLE_TEST_BREW_UPDATE_OUTPUT="Updated 0 formulae"
MOLE_TEST_BREW_UPGRADE_OUTPUT="Warning: mole 1.7.9 already installed"
MOLE_TEST_BREW_LIST_OUTPUT="mole 1.7.9"
start_inline_spinner() { :; }
stop_inline_spinner() { :; }
brew() {
case "$1" in
update) echo "$MOLE_TEST_BREW_UPDATE_OUTPUT";;
upgrade) echo "$MOLE_TEST_BREW_UPGRADE_OUTPUT";;
list) if [[ "$2" == "--versions" ]]; then echo "$MOLE_TEST_BREW_LIST_OUTPUT"; fi ;;
esac
}
export -f brew start_inline_spinner stop_inline_spinner
source "$PROJECT_ROOT/lib/core/common.sh"
update_via_homebrew "1.7.9"
EOF
[ "$status" -eq 0 ]
[[ "$output" == *"Already on latest version"* ]]
}
@test "update_mole skips download when already latest" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" PATH="$HOME/fake-bin:/usr/bin:/bin" TERM="dumb" bash --noprofile --norc << 'EOF'
set -euo pipefail
mkdir -p "$HOME/fake-bin"
cat > "$HOME/fake-bin/curl" <<'SCRIPT'
#!/usr/bin/env bash
out=""
while [[ $# -gt 0 ]]; do
case "$1" in
-o)
out="$2"
shift 2
;;
*)
shift
;;
esac
done
if [[ -n "$out" ]]; then
cat <<'INSTALLER' > "$out"
#!/usr/bin/env bash
echo "Installer executed"
INSTALLER
else
echo "VERSION=\"$CURRENT_VERSION\""
fi
SCRIPT
chmod +x "$HOME/fake-bin/curl"
cat > "$HOME/fake-bin/brew" <<'SCRIPT'
#!/usr/bin/env bash
exit 1
SCRIPT
chmod +x "$HOME/fake-bin/brew"
"$PROJECT_ROOT/mole" update
EOF
[ "$status" -eq 0 ]
[[ "$output" == *"Already on latest version"* ]]
}
@test "remove_mole deletes manual binaries and caches" {
mkdir -p "$HOME/.local/bin"
touch "$HOME/.local/bin/mole"
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'
set -euo pipefail
start_inline_spinner() { :; }
stop_inline_spinner() { :; }
export -f start_inline_spinner stop_inline_spinner
printf '\n' | "$PROJECT_ROOT/mole" remove
EOF
[ "$status" -eq 0 ]
[ ! -f "$HOME/.local/bin/mole" ]
[ ! -f "$HOME/.local/bin/mo" ]
[ ! -d "$HOME/.config/mole" ]
[ ! -d "$HOME/.cache/mole" ]
}

View File

@@ -118,3 +118,43 @@ EOF
[[ "$output" == *"Empty Library folders"* ]] [[ "$output" == *"Empty Library folders"* ]]
[[ "$output" != *"Empty Library files"* ]] [[ "$output" != *"Empty Library files"* ]]
} }
@test "clean_browsers calls expected cache paths" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/core/common.sh"
source "$PROJECT_ROOT/lib/clean/user.sh"
safe_clean() { echo "$2"; }
clean_browsers
EOF
[ "$status" -eq 0 ]
[[ "$output" == *"Safari cache"* ]]
[[ "$output" == *"Firefox cache"* ]]
}
@test "clean_application_support_logs skips when no access" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/core/common.sh"
source "$PROJECT_ROOT/lib/clean/user.sh"
note_activity() { :; }
clean_application_support_logs
EOF
[ "$status" -eq 0 ]
[[ "$output" == *"Skipped: No permission"* ]]
}
@test "clean_apple_silicon_caches exits when not M-series" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" IS_M_SERIES=false bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/core/common.sh"
source "$PROJECT_ROOT/lib/clean/user.sh"
safe_clean() { echo "$2"; }
clean_apple_silicon_caches
EOF
[ "$status" -eq 0 ]
[[ -z "$output" ]]
}

View File

@@ -1,61 +0,0 @@
#!/usr/bin/env bats
setup_file() {
PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)"
export PROJECT_ROOT
ORIGINAL_HOME="${HOME:-}"
export ORIGINAL_HOME
HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-user-clean.XXXXXX")"
export HOME
mkdir -p "$HOME"
}
teardown_file() {
rm -rf "$HOME"
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
export HOME="$ORIGINAL_HOME"
fi
}
@test "clean_browsers calls expected cache paths" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/core/common.sh"
source "$PROJECT_ROOT/lib/clean/user.sh"
safe_clean() { echo "$2"; }
clean_browsers
EOF
[ "$status" -eq 0 ]
[[ "$output" == *"Safari cache"* ]]
[[ "$output" == *"Firefox cache"* ]]
}
@test "clean_application_support_logs skips when no access" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/core/common.sh"
source "$PROJECT_ROOT/lib/clean/user.sh"
note_activity() { :; }
clean_application_support_logs
EOF
[ "$status" -eq 0 ]
[[ "$output" == *"Skipped: No permission"* ]]
}
@test "clean_apple_silicon_caches exits when not M-series" {
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" IS_M_SERIES=false bash --noprofile --norc <<'EOF'
set -euo pipefail
source "$PROJECT_ROOT/lib/core/common.sh"
source "$PROJECT_ROOT/lib/clean/user.sh"
safe_clean() { echo "$2"; }
clean_apple_silicon_caches
EOF
[ "$status" -eq 0 ]
[[ -z "$output" ]]
}