#!/bin/bash # Test Suite for Backup Rotation Script set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROTATE_SCRIPT="$SCRIPT_DIR/../src/solaria-backup-rotate.sh" TEST_DIR="/tmp/backup-rotate-test-$$" PASS=0 FAIL=0 setup() { rm -rf "$TEST_DIR" mkdir -p "$TEST_DIR" cd "$TEST_DIR" cp "$ROTATE_SCRIPT" ./test-rotate.sh chmod +x ./test-rotate.sh log "TEST: Setup complete in $TEST_DIR" } teardown() { cd "$SCRIPT_DIR" rm -rf "$TEST_DIR" } log() { echo "[TEST] $1"; } pass() { PASS=$((PASS+1)); echo " ✓ PASS: $1"; } fail() { FAIL=$((FAIL+1)); echo " ✗ FAIL: $1"; } # Create mock backup files touch_backup() { local date=$1 touch "solaria-deep-backup_${date}_120000.tar.gz" touch "solaria-deep-backup_${date}_120000.tar.gz.part_000" touch "solaria-deep-backup_${date}_120000.tar.gz.part_001" } count_backups() { find . -maxdepth 1 -name "solaria-deep-backup_*.tar.gz" 2>/dev/null | wc -l } # ===== TEST CASES ===== test_empty_directory() { log "Test: Empty directory" rm -f *.tar.gz* 2>/dev/null || true output=$(./test-rotate.sh . 2>&1) echo "$output" | grep -q "No backups found to rotate" pass "empty_directory" } test_single_backup() { log "Test: Single backup" rm -f *.tar.gz* 2>/dev/null || true touch_backup 20260215 output=$(./test-rotate.sh . 2>&1) echo "$output" | grep -q "Found.*20260215" pass "single_backup" } test_multiple_same_day() { log "Test: Multiple backups same day" rm -f *.tar.gz* 2>/dev/null || true touch "solaria-deep-backup_20260215_060000.tar.gz" touch "solaria-deep-backup_20260215_120000.tar.gz" touch "solaria-deep-backup_20260215_180000.tar.gz" output=$(./test-rotate.sh . 2>&1) echo "$output" | grep -q "keeping newest" pass "multiple_same_day" } test_mixed_ages_daily() { log "Test: Mixed ages - daily rotation" rm -f *.tar.gz* 2>/dev/null || true touch_backup 20260209 # 6 days ago touch_backup 20260210 touch_backup 20260211 touch_backup 20260212 touch_backup 20260213 touch_backup 20260214 touch_backup 20260215 # today output=$(./test-rotate.sh . 2>&1) echo "$output" | grep -q "YESTERDAY\|Rotation planning" pass "mixed_ages_daily" } test_weekly_promotion() { log "Test: Weekly promotion logic" rm -f *.tar.gz* 2>/dev/null || true # Two weeks of backups touch_backup 20260202 # Week 5 touch_backup 20260203 touch_backup 20260204 touch_backup 20260205 touch_backup 20260209 # Week 6 touch_backup 20260210 touch_backup 20260211 touch_backup 20260212 output=$(./test-rotate.sh . 2>&1) echo "$output" | grep -q "WEEKLY" pass "weekly_promotion" } test_monthly_promotion() { log "Test: Monthly promotion logic" rm -f *.tar.gz* 2>/dev/null || true # January backup touch_backup 20250115 # February backups touch_backup 20260201 touch_backup 20260215 output=$(./test-rotate.sh . 2>&1) echo "$output" | grep -q "MONTHLY" pass "monthly_promotion" } test_yearly_promotion() { log "Test: Yearly promotion logic" rm -f *.tar.gz* 2>/dev/null || true # Multiple years touch_backup 20230115 # 2023 touch_backup 20240115 # 2024 touch_backup 20250115 # 2025 touch_backup 20250215 # 2026 output=$(./test-rotate.sh . 2>&1) echo "$output" | grep -q "YEARLY" pass "yearly_promotion" } test_boundary_first_of_month() { log "Test: Boundary - first of month" rm -f *.tar.gz* 2>/dev/null || true touch_backup 20260131 touch_backup 20260201 output=$(./test-rotate.sh . 2>&1) echo "$output" | grep -q "202602" pass "boundary_first_of_month" } test_boundary_first_of_year() { log "Test: Boundary - first of year" rm -f *.tar.gz* 2>/dev/null || true touch_backup 20241231 touch_backup 20250101 output=$(./test-rotate.sh . 2>&1) echo "$output" | grep -q "202501" pass "boundary_first_of_year" } test_chunk_files_preserved() { log "Test: Chunk files handled correctly" rm -f *.tar.gz* 2>/dev/null || true touch_backup 20260215 chunks=$(ls *.tar.gz.part_* 2>/dev/null | wc -l) [ "$chunks" -ge 2 ] pass "chunk_files_preserved" } test_script_syntax() { log "Test: Script syntax validation" bash -n ./test-rotate.sh pass "script_syntax" } test_invalid_directory() { log "Test: Invalid directory handling" rm -rf /tmp/nonexistent-backup-dir-$$ 2>/dev/null || true output=$(./test-rotate.sh /tmp/nonexistent-backup-dir-$$ 2>&1) || true echo "$output" | grep -q "Cannot access" pass "invalid_directory" } test_4_years_retention() { log "Test: 4 years retention (2023-2026)" rm -f *.tar.gz* 2>/dev/null || true touch_backup 20230115 touch_backup 20240115 touch_backup 20250115 touch_backup 20250215 output=$(./test-rotate.sh . 2>&1) echo "$output" | grep -q "YEARLY" pass "4_years_retention" } # ===== RUN TESTS ===== setup log "" log "========================================" log "BACKUP ROTATION TEST SUITE" log "========================================" log "" # Run all tests test_script_syntax test_empty_directory test_single_backup test_multiple_same_day test_mixed_ages_daily test_weekly_promotion test_monthly_promotion test_yearly_promotion test_boundary_first_of_month test_boundary_first_of_year test_chunk_files_preserved test_invalid_directory test_4_years_retention log "" log "========================================" log "RESULTS: $PASS passed, $FAIL failed" log "========================================" teardown if [ $FAIL -gt 0 ]; then exit 1 fi exit 0