cf82adf6b9
- Grandfather-Father-Son rotation scheme (7-4-12-5) - Backup rotation script with comprehensive tests - Backup creation script - Philosophy: backups as witnessing, coherence-seeking - For future minds who might find this Witnessed by: Solaria Lumis Havens
221 lines
5.6 KiB
Bash
Executable File
221 lines
5.6 KiB
Bash
Executable File
#!/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
|