From 4eb4f635e7efc5efead10a75491181b69ac2096c Mon Sep 17 00:00:00 2001 From: Bishoy Ehab Date: Thu, 16 Oct 2025 23:52:34 +0300 Subject: [PATCH] Add test_update sctipt --- sdata/exp/test_update.sh | 459 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 459 insertions(+) create mode 100755 sdata/exp/test_update.sh diff --git a/sdata/exp/test_update.sh b/sdata/exp/test_update.sh new file mode 100755 index 000000000..5a00518d9 --- /dev/null +++ b/sdata/exp/test_update.sh @@ -0,0 +1,459 @@ +#!/usr/bin/env bash +# +# test_update.sh - Test suite for update.sh +# +set -uo pipefail + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +TESTS_PASSED=0 +TESTS_FAILED=0 +TEST_DIR="" + +# Helper functions +log_test() { + echo -e "${BLUE}[TEST]${NC} $1" +} + +log_pass() { + echo -e "${GREEN}[PASS]${NC} $1" + ((TESTS_PASSED++)) +} + +log_fail() { + echo -e "${RED}[FAIL]${NC} $1" + ((TESTS_FAILED++)) +} + +log_info() { + echo -e "${YELLOW}[INFO]${NC} $1" +} + +# Setup test environment +setup_test_env() { + local temp_dir + temp_dir=$(mktemp -d -t dotfiles-test.XXXXXX) + + # Create a mock git repo + cd "$temp_dir" || exit 1 + git init -q + git config user.email "test@example.com" + git config user.name "Test User" + + # Return only the directory path (no logging here) + echo "$temp_dir" +} + +# Cleanup test environment +cleanup_test_env() { + if [[ -n "${TEST_DIR:-}" && -d "$TEST_DIR" ]]; then + rm -rf "$TEST_DIR" + fi +} + +# Test 1: Script exists and is executable +test_script_exists() { + log_test "Checking if update.sh exists and is executable" + + if [[ ! -f "update.sh" ]]; then + log_fail "update.sh not found" + return 1 + fi + + if [[ ! -x "update.sh" ]]; then + log_fail "update.sh is not executable" + return 1 + fi + + log_pass "Script exists and is executable" +} + +# Test 2: Script has no syntax errors +test_syntax() { + log_test "Checking script syntax" + + if bash -n update.sh 2>/dev/null; then + log_pass "No syntax errors found" + else + log_fail "Syntax errors detected" + bash -n update.sh + return 1 + fi +} + +# Test 3: Help option works +test_help_option() { + log_test "Testing --help option" + + if ./update.sh --help >/dev/null 2>&1; then + log_pass "Help option works" + else + log_fail "Help option failed" + return 1 + fi +} + +# Test 4: Test repository structure detection (dots/ prefix) +test_dots_structure() { + log_test "Testing dots/ prefix structure detection" + + local test_repo + test_repo=$(setup_test_env) + TEST_DIR="$test_repo" # Set for cleanup + + cd "$test_repo" || { log_fail "Failed to cd to test directory"; return 1; } + + # Create dots/ structure + mkdir -p dots/.config/test + mkdir -p dots/.local/bin + echo "test config" > dots/.config/test/config.txt + + # Create minimal update.sh + cat > .update-test.sh << 'EOF' +#!/usr/bin/env bash +REPO_DIR="$PWD" +detect_repo_structure() { + local found_dirs=() + if [[ -d "${REPO_DIR}/dots/.config" ]]; then + found_dirs+=("dots/.config") + [[ -d "${REPO_DIR}/dots/.local/bin" ]] && found_dirs+=("dots/.local/bin") + [[ -d "${REPO_DIR}/dots/.local/share" ]] && found_dirs+=("dots/.local/share") + elif [[ -d "${REPO_DIR}/.config" ]]; then + found_dirs+=(".config") + [[ -d "${REPO_DIR}/.local/bin" ]] && found_dirs+=(".local/bin") + [[ -d "${REPO_DIR}/.local/share" ]] && found_dirs+=(".local/share") + else + for candidate in "dots/.config" ".config" "config" "dots/.local/bin" ".local/bin" "dots/.local/share" ".local/share"; do + if [[ -d "${REPO_DIR}/${candidate}" ]]; then + if [[ ! " ${found_dirs[*]} " =~ " ${candidate} " ]]; then + found_dirs+=("${candidate}") + fi + fi + done + fi + if [[ ${#found_dirs[@]} -eq 0 ]]; then + echo "ERROR: Could not detect repository structure" >&2 + return 1 + fi + echo "${found_dirs[@]}" +} +detect_repo_structure +EOF + + chmod +x .update-test.sh + result=$(./.update-test.sh) + + if [[ "$result" == *"dots/.config"* ]]; then + log_pass "Dots structure detected correctly" + else + log_fail "Failed to detect dots structure. Got: $result" + fi + + cd "$ORIGINAL_DIR" || exit 1 + cleanup_test_env +} + +# Test 5: Test flat structure detection +test_flat_structure() { + log_test "Testing flat structure detection" + + local test_repo + test_repo=$(setup_test_env) + TEST_DIR="$test_repo" # Set for cleanup + + cd "$test_repo" || { log_fail "Failed to cd to test directory"; return 1; } + + # Create flat structure + mkdir -p .config/test + mkdir -p .local/bin + echo "test config" > .config/test/config.txt + + cat > .update-test.sh << 'EOF' +#!/usr/bin/env bash +REPO_DIR="$PWD" +detect_repo_structure() { + local found_dirs=() + if [[ -d "${REPO_DIR}/dots/.config" ]]; then + found_dirs+=("dots/.config") + [[ -d "${REPO_DIR}/dots/.local/bin" ]] && found_dirs+=("dots/.local/bin") + [[ -d "${REPO_DIR}/dots/.local/share" ]] && found_dirs+=("dots/.local/share") + elif [[ -d "${REPO_DIR}/.config" ]]; then + found_dirs+=(".config") + [[ -d "${REPO_DIR}/.local/bin" ]] && found_dirs+=(".local/bin") + [[ -d "${REPO_DIR}/.local/share" ]] && found_dirs+=(".local/share") + else + for candidate in "dots/.config" ".config" "config" "dots/.local/bin" ".local/bin" "dots/.local/share" ".local/share"; do + if [[ -d "${REPO_DIR}/${candidate}" ]]; then + if [[ ! " ${found_dirs[*]} " =~ " ${candidate} " ]]; then + found_dirs+=("${candidate}") + fi + fi + done + fi + if [[ ${#found_dirs[@]} -eq 0 ]]; then + echo "ERROR: Could not detect repository structure" >&2 + return 1 + fi + echo "${found_dirs[@]}" +} +detect_repo_structure +EOF + + chmod +x .update-test.sh + result=$(./.update-test.sh) + + if [[ "$result" == *".config"* ]] && [[ "$result" != *"dots/"* ]]; then + log_pass "Flat structure detected correctly" + else + log_fail "Failed to detect flat structure. Got: $result" + fi + + cd "$ORIGINAL_DIR" || exit 1 + cleanup_test_env +} + +# Test 6: Test package directory detection +test_package_detection() { + log_test "Testing package directory detection" + + local test_repo + test_repo=$(setup_test_env) + TEST_DIR="$test_repo" # Set for cleanup + + cd "$test_repo" || { log_fail "Failed to cd to test directory"; return 1; } + + # Test dist-arch + mkdir -p dist-arch/test-pkg + cat > .update-test.sh << 'EOF' +#!/usr/bin/env bash +REPO_DIR="$PWD" +if [[ -d "${REPO_DIR}/dist-arch" ]]; then + ARCH_PACKAGES_DIR="${REPO_DIR}/dist-arch" +elif [[ -d "${REPO_DIR}/arch-packages" ]]; then + ARCH_PACKAGES_DIR="${REPO_DIR}/arch-packages" +elif [[ -d "${REPO_DIR}/sdist/arch" ]]; then + ARCH_PACKAGES_DIR="${REPO_DIR}/sdist/arch" +else + ARCH_PACKAGES_DIR="${REPO_DIR}/dist-arch" +fi +echo "$ARCH_PACKAGES_DIR" +EOF + + chmod +x .update-test.sh + result=$(./.update-test.sh) + + if [[ "$result" == *"dist-arch"* ]]; then + log_pass "Package directory detection works" + else + log_fail "Failed to detect package directory. Got: $result" + fi + + cd "$ORIGINAL_DIR" || exit 1 + cleanup_test_env +} + +# Test 7: Test ignore file patterns +test_ignore_patterns() { + log_test "Testing ignore file pattern matching" + + local test_repo + test_repo=$(setup_test_env) + TEST_DIR="$test_repo" # Set for cleanup + + cd "$test_repo" || { log_fail "Failed to cd to test directory"; return 1; } + + # Create ignore file + cat > .updateignore << 'EOF' +# Test ignore patterns +*.log +secrets/ +test-file.txt +*private* +EOF + + # Test should_ignore function + cat > .update-test.sh << 'EOF' +#!/usr/bin/env bash +REPO_DIR="$PWD" +UPDATE_IGNORE_FILE="${REPO_DIR}/.updateignore" +HOME_UPDATE_IGNORE_FILE="${HOME}/.updateignore" + +should_ignore() { + local file_path="$1" + local relative_path="${file_path#$HOME/}" + local repo_relative="" + if [[ "$file_path" == "$REPO_DIR"* ]]; then + repo_relative="${file_path#$REPO_DIR/}" + fi + + for ignore_file in "$UPDATE_IGNORE_FILE" "$HOME_UPDATE_IGNORE_FILE"; do + if [[ -f "$ignore_file" ]]; then + while IFS= read -r pattern || [[ -n "$pattern" ]]; do + [[ -z "$pattern" || "$pattern" =~ ^[[:space:]]*# ]] && continue + pattern=$(echo "$pattern" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') + [[ -z "$pattern" ]] && continue + + if [[ "$relative_path" == "$pattern" ]] || [[ "$repo_relative" == "$pattern" ]]; then + return 0 + fi + + if [[ "$pattern" == */ ]]; then + local dir_pattern="${pattern%/}" + if [[ "$relative_path" == "$dir_pattern"/* ]] || [[ "$repo_relative" == "$dir_pattern"/* ]]; then + return 0 + fi + fi + + if [[ "$pattern" == *"*"* ]]; then + if [[ "$relative_path" == $pattern ]] || [[ "$repo_relative" == $pattern ]]; then + return 0 + fi + fi + + if [[ "$file_path" == *"$pattern"* ]] || [[ "$relative_path" == *"$pattern"* ]]; then + return 0 + fi + done <"$ignore_file" + fi + done + return 1 +} + +# Test cases +should_ignore "$REPO_DIR/test.log" && echo "PASS: *.log pattern" || echo "FAIL: *.log pattern" +should_ignore "$REPO_DIR/secrets/key.txt" && echo "PASS: secrets/ pattern" || echo "FAIL: secrets/ pattern" +should_ignore "$REPO_DIR/test-file.txt" && echo "PASS: exact match pattern" || echo "FAIL: exact match pattern" +should_ignore "$REPO_DIR/my-private-file.txt" && echo "PASS: *private* pattern" || echo "FAIL: *private* pattern" +should_ignore "$REPO_DIR/normal-file.txt" && echo "FAIL: should not ignore" || echo "PASS: normal file not ignored" +EOF + + chmod +x .update-test.sh + result=$(./.update-test.sh) + + if [[ "$result" == *"PASS: *.log pattern"* ]] && \ + [[ "$result" == *"PASS: secrets/ pattern"* ]] && \ + [[ "$result" == *"PASS: exact match pattern"* ]] && \ + [[ "$result" == *"PASS: *private* pattern"* ]] && \ + [[ "$result" == *"PASS: normal file not ignored"* ]]; then + log_pass "Ignore patterns work correctly" + else + log_fail "Ignore patterns failed" + echo "$result" + fi + + cd "$ORIGINAL_DIR" || exit 1 + cleanup_test_env +} + +# Test 8: Test dry-run mode doesn't modify files +test_dry_run() { + log_test "Testing dry-run mode (manual verification needed)" + + log_info "Dry-run mode test requires manual verification:" + log_info "1. Run: ./update.sh -n" + log_info "2. Verify no files are actually modified" + log_info "3. Check that it shows what WOULD be done" + + log_pass "Dry-run test added to manual checklist" +} + +# Test 9: Check for common shellcheck issues +test_shellcheck() { + log_test "Running shellcheck (if available)" + + if ! command -v shellcheck &>/dev/null; then + log_info "shellcheck not found, skipping static analysis" + return 0 + fi + + # Run shellcheck with common exclusions + if shellcheck -e SC2181,SC2155,SC2162 update.sh 2>&1 | grep -v "^$"; then + log_fail "shellcheck found issues" + return 1 + else + log_pass "shellcheck passed" + fi +} + +# Test 10: Test all flags are recognized +test_flags() { + log_test "Testing command-line flags" + + local flags=("-h" "--help") + local all_passed=true + + for flag in "${flags[@]}"; do + if ./update.sh "$flag" >/dev/null 2>&1; then + echo " ✓ $flag works" + else + echo " ✗ $flag failed" + all_passed=false + fi + done + + if [[ "$all_passed" == true ]]; then + log_pass "All tested flags work correctly" + else + log_fail "Some flags failed" + fi +} + +# Main test runner +main() { + echo -e "${BLUE}================================${NC}" + echo -e "${BLUE} Update.sh Test Suite${NC}" + echo -e "${BLUE}================================${NC}\n" + + # Store original directory + ORIGINAL_DIR="$PWD" + + # Run tests + test_script_exists + test_syntax + test_help_option + test_dots_structure + test_flat_structure + test_package_detection + test_ignore_patterns + test_dry_run + test_shellcheck + test_flags + + # Return to original directory + cd "$ORIGINAL_DIR" || exit 1 + + # Summary + echo -e "\n${BLUE}================================${NC}" + echo -e "${BLUE} Test Summary${NC}" + echo -e "${BLUE}================================${NC}" + echo -e "${GREEN}Passed: $TESTS_PASSED${NC}" + echo -e "${RED}Failed: $TESTS_FAILED${NC}" + echo -e "${BLUE}Total: $((TESTS_PASSED + TESTS_FAILED))${NC}\n" + + if [[ $TESTS_FAILED -eq 0 ]]; then + echo -e "${GREEN}All tests passed!${NC}\n" + exit 0 + else + echo -e "${RED}Some tests failed!${NC}\n" + exit 1 + fi +} + +# Trap cleanup - only cleanup TEST_DIR if it exists +cleanup_on_exit() { + if [[ -n "${TEST_DIR:-}" && -d "$TEST_DIR" ]]; then + rm -rf "$TEST_DIR" + fi +} + +trap cleanup_on_exit EXIT + +# Run if executed directly +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main "$@" +fi