mailship/test/integration.sh

226 lines
6.3 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
# Integration test for Mailship
#
# Starts the server, runs through the full E2E flow, cleans up.
# Exits 0 on success, 1 on failure.
#
# Usage: ./test/integration.sh [--server-only]
# --server-only: just start the server (for manual testing)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
PORT="${PORT:-4741}"
BASE_URL="http://localhost:$PORT"
PASS=0
FAIL=0
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m'
cleanup() {
echo ""
echo "=== Cleaning up ==="
kill "$SERVER_PID" 2>/dev/null || true
wait "$SERVER_PID" 2>/dev/null || true
rm -rf "$PROJECT_DIR/test-data"
echo "Done."
}
trap cleanup EXIT
# Ensure we have a fresh build
cd "$PROJECT_DIR"
if [ ! -d "dist" ]; then
echo "Building project..."
npx tsc
fi
# Generate a random secret for the test
SECRET="$(openssl rand -hex 32)"
# Export env vars for the server
export MAILSHIP_SECRET="$SECRET"
export MAILSHIP_NAME="Mailship Test"
export MAILSHIP_URL="$BASE_URL"
export BASE_URL="$BASE_URL"
export SMTP_HOST="localhost"
export SMTP_PORT="587"
export SMTP_USER="test@test.com"
export SMTP_PASSWORD="test"
export SMTP_FROM="test@test.com"
export DEFAULT_RELAYS="wss://relay.damus.io"
export INDEXER_RELAYS="wss://purplepag.es"
export SEARCH_RELAYS="wss://relay.nostr.band"
export PORT="$PORT"
export DATA_DIR="$PROJECT_DIR/test-data"
mkdir -p "$DATA_DIR"
DB_PATH="$DATA_DIR/mailship.db"
echo "=== Starting server on port $PORT ==="
node dist/index.js &
SERVER_PID=$!
sleep 3
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
echo -e "${RED}Server failed to start${NC}"
exit 1
fi
echo "Server PID: $SERVER_PID"
if [ "${1:-}" = "--server-only" ]; then
echo ""
echo "Server running at $BASE_URL"
echo "DB at $DB_PATH"
echo "Press Ctrl+C to stop."
wait "$SERVER_PID"
exit 0
fi
echo ""
echo "========================================="
echo " INTEGRATION TESTS"
echo "========================================="
echo ""
pass() {
PASS=$((PASS + 1))
echo -e " ${GREEN}✓${NC} $1"
}
fail() {
FAIL=$((FAIL + 1))
echo -e " ${RED}✗${NC} $1"
}
# Helper: check that a JSON field equals expected value
check_field() {
local label="$1"
local json="$2"
local field="$3"
local expected="$4"
local actual
actual=$(echo "$json" | python3 -c "import sys,json; print(json.load(sys.stdin).get('$field',''))" 2>/dev/null)
if [ "$actual" = "$expected" ]; then
pass "$label"
else
fail "$label (expected $field=$expected, got $actual)"
fi
}
# Test 1: Health check
echo "1. Health check"
HEALTH=$(curl -s "$BASE_URL/")
check_field "Root endpoint returns Mailship" "$HEALTH" "name" "Mailship"
# Test 2: Register subscription
echo ""
echo "2. Register subscription"
REG=$(curl -s "$BASE_URL/subscription/email" -X PUT -H "Content-Type: application/json" \
-d '{"email":"test@example.com","frequency":"daily","pubkey":"abc123"}')
KEY=$(echo "$REG" | python3 -c "import sys,json; print(json.load(sys.stdin).get('key',''))" 2>/dev/null)
CALLBACK=$(echo "$REG" | python3 -c "import sys,json; print(json.load(sys.stdin).get('callback',''))" 2>/dev/null)
if [ -n "$KEY" ] && [ -n "$CALLBACK" ]; then
pass "Registration returns key and callback"
else
fail "Registration missing key or callback (got: $REG)"
fi
# Test 3: Confirm subscription
echo ""
echo "3. Confirm subscription"
CONFIRM=$(curl -s "$BASE_URL/confirm?token=$KEY" 2>&1)
if echo "$CONFIRM" | grep -qi "success"; then
pass "Confirmation page shows success"
else
fail "Confirmation page doesn't show success"
fi
# Test 4: Check SQLite state
echo ""
echo "4. Database state"
CONFIRMED=$(sqlite3 "$DB_PATH" "SELECT confirmed_at FROM subscriptions WHERE email='test@example.com';" 2>/dev/null)
if [ -n "$CONFIRMED" ] && [ "$CONFIRMED" -gt 0 ]; then
pass "Subscription confirmed in DB"
else
fail "Subscription not confirmed in DB"
fi
# Test 5: Push event to notify endpoint
echo ""
echo "5. Push event via notify"
SUB_ID=$(sqlite3 "$DB_PATH" "SELECT id FROM subscriptions WHERE email='test@example.com';" 2>/dev/null)
# Fetch a real event from a relay
EVENT_ID=$(nak req -k 1 -l 1 wss://relay.primal.net 2>/dev/null | head -1 | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['id'])" 2>/dev/null)
if [ -z "$EVENT_ID" ]; then
# Try another relay
EVENT_ID=$(nak req -k 1 -l 1 wss://nos.lol 2>/dev/null | head -1 | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['id'])" 2>/dev/null)
fi
if [ -z "$EVENT_ID" ]; then
fail "Could not fetch event from relay (network issue?)"
else
NOTIFY=$(curl -s "$BASE_URL/notify/$SUB_ID" -X POST -H "Content-Type: application/json" \
-d "{\"id\":\"$EVENT_ID\",\"relay\":\"wss://relay.primal.net\"}")
check_field "Event stored in DB" "$NOTIFY" "stored" "True"
fi
# Test 6: Dedup
echo ""
echo "6. Dedup"
if [ -n "$EVENT_ID" ]; then
DEDUP=$(curl -s "$BASE_URL/notify/$SUB_ID" -X POST -H "Content-Type: application/json" \
-d "{\"id\":\"$EVENT_ID\",\"relay\":\"wss://relay.primal.net\"}")
check_field "Duplicate event rejected" "$DEDUP" "stored" "False"
fi
# Test 7: 404 for nonexistent subscription
echo ""
echo "7. 404 for nonexistent subscription"
NOT_FOUND=$(curl -s "$BASE_URL/notify/nonexistent-id" -X POST -H "Content-Type: application/json" \
-d '{"id":"abc","relay":"wss://relay.primal.net"}')
check_field "Nonexistent subscription returns 404" "$NOT_FOUND" "error" "Subscription not found"
# Test 8: Unsubscribe
echo ""
echo "8. Unsubscribe"
UNSUB=$(curl -s "$BASE_URL/unsubscribe?token=$KEY")
if echo "$UNSUB" | grep -qi "unsubscribed\|success"; then
pass "Unsubscribe page renders"
else
fail "Unsubscribe page didn't render"
fi
# Test 9: Notify after unsubscribe returns 404
echo ""
echo "9. No push after unsubscribe"
if [ -n "$EVENT_ID" ]; then
AFTER_UNSUB=$(curl -s "$BASE_URL/notify/$SUB_ID" -X POST -H "Content-Type: application/json" \
-d "{\"id\":\"$EVENT_ID\",\"relay\":\"wss://relay.primal.net\"}")
check_field "Push after unsubscribe returns 404" "$AFTER_UNSUB" "error" "Subscription not active"
fi
# Test 10: Delete subscription
echo ""
echo "10. Delete subscription"
DELETE=$(curl -s "$BASE_URL/subscription/$KEY" -X DELETE 2>&1)
check_field "Delete returns ok" "$DELETE" "ok" "True"
# Summary
echo ""
echo "========================================="
echo " RESULTS: $PASS passed, $FAIL failed"
echo "========================================="
if [ "$FAIL" -gt 0 ]; then
exit 1
fi
exit 0