#!/bin/bash # Comprehensive Error Handling and Recovery Testing for Event-Based Configuration System # Tests various failure scenarios and recovery mechanisms set -e # Configuration RELAY_BINARY="./build/c_relay_x86" TEST_DB_PREFIX="test_relay" LOG_FILE="test_results.log" # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Test results tracking TESTS_PASSED=0 TESTS_FAILED=0 TESTS_TOTAL=0 # Function to print colored output print_test_header() { echo -e "${BLUE}[TEST]${NC} $1" ((TESTS_TOTAL++)) } print_success() { echo -e "${GREEN}[PASS]${NC} $1" ((TESTS_PASSED++)) } print_failure() { echo -e "${RED}[FAIL]${NC} $1" ((TESTS_FAILED++)) } print_info() { echo -e "${YELLOW}[INFO]${NC} $1" } # Clean up function cleanup_test_files() { print_info "Cleaning up test files..." pkill -f "c_relay_" 2>/dev/null || true rm -f ${TEST_DB_PREFIX}*.nrdb* 2>/dev/null || true rm -f test_*.log 2>/dev/null || true sleep 1 } # Function to start relay and capture output start_relay_test() { local test_name="$1" local timeout="${2:-10}" print_info "Starting relay for test: $test_name" timeout $timeout $RELAY_BINARY > "test_${test_name}.log" 2>&1 & local relay_pid=$! sleep 2 if kill -0 $relay_pid 2>/dev/null; then echo $relay_pid else echo "0" fi } # Function to stop relay stop_relay_test() { local relay_pid="$1" if [ "$relay_pid" != "0" ]; then kill $relay_pid 2>/dev/null || true wait $relay_pid 2>/dev/null || true fi } # Function to check if relay started successfully check_relay_startup() { local log_file="$1" if grep -q "First-time startup sequence completed\|Existing relay startup" "$log_file" 2>/dev/null; then return 0 else return 1 fi } # Function to check if relay has admin keys check_admin_keys() { local log_file="$1" if grep -q "Admin Private Key:" "$log_file" 2>/dev/null; then return 0 else return 1 fi } # Function to check database file creation check_database_creation() { if ls *.nrdb 2>/dev/null | head -1; then return 0 else return 1 fi } # Function to check configuration event in database check_config_event_stored() { local db_file="$1" if [ -f "$db_file" ]; then local count=$(sqlite3 "$db_file" "SELECT COUNT(*) FROM events WHERE kind = 33334;" 2>/dev/null || echo "0") if [ "$count" -gt 0 ]; then return 0 fi fi return 1 } echo "========================================" echo "Event-Based Configuration System Tests" echo "========================================" echo # Ensure binary exists if [ ! -f "$RELAY_BINARY" ]; then print_failure "Relay binary not found. Please build first: make" exit 1 fi print_info "Starting comprehensive error handling and recovery tests..." echo # TEST 1: Normal First-Time Startup print_test_header "Test 1: Normal First-Time Startup" cleanup_test_files relay_pid=$(start_relay_test "first_startup" 15) sleep 5 stop_relay_test $relay_pid if check_relay_startup "test_first_startup.log"; then if check_admin_keys "test_first_startup.log"; then if db_file=$(check_database_creation); then if check_config_event_stored "$db_file"; then print_success "First-time startup completed successfully" else print_failure "Configuration event not stored in database" fi else print_failure "Database file not created" fi else print_failure "Admin keys not generated" fi else print_failure "Relay failed to complete startup" fi # TEST 2: Existing Relay Startup print_test_header "Test 2: Existing Relay Startup (using existing database)" relay_pid=$(start_relay_test "existing_startup" 10) sleep 3 stop_relay_test $relay_pid if check_relay_startup "test_existing_startup.log"; then if ! check_admin_keys "test_existing_startup.log"; then print_success "Existing relay startup (no new keys generated)" else print_failure "New admin keys generated for existing relay" fi else print_failure "Existing relay failed to start" fi # TEST 3: Corrupted Database Recovery print_test_header "Test 3: Corrupted Database Recovery" if db_file=$(check_database_creation); then # Corrupt the database by truncating it truncate -s 100 "$db_file" print_info "Database corrupted for recovery test" relay_pid=$(start_relay_test "corrupted_db" 10) sleep 3 stop_relay_test $relay_pid if grep -q "ERROR.*database\|Failed.*database\|disk I/O error" "test_corrupted_db.log"; then print_success "Corrupted database properly detected and handled" else print_failure "Corrupted database not properly handled" fi fi # TEST 4: Missing Database File Recovery print_test_header "Test 4: Missing Database File Recovery" cleanup_test_files # Create a database then remove it to simulate loss relay_pid=$(start_relay_test "create_db" 10) sleep 3 stop_relay_test $relay_pid if db_file=$(check_database_creation); then rm -f "$db_file"* print_info "Database files removed to test recovery" relay_pid=$(start_relay_test "missing_db" 15) sleep 5 stop_relay_test $relay_pid if check_relay_startup "test_missing_db.log"; then if check_admin_keys "test_missing_db.log"; then print_success "Missing database recovery successful (new keys generated)" else print_failure "New admin keys not generated after database loss" fi else print_failure "Failed to recover from missing database" fi fi # TEST 5: Invalid Configuration Event Handling print_test_header "Test 5: Configuration Event Structure Validation" # This test would require injecting an invalid configuration event # For now, we check that the validation functions are properly integrated if grep -q "nostr_validate_event_structure\|nostr_verify_event_signature" src/config.c; then print_success "Configuration event validation functions integrated" else print_failure "Configuration event validation functions not found" fi # TEST 6: Database Schema Version Check print_test_header "Test 6: Database Schema Consistency" if db_file=$(check_database_creation); then # Check that the database has the correct schema version schema_version=$(sqlite3 "$db_file" "SELECT value FROM schema_info WHERE key = 'version';" 2>/dev/null || echo "") if [ "$schema_version" = "4" ]; then print_success "Database schema version is correct (v4)" else print_failure "Database schema version incorrect: $schema_version (expected: 4)" fi # Check that legacy tables don't exist if ! sqlite3 "$db_file" ".tables" 2>/dev/null | grep -q "config_file_cache\|active_config"; then print_success "Legacy configuration tables properly removed" else print_failure "Legacy configuration tables still present" fi fi # TEST 7: Memory and Resource Management print_test_header "Test 7: Resource Cleanup and Memory Management" relay_pid=$(start_relay_test "resource_test" 15) sleep 5 # Check for memory leaks or resource issues (basic check) if kill -0 $relay_pid 2>/dev/null; then # Send termination signal and check cleanup kill -TERM $relay_pid 2>/dev/null || true sleep 2 if ! kill -0 $relay_pid 2>/dev/null; then if grep -q "Configuration system cleaned up" "test_resource_test.log"; then print_success "Resource cleanup completed successfully" else print_failure "Resource cleanup not logged properly" fi else kill -KILL $relay_pid 2>/dev/null || true print_failure "Relay did not shut down cleanly" fi else print_failure "Relay process not running for resource test" fi # TEST 8: Configuration Cache Consistency print_test_header "Test 8: Configuration Cache Consistency" if db_file=$(check_database_creation); then # Check that configuration is properly cached and accessible config_count=$(sqlite3 "$db_file" "SELECT COUNT(*) FROM events WHERE kind = 33334;" 2>/dev/null || echo "0") if [ "$config_count" -eq 1 ]; then print_success "Single configuration event stored (replaceable event working)" else print_failure "Multiple or no configuration events found: $config_count" fi fi # TEST 9: Network Port Binding print_test_header "Test 9: Network Port Availability and Binding" relay_pid=$(start_relay_test "network_test" 10) sleep 3 if kill -0 $relay_pid 2>/dev/null; then # Check if port 8888 is being used if netstat -tln 2>/dev/null | grep -q ":8888"; then print_success "Relay successfully bound to network port 8888" else print_failure "Relay not bound to expected port 8888" fi stop_relay_test $relay_pid else print_failure "Relay failed to start for network test" fi # TEST 10: Multiple Startup Attempts (Port Conflict) print_test_header "Test 10: Port Conflict Handling" relay_pid1=$(start_relay_test "port_conflict_1" 10) sleep 2 if kill -0 $relay_pid1 2>/dev/null; then # Try to start a second relay (should fail due to port conflict) relay_pid2=$(start_relay_test "port_conflict_2" 5) sleep 1 if [ "$relay_pid2" = "0" ] || ! kill -0 $relay_pid2 2>/dev/null; then print_success "Port conflict properly handled (second instance failed to start)" else print_failure "Multiple relay instances started (port conflict not handled)" stop_relay_test $relay_pid2 fi stop_relay_test $relay_pid1 else print_failure "First relay instance failed to start" fi # Final cleanup cleanup_test_files # Test Results Summary echo echo "========================================" echo "Test Results Summary" echo "========================================" echo "Tests Passed: $TESTS_PASSED" echo "Tests Failed: $TESTS_FAILED" echo "Total Tests: $TESTS_TOTAL" echo if [ $TESTS_FAILED -eq 0 ]; then print_success "ALL TESTS PASSED! Event-based configuration system is robust." exit 0 else print_failure "$TESTS_FAILED tests failed. Review the results above." echo print_info "Check individual test log files (test_*.log) for detailed error information." exit 1 fi