1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-04 13:16:47 +00:00

Reconstruct the clean split logic

This commit is contained in:
Tw93
2025-11-28 22:39:11 +09:00
parent 58da36afdd
commit a47f7d0d32
12 changed files with 2138 additions and 1069 deletions

200
tests/clean_caches.bats Normal file
View File

@@ -0,0 +1,200 @@
#!/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-caches.XXXXXX")"
export HOME
mkdir -p "$HOME"
mkdir -p "$HOME/.cache/mole"
mkdir -p "$HOME/Library/Caches"
mkdir -p "$HOME/Library/Logs"
}
teardown_file() {
rm -rf "$HOME"
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
export HOME="$ORIGINAL_HOME"
fi
}
setup() {
source "$PROJECT_ROOT/lib/common.sh"
source "$PROJECT_ROOT/lib/clean_caches.sh"
# Clean permission flag for each test
rm -f "$HOME/.cache/mole/permissions_granted"
}
# Test check_tcc_permissions in non-interactive mode
@test "check_tcc_permissions skips in non-interactive mode" {
# Redirect stdin to simulate non-TTY
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/clean_caches.sh'; check_tcc_permissions" < /dev/null
[ "$status" -eq 0 ]
# Should not create permission flag in non-interactive mode
[[ ! -f "$HOME/.cache/mole/permissions_granted" ]]
}
# Test check_tcc_permissions with existing permission flag
@test "check_tcc_permissions skips when permissions already granted" {
# Create permission flag
mkdir -p "$HOME/.cache/mole"
touch "$HOME/.cache/mole/permissions_granted"
# Even in TTY mode, should skip if flag exists
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/clean_caches.sh'; [[ -t 1 ]] || true; check_tcc_permissions"
[ "$status" -eq 0 ]
}
# Test check_tcc_permissions directory checks
@test "check_tcc_permissions validates protected directories" {
# The function checks these directories exist:
# - ~/Library/Caches
# - ~/Library/Logs
# - ~/Library/Application Support
# - ~/Library/Containers
# - ~/.cache
# Ensure test environment has these directories
[[ -d "$HOME/Library/Caches" ]]
[[ -d "$HOME/Library/Logs" ]]
[[ -d "$HOME/.cache/mole" ]]
# Function should handle missing directories gracefully
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/clean_caches.sh'; check_tcc_permissions < /dev/null"
[ "$status" -eq 0 ]
}
# Test clean_service_worker_cache with non-existent path
@test "clean_service_worker_cache returns early when path doesn't exist" {
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/clean_caches.sh'; clean_service_worker_cache 'TestBrowser' '/nonexistent/path'"
[ "$status" -eq 0 ]
}
# Test clean_service_worker_cache with empty directory
@test "clean_service_worker_cache handles empty cache directory" {
local test_cache="$HOME/test_sw_cache"
mkdir -p "$test_cache"
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/clean_caches.sh'; clean_service_worker_cache 'TestBrowser' '$test_cache'"
[ "$status" -eq 0 ]
rm -rf "$test_cache"
}
# Test clean_service_worker_cache domain protection
@test "clean_service_worker_cache protects specified domains" {
local test_cache="$HOME/test_sw_cache"
mkdir -p "$test_cache/abc123_https_capcut.com_0"
mkdir -p "$test_cache/def456_https_example.com_0"
# Mock PROTECTED_SW_DOMAINS
export PROTECTED_SW_DOMAINS=("capcut.com" "photopea.com")
# Dry run to check protection logic
run bash -c "
export DRY_RUN=true
export PROTECTED_SW_DOMAINS=(capcut.com photopea.com)
source '$PROJECT_ROOT/lib/common.sh'
source '$PROJECT_ROOT/lib/clean_caches.sh'
clean_service_worker_cache 'TestBrowser' '$test_cache'
"
[ "$status" -eq 0 ]
# Protected domain directory should still exist
[[ -d "$test_cache/abc123_https_capcut.com_0" ]]
rm -rf "$test_cache"
}
# Test clean_project_caches function
@test "clean_project_caches completes without errors" {
# Create test project structures
mkdir -p "$HOME/projects/test-app/.next/cache"
mkdir -p "$HOME/projects/python-app/__pycache__"
# Create some dummy cache files
touch "$HOME/projects/test-app/.next/cache/test.cache"
touch "$HOME/projects/python-app/__pycache__/module.pyc"
run bash -c "
export DRY_RUN=true
source '$PROJECT_ROOT/lib/common.sh'
source '$PROJECT_ROOT/lib/clean_caches.sh'
clean_project_caches
"
[ "$status" -eq 0 ]
rm -rf "$HOME/projects"
}
# Test clean_project_caches timeout protection
@test "clean_project_caches handles timeout gracefully" {
# Create a test directory structure
mkdir -p "$HOME/test-project/.next"
# Mock find to simulate slow operation
function find() {
sleep 2 # Simulate slow find
echo "$HOME/test-project/.next"
}
export -f find
# Should complete within reasonable time even with slow find
run timeout 15 bash -c "
source '$PROJECT_ROOT/lib/common.sh'
source '$PROJECT_ROOT/lib/clean_caches.sh'
clean_project_caches
"
# Either succeeds or times out gracefully (both acceptable)
[ "$status" -eq 0 ] || [ "$status" -eq 124 ]
rm -rf "$HOME/test-project"
}
# Test clean_project_caches exclusions
@test "clean_project_caches excludes Library and Trash directories" {
# These directories should be excluded from scan
mkdir -p "$HOME/Library/.next/cache"
mkdir -p "$HOME/.Trash/.next/cache"
mkdir -p "$HOME/projects/.next/cache"
# Only non-excluded directories should be scanned
# We can't easily test this without mocking, but we can verify no crashes
run bash -c "
export DRY_RUN=true
source '$PROJECT_ROOT/lib/common.sh'
source '$PROJECT_ROOT/lib/clean_caches.sh'
clean_project_caches
"
[ "$status" -eq 0 ]
rm -rf "$HOME/projects"
}
# Test permission flag creation
@test "check_tcc_permissions creates permission flag after check" {
# Remove flag if exists
rm -f "$HOME/.cache/mole/permissions_granted"
# Simulate accessible Library/Caches (skip TCC dialog)
function ls() {
return 0 # Simulate accessible
}
export -f ls
run bash -c "
source '$PROJECT_ROOT/lib/common.sh'
source '$PROJECT_ROOT/lib/clean_caches.sh'
check_tcc_permissions < /dev/null
"
[ "$status" -eq 0 ]
# Permission flag should be created
[[ -f "$HOME/.cache/mole/permissions_granted" ]]
}

