mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 13:16:47 +00:00
Complete automated testing
This commit is contained in:
71
tests/analyze.bats
Normal file
71
tests/analyze.bats
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/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-analyze-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 "scan_directories lists largest folders first" {
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/bin/analyze.sh"
|
||||
|
||||
root="$HOME/analyze-root"
|
||||
mkdir -p "$root/Small" "$root/Large"
|
||||
printf 'tiny' > "$root/Small/file.txt"
|
||||
dd if=/dev/zero of="$root/Large/big.dat" bs=1024 count=200 >/dev/null 2>&1
|
||||
|
||||
output_file="$HOME/directories.txt"
|
||||
scan_directories "$root" "$output_file" 1
|
||||
|
||||
head -n1 "$output_file"
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Large"* ]]
|
||||
}
|
||||
|
||||
@test "aggregate_by_directory sums child sizes per parent" {
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/bin/analyze.sh"
|
||||
|
||||
root="$HOME/group"
|
||||
mkdir -p "$root/a" "$root/b"
|
||||
|
||||
input_file="$HOME/files.txt"
|
||||
cat > "$input_file" <<LIST
|
||||
1024|$root/a/file1
|
||||
2048|$root/a/file2
|
||||
512|$root/b/data.bin
|
||||
LIST
|
||||
|
||||
output_file="$HOME/aggregated.txt"
|
||||
aggregate_by_directory "$input_file" "$output_file"
|
||||
|
||||
cat "$output_file"
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"3072|$HOME/group/a/"* ]]
|
||||
[[ "$output" == *"512|$HOME/group/b/"* ]]
|
||||
}
|
||||
60
tests/clean.bats
Normal file
60
tests/clean.bats
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/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-clean-home.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:?}"/*
|
||||
rm -rf "$HOME/Library" "$HOME/.config"
|
||||
mkdir -p "$HOME/Library/Caches" "$HOME/.config/mole"
|
||||
}
|
||||
|
||||
@test "mo clean --dry-run skips system cleanup in non-interactive mode" {
|
||||
run env HOME="$HOME" "$PROJECT_ROOT/mole" clean --dry-run
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Dry Run Mode"* ]]
|
||||
[[ "$output" != *"Deep system-level cleanup"* ]]
|
||||
}
|
||||
|
||||
@test "mo clean --dry-run reports user cache without deleting it" {
|
||||
mkdir -p "$HOME/Library/Caches/TestApp"
|
||||
echo "cache data" > "$HOME/Library/Caches/TestApp/cache.tmp"
|
||||
|
||||
run env HOME="$HOME" "$PROJECT_ROOT/mole" clean --dry-run
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"User app cache"* ]]
|
||||
[[ "$output" == *"Potential space"* ]]
|
||||
[ -f "$HOME/Library/Caches/TestApp/cache.tmp" ]
|
||||
}
|
||||
|
||||
@test "mo clean honors whitelist entries" {
|
||||
mkdir -p "$HOME/Library/Caches/WhitelistedApp"
|
||||
echo "keep me" > "$HOME/Library/Caches/WhitelistedApp/data.tmp"
|
||||
|
||||
cat > "$HOME/.config/mole/whitelist" <<EOF
|
||||
$HOME/Library/Caches/WhitelistedApp*
|
||||
EOF
|
||||
|
||||
run env HOME="$HOME" "$PROJECT_ROOT/mole" clean --dry-run
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Protected: 1"* ]]
|
||||
[ -f "$HOME/Library/Caches/WhitelistedApp/data.tmp" ]
|
||||
}
|
||||
80
tests/cli.bats
Normal file
80
tests/cli.bats
Normal file
@@ -0,0 +1,80 @@
|
||||
#!/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-cli-home.XXXXXX")"
|
||||
export HOME
|
||||
|
||||
mkdir -p "$HOME"
|
||||
}
|
||||
|
||||
teardown_file() {
|
||||
rm -rf "$HOME"
|
||||
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
|
||||
export HOME="$ORIGINAL_HOME"
|
||||
fi
|
||||
}
|
||||
|
||||
setup() {
|
||||
rm -rf "$HOME/.config"
|
||||
mkdir -p "$HOME"
|
||||
}
|
||||
|
||||
@test "mole --help prints command overview" {
|
||||
run env HOME="$HOME" "$PROJECT_ROOT/mole" --help
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"mo clean"* ]]
|
||||
[[ "$output" == *"mo analyze"* ]]
|
||||
}
|
||||
|
||||
@test "mole --version reports script version" {
|
||||
expected_version="$(grep '^VERSION=' "$PROJECT_ROOT/mole" | head -1 | sed 's/VERSION=\"\(.*\)\"/\1/')"
|
||||
run env HOME="$HOME" "$PROJECT_ROOT/mole" --version
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"$expected_version"* ]]
|
||||
}
|
||||
|
||||
@test "mole unknown command returns error" {
|
||||
run env HOME="$HOME" "$PROJECT_ROOT/mole" unknown-command
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"Unknown command: unknown-command"* ]]
|
||||
}
|
||||
|
||||
@test "clean.sh --help shows usage details" {
|
||||
run env HOME="$HOME" "$PROJECT_ROOT/bin/clean.sh" --help
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Mole - Deeper system cleanup"* ]]
|
||||
[[ "$output" == *"--dry-run"* ]]
|
||||
}
|
||||
|
||||
@test "uninstall.sh --help highlights controls" {
|
||||
run env HOME="$HOME" "$PROJECT_ROOT/bin/uninstall.sh" --help
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Usage: mole uninstall"* ]]
|
||||
[[ "$output" == *"Keyboard Controls"* ]]
|
||||
}
|
||||
|
||||
@test "analyze.sh --help outlines explorer features" {
|
||||
run env HOME="$HOME" "$PROJECT_ROOT/bin/analyze.sh" --help
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Interactive disk space explorer"* ]]
|
||||
[[ "$output" == *"mole analyze"* ]]
|
||||
}
|
||||
|
||||
@test "touchid --help describes configuration options" {
|
||||
run env HOME="$HOME" "$PROJECT_ROOT/mole" touchid --help
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Touch ID"* ]]
|
||||
[[ "$output" == *"mo touchid enable"* ]]
|
||||
}
|
||||
|
||||
@test "touchid status reports current configuration" {
|
||||
run env HOME="$HOME" "$PROJECT_ROOT/mole" touchid status
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Touch ID"* ]]
|
||||
}
|
||||
140
tests/common.bats
Normal file
140
tests/common.bats
Normal file
@@ -0,0 +1,140 @@
|
||||
#!/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-home.XXXXXX")"
|
||||
export HOME
|
||||
|
||||
mkdir -p "$HOME"
|
||||
}
|
||||
|
||||
teardown_file() {
|
||||
rm -rf "$HOME"
|
||||
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
|
||||
export HOME="$ORIGINAL_HOME"
|
||||
fi
|
||||
}
|
||||
|
||||
setup() {
|
||||
rm -rf "$HOME/.config"
|
||||
mkdir -p "$HOME"
|
||||
}
|
||||
|
||||
teardown() {
|
||||
unset MO_SPINNER_CHARS || true
|
||||
}
|
||||
|
||||
@test "mo_spinner_chars returns default sequence when unset" {
|
||||
result="$(HOME="$HOME" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/common.sh'; mo_spinner_chars")"
|
||||
[ "$result" = "|/-\\" ]
|
||||
}
|
||||
|
||||
@test "mo_spinner_chars respects MO_SPINNER_CHARS override" {
|
||||
export MO_SPINNER_CHARS="abcd"
|
||||
result="$(HOME="$HOME" MO_SPINNER_CHARS="$MO_SPINNER_CHARS" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/common.sh'; mo_spinner_chars")"
|
||||
[ "$result" = "abcd" ]
|
||||
}
|
||||
|
||||
@test "detect_architecture maps current CPU to friendly label" {
|
||||
expected="Intel"
|
||||
if [[ "$(uname -m)" == "arm64" ]]; then
|
||||
expected="Apple Silicon"
|
||||
fi
|
||||
result="$(HOME="$HOME" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/common.sh'; detect_architecture")"
|
||||
[ "$result" = "$expected" ]
|
||||
}
|
||||
|
||||
@test "get_free_space returns a non-empty value" {
|
||||
result="$(HOME="$HOME" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/common.sh'; get_free_space")"
|
||||
[[ -n "$result" ]]
|
||||
}
|
||||
|
||||
@test "log_info prints message and appends to log file" {
|
||||
local message="Informational message from test"
|
||||
local stdout_output
|
||||
stdout_output="$(HOME="$HOME" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/common.sh'; log_info '$message'")"
|
||||
[[ "$stdout_output" == *"$message"* ]]
|
||||
|
||||
local log_file="$HOME/.config/mole/mole.log"
|
||||
[[ -f "$log_file" ]]
|
||||
grep -q "INFO: $message" "$log_file"
|
||||
}
|
||||
|
||||
@test "log_error writes to stderr and log file" {
|
||||
local message="Something went wrong"
|
||||
local stderr_file="$HOME/log_error_stderr.txt"
|
||||
|
||||
HOME="$HOME" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/common.sh'; log_error '$message' 1>/dev/null 2>'$stderr_file'"
|
||||
|
||||
[[ -s "$stderr_file" ]]
|
||||
grep -q "$message" "$stderr_file"
|
||||
|
||||
local log_file="$HOME/.config/mole/mole.log"
|
||||
[[ -f "$log_file" ]]
|
||||
grep -q "ERROR: $message" "$log_file"
|
||||
}
|
||||
|
||||
@test "bytes_to_human converts byte counts into readable units" {
|
||||
output="$(HOME="$HOME" bash --noprofile --norc <<'EOF'
|
||||
source "$PROJECT_ROOT/lib/common.sh"
|
||||
bytes_to_human 512
|
||||
bytes_to_human 2048
|
||||
bytes_to_human $((5 * 1024 * 1024))
|
||||
bytes_to_human $((3 * 1024 * 1024 * 1024))
|
||||
EOF
|
||||
)"
|
||||
|
||||
bytes_lines=()
|
||||
while IFS= read -r line; do
|
||||
bytes_lines+=("$line")
|
||||
done <<< "$output"
|
||||
|
||||
[ "${bytes_lines[0]}" = "512B" ]
|
||||
[ "${bytes_lines[1]}" = "2KB" ]
|
||||
[ "${bytes_lines[2]}" = "5.0MB" ]
|
||||
[ "${bytes_lines[3]}" = "3.00GB" ]
|
||||
}
|
||||
|
||||
@test "create_temp_file and create_temp_dir are tracked and cleaned" {
|
||||
HOME="$HOME" bash --noprofile --norc <<'EOF'
|
||||
source "$PROJECT_ROOT/lib/common.sh"
|
||||
create_temp_file > "$HOME/temp_file_path.txt"
|
||||
create_temp_dir > "$HOME/temp_dir_path.txt"
|
||||
cleanup_temp_files
|
||||
EOF
|
||||
|
||||
file_path="$(cat "$HOME/temp_file_path.txt")"
|
||||
dir_path="$(cat "$HOME/temp_dir_path.txt")"
|
||||
[ ! -e "$file_path" ]
|
||||
[ ! -e "$dir_path" ]
|
||||
rm -f "$HOME/temp_file_path.txt" "$HOME/temp_dir_path.txt"
|
||||
}
|
||||
|
||||
@test "parallel_execute runs worker across all items" {
|
||||
output_file="$HOME/parallel_output.txt"
|
||||
HOME="$HOME" bash --noprofile --norc <<'EOF'
|
||||
source "$PROJECT_ROOT/lib/common.sh"
|
||||
worker() {
|
||||
echo "$1" >> "$HOME/parallel_output.txt"
|
||||
}
|
||||
parallel_execute 2 worker "first" "second" "third"
|
||||
EOF
|
||||
|
||||
sort "$output_file" > "$output_file.sorted"
|
||||
results=()
|
||||
while IFS= read -r line; do
|
||||
results+=("$line")
|
||||
done < "$output_file.sorted"
|
||||
|
||||
[ "${#results[@]}" -eq 3 ]
|
||||
joined=" ${results[*]} "
|
||||
[[ "$joined" == *" first "* ]]
|
||||
[[ "$joined" == *" second "* ]]
|
||||
[[ "$joined" == *" third "* ]]
|
||||
rm -f "$output_file" "$output_file.sorted"
|
||||
}
|
||||
22
tests/helpers/uninstall_stubs.sh
Normal file
22
tests/helpers/uninstall_stubs.sh
Normal file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC2329
|
||||
# Helper stub definitions for uninstall tests
|
||||
|
||||
setup_uninstall_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; }
|
||||
|
||||
export -f request_sudo_access start_inline_spinner stop_inline_spinner \
|
||||
enter_alt_screen leave_alt_screen hide_cursor show_cursor \
|
||||
remove_apps_from_dock pgrep pkill sudo || true
|
||||
}
|
||||
29
tests/run.sh
Executable file
29
tests/run.sh
Executable file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
if ! command -v bats >/dev/null 2>&1; then
|
||||
cat <<'EOF' >&2
|
||||
bats is required to run Mole's test suite.
|
||||
Install via Homebrew with 'brew install bats-core' or via npm with 'npm install -g bats'.
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
if [[ -z "${TERM:-}" ]]; then
|
||||
export TERM="xterm-256color"
|
||||
fi
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
set -- tests
|
||||
fi
|
||||
|
||||
if [[ -t 1 ]]; then
|
||||
bats -p "$@"
|
||||
else
|
||||
TERM="${TERM:-xterm-256color}" bats --tap "$@"
|
||||
fi
|
||||
104
tests/uninstall.bats
Normal file
104
tests/uninstall.bats
Normal file
@@ -0,0 +1,104 @@
|
||||
#!/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-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/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/common.sh"
|
||||
files="$(printf '%s\n%s\n' "$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/common.sh"
|
||||
source "$PROJECT_ROOT/lib/batch_uninstall.sh"
|
||||
source "$PROJECT_ROOT/tests/helpers/uninstall_stubs.sh"
|
||||
setup_uninstall_stubs
|
||||
|
||||
app_bundle="$HOME/Applications/TestApp.app"
|
||||
mkdir -p "$app_bundle"
|
||||
|
||||
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
|
||||
|
||||
printf '\n' | batch_uninstall_applications >/dev/null
|
||||
|
||||
[[ ! -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 ]
|
||||
}
|
||||
111
tests/update_remove.bats
Normal file
111
tests/update_remove.bats
Normal file
@@ -0,0 +1,111 @@
|
||||
#!/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-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/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="1.7.9"'
|
||||
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" ]
|
||||
}
|
||||
99
tests/whitelist.bats
Normal file
99
tests/whitelist.bats
Normal file
@@ -0,0 +1,99 @@
|
||||
#!/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-whitelist-home.XXXXXX")"
|
||||
export HOME
|
||||
|
||||
mkdir -p "$HOME"
|
||||
}
|
||||
|
||||
teardown_file() {
|
||||
rm -rf "$HOME"
|
||||
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
|
||||
export HOME="$ORIGINAL_HOME"
|
||||
fi
|
||||
}
|
||||
|
||||
setup() {
|
||||
rm -rf "$HOME/.config"
|
||||
mkdir -p "$HOME"
|
||||
WHITELIST_PATH="$HOME/.config/mole/whitelist"
|
||||
}
|
||||
|
||||
@test "patterns_equivalent treats paths with tilde expansion as equal" {
|
||||
local status
|
||||
if HOME="$HOME" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/whitelist_manager.sh'; patterns_equivalent '~/.cache/test' \"\$HOME/.cache/test\""; then
|
||||
status=0
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "patterns_equivalent distinguishes different paths" {
|
||||
local status
|
||||
if HOME="$HOME" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/whitelist_manager.sh'; patterns_equivalent '~/.cache/test' \"\$HOME/.cache/other\""; then
|
||||
status=0
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "save_whitelist_patterns keeps unique entries and preserves header" {
|
||||
HOME="$HOME" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/whitelist_manager.sh'; save_whitelist_patterns \"\$HOME/.cache/foo\" \"\$HOME/.cache/foo\" \"\$HOME/.cache/bar\""
|
||||
|
||||
[[ -f "$WHITELIST_PATH" ]]
|
||||
|
||||
lines=()
|
||||
while IFS= read -r line; do
|
||||
lines+=("$line")
|
||||
done < "$WHITELIST_PATH"
|
||||
# Header is at least two lines (comments), plus two unique patterns
|
||||
[ "${#lines[@]}" -ge 4 ]
|
||||
# Ensure duplicate was not written twice
|
||||
occurrences=$(grep -c "$HOME/.cache/foo" "$WHITELIST_PATH")
|
||||
[ "$occurrences" -eq 1 ]
|
||||
}
|
||||
|
||||
@test "load_whitelist falls back to defaults when config missing" {
|
||||
rm -f "$WHITELIST_PATH"
|
||||
HOME="$HOME" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/whitelist_manager.sh'; rm -f \"\$HOME/.config/mole/whitelist\"; load_whitelist; printf '%s\n' \"\${CURRENT_WHITELIST_PATTERNS[@]}\"" > "$HOME/current_whitelist.txt"
|
||||
HOME="$HOME" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/whitelist_manager.sh'; printf '%s\n' \"\${DEFAULT_WHITELIST_PATTERNS[@]}\"" > "$HOME/default_whitelist.txt"
|
||||
|
||||
current=()
|
||||
while IFS= read -r line; do
|
||||
current+=("$line")
|
||||
done < "$HOME/current_whitelist.txt"
|
||||
|
||||
defaults=()
|
||||
while IFS= read -r line; do
|
||||
defaults+=("$line")
|
||||
done < "$HOME/default_whitelist.txt"
|
||||
|
||||
[ "${#current[@]}" -eq "${#defaults[@]}" ]
|
||||
[ "${current[0]}" = "${defaults[0]/\$HOME/$HOME}" ]
|
||||
}
|
||||
|
||||
@test "is_whitelisted matches saved patterns exactly" {
|
||||
local status
|
||||
if HOME="$HOME" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/whitelist_manager.sh'; save_whitelist_patterns \"\$HOME/.cache/unique-pattern\"; load_whitelist; is_whitelisted \"\$HOME/.cache/unique-pattern\""; then
|
||||
status=0
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
if HOME="$HOME" bash --noprofile --norc -c "source '$PROJECT_ROOT/lib/whitelist_manager.sh'; save_whitelist_patterns \"\$HOME/.cache/unique-pattern\"; load_whitelist; is_whitelisted \"\$HOME/.cache/other-pattern\""; then
|
||||
status=0
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
Reference in New Issue
Block a user