|
| 1 | +#!/bin/bash |
| 2 | +# test-restart-persistence.sh |
| 3 | +# |
| 4 | +# End-to-end test demonstrating that "docker compose down && docker compose up -d" |
| 5 | +# does not lose durable state. |
| 6 | +# |
| 7 | +# Requirements: |
| 8 | +# - Docker and docker compose installed |
| 9 | +# - curl and jq installed |
| 10 | +# - Run from the repository root |
| 11 | +# |
| 12 | +# What this script tests: |
| 13 | +# 1. Start the stack |
| 14 | +# 2. Create a participant and schedule a job |
| 15 | +# 3. Stop the stack (docker compose down) |
| 16 | +# 4. Restart the stack (docker compose up -d) |
| 17 | +# 5. Verify that flow state and the scheduled job persist |
| 18 | +# |
| 19 | +# Usage: |
| 20 | +# ./test-scripts/test-restart-persistence.sh |
| 21 | + |
| 22 | +set -euo pipefail |
| 23 | + |
| 24 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 25 | +PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" |
| 26 | + |
| 27 | +# Colors |
| 28 | +RED='\033[0;31m' |
| 29 | +GREEN='\033[0;32m' |
| 30 | +YELLOW='\033[1;33m' |
| 31 | +NC='\033[0m' |
| 32 | + |
| 33 | +TESTS_PASSED=0 |
| 34 | +TESTS_FAILED=0 |
| 35 | + |
| 36 | +success() { echo -e "${GREEN}✓${NC} $1"; ((TESTS_PASSED++)); } |
| 37 | +error() { echo -e "${RED}✗${NC} $1"; ((TESTS_FAILED++)); } |
| 38 | +info() { echo -e "${YELLOW}→${NC} $1"; } |
| 39 | + |
| 40 | +API_BASE_URL="${API_BASE_URL:-http://localhost:8080}" |
| 41 | +TEST_PHONE="${TEST_PHONE:-15551234567}" |
| 42 | + |
| 43 | +wait_for_api() { |
| 44 | + local max_attempts=30 |
| 45 | + local attempt=0 |
| 46 | + info "Waiting for API at $API_BASE_URL..." |
| 47 | + while [ $attempt -lt $max_attempts ]; do |
| 48 | + if curl -sf "$API_BASE_URL/receipts" >/dev/null 2>&1; then |
| 49 | + success "API is ready" |
| 50 | + return 0 |
| 51 | + fi |
| 52 | + attempt=$((attempt + 1)) |
| 53 | + sleep 2 |
| 54 | + done |
| 55 | + error "API did not become ready after $((max_attempts * 2))s" |
| 56 | + return 1 |
| 57 | +} |
| 58 | + |
| 59 | +cleanup() { |
| 60 | + info "Cleaning up..." |
| 61 | + cd "$PROJECT_DIR" |
| 62 | + docker compose down --timeout 10 2>/dev/null || true |
| 63 | +} |
| 64 | + |
| 65 | +# ---- Main ---- |
| 66 | +cd "$PROJECT_DIR" |
| 67 | +trap cleanup EXIT |
| 68 | + |
| 69 | +info "Step 1: Starting stack" |
| 70 | +docker compose up -d --build --wait 2>&1 || { error "docker compose up failed"; exit 1; } |
| 71 | +wait_for_api |
| 72 | + |
| 73 | +info "Step 2: Creating a conversation participant" |
| 74 | +ENROLL_RESPONSE=$(curl -sf -X POST -H "Content-Type: application/json" \ |
| 75 | + -d "{\"phone_number\": \"$TEST_PHONE\", \"name\": \"Restart Test\"}" \ |
| 76 | + "$API_BASE_URL/conversation/participants" 2>&1) || true |
| 77 | + |
| 78 | +if echo "$ENROLL_RESPONSE" | jq -e '.result.id' >/dev/null 2>&1; then |
| 79 | + PARTICIPANT_ID=$(echo "$ENROLL_RESPONSE" | jq -r '.result.id') |
| 80 | + success "Participant created: $PARTICIPANT_ID" |
| 81 | +else |
| 82 | + # Participant may already exist, try to look up by phone |
| 83 | + LOOKUP_RESPONSE=$(curl -sf "$API_BASE_URL/conversation/participants" 2>&1) || true |
| 84 | + PARTICIPANT_ID=$(echo "$LOOKUP_RESPONSE" | jq -r ".result[] | select(.phone_number==\"$TEST_PHONE\") | .id" 2>/dev/null) || true |
| 85 | + if [ -n "$PARTICIPANT_ID" ] && [ "$PARTICIPANT_ID" != "null" ]; then |
| 86 | + success "Participant already exists: $PARTICIPANT_ID" |
| 87 | + else |
| 88 | + error "Could not create or find participant" |
| 89 | + info "Enroll response: $ENROLL_RESPONSE" |
| 90 | + fi |
| 91 | +fi |
| 92 | + |
| 93 | +info "Step 3: Checking receipts count (baseline)" |
| 94 | +RECEIPTS_BEFORE=$(curl -sf "$API_BASE_URL/receipts" | jq 'if type == "array" then length elif .result then (.result | length) else 0 end' 2>/dev/null || echo "0") |
| 95 | +info "Receipts before restart: $RECEIPTS_BEFORE" |
| 96 | + |
| 97 | +info "Step 4: Stopping stack (docker compose down)" |
| 98 | +docker compose down --timeout 10 |
| 99 | +success "Stack stopped" |
| 100 | + |
| 101 | +info "Step 5: Restarting stack (docker compose up -d)" |
| 102 | +docker compose up -d --wait 2>&1 || { error "docker compose up after restart failed"; exit 1; } |
| 103 | +wait_for_api |
| 104 | + |
| 105 | +info "Step 6: Verifying state persisted after restart" |
| 106 | + |
| 107 | +# Check participant still exists |
| 108 | +PARTICIPANTS=$(curl -sf "$API_BASE_URL/conversation/participants" 2>&1) || true |
| 109 | +FOUND_PARTICIPANT=$(echo "$PARTICIPANTS" | jq -r ".result[] | select(.phone_number==\"$TEST_PHONE\") | .id" 2>/dev/null) || true |
| 110 | + |
| 111 | +if [ -n "$FOUND_PARTICIPANT" ] && [ "$FOUND_PARTICIPANT" != "null" ]; then |
| 112 | + success "Participant persisted after restart: $FOUND_PARTICIPANT" |
| 113 | +else |
| 114 | + error "Participant NOT found after restart" |
| 115 | +fi |
| 116 | + |
| 117 | +# Check receipts still exist |
| 118 | +RECEIPTS_AFTER=$(curl -sf "$API_BASE_URL/receipts" | jq 'if type == "array" then length elif .result then (.result | length) else 0 end' 2>/dev/null || echo "0") |
| 119 | +info "Receipts after restart: $RECEIPTS_AFTER" |
| 120 | + |
| 121 | +if [ "$RECEIPTS_AFTER" -ge "$RECEIPTS_BEFORE" ]; then |
| 122 | + success "Receipts persisted after restart ($RECEIPTS_BEFORE -> $RECEIPTS_AFTER)" |
| 123 | +else |
| 124 | + error "Receipts lost after restart ($RECEIPTS_BEFORE -> $RECEIPTS_AFTER)" |
| 125 | +fi |
| 126 | + |
| 127 | +# ---- Summary ---- |
| 128 | +echo |
| 129 | +echo "==================================" |
| 130 | +echo "Restart Persistence Test Summary" |
| 131 | +echo "==================================" |
| 132 | +echo -e "${GREEN}Passed: $TESTS_PASSED${NC}" |
| 133 | +echo -e "${RED}Failed: $TESTS_FAILED${NC}" |
| 134 | +echo "Total: $((TESTS_PASSED + TESTS_FAILED))" |
| 135 | + |
| 136 | +if [ $TESTS_FAILED -eq 0 ]; then |
| 137 | + echo -e "${GREEN}All tests passed! State survives restart.${NC}" |
| 138 | + exit 0 |
| 139 | +else |
| 140 | + echo -e "${RED}Some tests failed!${NC}" |
| 141 | + exit 1 |
| 142 | +fi |
0 commit comments