#!/bin/bash # NIP-11 Relay Information Document Test # Tests HTTP endpoint for relay information according to NIP-11 specification set -e # Exit on any error # Color constants RED='\033[31m' GREEN='\033[32m' YELLOW='\033[33m' BLUE='\033[34m' BOLD='\033[1m' RESET='\033[0m' # Test configuration RELAY_URL="http://127.0.0.1:8888" RELAY_WS_URL="ws://127.0.0.1:8888" # Print functions print_header() { echo -e "${BLUE}${BOLD}=== $1 ===${RESET}" } print_step() { echo -e "${YELLOW}[STEP]${RESET} $1" } print_success() { echo -e "${GREEN}✓${RESET} $1" } print_error() { echo -e "${RED}✗${RESET} $1" } print_info() { echo -e "${BLUE}[INFO]${RESET} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${RESET} $1" } # Test functions test_http_with_correct_header() { print_step "Testing HTTP request with correct Accept header" local response="" local http_code="" if command -v curl &> /dev/null; then # Use curl to test with proper Accept header response=$(curl -s -H "Accept: application/nostr+json" "$RELAY_URL/" 2>/dev/null || echo "") http_code=$(curl -s -o /dev/null -w "%{http_code}" -H "Accept: application/nostr+json" "$RELAY_URL/" 2>/dev/null || echo "000") else print_error "curl command not found - required for NIP-11 testing" return 1 fi if [[ "$http_code" == "200" ]]; then print_success "HTTP 200 OK received with correct Accept header" # Validate JSON response if echo "$response" | jq . >/dev/null 2>&1; then print_success "Response is valid JSON" return 0 else print_error "Response is not valid JSON" return 1 fi else print_error "Expected HTTP 200, got HTTP $http_code" return 1 fi } test_http_without_header() { print_step "Testing HTTP request without Accept header (should return 406)" local http_code="" if command -v curl &> /dev/null; then http_code=$(curl -s -o /dev/null -w "%{http_code}" "$RELAY_URL/" 2>/dev/null || echo "000") else print_error "curl command not found - required for NIP-11 testing" return 1 fi if [[ "$http_code" == "406" ]]; then print_success "HTTP 406 Not Acceptable received without proper Accept header" return 0 else print_error "Expected HTTP 406, got HTTP $http_code" return 1 fi } test_http_with_wrong_header() { print_step "Testing HTTP request with wrong Accept header (should return 406)" local http_code="" if command -v curl &> /dev/null; then http_code=$(curl -s -o /dev/null -w "%{http_code}" -H "Accept: application/json" "$RELAY_URL/" 2>/dev/null || echo "000") else print_error "curl command not found - required for NIP-11 testing" return 1 fi if [[ "$http_code" == "406" ]]; then print_success "HTTP 406 Not Acceptable received with wrong Accept header" return 0 else print_error "Expected HTTP 406, got HTTP $http_code" return 1 fi } test_cors_headers() { print_step "Testing CORS headers presence" local headers="" if command -v curl &> /dev/null; then headers=$(curl -s -I -H "Accept: application/nostr+json" "$RELAY_URL/" 2>/dev/null || echo "") else print_error "curl command not found - required for NIP-11 testing" return 1 fi local cors_origin_found=false local cors_headers_found=false local cors_methods_found=false if echo "$headers" | grep -qi "access-control-allow-origin"; then cors_origin_found=true print_success "Access-Control-Allow-Origin header found" fi if echo "$headers" | grep -qi "access-control-allow-headers"; then cors_headers_found=true print_success "Access-Control-Allow-Headers header found" fi if echo "$headers" | grep -qi "access-control-allow-methods"; then cors_methods_found=true print_success "Access-Control-Allow-Methods header found" fi if [[ "$cors_origin_found" == true && "$cors_headers_found" == true && "$cors_methods_found" == true ]]; then print_success "All required CORS headers present" return 0 else print_error "Missing CORS headers" return 1 fi } test_json_structure() { print_step "Testing NIP-11 JSON structure and required fields" local response="" if command -v curl &> /dev/null; then response=$(curl -s -H "Accept: application/nostr+json" "$RELAY_URL/" 2>/dev/null || echo "") else print_error "curl command not found - required for NIP-11 testing" return 1 fi if [[ -z "$response" ]]; then print_error "Empty response received" return 1 fi # Validate JSON structure using jq if ! echo "$response" | jq . >/dev/null 2>&1; then print_error "Response is not valid JSON" return 1 fi print_success "Valid JSON structure confirmed" # Check for required fields local required_checks=0 local total_checks=0 # Test name field ((total_checks++)) if echo "$response" | jq -e '.name' >/dev/null 2>&1; then local name=$(echo "$response" | jq -r '.name') print_success "Name field present: $name" ((required_checks++)) else print_warning "Name field missing (optional)" fi # Test supported_nips field (required) ((total_checks++)) if echo "$response" | jq -e '.supported_nips' >/dev/null 2>&1; then local nips=$(echo "$response" | jq -r '.supported_nips | @json') print_success "Supported NIPs field present: $nips" ((required_checks++)) # Verify NIP-11 is in the supported list if echo "$response" | jq -e '.supported_nips | contains([11])' >/dev/null 2>&1; then print_success "NIP-11 correctly listed in supported NIPs" else print_warning "NIP-11 not found in supported NIPs list" fi else print_error "Supported NIPs field missing (should be present)" fi # Test software field ((total_checks++)) if echo "$response" | jq -e '.software' >/dev/null 2>&1; then local software=$(echo "$response" | jq -r '.software') print_success "Software field present: $software" ((required_checks++)) else print_warning "Software field missing (optional)" fi # Test version field ((total_checks++)) if echo "$response" | jq -e '.version' >/dev/null 2>&1; then local version=$(echo "$response" | jq -r '.version') print_success "Version field present: $version" ((required_checks++)) else print_warning "Version field missing (optional)" fi # Test limitation object ((total_checks++)) if echo "$response" | jq -e '.limitation' >/dev/null 2>&1; then print_success "Limitation object present" ((required_checks++)) # Check some common limitation fields if echo "$response" | jq -e '.limitation.max_message_length' >/dev/null 2>&1; then local max_msg=$(echo "$response" | jq -r '.limitation.max_message_length') print_info " max_message_length: $max_msg" fi if echo "$response" | jq -e '.limitation.max_subscriptions' >/dev/null 2>&1; then local max_subs=$(echo "$response" | jq -r '.limitation.max_subscriptions') print_info " max_subscriptions: $max_subs" fi else print_warning "Limitation object missing (recommended)" fi # Test description field if echo "$response" | jq -e '.description' >/dev/null 2>&1; then local description=$(echo "$response" | jq -r '.description') print_success "Description field present: ${description:0:50}..." else print_warning "Description field missing (optional)" fi print_info "JSON structure validation: $required_checks/$total_checks core fields present" return 0 } test_content_type_header() { print_step "Testing Content-Type header" local headers="" if command -v curl &> /dev/null; then headers=$(curl -s -I -H "Accept: application/nostr+json" "$RELAY_URL/" 2>/dev/null || echo "") else print_error "curl command not found - required for NIP-11 testing" return 1 fi if echo "$headers" | grep -qi "content-type.*application/nostr+json"; then print_success "Correct Content-Type header: application/nostr+json" return 0 else print_warning "Content-Type header not exactly 'application/nostr+json'" echo "$headers" | grep -i "content-type" | head -1 return 1 fi } test_non_root_path() { print_step "Testing non-root path (should return 404)" local http_code="" if command -v curl &> /dev/null; then http_code=$(curl -s -o /dev/null -w "%{http_code}" -H "Accept: application/nostr+json" "$RELAY_URL/nonexistent" 2>/dev/null || echo "000") else print_error "curl command not found - required for NIP-11 testing" return 1 fi if [[ "$http_code" == "404" ]]; then print_success "HTTP 404 Not Found received for non-root path" return 0 else print_error "Expected HTTP 404 for non-root path, got HTTP $http_code" return 1 fi } test_websocket_still_works() { print_step "Testing that WebSocket functionality still works on same port" if ! command -v websocat &> /dev/null; then print_warning "websocat not available - skipping WebSocket test" return 0 fi # Try to connect to WebSocket and send a simple REQ local response="" response=$(echo '["REQ","test_ws_nip11",{}]' | timeout 3s websocat "$RELAY_WS_URL" 2>/dev/null || echo "Connection failed") if [[ "$response" == *"Connection failed"* ]]; then print_error "WebSocket connection failed" return 1 elif [[ "$response" == *"EOSE"* ]]; then print_success "WebSocket still functional - received EOSE response" return 0 else print_warning "WebSocket response unclear, but connection succeeded" return 0 fi } # Main test function run_nip11_tests() { print_header "NIP-11 Relay Information Document Tests" # Check dependencies print_step "Checking dependencies..." if ! command -v curl &> /dev/null; then print_error "curl command not found - required for NIP-11 HTTP testing" return 1 fi if ! command -v jq &> /dev/null; then print_error "jq command not found - required for JSON validation" return 1 fi print_success "All dependencies found" print_header "PHASE 1: Basic HTTP Functionality" # Test 1: Correct Accept header if ! test_http_with_correct_header; then return 1 fi # Test 2: Missing Accept header if ! test_http_without_header; then return 1 fi # Test 3: Wrong Accept header if ! test_http_with_wrong_header; then return 1 fi print_header "PHASE 2: HTTP Headers Validation" # Test 4: CORS headers if ! test_cors_headers; then return 1 fi # Test 5: Content-Type header if ! test_content_type_header; then return 1 fi print_header "PHASE 3: JSON Structure Validation" # Test 6: JSON structure and required fields if ! test_json_structure; then return 1 fi print_header "PHASE 4: Additional Endpoint Behavior" # Test 7: Non-root paths if ! test_non_root_path; then return 1 fi # Test 8: WebSocket compatibility if ! test_websocket_still_works; then return 1 fi print_header "PHASE 5: NIP-11 Compliance Summary" # Final validation - get the actual response and display it print_step "Displaying complete NIP-11 response..." local response="" if command -v curl &> /dev/null; then response=$(curl -s -H "Accept: application/nostr+json" "$RELAY_URL/" 2>/dev/null || echo "") if [[ -n "$response" ]] && echo "$response" | jq . >/dev/null 2>&1; then echo "$response" | jq . else print_error "Failed to retrieve or parse final response" fi fi print_success "All NIP-11 tests passed!" return 0 } # Main execution print_header "Starting NIP-11 Relay Information Document Test Suite" echo if run_nip11_tests; then echo print_success "All NIP-11 tests completed successfully!" print_info "The C-Relay NIP-11 implementation is fully compliant" print_info "✅ HTTP endpoint, Accept header validation, CORS, and JSON structure all working" echo exit 0 else echo print_error "Some NIP-11 tests failed" exit 1 fi