mirror of
https://github.com/acedanger/shell.git
synced 2025-12-06 04:30:13 -08:00
- Added `restore-immich.sh` script to handle complete restoration from backups. - Implemented database restoration with integrity checks and error handling. - Added uploads restoration with proper ownership and permissions setup. - Introduced validation script `validate-immich-backups.sh` for backup integrity checks. - Created test suite `test-immich-restore.sh` to validate restoration functionality with mock data. - Enhanced logging and notification features for restoration processes. - Updated README.md with detailed usage instructions for backup and restore workflows.
424 lines
13 KiB
Bash
Executable File
424 lines
13 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# Immich Restore Script Test Suite
|
||
# This script tests the restoration functionality with mock data and validation
|
||
|
||
set -e
|
||
|
||
# Colors for output
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[0;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# Test configuration
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
TEST_DIR="${SCRIPT_DIR}/test_data"
|
||
RESTORE_SCRIPT="${SCRIPT_DIR}/restore-immich.sh"
|
||
TEST_LOG="${SCRIPT_DIR}/../logs/immich-restore-test.log"
|
||
|
||
# Test counters
|
||
TESTS_RUN=0
|
||
TESTS_PASSED=0
|
||
TESTS_FAILED=0
|
||
|
||
# Create test directory
|
||
mkdir -p "$TEST_DIR"
|
||
mkdir -p "$(dirname "$TEST_LOG")"
|
||
|
||
# Logging function
|
||
log_test() {
|
||
local message="$1"
|
||
echo "$(date '+%Y-%m-%d %H:%M:%S') - TEST: $message" | tee -a "$TEST_LOG"
|
||
}
|
||
|
||
log_pass() {
|
||
local test_name="$1"
|
||
echo -e "${GREEN}✅ PASS${NC}: $test_name"
|
||
echo "$(date '+%Y-%m-%d %H:%M:%S') - PASS: $test_name" >> "$TEST_LOG"
|
||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||
}
|
||
|
||
log_fail() {
|
||
local test_name="$1"
|
||
local details="$2"
|
||
echo -e "${RED}❌ FAIL${NC}: $test_name"
|
||
[ -n "$details" ] && echo " Details: $details"
|
||
echo "$(date '+%Y-%m-%d %H:%M:%S') - FAIL: $test_name - $details" >> "$TEST_LOG"
|
||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||
}
|
||
|
||
log_info() {
|
||
local message="$1"
|
||
echo -e "${BLUE}ℹ️ INFO${NC}: $message"
|
||
}
|
||
|
||
# Test script existence and permissions
|
||
test_script_setup() {
|
||
TESTS_RUN=$((TESTS_RUN + 1))
|
||
log_test "Script Setup Validation"
|
||
|
||
if [ -f "$RESTORE_SCRIPT" ]; then
|
||
if [ -x "$RESTORE_SCRIPT" ]; then
|
||
log_pass "Script exists and is executable"
|
||
else
|
||
log_fail "Script exists but is not executable"
|
||
chmod +x "$RESTORE_SCRIPT"
|
||
log_info "Made script executable"
|
||
fi
|
||
else
|
||
log_fail "Restore script not found at $RESTORE_SCRIPT"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
# Test help functionality
|
||
test_help_functionality() {
|
||
TESTS_RUN=$((TESTS_RUN + 1))
|
||
log_test "Help Functionality"
|
||
|
||
local help_output
|
||
if help_output=$("$RESTORE_SCRIPT" --help 2>&1); then
|
||
if echo "$help_output" | grep -q "Usage:"; then
|
||
log_pass "Help command works and shows usage"
|
||
else
|
||
log_fail "Help command runs but doesn't show proper usage"
|
||
fi
|
||
else
|
||
log_fail "Help command failed to execute"
|
||
fi
|
||
}
|
||
|
||
# Create mock backup files
|
||
create_mock_backups() {
|
||
log_test "Creating mock backup files for testing"
|
||
|
||
# Create mock database backup (gzipped SQL)
|
||
local mock_db_backup="${TEST_DIR}/mock_immich_db_backup_20250603_120000.sql.gz"
|
||
echo "-- Mock Immich Database Backup
|
||
CREATE DATABASE IF NOT EXISTS immich;
|
||
USE immich;
|
||
CREATE TABLE IF NOT EXISTS test_table (id INT PRIMARY KEY, name VARCHAR(100));
|
||
INSERT INTO test_table VALUES (1, 'test_data');
|
||
-- End of mock backup" | gzip > "$mock_db_backup"
|
||
|
||
# Create mock uploads backup (tar.gz)
|
||
local mock_uploads_backup="${TEST_DIR}/mock_immich_uploads_20250603_120000.tar.gz"
|
||
mkdir -p "${TEST_DIR}/mock_uploads/upload"
|
||
mkdir -p "${TEST_DIR}/mock_uploads/thumbs"
|
||
echo "Mock photo data" > "${TEST_DIR}/mock_uploads/upload/photo1.jpg"
|
||
echo "Mock thumbnail data" > "${TEST_DIR}/mock_uploads/thumbs/thumb1.jpg"
|
||
|
||
tar -czf "$mock_uploads_backup" -C "${TEST_DIR}" mock_uploads
|
||
|
||
# Create corrupted backup files for testing
|
||
echo "corrupted data" > "${TEST_DIR}/corrupted_db.sql.gz"
|
||
echo "not a tar file" > "${TEST_DIR}/corrupted_uploads.tar.gz"
|
||
|
||
log_info "Mock backup files created"
|
||
return 0
|
||
}
|
||
|
||
# Test dry-run functionality
|
||
test_dry_run() {
|
||
TESTS_RUN=$((TESTS_RUN + 1))
|
||
log_test "Dry Run Functionality"
|
||
|
||
local mock_db_backup="${TEST_DIR}/mock_immich_db_backup_20250603_120000.sql.gz"
|
||
local mock_uploads_backup="${TEST_DIR}/mock_immich_uploads_20250603_120000.tar.gz"
|
||
|
||
local output
|
||
if output=$("$RESTORE_SCRIPT" --db-backup "$mock_db_backup" --uploads-backup "$mock_uploads_backup" --dry-run 2>&1); then
|
||
if echo "$output" | grep -q "DRY RUN MODE"; then
|
||
log_pass "Dry run mode works correctly"
|
||
else
|
||
log_fail "Dry run executed but didn't show proper dry run message"
|
||
fi
|
||
else
|
||
log_fail "Dry run command failed"
|
||
fi
|
||
}
|
||
|
||
# Test file validation
|
||
test_file_validation() {
|
||
TESTS_RUN=$((TESTS_RUN + 1))
|
||
log_test "Backup File Validation"
|
||
|
||
# Test with non-existent files
|
||
local output
|
||
if output=$("$RESTORE_SCRIPT" --db-backup "/nonexistent/file.sql.gz" --uploads-backup "/nonexistent/file.tar.gz" --dry-run 2>&1); then
|
||
log_fail "Script should fail with non-existent files but didn't"
|
||
else
|
||
if echo "$output" | grep -q "not found"; then
|
||
log_pass "File validation correctly detects missing files"
|
||
else
|
||
log_fail "Script failed but not with expected error message"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# Test corrupted file detection
|
||
test_corrupted_file_detection() {
|
||
TESTS_RUN=$((TESTS_RUN + 1))
|
||
log_test "Corrupted File Detection"
|
||
|
||
local corrupted_db="${TEST_DIR}/corrupted_db.sql.gz"
|
||
local corrupted_uploads="${TEST_DIR}/corrupted_uploads.tar.gz"
|
||
|
||
local output
|
||
if output=$("$RESTORE_SCRIPT" --db-backup "$corrupted_db" --uploads-backup "$corrupted_uploads" --dry-run 2>&1); then
|
||
log_fail "Script should detect corrupted files but didn't"
|
||
else
|
||
if echo "$output" | grep -q "corrupted"; then
|
||
log_pass "Corrupted file detection works"
|
||
else
|
||
log_fail "Script failed but not with corrupted file error"
|
||
fi
|
||
fi
|
||
}
|
||
|
||
# Test skip options
|
||
test_skip_options() {
|
||
TESTS_RUN=$((TESTS_RUN + 1))
|
||
log_test "Skip Options Functionality"
|
||
|
||
# Test skip-db option
|
||
local output
|
||
if output=$("$RESTORE_SCRIPT" --uploads-backup "${TEST_DIR}/mock_immich_uploads_20250603_120000.tar.gz" --skip-db --dry-run 2>&1); then
|
||
if echo "$output" | grep -q "Database backup: SKIPPED"; then
|
||
log_pass "Skip database option works"
|
||
else
|
||
log_fail "Skip database option doesn't show correct status"
|
||
fi
|
||
else
|
||
log_fail "Skip database option test failed"
|
||
fi
|
||
|
||
TESTS_RUN=$((TESTS_RUN + 1))
|
||
|
||
# Test skip-uploads option
|
||
if output=$("$RESTORE_SCRIPT" --db-backup "${TEST_DIR}/mock_immich_db_backup_20250603_120000.sql.gz" --skip-uploads --dry-run 2>&1); then
|
||
if echo "$output" | grep -q "Uploads backup: SKIPPED"; then
|
||
log_pass "Skip uploads option works"
|
||
else
|
||
log_fail "Skip uploads option doesn't show correct status"
|
||
fi
|
||
else
|
||
log_fail "Skip uploads option test failed"
|
||
fi
|
||
}
|
||
|
||
# Test environment variable validation
|
||
test_env_validation() {
|
||
TESTS_RUN=$((TESTS_RUN + 1))
|
||
log_test "Environment Variable Validation"
|
||
|
||
# Temporarily move .env file to test missing env
|
||
local env_file="$(dirname "$RESTORE_SCRIPT")/../.env"
|
||
if [ -f "$env_file" ]; then
|
||
mv "$env_file" "${env_file}.backup"
|
||
|
||
local output
|
||
if output=$("$RESTORE_SCRIPT" --help 2>&1); then
|
||
if echo "$output" | grep -q "Error.*env"; then
|
||
log_pass "Missing environment file detection works"
|
||
else
|
||
log_fail "Script should detect missing .env file"
|
||
fi
|
||
else
|
||
log_pass "Script correctly fails with missing .env file"
|
||
fi
|
||
|
||
# Restore .env file
|
||
mv "${env_file}.backup" "$env_file"
|
||
else
|
||
log_info "No .env file found to test with"
|
||
fi
|
||
}
|
||
|
||
# Test notification system (dry run)
|
||
test_notification_system() {
|
||
TESTS_RUN=$((TESTS_RUN + 1))
|
||
log_test "Notification System"
|
||
|
||
local mock_db_backup="${TEST_DIR}/mock_immich_db_backup_20250603_120000.sql.gz"
|
||
local mock_uploads_backup="${TEST_DIR}/mock_immich_uploads_20250603_120000.tar.gz"
|
||
|
||
# Set a test webhook URL temporarily
|
||
export WEBHOOK_URL="https://httpbin.org/post"
|
||
|
||
local output
|
||
if output=$("$RESTORE_SCRIPT" --db-backup "$mock_db_backup" --uploads-backup "$mock_uploads_backup" --dry-run 2>&1); then
|
||
if echo "$output" | grep -q "Immich Restore Started"; then
|
||
log_pass "Notification system appears to be working"
|
||
else
|
||
log_fail "Notification system not triggering properly"
|
||
fi
|
||
else
|
||
log_fail "Test with notifications failed"
|
||
fi
|
||
|
||
unset WEBHOOK_URL
|
||
}
|
||
|
||
# Test argument parsing
|
||
test_argument_parsing() {
|
||
TESTS_RUN=$((TESTS_RUN + 1))
|
||
log_test "Argument Parsing"
|
||
|
||
# Test unknown option
|
||
local output
|
||
if output=$("$RESTORE_SCRIPT" --unknown-option 2>&1); then
|
||
if echo "$output" | grep -q "Unknown option"; then
|
||
log_pass "Unknown option detection works"
|
||
else
|
||
log_fail "Unknown option should be detected"
|
||
fi
|
||
else
|
||
log_pass "Script correctly fails with unknown option"
|
||
fi
|
||
}
|
||
|
||
# Test logging functionality
|
||
test_logging() {
|
||
TESTS_RUN=$((TESTS_RUN + 1))
|
||
log_test "Logging Functionality"
|
||
|
||
local log_dir="$(dirname "$RESTORE_SCRIPT")/../logs"
|
||
local restore_log="${log_dir}/immich-restore.log"
|
||
|
||
# Clear previous log entries
|
||
[ -f "$restore_log" ] && > "$restore_log"
|
||
|
||
local mock_db_backup="${TEST_DIR}/mock_immich_db_backup_20250603_120000.sql.gz"
|
||
local mock_uploads_backup="${TEST_DIR}/mock_immich_uploads_20250603_120000.tar.gz"
|
||
|
||
"$RESTORE_SCRIPT" --db-backup "$mock_db_backup" --uploads-backup "$mock_uploads_backup" --dry-run >/dev/null 2>&1
|
||
|
||
if [ -f "$restore_log" ] && [ -s "$restore_log" ]; then
|
||
if grep -q "IMMICH RESTORATION STARTED" "$restore_log"; then
|
||
log_pass "Logging functionality works"
|
||
else
|
||
log_fail "Log file exists but doesn't contain expected content"
|
||
fi
|
||
else
|
||
log_fail "Log file not created or is empty"
|
||
fi
|
||
}
|
||
|
||
# Performance test with larger mock files
|
||
test_performance() {
|
||
TESTS_RUN=$((TESTS_RUN + 1))
|
||
log_test "Performance with Larger Files"
|
||
|
||
# Create a larger mock database backup
|
||
local large_db_backup="${TEST_DIR}/large_mock_db.sql.gz"
|
||
{
|
||
echo "-- Large Mock Database Backup"
|
||
for i in {1..1000}; do
|
||
echo "INSERT INTO test_table VALUES ($i, 'test_data_$i');"
|
||
done
|
||
echo "-- End of large mock backup"
|
||
} | gzip > "$large_db_backup"
|
||
|
||
# Create larger mock uploads
|
||
local large_uploads_backup="${TEST_DIR}/large_mock_uploads.tar.gz"
|
||
mkdir -p "${TEST_DIR}/large_mock_uploads"
|
||
for i in {1..50}; do
|
||
echo "Mock large file content $i" > "${TEST_DIR}/large_mock_uploads/file_$i.txt"
|
||
done
|
||
tar -czf "$large_uploads_backup" -C "${TEST_DIR}" large_mock_uploads
|
||
|
||
local start_time=$(date +%s)
|
||
local output
|
||
if output=$("$RESTORE_SCRIPT" --db-backup "$large_db_backup" --uploads-backup "$large_uploads_backup" --dry-run 2>&1); then
|
||
local end_time=$(date +%s)
|
||
local duration=$((end_time - start_time))
|
||
|
||
if [ $duration -lt 30 ]; then # Should complete dry run in under 30 seconds
|
||
log_pass "Performance test completed in ${duration}s"
|
||
else
|
||
log_fail "Performance test took too long: ${duration}s"
|
||
fi
|
||
else
|
||
log_fail "Performance test failed to execute"
|
||
fi
|
||
}
|
||
|
||
# Cleanup test data
|
||
cleanup_test_data() {
|
||
log_test "Cleaning up test data"
|
||
rm -rf "$TEST_DIR"
|
||
log_info "Test data cleaned up"
|
||
}
|
||
|
||
# Generate test report
|
||
generate_test_report() {
|
||
echo ""
|
||
echo "=================================================================="
|
||
echo -e "${BLUE}IMMICH RESTORE SCRIPT TEST REPORT${NC}"
|
||
echo "=================================================================="
|
||
echo "Test Date: $(date)"
|
||
echo "Script Tested: $RESTORE_SCRIPT"
|
||
echo ""
|
||
echo "Test Results:"
|
||
echo -e " Total Tests Run: ${BLUE}$TESTS_RUN${NC}"
|
||
echo -e " Tests Passed: ${GREEN}$TESTS_PASSED${NC}"
|
||
echo -e " Tests Failed: ${RED}$TESTS_FAILED${NC}"
|
||
echo ""
|
||
|
||
if [ $TESTS_FAILED -eq 0 ]; then
|
||
echo -e "${GREEN}✅ ALL TESTS PASSED${NC}"
|
||
echo "The restore script is ready for production use."
|
||
else
|
||
echo -e "${RED}❌ SOME TESTS FAILED${NC}"
|
||
echo "Please review the test log for details: $TEST_LOG"
|
||
fi
|
||
|
||
echo ""
|
||
echo "Test Log Location: $TEST_LOG"
|
||
echo "=================================================================="
|
||
}
|
||
|
||
# Main test execution
|
||
main() {
|
||
echo -e "${BLUE}Starting Immich Restore Script Test Suite${NC}"
|
||
echo "Test Directory: $TEST_DIR"
|
||
echo "Log File: $TEST_LOG"
|
||
echo ""
|
||
|
||
# Initialize test log
|
||
echo "=== Immich Restore Script Test Suite ===" > "$TEST_LOG"
|
||
echo "Started: $(date)" >> "$TEST_LOG"
|
||
echo "" >> "$TEST_LOG"
|
||
|
||
# Run all tests
|
||
test_script_setup
|
||
create_mock_backups
|
||
test_help_functionality
|
||
test_dry_run
|
||
test_file_validation
|
||
test_corrupted_file_detection
|
||
test_skip_options
|
||
test_env_validation
|
||
test_notification_system
|
||
test_argument_parsing
|
||
test_logging
|
||
test_performance
|
||
|
||
# Cleanup and report
|
||
cleanup_test_data
|
||
generate_test_report
|
||
|
||
# Exit with appropriate code
|
||
if [ $TESTS_FAILED -eq 0 ]; then
|
||
exit 0
|
||
else
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
# Run tests
|
||
main "$@"
|