mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 17:24:45 +00:00
compatibility and cleanup improvements - Fix field count mismatch and base64 BSD/GNU compatibility - Add sensitive data detection and macOS defaults cleanup - Improve error handling and add compatibility tests
238 lines
6.8 KiB
Bash
238 lines
6.8 KiB
Bash
#!/usr/bin/env bats
|
|
|
|
setup_file() {
|
|
PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)"
|
|
export PROJECT_ROOT
|
|
|
|
ORIGINAL_HOME="${BATS_TMPDIR:-}" # Use BATS_TMPDIR as original HOME if set by bats
|
|
if [[ -z "$ORIGINAL_HOME" ]]; then
|
|
ORIGINAL_HOME="${HOME:-}"
|
|
fi
|
|
export ORIGINAL_HOME
|
|
|
|
HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-uninstall-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"
|
|
}
|
|
|
|
create_app_artifacts() {
|
|
mkdir -p "$HOME/Applications/TestApp.app"
|
|
mkdir -p "$HOME/Library/Application Support/TestApp"
|
|
mkdir -p "$HOME/Library/Caches/TestApp"
|
|
mkdir -p "$HOME/Library/Containers/com.example.TestApp"
|
|
mkdir -p "$HOME/Library/Preferences"
|
|
touch "$HOME/Library/Preferences/com.example.TestApp.plist"
|
|
mkdir -p "$HOME/Library/Preferences/ByHost"
|
|
touch "$HOME/Library/Preferences/ByHost/com.example.TestApp.ABC123.plist"
|
|
mkdir -p "$HOME/Library/Saved Application State/com.example.TestApp.savedState"
|
|
}
|
|
|
|
@test "find_app_files discovers user-level leftovers" {
|
|
create_app_artifacts
|
|
|
|
result="$(
|
|
HOME="$HOME" bash --noprofile --norc << 'EOF'
|
|
set -euo pipefail
|
|
source "$PROJECT_ROOT/lib/core/common.sh"
|
|
find_app_files "com.example.TestApp" "TestApp"
|
|
EOF
|
|
)"
|
|
|
|
[[ "$result" == *"Application Support/TestApp"* ]]
|
|
[[ "$result" == *"Caches/TestApp"* ]]
|
|
[[ "$result" == *"Preferences/com.example.TestApp.plist"* ]]
|
|
[[ "$result" == *"Saved Application State/com.example.TestApp.savedState"* ]]
|
|
[[ "$result" == *"Containers/com.example.TestApp"* ]]
|
|
}
|
|
|
|
@test "calculate_total_size returns aggregate kilobytes" {
|
|
mkdir -p "$HOME/sized"
|
|
dd if=/dev/zero of="$HOME/sized/file1" bs=1024 count=1 > /dev/null 2>&1
|
|
dd if=/dev/zero of="$HOME/sized/file2" bs=1024 count=2 > /dev/null 2>&1
|
|
|
|
result="$(
|
|
HOME="$HOME" bash --noprofile --norc << 'EOF'
|
|
set -euo pipefail
|
|
source "$PROJECT_ROOT/lib/core/common.sh"
|
|
files="$(printf '%s
|
|
%s
|
|
' "$HOME/sized/file1" "$HOME/sized/file2")"
|
|
calculate_total_size "$files"
|
|
EOF
|
|
)"
|
|
|
|
# Result should be >=3 KB (some filesystems allocate slightly more)
|
|
[ "$result" -ge 3 ]
|
|
}
|
|
|
|
@test "batch_uninstall_applications removes selected app data" {
|
|
create_app_artifacts
|
|
|
|
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/uninstall/batch.sh"
|
|
|
|
# Test stubs
|
|
request_sudo_access() { return 0; }
|
|
start_inline_spinner() { :; }
|
|
stop_inline_spinner() { :; }
|
|
enter_alt_screen() { :; }
|
|
leave_alt_screen() { :; }
|
|
hide_cursor() { :; }
|
|
show_cursor() { :; }
|
|
remove_apps_from_dock() { :; }
|
|
pgrep() { return 1; }
|
|
pkill() { return 0; }
|
|
sudo() { return 0; } # Mock sudo command
|
|
|
|
app_bundle="$HOME/Applications/TestApp.app"
|
|
mkdir -p "$app_bundle" # Ensure this is created in the temp HOME
|
|
|
|
related="$(find_app_files "com.example.TestApp" "TestApp")"
|
|
encoded_related=$(printf '%s' "$related" | base64 | tr -d '\n')
|
|
|
|
selected_apps=()
|
|
selected_apps+=("0|$app_bundle|TestApp|com.example.TestApp|0|Never")
|
|
files_cleaned=0
|
|
total_items=0
|
|
total_size_cleaned=0
|
|
|
|
# Use the actual bash function directly, don't pipe printf as that complicates stdin
|
|
batch_uninstall_applications
|
|
|
|
# Verify cleanup
|
|
[[ ! -d "$app_bundle" ]] || exit 1
|
|
[[ ! -d "$HOME/Library/Application Support/TestApp" ]] || exit 1
|
|
[[ ! -d "$HOME/Library/Caches/TestApp" ]] || exit 1
|
|
[[ ! -f "$HOME/Library/Preferences/com.example.TestApp.plist" ]] || exit 1
|
|
EOF
|
|
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
@test "safe_remove can remove a simple directory" {
|
|
mkdir -p "$HOME/test_dir"
|
|
touch "$HOME/test_dir/file.txt"
|
|
|
|
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc << 'EOF'
|
|
set -euo pipefail
|
|
source "$PROJECT_ROOT/lib/core/common.sh"
|
|
|
|
safe_remove "$HOME/test_dir"
|
|
[[ ! -d "$HOME/test_dir" ]] || exit 1
|
|
EOF
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
|
|
@test "decode_file_list validates base64 encoding" {
|
|
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/uninstall/batch.sh"
|
|
|
|
# Valid base64 encoded path list
|
|
valid_data=$(printf '/path/one
|
|
/path/two' | base64)
|
|
result=$(decode_file_list "$valid_data" "TestApp")
|
|
[[ -n "$result" ]] || exit 1
|
|
EOF
|
|
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
@test "decode_file_list rejects invalid base64" {
|
|
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/uninstall/batch.sh"
|
|
|
|
# Invalid base64 - function should return empty and fail
|
|
if result=$(decode_file_list "not-valid-base64!!!" "TestApp" 2>/dev/null); then
|
|
# If decode succeeded, result should be empty
|
|
[[ -z "$result" ]]
|
|
else
|
|
# Function returned error, which is expected
|
|
true
|
|
fi
|
|
EOF
|
|
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
@test "decode_file_list handles empty input" {
|
|
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/uninstall/batch.sh"
|
|
|
|
# Empty base64
|
|
empty_data=$(printf '' | base64)
|
|
result=$(decode_file_list "$empty_data" "TestApp" 2>/dev/null) || true
|
|
# Empty result is acceptable
|
|
[[ -z "$result" ]]
|
|
EOF
|
|
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
@test "decode_file_list rejects non-absolute 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/uninstall/batch.sh"
|
|
|
|
# Relative path - function should reject it
|
|
bad_data=$(printf 'relative/path' | base64)
|
|
if result=$(decode_file_list "$bad_data" "TestApp" 2>/dev/null); then
|
|
# Should return empty string
|
|
[[ -z "$result" ]]
|
|
else
|
|
# Or return error code
|
|
true
|
|
fi
|
|
EOF
|
|
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
@test "decode_file_list handles both BSD and GNU base64 formats" {
|
|
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/uninstall/batch.sh"
|
|
|
|
# Test data: absolute paths
|
|
test_paths="/path/to/file1
|
|
/path/to/file2"
|
|
|
|
# Encode with whatever base64 is available (no flags)
|
|
encoded_data=$(printf '%s' "$test_paths" | base64 | tr -d '\n')
|
|
|
|
# decode_file_list should handle it regardless of BSD (-D) or GNU (-d)
|
|
result=$(decode_file_list "$encoded_data" "TestApp")
|
|
|
|
# Verify result contains expected paths
|
|
[[ "$result" == *"/path/to/file1"* ]] || exit 1
|
|
[[ "$result" == *"/path/to/file2"* ]] || exit 1
|
|
|
|
# Verify the function tries both -D and -d by checking it doesn't fail
|
|
# This tests the fallback logic in decode_file_list
|
|
[[ -n "$result" ]] || exit 1
|
|
EOF
|
|
|
|
[ "$status" -eq 0 ]
|
|
} |