mirror of
https://github.com/tw93/Mole.git
synced 2026-02-04 12:41:46 +00:00
feat: Add browser version cleanup tests, enhance shell completion with new tests, and introduce core command definitions.
This commit is contained in:
@@ -51,6 +51,7 @@ mo status # Live system health dashboard
|
||||
mo purge # Clean project build artifacts
|
||||
|
||||
mo touchid # Configure Touch ID for sudo
|
||||
mo completion # Setup shell tab completion
|
||||
mo update # Update Mole
|
||||
mo remove # Remove Mole from system
|
||||
mo --help # Show help
|
||||
@@ -70,6 +71,7 @@ mo purge --paths # Configure project scan directories
|
||||
- **Safety**: Built with strict protections. See our [Security Audit](SECURITY_AUDIT.md). Preview changes with `mo clean --dry-run`.
|
||||
- **Whitelist**: Manage protected paths with `mo clean --whitelist`.
|
||||
- **Touch ID**: Enable Touch ID for sudo commands by running `mo touchid`.
|
||||
- **Shell Completion**: Enable tab completion by running `mo completion` (auto-detect and install).
|
||||
- **Navigation**: Supports standard arrow keys and Vim bindings (`h/j/k/l`).
|
||||
- **Debug**: View detailed logs by appending the `--debug` flag (e.g., `mo clean --debug`).
|
||||
|
||||
|
||||
@@ -1,20 +1,178 @@
|
||||
#!/bin/bash
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
|
||||
source "$ROOT_DIR/lib/core/common.sh"
|
||||
source "$ROOT_DIR/lib/core/commands.sh"
|
||||
|
||||
command_names=()
|
||||
for entry in "${MOLE_COMMANDS[@]}"; do
|
||||
command_names+=("${entry%%:*}")
|
||||
done
|
||||
command_words="${command_names[*]}"
|
||||
|
||||
emit_zsh_subcommands() {
|
||||
for entry in "${MOLE_COMMANDS[@]}"; do
|
||||
printf " '%s:%s'\n" "${entry%%:*}" "${entry#*:}"
|
||||
done
|
||||
}
|
||||
|
||||
emit_fish_completions() {
|
||||
local cmd="$1"
|
||||
for entry in "${MOLE_COMMANDS[@]}"; do
|
||||
local name="${entry%%:*}"
|
||||
local desc="${entry#*:}"
|
||||
printf 'complete -c %s -n "__fish_mole_no_subcommand" -a %s -d "%s"\n' "$cmd" "$name" "$desc"
|
||||
done
|
||||
|
||||
printf '\n'
|
||||
printf 'complete -c %s -n "not __fish_mole_no_subcommand" -a bash -d "generate bash completion" -n "__fish_see_subcommand_path completion"\n' "$cmd"
|
||||
printf 'complete -c %s -n "not __fish_mole_no_subcommand" -a zsh -d "generate zsh completion" -n "__fish_see_subcommand_path completion"\n' "$cmd"
|
||||
printf 'complete -c %s -n "not __fish_mole_no_subcommand" -a fish -d "generate fish completion" -n "__fish_see_subcommand_path completion"\n' "$cmd"
|
||||
}
|
||||
|
||||
# Auto-install mode when run without arguments
|
||||
if [[ $# -eq 0 ]]; then
|
||||
# Detect current shell
|
||||
current_shell="${SHELL##*/}"
|
||||
if [[ -z "$current_shell" ]]; then
|
||||
current_shell="$(ps -p "$PPID" -o comm= 2>/dev/null | awk '{print $1}')"
|
||||
fi
|
||||
|
||||
completion_name=""
|
||||
if command -v mole > /dev/null 2>&1; then
|
||||
completion_name="mole"
|
||||
elif command -v mo > /dev/null 2>&1; then
|
||||
completion_name="mo"
|
||||
fi
|
||||
|
||||
case "$current_shell" in
|
||||
bash)
|
||||
config_file="${HOME}/.bashrc"
|
||||
[[ -f "${HOME}/.bash_profile" ]] && config_file="${HOME}/.bash_profile"
|
||||
completion_line='if output="$('"$completion_name"' completion bash 2>/dev/null)"; then eval "$output"; fi'
|
||||
;;
|
||||
zsh)
|
||||
config_file="${HOME}/.zshrc"
|
||||
completion_line='if output="$('"$completion_name"' completion zsh 2>/dev/null)"; then eval "$output"; fi'
|
||||
;;
|
||||
fish)
|
||||
config_file="${HOME}/.config/fish/config.fish"
|
||||
completion_line='set -l output ('"$completion_name"' completion fish 2>/dev/null); and echo "$output" | source'
|
||||
;;
|
||||
*)
|
||||
log_error "Unsupported shell: $current_shell"
|
||||
echo " mole completion <bash|zsh|fish>"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -z "$completion_name" ]]; then
|
||||
if [[ -f "$config_file" ]] && grep -Eq "(^# Mole shell completion$|(mole|mo)[[:space:]]+completion)" "$config_file" 2>/dev/null; then
|
||||
original_mode=""
|
||||
original_mode="$(stat -f '%Mp%Lp' "$config_file" 2>/dev/null || true)"
|
||||
temp_file="$(mktemp)"
|
||||
grep -Ev "(^# Mole shell completion$|(mole|mo)[[:space:]]+completion)" "$config_file" > "$temp_file" || true
|
||||
mv "$temp_file" "$config_file"
|
||||
if [[ -n "$original_mode" ]]; then
|
||||
chmod "$original_mode" "$config_file" 2> /dev/null || true
|
||||
fi
|
||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Removed stale completion entries from $config_file"
|
||||
echo ""
|
||||
fi
|
||||
log_error "mole not found in PATH - install Mole before enabling completion"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if already installed and normalize to latest line
|
||||
if [[ -f "$config_file" ]] && grep -Eq "(mole|mo)[[:space:]]+completion" "$config_file" 2>/dev/null; then
|
||||
original_mode=""
|
||||
original_mode="$(stat -f '%Mp%Lp' "$config_file" 2>/dev/null || true)"
|
||||
temp_file="$(mktemp)"
|
||||
grep -Ev "(^# Mole shell completion$|(mole|mo)[[:space:]]+completion)" "$config_file" > "$temp_file" || true
|
||||
mv "$temp_file" "$config_file"
|
||||
if [[ -n "$original_mode" ]]; then
|
||||
chmod "$original_mode" "$config_file" 2> /dev/null || true
|
||||
fi
|
||||
echo "" >> "$config_file"
|
||||
echo "# Mole shell completion" >> "$config_file"
|
||||
echo "$completion_line" >> "$config_file"
|
||||
echo ""
|
||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Shell completion updated in $config_file"
|
||||
echo ""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Prompt user for installation
|
||||
echo ""
|
||||
echo -e "${GRAY}Will add to ${config_file}:${NC}"
|
||||
echo " $completion_line"
|
||||
echo ""
|
||||
echo -ne "${PURPLE}${ICON_ARROW}${NC} Enable completion for ${GREEN}${current_shell}${NC}? ${GRAY}Enter confirm / Q cancel${NC}: "
|
||||
IFS= read -r -s -n1 key || key=""
|
||||
drain_pending_input
|
||||
echo ""
|
||||
|
||||
case "$key" in
|
||||
$'\e' | [Qq] | [Nn])
|
||||
echo -e "${YELLOW}Cancelled${NC}"
|
||||
exit 0
|
||||
;;
|
||||
"" | $'\n' | $'\r' | [Yy])
|
||||
;;
|
||||
*)
|
||||
log_error "Invalid key"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
# Create config file if it doesn't exist
|
||||
if [[ ! -f "$config_file" ]]; then
|
||||
mkdir -p "$(dirname "$config_file")"
|
||||
touch "$config_file"
|
||||
fi
|
||||
|
||||
# Remove previous Mole completion lines to avoid duplicates
|
||||
if [[ -f "$config_file" ]]; then
|
||||
original_mode=""
|
||||
original_mode="$(stat -f '%Mp%Lp' "$config_file" 2>/dev/null || true)"
|
||||
temp_file="$(mktemp)"
|
||||
grep -Ev "(^# Mole shell completion$|(mole|mo)[[:space:]]+completion)" "$config_file" > "$temp_file" || true
|
||||
mv "$temp_file" "$config_file"
|
||||
if [[ -n "$original_mode" ]]; then
|
||||
chmod "$original_mode" "$config_file" 2> /dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Add completion line
|
||||
echo "" >> "$config_file"
|
||||
echo "# Mole shell completion" >> "$config_file"
|
||||
echo "$completion_line" >> "$config_file"
|
||||
|
||||
echo -e "${GREEN}${ICON_SUCCESS}${NC} Completion added to $config_file"
|
||||
echo ""
|
||||
echo ""
|
||||
echo -e "${GRAY}To activate now:${NC}"
|
||||
echo -e " ${GREEN}source $config_file${NC}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
bash)
|
||||
cat << 'EOF'
|
||||
cat << EOF
|
||||
_mole_completions()
|
||||
{
|
||||
local cur_word prev_word
|
||||
cur_word="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev_word="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
cur_word="\${COMP_WORDS[\$COMP_CWORD]}"
|
||||
prev_word="\${COMP_WORDS[\$COMP_CWORD-1]}"
|
||||
|
||||
if [ "$COMP_CWORD" -eq 1 ]; then
|
||||
COMPREPLY=( $(compgen -W "optimize clean uninstall analyze status purge touchid update remove help version completion" -- "$cur_word") )
|
||||
if [ "\$COMP_CWORD" -eq 1 ]; then
|
||||
COMPREPLY=( \$(compgen -W "$command_words" -- "\$cur_word") )
|
||||
else
|
||||
case "$prev_word" in
|
||||
case "\$prev_word" in
|
||||
completion)
|
||||
COMPREPLY=( $(compgen -W "bash zsh fish" -- "$cur_word") )
|
||||
COMPREPLY=( \$(compgen -W "bash zsh fish" -- "\$cur_word") )
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=()
|
||||
@@ -23,70 +181,63 @@ _mole_completions()
|
||||
fi
|
||||
}
|
||||
|
||||
complete -F _mole_completions mole
|
||||
complete -F _mole_completions mole mo
|
||||
EOF
|
||||
;;
|
||||
zsh)
|
||||
cat << 'EOF'
|
||||
#compdef mole
|
||||
|
||||
_mole() {
|
||||
local -a subcommands
|
||||
subcommands=(
|
||||
'optimize:Free up disk space'
|
||||
'clean:Remove apps completely'
|
||||
'uninstall:Check and maintain system'
|
||||
'analyze:Explore disk usage'
|
||||
'status:Monitor system health'
|
||||
'purge:Remove old project artifacts'
|
||||
'touchid:Configure Touch ID for sudo'
|
||||
'update:Update to latest version'
|
||||
'remove:Remove Mole from system'
|
||||
'help:Show help'
|
||||
'version:Show version'
|
||||
'completion:Generate shell completions'
|
||||
)
|
||||
_describe 'subcommand' subcommands
|
||||
}
|
||||
|
||||
_mole
|
||||
EOF
|
||||
printf '#compdef mole mo\n\n'
|
||||
printf '_mole() {\n'
|
||||
printf ' local -a subcommands\n'
|
||||
printf ' subcommands=(\n'
|
||||
emit_zsh_subcommands
|
||||
printf ' )\n'
|
||||
printf " _describe 'subcommand' subcommands\n"
|
||||
printf '}\n\n'
|
||||
;;
|
||||
fish)
|
||||
cat << 'EOF'
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a optimize -d "Free up disk space"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a clean -d "Remove apps completely"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a uninstall -d "Check and maintain system"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a analyze -d "Explore disk usage"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a status -d "Monitor system health"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a purge -d "Remove old project artifacts"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a touchid -d "Configure Touch ID for sudo"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a update -d "Update to latest version"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a remove -d "Remove Mole from system"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a help -d "Show help"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a version -d "Show version"
|
||||
complete -c mole -n "__fish_mole_no_subcommand" -a completion -d "Generate shell completions"
|
||||
|
||||
complete -c mole -n "not __fish_mole_no_subcommand" -a bash -d "generate bash completion" -n "__fish_see_subcommand_path completion"
|
||||
complete -c mole -n "not __fish_mole_no_subcommand" -a zsh -d "generate zsh completion" -n "__fish_see_subcommand_path completion"
|
||||
complete -c mole -n "not __fish_mole_no_subcommand" -a fish -d "generate fish completion" -n "__fish_see_subcommand_path completion"
|
||||
|
||||
function __fish_mole_no_subcommand
|
||||
for i in (commandline -opc)
|
||||
if contains -- $i optimize clean uninstall analyze status purge touchid update remove help version completion
|
||||
return 1
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function __fish_see_subcommand_path
|
||||
string match -q -- "completion" (commandline -opc)[1]
|
||||
end
|
||||
EOF
|
||||
printf '# Completions for mole\n'
|
||||
emit_fish_completions mole
|
||||
printf '\n# Completions for mo (alias)\n'
|
||||
emit_fish_completions mo
|
||||
printf '\nfunction __fish_mole_no_subcommand\n'
|
||||
printf ' for i in (commandline -opc)\n'
|
||||
printf ' if contains -- $i %s\n' "$command_words"
|
||||
printf ' return 1\n'
|
||||
printf ' end\n'
|
||||
printf ' end\n'
|
||||
printf ' return 0\n'
|
||||
printf 'end\n\n'
|
||||
printf 'function __fish_see_subcommand_path\n'
|
||||
printf ' string match -q -- "completion" (commandline -opc)[1]\n'
|
||||
printf 'end\n'
|
||||
;;
|
||||
*)
|
||||
echo "Usage: mole completion [bash|zsh|fish]"
|
||||
cat << 'EOF'
|
||||
Usage: mole completion [bash|zsh|fish]
|
||||
|
||||
Setup shell tab completion for mole and mo commands.
|
||||
|
||||
Auto-install:
|
||||
mole completion # Auto-detect shell and install
|
||||
|
||||
Manual install:
|
||||
mole completion bash # Generate bash completion script
|
||||
mole completion zsh # Generate zsh completion script
|
||||
mole completion fish # Generate fish completion script
|
||||
|
||||
Examples:
|
||||
# Auto-install (recommended)
|
||||
mole completion
|
||||
|
||||
# Manual install - Bash
|
||||
eval "$(mole completion bash)"
|
||||
|
||||
# Manual install - Zsh
|
||||
eval "$(mole completion zsh)"
|
||||
|
||||
# Manual install - Fish
|
||||
mole completion fish | source
|
||||
EOF
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
17
lib/core/commands.sh
Normal file
17
lib/core/commands.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Shared command list for help text and completions.
|
||||
MOLE_COMMANDS=(
|
||||
"clean:Free up disk space"
|
||||
"uninstall:Remove apps completely"
|
||||
"optimize:Check and maintain system"
|
||||
"analyze:Explore disk usage"
|
||||
"status:Monitor system health"
|
||||
"purge:Remove old project artifacts"
|
||||
"touchid:Configure Touch ID for sudo"
|
||||
"completion:Setup shell tab completion"
|
||||
"update:Update to latest version"
|
||||
"remove:Remove Mole from system"
|
||||
"help:Show help"
|
||||
"version:Show version"
|
||||
)
|
||||
25
mole
25
mole
@@ -8,6 +8,7 @@ set -euo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
source "$SCRIPT_DIR/lib/core/common.sh"
|
||||
source "$SCRIPT_DIR/lib/core/commands.sh"
|
||||
|
||||
trap cleanup_temp_files EXIT INT TERM
|
||||
|
||||
@@ -238,17 +239,14 @@ show_help() {
|
||||
echo
|
||||
printf "%s%s%s\n" "$BLUE" "COMMANDS" "$NC"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo" "$NC" "Main menu"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo clean" "$NC" "Free up disk space"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo uninstall" "$NC" "Remove apps completely"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo optimize" "$NC" "Check and maintain system"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo analyze" "$NC" "Explore disk usage"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo status" "$NC" "Monitor system health"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo purge" "$NC" "Remove old project artifacts"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo touchid" "$NC" "Configure Touch ID for sudo"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo update" "$NC" "Update to latest version"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo remove" "$NC" "Remove Mole from system"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo --help" "$NC" "Show help"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo --version" "$NC" "Show version"
|
||||
for entry in "${MOLE_COMMANDS[@]}"; do
|
||||
local name="${entry%%:*}"
|
||||
local desc="${entry#*:}"
|
||||
local display="mo $name"
|
||||
[[ "$name" == "help" ]] && display="mo --help"
|
||||
[[ "$name" == "version" ]] && display="mo --version"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "$display" "$NC" "$desc"
|
||||
done
|
||||
echo
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo clean --dry-run" "$NC" "Preview cleanup"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo clean --whitelist" "$NC" "Manage protected caches"
|
||||
@@ -257,8 +255,6 @@ show_help() {
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo optimize --whitelist" "$NC" "Manage protected items"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo purge --paths" "$NC" "Configure scan directories"
|
||||
echo
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "mo completion" "$NC" "Configure shell completion"
|
||||
echo
|
||||
printf "%s%s%s\n" "$BLUE" "OPTIONS" "$NC"
|
||||
printf " %s%-28s%s %s\n" "$GREEN" "--debug" "$NC" "Show detailed operation logs"
|
||||
echo
|
||||
@@ -787,6 +783,9 @@ main() {
|
||||
"touchid")
|
||||
exec "$SCRIPT_DIR/bin/touchid.sh" "${args[@]:1}"
|
||||
;;
|
||||
"completion")
|
||||
exec "$SCRIPT_DIR/bin/completion.sh" "${args[@]:1}"
|
||||
;;
|
||||
"update")
|
||||
update_mole
|
||||
exit 0
|
||||
|
||||
289
tests/browser_version_cleanup.bats
Normal file
289
tests/browser_version_cleanup.bats
Normal file
@@ -0,0 +1,289 @@
|
||||
#!/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-browser-cleanup.XXXXXX")"
|
||||
export HOME
|
||||
|
||||
mkdir -p "$HOME"
|
||||
}
|
||||
|
||||
teardown_file() {
|
||||
rm -rf "$HOME"
|
||||
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
|
||||
export HOME="$ORIGINAL_HOME"
|
||||
fi
|
||||
}
|
||||
|
||||
@test "clean_chrome_old_versions skips when Chrome is running" {
|
||||
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"
|
||||
|
||||
# Mock pgrep to simulate Chrome running
|
||||
pgrep() { return 0; }
|
||||
export -f pgrep
|
||||
|
||||
clean_chrome_old_versions
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Google Chrome running"* ]]
|
||||
[[ "$output" == *"old versions cleanup skipped"* ]]
|
||||
}
|
||||
|
||||
@test "clean_chrome_old_versions removes old versions but keeps current" {
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" DRY_RUN=true bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/clean/user.sh"
|
||||
|
||||
# Mock pgrep to simulate Chrome not running
|
||||
pgrep() { return 1; }
|
||||
export -f pgrep
|
||||
|
||||
# Create mock Chrome directory structure
|
||||
CHROME_APP="$HOME/Applications/Google Chrome.app"
|
||||
VERSIONS_DIR="$CHROME_APP/Contents/Frameworks/Google Chrome Framework.framework/Versions"
|
||||
mkdir -p "$VERSIONS_DIR"/{128.0.0.0,129.0.0.0,130.0.0.0}
|
||||
|
||||
# Create Current symlink pointing to 130.0.0.0
|
||||
ln -s "130.0.0.0" "$VERSIONS_DIR/Current"
|
||||
|
||||
# Mock functions
|
||||
is_path_whitelisted() { return 1; }
|
||||
get_path_size_kb() { echo "10240"; }
|
||||
bytes_to_human() { echo "10M"; }
|
||||
note_activity() { :; }
|
||||
export -f is_path_whitelisted get_path_size_kb bytes_to_human note_activity
|
||||
|
||||
# Initialize counters
|
||||
files_cleaned=0
|
||||
total_size_cleaned=0
|
||||
total_items=0
|
||||
|
||||
clean_chrome_old_versions
|
||||
|
||||
# Verify output mentions old versions cleanup
|
||||
echo "Cleaned: $files_cleaned items"
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Chrome old versions"* ]]
|
||||
[[ "$output" == *"dry"* ]]
|
||||
[[ "$output" == *"Cleaned: 2 items"* ]]
|
||||
}
|
||||
|
||||
@test "clean_chrome_old_versions respects whitelist" {
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" DRY_RUN=true bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/clean/user.sh"
|
||||
|
||||
# Mock pgrep to simulate Chrome not running
|
||||
pgrep() { return 1; }
|
||||
export -f pgrep
|
||||
|
||||
# Create mock Chrome directory structure
|
||||
CHROME_APP="$HOME/Applications/Google Chrome.app"
|
||||
VERSIONS_DIR="$CHROME_APP/Contents/Frameworks/Google Chrome Framework.framework/Versions"
|
||||
mkdir -p "$VERSIONS_DIR"/{128.0.0.0,129.0.0.0,130.0.0.0}
|
||||
|
||||
# Create Current symlink pointing to 130.0.0.0
|
||||
ln -s "130.0.0.0" "$VERSIONS_DIR/Current"
|
||||
|
||||
# Mock is_path_whitelisted to protect version 128.0.0.0
|
||||
is_path_whitelisted() {
|
||||
[[ "$1" == *"128.0.0.0"* ]] && return 0
|
||||
return 1
|
||||
}
|
||||
get_path_size_kb() { echo "10240"; }
|
||||
bytes_to_human() { echo "10M"; }
|
||||
note_activity() { :; }
|
||||
export -f is_path_whitelisted get_path_size_kb bytes_to_human note_activity
|
||||
|
||||
# Initialize counters
|
||||
files_cleaned=0
|
||||
total_size_cleaned=0
|
||||
total_items=0
|
||||
|
||||
clean_chrome_old_versions
|
||||
|
||||
# Should only clean 129.0.0.0 (not 128.0.0.0 which is whitelisted)
|
||||
echo "Cleaned: $files_cleaned items"
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Cleaned: 1 items"* ]]
|
||||
}
|
||||
|
||||
@test "clean_chrome_old_versions DRY_RUN mode does not delete files" {
|
||||
# Create test directory
|
||||
CHROME_APP="$HOME/Applications/Google Chrome.app"
|
||||
VERSIONS_DIR="$CHROME_APP/Contents/Frameworks/Google Chrome Framework.framework/Versions"
|
||||
mkdir -p "$VERSIONS_DIR"/{128.0.0.0,130.0.0.0}
|
||||
|
||||
# Remove Current if it exists as a directory, then create symlink
|
||||
rm -rf "$VERSIONS_DIR/Current"
|
||||
ln -s "130.0.0.0" "$VERSIONS_DIR/Current"
|
||||
|
||||
# Create a marker file in old version
|
||||
touch "$VERSIONS_DIR/128.0.0.0/marker.txt"
|
||||
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" DRY_RUN=true bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/clean/user.sh"
|
||||
|
||||
pgrep() { return 1; }
|
||||
is_path_whitelisted() { return 1; }
|
||||
get_path_size_kb() { echo "10240"; }
|
||||
bytes_to_human() { echo "10M"; }
|
||||
note_activity() { :; }
|
||||
export -f pgrep is_path_whitelisted get_path_size_kb bytes_to_human note_activity
|
||||
|
||||
files_cleaned=0
|
||||
total_size_cleaned=0
|
||||
total_items=0
|
||||
|
||||
clean_chrome_old_versions
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"dry"* ]]
|
||||
# Verify marker file still exists (not deleted in dry run)
|
||||
[ -f "$VERSIONS_DIR/128.0.0.0/marker.txt" ]
|
||||
}
|
||||
|
||||
@test "clean_chrome_old_versions handles missing Current symlink gracefully" {
|
||||
# Use a fresh temp directory for this test
|
||||
TEST_HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-test5.XXXXXX")"
|
||||
|
||||
run env HOME="$TEST_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"
|
||||
|
||||
pgrep() { return 1; }
|
||||
is_path_whitelisted() { return 1; }
|
||||
get_path_size_kb() { echo "10240"; }
|
||||
bytes_to_human() { echo "10M"; }
|
||||
note_activity() { :; }
|
||||
export -f pgrep is_path_whitelisted get_path_size_kb bytes_to_human note_activity
|
||||
|
||||
# Initialize counters to prevent unbound variable errors
|
||||
files_cleaned=0
|
||||
total_size_cleaned=0
|
||||
total_items=0
|
||||
|
||||
# Create Chrome app without Current symlink
|
||||
CHROME_APP="$HOME/Applications/Google Chrome.app"
|
||||
VERSIONS_DIR="$CHROME_APP/Contents/Frameworks/Google Chrome Framework.framework/Versions"
|
||||
mkdir -p "$VERSIONS_DIR"/{128.0.0.0,129.0.0.0}
|
||||
# No Current symlink created
|
||||
|
||||
clean_chrome_old_versions
|
||||
EOF
|
||||
|
||||
rm -rf "$TEST_HOME"
|
||||
[ "$status" -eq 0 ]
|
||||
# Should exit gracefully with no output
|
||||
}
|
||||
|
||||
@test "clean_edge_old_versions skips when Edge is running" {
|
||||
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"
|
||||
|
||||
# Mock pgrep to simulate Edge running
|
||||
pgrep() { return 0; }
|
||||
export -f pgrep
|
||||
|
||||
clean_edge_old_versions
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Microsoft Edge running"* ]]
|
||||
[[ "$output" == *"old versions cleanup skipped"* ]]
|
||||
}
|
||||
|
||||
@test "clean_edge_old_versions removes old versions but keeps current" {
|
||||
run env HOME="$HOME" PROJECT_ROOT="$PROJECT_ROOT" DRY_RUN=true bash --noprofile --norc <<'EOF'
|
||||
set -euo pipefail
|
||||
source "$PROJECT_ROOT/lib/core/common.sh"
|
||||
source "$PROJECT_ROOT/lib/clean/user.sh"
|
||||
|
||||
pgrep() { return 1; }
|
||||
export -f pgrep
|
||||
|
||||
# Create mock Edge directory structure
|
||||
EDGE_APP="$HOME/Applications/Microsoft Edge.app"
|
||||
VERSIONS_DIR="$EDGE_APP/Contents/Frameworks/Microsoft Edge Framework.framework/Versions"
|
||||
mkdir -p "$VERSIONS_DIR"/{120.0.0.0,121.0.0.0,122.0.0.0}
|
||||
|
||||
# Create Current symlink pointing to 122.0.0.0
|
||||
ln -s "122.0.0.0" "$VERSIONS_DIR/Current"
|
||||
|
||||
is_path_whitelisted() { return 1; }
|
||||
get_path_size_kb() { echo "10240"; }
|
||||
bytes_to_human() { echo "10M"; }
|
||||
note_activity() { :; }
|
||||
export -f is_path_whitelisted get_path_size_kb bytes_to_human note_activity
|
||||
|
||||
files_cleaned=0
|
||||
total_size_cleaned=0
|
||||
total_items=0
|
||||
|
||||
clean_edge_old_versions
|
||||
|
||||
echo "Cleaned: $files_cleaned items"
|
||||
EOF
|
||||
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Edge old versions"* ]]
|
||||
[[ "$output" == *"dry"* ]]
|
||||
[[ "$output" == *"Cleaned: 2 items"* ]]
|
||||
}
|
||||
|
||||
@test "clean_edge_old_versions handles no old versions gracefully" {
|
||||
# Use a fresh temp directory for this test
|
||||
TEST_HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-test8.XXXXXX")"
|
||||
|
||||
run env HOME="$TEST_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"
|
||||
|
||||
pgrep() { return 1; }
|
||||
is_path_whitelisted() { return 1; }
|
||||
get_path_size_kb() { echo "10240"; }
|
||||
bytes_to_human() { echo "10M"; }
|
||||
note_activity() { :; }
|
||||
export -f pgrep is_path_whitelisted get_path_size_kb bytes_to_human note_activity
|
||||
|
||||
# Initialize counters
|
||||
files_cleaned=0
|
||||
total_size_cleaned=0
|
||||
total_items=0
|
||||
|
||||
# Create Edge with only current version
|
||||
EDGE_APP="$HOME/Applications/Microsoft Edge.app"
|
||||
VERSIONS_DIR="$EDGE_APP/Contents/Frameworks/Microsoft Edge Framework.framework/Versions"
|
||||
mkdir -p "$VERSIONS_DIR/122.0.0.0"
|
||||
ln -s "122.0.0.0" "$VERSIONS_DIR/Current"
|
||||
|
||||
clean_edge_old_versions
|
||||
EOF
|
||||
|
||||
rm -rf "$TEST_HOME"
|
||||
[ "$status" -eq 0 ]
|
||||
# Should exit gracefully with no cleanup output
|
||||
[[ "$output" != *"Edge old versions"* ]]
|
||||
}
|
||||
157
tests/completion.bats
Executable file
157
tests/completion.bats
Executable file
@@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env bats
|
||||
|
||||
setup_file() {
|
||||
PROJECT_ROOT="$(cd "${BATS_TEST_DIRNAME}/.." && pwd)"
|
||||
export PROJECT_ROOT
|
||||
|
||||
ORIGINAL_HOME="${HOME:-}"
|
||||
export ORIGINAL_HOME
|
||||
|
||||
ORIGINAL_PATH="${PATH:-}"
|
||||
export ORIGINAL_PATH
|
||||
|
||||
HOME="$(mktemp -d "${BATS_TEST_DIRNAME}/tmp-completion-home.XXXXXX")"
|
||||
export HOME
|
||||
|
||||
mkdir -p "$HOME"
|
||||
|
||||
PATH="$PROJECT_ROOT:$PATH"
|
||||
export PATH
|
||||
}
|
||||
|
||||
teardown_file() {
|
||||
rm -rf "$HOME"
|
||||
if [[ -n "${ORIGINAL_HOME:-}" ]]; then
|
||||
export HOME="$ORIGINAL_HOME"
|
||||
fi
|
||||
if [[ -n "${ORIGINAL_PATH:-}" ]]; then
|
||||
export PATH="$ORIGINAL_PATH"
|
||||
fi
|
||||
}
|
||||
|
||||
setup() {
|
||||
rm -rf "$HOME/.config"
|
||||
rm -rf "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile"
|
||||
mkdir -p "$HOME"
|
||||
}
|
||||
|
||||
@test "completion script exists and is executable" {
|
||||
[ -f "$PROJECT_ROOT/bin/completion.sh" ]
|
||||
[ -x "$PROJECT_ROOT/bin/completion.sh" ]
|
||||
}
|
||||
|
||||
@test "completion script has valid bash syntax" {
|
||||
run bash -n "$PROJECT_ROOT/bin/completion.sh"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "completion --help shows usage" {
|
||||
run "$PROJECT_ROOT/bin/completion.sh" --help
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"Usage: mole completion"* ]]
|
||||
[[ "$output" == *"Auto-install"* ]]
|
||||
}
|
||||
|
||||
@test "completion bash generates valid bash script" {
|
||||
run "$PROJECT_ROOT/bin/completion.sh" bash
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"_mole_completions"* ]]
|
||||
[[ "$output" == *"complete -F _mole_completions mole mo"* ]]
|
||||
}
|
||||
|
||||
@test "completion bash script includes all commands" {
|
||||
run "$PROJECT_ROOT/bin/completion.sh" bash
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"optimize"* ]]
|
||||
[[ "$output" == *"clean"* ]]
|
||||
[[ "$output" == *"uninstall"* ]]
|
||||
[[ "$output" == *"analyze"* ]]
|
||||
[[ "$output" == *"status"* ]]
|
||||
[[ "$output" == *"purge"* ]]
|
||||
[[ "$output" == *"touchid"* ]]
|
||||
[[ "$output" == *"completion"* ]]
|
||||
}
|
||||
|
||||
@test "completion bash script supports mo command" {
|
||||
run "$PROJECT_ROOT/bin/completion.sh" bash
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"complete -F _mole_completions mole mo"* ]]
|
||||
}
|
||||
|
||||
@test "completion bash can be loaded in bash" {
|
||||
run bash -c "eval \"\$(\"$PROJECT_ROOT/bin/completion.sh\" bash)\" && complete -p mole"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"_mole_completions"* ]]
|
||||
}
|
||||
|
||||
@test "completion zsh generates valid zsh script" {
|
||||
run "$PROJECT_ROOT/bin/completion.sh" zsh
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"#compdef mole mo"* ]]
|
||||
[[ "$output" == *"_mole()"* ]]
|
||||
}
|
||||
|
||||
@test "completion zsh includes command descriptions" {
|
||||
run "$PROJECT_ROOT/bin/completion.sh" zsh
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"optimize:Free up disk space"* ]]
|
||||
[[ "$output" == *"clean:Remove apps completely"* ]]
|
||||
}
|
||||
|
||||
@test "completion fish generates valid fish script" {
|
||||
run "$PROJECT_ROOT/bin/completion.sh" fish
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"complete -c mole"* ]]
|
||||
[[ "$output" == *"complete -c mo"* ]]
|
||||
}
|
||||
|
||||
@test "completion fish includes both mole and mo commands" {
|
||||
output="$("$PROJECT_ROOT/bin/completion.sh" fish)"
|
||||
mole_count=$(echo "$output" | grep -c "complete -c mole")
|
||||
mo_count=$(echo "$output" | grep -c "complete -c mo")
|
||||
|
||||
[ "$mole_count" -gt 0 ]
|
||||
[ "$mo_count" -gt 0 ]
|
||||
}
|
||||
|
||||
@test "completion auto-install detects zsh" {
|
||||
export SHELL=/bin/zsh
|
||||
|
||||
# Simulate auto-install (no interaction)
|
||||
run bash -c "echo 'y' | \"$PROJECT_ROOT/bin/completion.sh\""
|
||||
|
||||
if [[ "$output" == *"Already configured"* ]]; then
|
||||
skip "Already configured from previous test"
|
||||
fi
|
||||
|
||||
[ -f "$HOME/.zshrc" ] || skip "Auto-install didn't create .zshrc"
|
||||
|
||||
run grep -E "mole[[:space:]]+completion" "$HOME/.zshrc"
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
@test "completion auto-install detects already installed" {
|
||||
export SHELL=/bin/zsh
|
||||
mkdir -p "$HOME"
|
||||
echo 'eval "$(mole completion zsh)"' > "$HOME/.zshrc"
|
||||
|
||||
run "$PROJECT_ROOT/bin/completion.sh"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"updated"* ]]
|
||||
}
|
||||
|
||||
@test "completion script handles invalid shell argument" {
|
||||
run "$PROJECT_ROOT/bin/completion.sh" invalid-shell
|
||||
[ "$status" -ne 0 ]
|
||||
}
|
||||
|
||||
@test "completion subcommand supports bash/zsh/fish" {
|
||||
run "$PROJECT_ROOT/bin/completion.sh" bash
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run "$PROJECT_ROOT/bin/completion.sh" zsh
|
||||
[ "$status" -eq 0 ]
|
||||
|
||||
run "$PROJECT_ROOT/bin/completion.sh" fish
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
Reference in New Issue
Block a user