Files
c-relay-pg/tests/udp_ingress_test.sh
T

204 lines
8.5 KiB
Bash
Executable File

#!/bin/bash
# UDP Ingress Test — sends Nostr events via UDP datagrams and verifies storage
set -e
RELAY_HOST="127.0.0.1"
RELAY_PORT="${RELAY_PORT:-8888}"
UDP_PORT="${UDP_PORT:-8888}"
DB_NAME="${DB_NAME:-crelay}"
DB_USER="${DB_USER:-crelay}"
DB_HOST="${DB_HOST:-localhost}"
PASS=0
FAIL=0
# Color constants
RED='\033[31m'
GREEN='\033[32m'
YELLOW='\033[33m'
BLUE='\033[34m'
BOLD='\033[1m'
RESET='\033[0m'
pass() { echo -e "${GREEN}✅ PASS:${RESET} $1"; PASS=$((PASS+1)); }
fail() { echo -e "${RED}❌ FAIL:${RESET} $1"; FAIL=$((FAIL+1)); }
info() { echo -e "${BLUE}[INFO]${RESET} $1"; }
warn() { echo -e "${YELLOW}[WARN]${RESET} $1"; }
echo -e "${BOLD}=== UDP Ingress Test ===${RESET}"
echo "Relay: ${RELAY_HOST}:${RELAY_PORT}, UDP: ${UDP_PORT}"
echo ""
# ---------------------------------------------------------------------------
# Test 1: Check relay is running
# ---------------------------------------------------------------------------
if pgrep -f "c_relay_pg" >/dev/null 2>&1; then
pass "Relay process is running"
else
fail "Relay process is not running"
echo "Start it with: ./make_and_restart_relay.sh"
exit 1
fi
# ---------------------------------------------------------------------------
# Test 2: Check UDP socket is listening
# ---------------------------------------------------------------------------
if ss -lun 2>/dev/null | grep -q ":${UDP_PORT} "; then
pass "UDP socket is listening on port ${UDP_PORT}"
else
fail "UDP socket is NOT listening on port ${UDP_PORT}"
echo ""
echo "Enable UDP ingress with:"
echo " PGPASSWORD=crelay psql -h localhost -U crelay -d crelay -c \\"
echo " \"UPDATE config SET value='true' WHERE key='udp_ingress_enabled';\""
echo " PGPASSWORD=crelay psql -h localhost -U crelay -d crelay -c \\"
echo " \"UPDATE config SET value='8888' WHERE key='udp_ingress_port';\""
echo "Then restart the relay or wait for hot-reload."
exit 1
fi
# ---------------------------------------------------------------------------
# Test 3: Send a valid event via UDP and verify it was stored
# ---------------------------------------------------------------------------
info "Test 3: Sending valid Nostr event via UDP..."
if command -v nak >/dev/null 2>&1; then
# Generate a random key and event using nak
RAND_SEC=$(nak key generate 2>/dev/null)
EVENT_JSON=$(nak event -k 1 -c "UDP ingress test $(date +%s)" --sec "$RAND_SEC" 2>/dev/null)
if [ -n "$EVENT_JSON" ]; then
EVENT_ID=$(echo "$EVENT_JSON" | jq -r '.id' 2>/dev/null)
if [ -n "$EVENT_ID" ] && [ "$EVENT_ID" != "null" ]; then
# Send via UDP using Python
echo "$EVENT_JSON" | python3 -c "
import socket, sys
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
data = sys.stdin.read().encode()
sock.sendto(data, ('${RELAY_HOST}', ${UDP_PORT}))
" 2>/dev/null || {
# Fallback: try nc -u
echo "$EVENT_JSON" | nc -u -w1 "${RELAY_HOST}" "${UDP_PORT}" 2>/dev/null || true
}
sleep 1 # Give relay time to process
# Verify event was stored in database
RESULT=$(PGPASSWORD=crelay psql -h "${DB_HOST}" -U "${DB_USER}" -d "${DB_NAME}" \
-tAc "SELECT 1 FROM events WHERE id='${EVENT_ID}'" 2>/dev/null)
if [ "$RESULT" = "1" ]; then
pass "Valid event stored in database (id=${EVENT_ID:0:16}...)"
else
fail "Valid event NOT found in database (id=${EVENT_ID:0:16}...)"
fi
else
fail "Could not extract event ID from nak output"
fi
else
fail "nak event generation failed"
fi
else
warn "nak not found — generating event manually via Python"
# Fallback: create a minimal (but properly signed) event using Python
# This requires the secp256k1 library; if unavailable, skip.
if python3 -c "import secp256k1" 2>/dev/null; then
EVENT_JSON=$(python3 -c "
import json, time, hashlib, secp256k1
# Generate random private key
priv = secp256k1.PrivateKey()
pub = priv.pubkey.serialize().hex()
# Build event
event = {
'id': '',
'pubkey': pub,
'created_at': int(time.time()),
'kind': 1,
'tags': [],
'content': 'UDP ingress test ' + str(int(time.time())),
'sig': ''
}
# Compute id
serial = json.dumps([0, pub, event['created_at'], 1, [], event['content']], separators=(',', ':'))
event['id'] = hashlib.sha256(serial.encode()).hexdigest()
# Sign
sig = priv.ecdsa_serialize(priv.ecdsa_sign(hashlib.sha256(bytes.fromhex(event['id'])).digest()))
event['sig'] = sig.hex()
print(json.dumps(event))
" 2>/dev/null)
if [ -n "$EVENT_JSON" ]; then
EVENT_ID=$(echo "$EVENT_JSON" | jq -r '.id' 2>/dev/null)
echo "$EVENT_JSON" | python3 -c "
import socket, sys
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(sys.stdin.read().encode(), ('${RELAY_HOST}', ${UDP_PORT}))
" 2>/dev/null || true
sleep 1
RESULT=$(PGPASSWORD=crelay psql -h "${DB_HOST}" -U "${DB_USER}" -d "${DB_NAME}" \
-tAc "SELECT 1 FROM events WHERE id='${EVENT_ID}'" 2>/dev/null)
if [ "$RESULT" = "1" ]; then
pass "Valid event stored in database (id=${EVENT_ID:0:16}...)"
else
fail "Valid event NOT found in database (id=${EVENT_ID:0:16}...)"
fi
else
fail "Python event generation failed (no secp256k1?)"
fi
else
fail "nak not installed and Python secp256k1 unavailable — cannot generate valid event"
fi
fi
# ---------------------------------------------------------------------------
# Test 4: Send an invalid event (bad signature) — should be silently dropped
# ---------------------------------------------------------------------------
info "Test 4: Sending invalid event (bad signature) via UDP..."
INVALID_ID="0000000000000000000000000000000000000000000000000000000000000000"
echo '{"id":"0000000000000000000000000000000000000000000000000000000000000000","pubkey":"0000000000000000000000000000000000000000000000000000000000000000","created_at":1,"kind":1,"tags":[],"content":"invalid","sig":"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}' \
| python3 -c "
import socket, sys
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(sys.stdin.read().encode(), ('${RELAY_HOST}', ${UDP_PORT}))
" 2>/dev/null || echo "$INVALID_JSON" | nc -u -w1 "${RELAY_HOST}" "${UDP_PORT}" 2>/dev/null || true
sleep 1
# Verify the invalid event was NOT stored
RESULT=$(PGPASSWORD=crelay psql -h "${DB_HOST}" -U "${DB_USER}" -d "${DB_NAME}" \
-tAc "SELECT 1 FROM events WHERE id='${INVALID_ID}'" 2>/dev/null)
if [ "$RESULT" != "1" ]; then
pass "Invalid event was silently dropped (not stored)"
else
fail "Invalid event was incorrectly stored!"
fi
# ---------------------------------------------------------------------------
# Test 5: Send an oversized datagram — should be rejected
# ---------------------------------------------------------------------------
info "Test 5: Sending oversized datagram via UDP..."
# Build a payload larger than the default max datagram size (65535 bytes)
LARGE_CONTENT=$(python3 -c "print('x' * 70000)" 2>/dev/null)
LARGE_JSON="{\"id\":\"1111111111111111111111111111111111111111111111111111111111111111\",\"pubkey\":\"1111111111111111111111111111111111111111111111111111111111111111\",\"created_at\":1,\"kind\":1,\"tags\":[],\"content\":\"${LARGE_CONTENT}\",\"sig\":\"1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\"}"
echo "$LARGE_JSON" | python3 -c "
import socket, sys
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
data = sys.stdin.read().encode()
try:
sock.sendto(data, ('${RELAY_HOST}', ${UDP_PORT}))
except OSError as e:
print(f'Message too large (expected): {e}')
" 2>/dev/null || true
sleep 1
# The oversized message should not have been stored
RESULT=$(PGPASSWORD=crelay psql -h "${DB_HOST}" -U "${DB_USER}" -d "${DB_NAME}" \
-tAc "SELECT 1 FROM events WHERE id='1111111111111111111111111111111111111111111111111111111111111111'" 2>/dev/null)
if [ "$RESULT" != "1" ]; then
pass "Oversized datagram was rejected (not stored)"
else
fail "Oversized datagram was incorrectly stored!"
fi
# ---------------------------------------------------------------------------
# Summary
# ---------------------------------------------------------------------------
echo ""
echo -e "${BOLD}=== Results: ${PASS} passed, ${FAIL} failed ===${RESET}"
exit $FAIL