test-sync.sh raw

   1  #!/bin/bash
   2  #
   3  # Negentropy Interop Test Script
   4  # Tests NIP-77 negentropy sync between strfry and orly
   5  #
   6  # Usage:
   7  #   ./test-sync.sh
   8  #
   9  # Prerequisites:
  10  #   - docker compose up -d (containers running)
  11  #   - websocat and jq installed (for event generation)
  12  #
  13  
  14  set -e
  15  
  16  STRFRY_URL="ws://localhost:7777"
  17  ORLY_URL="ws://localhost:3334"
  18  TEST_EVENTS=10
  19  PASSED=0
  20  FAILED=0
  21  
  22  # Colors for output
  23  RED='\033[0;31m'
  24  GREEN='\033[0;32m'
  25  YELLOW='\033[1;33m'
  26  NC='\033[0m' # No Color
  27  
  28  log_info() {
  29      echo -e "${YELLOW}[INFO]${NC} $1"
  30  }
  31  
  32  log_pass() {
  33      echo -e "${GREEN}[PASS]${NC} $1"
  34      ((PASSED++))
  35  }
  36  
  37  log_fail() {
  38      echo -e "${RED}[FAIL]${NC} $1"
  39      ((FAILED++))
  40  }
  41  
  42  # Generate a test private key (for signing events)
  43  # This is a fixed test key - DO NOT use in production
  44  TEST_PRIVKEY="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
  45  TEST_PUBKEY="a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1"
  46  
  47  # Check if services are running
  48  check_services() {
  49      log_info "Checking if services are running..."
  50  
  51      if ! curl -s http://localhost:7777 > /dev/null 2>&1; then
  52          log_fail "strfry is not running on port 7777"
  53          exit 1
  54      fi
  55      log_pass "strfry is running"
  56  
  57      if ! curl -s http://localhost:3334 > /dev/null 2>&1; then
  58          log_fail "orly is not running on port 3334"
  59          exit 1
  60      fi
  61      log_pass "orly is running"
  62  }
  63  
  64  # Generate and send a test event to a relay
  65  # Uses websocat if available, otherwise uses netcat
  66  send_event() {
  67      local relay_url=$1
  68      local kind=$2
  69      local content=$3
  70      local timestamp=$(date +%s)
  71  
  72      # Create a simple unsigned event (strfry will accept it in test mode)
  73      # For real testing, we'd need proper signing
  74      local event_json=$(cat <<EOF
  75  ["EVENT", {
  76      "id": "$(echo -n "${timestamp}${content}" | sha256sum | cut -d' ' -f1)",
  77      "pubkey": "${TEST_PUBKEY}",
  78      "created_at": ${timestamp},
  79      "kind": ${kind},
  80      "tags": [],
  81      "content": "${content}",
  82      "sig": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
  83  }]
  84  EOF
  85  )
  86  
  87      echo "$event_json" | websocat -n1 "$relay_url" 2>/dev/null || true
  88  }
  89  
  90  # Count events on a relay matching a filter
  91  count_events() {
  92      local relay_url=$1
  93      local filter=$2
  94  
  95      # Send REQ and count EVENT responses before EOSE
  96      local count=$(echo "[\"REQ\", \"count\", $filter]" | \
  97          timeout 5 websocat -n "$relay_url" 2>/dev/null | \
  98          grep -c '"EVENT"' || echo "0")
  99  
 100      echo "$count"
 101  }
 102  
 103  # Test 1: Seed strfry with events
 104  test_seed_strfry() {
 105      log_info "Test 1: Seeding strfry with $TEST_EVENTS test events..."
 106  
 107      for i in $(seq 1 $TEST_EVENTS); do
 108          send_event "$STRFRY_URL" 1 "Test event $i from strfry seed"
 109          sleep 0.1
 110      done
 111  
 112      sleep 2  # Wait for events to be processed
 113  
 114      local count=$(count_events "$STRFRY_URL" '{"kinds": [1], "limit": 100}')
 115      log_info "strfry event count: $count"
 116  
 117      if [ "$count" -ge "$TEST_EVENTS" ]; then
 118          log_pass "strfry seeded with $count events"
 119      else
 120          log_fail "strfry only has $count events (expected >= $TEST_EVENTS)"
 121      fi
 122  }
 123  
 124  # Test 2: Run orly sync to pull from strfry
 125  test_orly_sync_from_strfry() {
 126      log_info "Test 2: Running orly sync from strfry (strfry -> orly)..."
 127  
 128      # Use the sync-runner container to run sync
 129      docker compose exec -T sync-runner /app/orly sync ws://strfry:7777 --filter '{"kinds": [1]}' --dir down -v
 130  
 131      sleep 3  # Wait for sync to complete
 132  
 133      local orly_count=$(count_events "$ORLY_URL" '{"kinds": [1], "limit": 100}')
 134      log_info "orly event count after sync: $orly_count"
 135  
 136      if [ "$orly_count" -ge "$TEST_EVENTS" ]; then
 137          log_pass "orly synced $orly_count events from strfry"
 138      else
 139          log_fail "orly only synced $orly_count events (expected >= $TEST_EVENTS)"
 140      fi
 141  }
 142  
 143  # Test 3: Seed orly with new events and sync back to strfry
 144  test_orly_sync_to_strfry() {
 145      log_info "Test 3: Seeding orly and syncing back to strfry (orly -> strfry)..."
 146  
 147      local new_events=5
 148      for i in $(seq 1 $new_events); do
 149          send_event "$ORLY_URL" 1 "Test event $i from orly for reverse sync"
 150          sleep 0.1
 151      done
 152  
 153      sleep 2
 154  
 155      # Check orly has the new events
 156      local orly_count=$(count_events "$ORLY_URL" '{"kinds": [1], "limit": 100}')
 157      log_info "orly event count: $orly_count"
 158  
 159      # Run strfry sync from orly (using strfry's sync command)
 160      # Note: strfry sync command format is: strfry sync <url> --filter <filter> --dir down
 161      docker compose exec -T strfry /app/strfry sync ws://orly:3334 --filter '{"kinds": [1]}' --dir down || true
 162  
 163      sleep 3
 164  
 165      local strfry_count=$(count_events "$STRFRY_URL" '{"kinds": [1], "limit": 100}')
 166      log_info "strfry event count after reverse sync: $strfry_count"
 167  
 168      local expected=$((TEST_EVENTS + new_events))
 169      if [ "$strfry_count" -ge "$expected" ]; then
 170          log_pass "strfry has $strfry_count events after bidirectional sync"
 171      else
 172          log_fail "strfry only has $strfry_count events (expected >= $expected)"
 173      fi
 174  }
 175  
 176  # Test 4: Verify event consistency
 177  test_event_consistency() {
 178      log_info "Test 4: Verifying event consistency between relays..."
 179  
 180      local strfry_count=$(count_events "$STRFRY_URL" '{"kinds": [1], "limit": 100}')
 181      local orly_count=$(count_events "$ORLY_URL" '{"kinds": [1], "limit": 100}')
 182  
 183      log_info "strfry: $strfry_count events, orly: $orly_count events"
 184  
 185      # After bidirectional sync, both should have similar counts
 186      local diff=$((strfry_count - orly_count))
 187      if [ "${diff#-}" -le 2 ]; then  # Allow small difference
 188          log_pass "Event counts are consistent (diff: $diff)"
 189      else
 190          log_fail "Event counts differ by $diff"
 191      fi
 192  }
 193  
 194  # Run all tests
 195  main() {
 196      echo "========================================"
 197      echo "Negentropy Interop Test Suite"
 198      echo "strfry <-> orly (NIP-77)"
 199      echo "========================================"
 200      echo
 201  
 202      check_services
 203      echo
 204  
 205      test_seed_strfry
 206      echo
 207  
 208      test_orly_sync_from_strfry
 209      echo
 210  
 211      test_orly_sync_to_strfry
 212      echo
 213  
 214      test_event_consistency
 215      echo
 216  
 217      echo "========================================"
 218      echo "Results: $PASSED passed, $FAILED failed"
 219      echo "========================================"
 220  
 221      if [ "$FAILED" -gt 0 ]; then
 222          exit 1
 223      fi
 224  }
 225  
 226  main "$@"
 227