123
tests/sudo_manager.bats Normal file
View File

@@ -0,0 +1,123 @@
#!/usr/bin/env bats
setup_file() {
PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)"
export PROJECT_ROOT
}
setup() {
# Source common.sh first (required by sudo_manager)
source "$PROJECT_ROOT/lib/common.sh"
source "$PROJECT_ROOT/lib/sudo_manager.sh"
}
# Test sudo session detection
@test "has_sudo_session returns 1 when no sudo session" {
# Most test environments don't have active sudo
# This test verifies the function handles no-sudo gracefully
run has_sudo_session
# Either no sudo (status 1) or sudo available (status 0)
# Both are valid - we just check it doesn't crash
[ "$status" -eq 0 ] || [ "$status" -eq 1 ]
}
# Test sudo keepalive lifecycle
@test "sudo keepalive functions don't crash" {
# Test that keepalive functions can be called without errors
# We can't actually test sudo without prompting, but we can test structure
# Mock sudo to avoid actual auth
function sudo() {
return 1 # Simulate no sudo available
}
export -f sudo
# These should not crash even without real sudo
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/sudo_manager.sh'; has_sudo_session"
[ "$status" -eq 1 ] # Expected: no sudo session
}
# Test keepalive PID management
@test "_start_sudo_keepalive returns a PID" {
# Mock sudo to simulate successful session
function sudo() {
case "$1" in
-n) return 0 ;; # Simulate valid sudo session
-v) return 0 ;; # Refresh succeeds
*) return 1 ;;
esac
}
export -f sudo
# Start keepalive (will run in background)
local pid
pid=$(bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/sudo_manager.sh'; _start_sudo_keepalive")
# Should return a PID (number)
[[ "$pid" =~ ^[0-9]+$ ]]
# Clean up background process
kill "$pid" 2>/dev/null || true
wait "$pid" 2>/dev/null || true
}
# Test _stop_sudo_keepalive
@test "_stop_sudo_keepalive handles invalid PID gracefully" {
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/sudo_manager.sh'; _stop_sudo_keepalive ''"
[ "$status" -eq 0 ]
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/sudo_manager.sh'; _stop_sudo_keepalive '99999'"
[ "$status" -eq 0 ]
}
# Test request_sudo function structure
@test "request_sudo accepts prompt parameter" {
# Mock request_sudo_access to avoid actual prompting
function request_sudo_access() {
return 1 # Simulate denied
}
export -f request_sudo_access
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/sudo_manager.sh'; request_sudo 'Test prompt'"
[ "$status" -eq 1 ] # Expected: denied
}
# Test start_sudo_session integration
@test "start_sudo_session handles success and failure paths" {
# Mock request_sudo to simulate denied access
function request_sudo() {
return 1
}
export -f request_sudo
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/sudo_manager.sh'; start_sudo_session 'Test'"
[ "$status" -eq 1 ]
# Mock request_sudo to simulate granted access
function request_sudo() {
return 0
}
function _start_sudo_keepalive() {
echo "12345"
}
export -f request_sudo
export -f _start_sudo_keepalive
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/sudo_manager.sh'; start_sudo_session 'Test'"
[ "$status" -eq 0 ]
}
# Test stop_sudo_session cleanup
@test "stop_sudo_session cleans up keepalive process" {
# Set a fake PID
export MOLE_SUDO_KEEPALIVE_PID="99999"
run bash -c "export MOLE_SUDO_KEEPALIVE_PID=99999; source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/sudo_manager.sh'; stop_sudo_session"
[ "$status" -eq 0 ]
}
# Test global state management
@test "sudo manager initializes global state correctly" {
result=$(bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/sudo_manager.sh'; echo \$MOLE_SUDO_ESTABLISHED")
[[ "$result" == "false" ]] || [[ -z "$result" ]]
}

197
tests/update_manager.bats Normal file
View File

@@ -0,0 +1,197 @@
#!/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-manager.XXXXXX")"
export HOME
mkdir -p "$HOME"
}
teardown_file() {
rm -rf "$HOME"
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
export HOME="$ORIGINAL_HOME"
fi
}
setup() {
source "$PROJECT_ROOT/lib/common.sh"
source "$PROJECT_ROOT/lib/update_manager.sh"
}
# Test brew_has_outdated function
@test "brew_has_outdated returns 1 when brew not installed" {
function brew() {
return 127 # Command not found
}
export -f brew
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; brew_has_outdated"
[ "$status" -eq 1 ]
}
@test "brew_has_outdated checks formula by default" {
# Mock brew to simulate outdated formulas
function brew() {
if [[ "$1" == "outdated" && "$2" != "--cask" ]]; then
echo "package1"
echo "package2"
return 0
fi
return 1
}
export -f brew
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; brew_has_outdated"
[ "$status" -eq 0 ]
}
@test "brew_has_outdated checks casks when specified" {
# Mock brew to simulate outdated casks
function brew() {
if [[ "$1" == "outdated" && "$2" == "--cask" ]]; then
echo "app1"
return 0
fi
return 1
}
export -f brew
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; brew_has_outdated cask"
[ "$status" -eq 0 ]
}
# Test format_brew_update_label function
@test "format_brew_update_label returns empty when no updates" {
result=$(BREW_OUTDATED_COUNT=0 bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; format_brew_update_label")
[[ -z "$result" ]]
}
@test "format_brew_update_label formats with formula and cask counts" {
result=$(BREW_OUTDATED_COUNT=5 BREW_FORMULA_OUTDATED_COUNT=3 BREW_CASK_OUTDATED_COUNT=2 bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; format_brew_update_label")
[[ "$result" =~ "3 formula" ]]
[[ "$result" =~ "2 cask" ]]
}
@test "format_brew_update_label shows total when breakdown unavailable" {
result=$(BREW_OUTDATED_COUNT=5 bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; format_brew_update_label")
[[ "$result" =~ "5 updates" ]]
}
# Test ask_for_updates function
@test "ask_for_updates returns 1 when no updates available" {
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; ask_for_updates < /dev/null"
[ "$status" -eq 1 ]
}
@test "ask_for_updates detects Homebrew updates" {
# Mock environment with Homebrew updates
export BREW_OUTDATED_COUNT=5
export BREW_FORMULA_OUTDATED_COUNT=3
export BREW_CASK_OUTDATED_COUNT=2
# Use input redirection to simulate ESC (cancel)
run bash -c "printf '\x1b' | source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; ask_for_updates"
# Should show updates and ask for confirmation
[ "$status" -eq 1 ] # ESC cancels
}
@test "ask_for_updates detects App Store updates" {
export APPSTORE_UPDATE_COUNT=3
run bash -c "printf '\x1b' | source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; ask_for_updates"
[ "$status" -eq 1 ] # ESC cancels
}
@test "ask_for_updates detects macOS updates" {
export MACOS_UPDATE_AVAILABLE=true
run bash -c "printf '\x1b' | source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; ask_for_updates"
[ "$status" -eq 1 ] # ESC cancels
}
@test "ask_for_updates detects Mole updates" {
export MOLE_UPDATE_AVAILABLE=true
run bash -c "printf '\x1b' | source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; ask_for_updates"
[ "$status" -eq 1 ] # ESC cancels
}
# Test perform_updates function structure
@test "perform_updates handles brew formula updates" {
# Mock brew to avoid actual updates
function brew() {
case "$1" in
outdated)
if [[ "${2:-}" == "--cask" ]]; then
return 1 # No cask updates
else
echo "test-package"
return 0 # Has formula updates
fi
;;
upgrade)
echo "Upgrading test-package..."
return 0
;;
*)
return 1
;;
esac
}
export -f brew
export BREW_OUTDATED_COUNT=1
export BREW_FORMULA_OUTDATED_COUNT=1
export BREW_CASK_OUTDATED_COUNT=0
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; perform_updates"
[ "$status" -eq 0 ]
}
# Test update_homebrew function
@test "update_homebrew returns early when no updates" {
function brew() {
case "$1" in
outdated)
return 1 # No updates
;;
*)
return 1
;;
esac
}
export -f brew
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; update_homebrew"
[ "$status" -eq 0 ]
}
@test "update_homebrew handles network failures gracefully" {
function brew() {
case "$1" in
outdated)
if [[ "${2:-}" == "--quiet" ]]; then
echo "test-package"
return 0
fi
;;
upgrade)
return 1 # Simulate failure
;;
*)
return 1
;;
esac
}
export -f brew
run bash -c "source '$PROJECT_ROOT/lib/common.sh'; source '$PROJECT_ROOT/lib/update_manager.sh'; update_homebrew"
# Should handle failure without crashing
[ "$status" -eq 0 ] || [ "$status" -eq 1 ]
}