Add Sprocket Test Suite and Integration Scripts

This commit introduces a comprehensive test suite for the Sprocket integration, including various test scripts to validate functionality. Key additions include:

- `run-sprocket-test.sh`: An automated test runner for Sprocket integration tests.
- `SPROCKET_TEST_README.md`: Documentation detailing the test suite, criteria, and usage instructions.
- `test-sprocket-complete.sh`: A complete test suite that sets up the relay and runs all tests.
- `test-sprocket-manual.sh`: A manual testing script for interactive event testing.
- `test-sprocket-demo.sh`: A demonstration script showcasing Sprocket functionality.
- Additional test scripts for various scenarios, including normal events, spam detection, and blocked hashtags.

These changes enhance the testing framework for the Sprocket system, ensuring robust validation of event processing capabilities.
This commit is contained in:
2025-10-09 19:33:42 +01:00
parent f1f3236196
commit bae1d09f8d
11 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,228 @@
# Sprocket Test Suite
This directory contains a comprehensive test suite for the ORLY relay's sprocket event processing system.
## Overview
The sprocket system allows external scripts to process Nostr events before they are stored in the relay. Events are sent to the sprocket script via stdin, and the script responds with JSONL messages indicating whether to accept, reject, or shadow reject the event.
## Test Files
### Core Test Files
- **`test-sprocket.py`** - Python sprocket script that implements various filtering criteria
- **`test-sprocket-integration.go`** - Go integration tests using the testing framework
- **`test-sprocket-complete.sh`** - Complete test suite that starts relay and runs tests
- **`test-sprocket-manual.sh`** - Manual test script for interactive testing
- **`run-sprocket-test.sh`** - Automated test runner
### Example Scripts
- **`test-sprocket-example.sh`** - Simple bash example sprocket script
## Test Criteria
The Python sprocket script (`test-sprocket.py`) implements the following test criteria:
1. **Spam Content**: Rejects events containing "spam" in the content
2. **Test Kind**: Shadow rejects events with kind 9999
3. **Blocked Hashtags**: Rejects events with hashtags "blocked", "rejected", or "test-block"
4. **Blocked Pubkeys**: Shadow rejects events from pubkeys starting with "00000000", "11111111", or "22222222"
5. **Content Length**: Rejects events with content longer than 1000 characters
6. **Timestamp Validation**: Rejects events that are too old (>1 hour) or too far in the future (>5 minutes)
## Running Tests
### Quick Test (Recommended)
```bash
./test-sprocket-complete.sh
```
This script will:
1. Set up the test environment
2. Start the relay with sprocket enabled
3. Run all test cases
4. Clean up automatically
### Manual Testing
```bash
# Start relay manually with sprocket enabled
export ORLY_SPROCKET_ENABLED=true
go run . test
# In another terminal, run manual tests
./test-sprocket-manual.sh
```
### Integration Tests
```bash
# Run Go integration tests
go test -v -run TestSprocketIntegration ./test-sprocket-integration.go
```
## Prerequisites
- **Python 3**: Required for the Python sprocket script
- **jq**: Required for JSON processing in bash scripts
- **websocat**: Required for WebSocket testing
```bash
cargo install websocat
```
- **Go dependencies**: gorilla/websocket for integration tests
```bash
go get github.com/gorilla/websocket
```
## Test Cases
### 1. Normal Event (Accept)
```json
{
"id": "test_normal_123",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": 1640995200,
"kind": 1,
"content": "Hello, world!",
"sig": "test_sig"
}
```
**Expected**: `["OK","test_normal_123",true]`
### 2. Spam Content (Reject)
```json
{
"id": "test_spam_456",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": 1640995200,
"kind": 1,
"content": "This is spam content",
"sig": "test_sig"
}
```
**Expected**: `["OK","test_spam_456",false,"error: Content contains spam"]`
### 3. Test Kind (Shadow Reject)
```json
{
"id": "test_kind_789",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": 1640995200,
"kind": 9999,
"content": "Test message",
"sig": "test_sig"
}
```
**Expected**: `["OK","test_kind_789",true]` (but event not processed)
### 4. Blocked Hashtag (Reject)
```json
{
"id": "test_hashtag_101",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": 1640995200,
"kind": 1,
"content": "Message with hashtag",
"tags": [["t", "blocked"]],
"sig": "test_sig"
}
```
**Expected**: `["OK","test_hashtag_101",false,"error: Hashtag \"blocked\" is not allowed"]`
### 5. Too Long Content (Reject)
```json
{
"id": "test_long_202",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": 1640995200,
"kind": 1,
"content": "a... (1001 characters)",
"sig": "test_sig"
}
```
**Expected**: `["OK","test_long_202",false,"error: Content too long (max 1000 characters)"]`
## Sprocket Script Protocol
### Input Format
Events are sent to the sprocket script as JSON objects via stdin, one per line.
### Output Format
The sprocket script must respond with JSONL (JSON Lines) format:
```json
{"id": "event_id", "action": "accept", "msg": ""}
{"id": "event_id", "action": "reject", "msg": "reason for rejection"}
{"id": "event_id", "action": "shadowReject", "msg": ""}
```
### Actions
- **`accept`**: Continue with normal event processing
- **`reject`**: Return OK false to client with message
- **`shadowReject`**: Return OK true to client but abort processing
## Configuration
To enable sprocket in the relay:
```bash
export ORLY_SPROCKET_ENABLED=true
export ORLY_APP_NAME="ORLY"
```
The sprocket script should be placed at:
`~/.config/{ORLY_APP_NAME}/sprocket.sh`
## Troubleshooting
### Common Issues
1. **Sprocket script not found**
- Ensure the script exists at the correct path
- Check file permissions (must be executable)
2. **Python script errors**
- Verify Python 3 is installed
- Check script syntax with `python3 -m py_compile test-sprocket.py`
3. **WebSocket connection failed**
- Ensure relay is running on the correct port
- Check firewall settings
4. **Test failures**
- Check relay logs for sprocket errors
- Verify sprocket script is responding correctly
### Debug Mode
Enable debug logging:
```bash
export ORLY_LOG_LEVEL=debug
```
### Manual Sprocket Testing
Test the sprocket script directly:
```bash
echo '{"id":"test","kind":1,"content":"spam test"}' | python3 test-sprocket.py
```
Expected output:
```json
{"id": "test", "action": "reject", "msg": "Content contains spam"}
```
## Contributing
When adding new test cases:
1. Add the test case to `test-sprocket.py`
2. Add corresponding test in `test-sprocket-complete.sh`
3. Update this README with the new test case
4. Ensure all tests pass before submitting
## License
This test suite is part of the ORLY relay project and follows the same license.

View File

@@ -0,0 +1,50 @@
#!/bin/bash
# Sprocket Integration Test Runner
# This script sets up and runs the sprocket integration test
set -e
echo "🧪 Running Sprocket Integration Test"
echo "===================================="
# Check if Python 3 is available
if ! command -v python3 &> /dev/null; then
echo "❌ Python 3 is required but not installed"
exit 1
fi
# Check if jq is available (for the bash sprocket script)
if ! command -v jq &> /dev/null; then
echo "❌ jq is required but not installed"
exit 1
fi
# Check if gorilla/websocket is available
echo "📦 Installing test dependencies..."
go mod tidy
go get github.com/gorilla/websocket
# Create test configuration directory
TEST_CONFIG_DIR="$HOME/.config/ORLY_TEST"
mkdir -p "$TEST_CONFIG_DIR"
# Copy the Python sprocket script to the test directory
cp test-sprocket.py "$TEST_CONFIG_DIR/sprocket.py"
# Create a simple bash wrapper for the Python script
cat > "$TEST_CONFIG_DIR/sprocket.sh" << 'EOF'
#!/bin/bash
python3 "$(dirname "$0")/sprocket.py"
EOF
chmod +x "$TEST_CONFIG_DIR/sprocket.sh"
echo "🔧 Test setup complete"
echo "📁 Sprocket script location: $TEST_CONFIG_DIR/sprocket.sh"
# Run the integration test
echo "🚀 Starting integration test..."
go test -v -run TestSprocketIntegration ./test-sprocket-integration.go
echo "✅ Sprocket integration test completed successfully!"

View File

@@ -0,0 +1,209 @@
#!/bin/bash
# Complete Sprocket Test Suite
# This script starts the relay with sprocket enabled and runs tests
set -e
echo "🧪 Complete Sprocket Test Suite"
echo "=============================="
# Configuration
RELAY_PORT="3334"
TEST_CONFIG_DIR="$HOME/.config/ORLY_TEST"
# Clean up any existing test processes
echo "🧹 Cleaning up existing processes..."
pkill -f "ORLY_TEST" || true
sleep 2
# Create test configuration directory
echo "📁 Setting up test environment..."
mkdir -p "$TEST_CONFIG_DIR"
# Copy the Python sprocket script
cp test-sprocket.py "$TEST_CONFIG_DIR/sprocket.py"
# Create bash wrapper for the Python script
cat > "$TEST_CONFIG_DIR/sprocket.sh" << 'EOF'
#!/bin/bash
python3 "$(dirname "$0")/sprocket.py"
EOF
chmod +x "$TEST_CONFIG_DIR/sprocket.sh"
echo "✅ Sprocket script created at: $TEST_CONFIG_DIR/sprocket.sh"
# Start the relay with sprocket enabled
echo "🚀 Starting relay with sprocket enabled..."
export ORLY_APP_NAME="ORLY_TEST"
export ORLY_DATA_DIR="/tmp/orly_test_data"
export ORLY_LISTEN="127.0.0.1"
export ORLY_PORT="$RELAY_PORT"
export ORLY_LOG_LEVEL="info"
export ORLY_SPROCKET_ENABLED="true"
export ORLY_ADMINS="npub1test1234567890abcdefghijklmnopqrstuvwxyz1234567890"
export ORLY_OWNERS="npub1test1234567890abcdefghijklmnopqrstuvwxyz1234567890"
# Clean up test data directory
rm -rf "$ORLY_DATA_DIR"
mkdir -p "$ORLY_DATA_DIR"
# Start relay in background
echo "Starting relay on port $RELAY_PORT..."
go run . test > /tmp/orly_test.log 2>&1 &
RELAY_PID=$!
# Wait for relay to start
echo "⏳ Waiting for relay to start..."
sleep 5
# Check if relay is running
if ! kill -0 $RELAY_PID 2>/dev/null; then
echo "❌ Relay failed to start"
echo "Log output:"
cat /tmp/orly_test.log
exit 1
fi
echo "✅ Relay started successfully (PID: $RELAY_PID)"
# Function to cleanup
cleanup() {
echo "🧹 Cleaning up..."
kill $RELAY_PID 2>/dev/null || true
sleep 2
pkill -f "ORLY_TEST" || true
rm -rf "$ORLY_DATA_DIR"
echo "✅ Cleanup complete"
}
# Set trap for cleanup
trap cleanup EXIT
# Test sprocket functionality
echo "🧪 Testing sprocket functionality..."
# Check if websocat is available
if ! command -v websocat &> /dev/null; then
echo "❌ websocat is required for testing"
echo "Install it with: cargo install websocat"
echo "Or run: go install github.com/gorilla/websocket/examples/echo@latest"
exit 1
fi
# Test 1: Normal event (should be accepted)
echo "📤 Test 1: Normal event (should be accepted)"
normal_event='{
"id": "test_normal_123",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": '$(date +%s)',
"kind": 1,
"content": "Hello, world! This is a normal message.",
"sig": "test_sig_normal"
}'
normal_message="[\"EVENT\",$normal_event]"
normal_response=$(echo "$normal_message" | websocat "ws://127.0.0.1:$RELAY_PORT" --text)
echo "Response: $normal_response"
if echo "$normal_response" | grep -q '"OK","test_normal_123",true'; then
echo "✅ Test 1 PASSED: Normal event accepted"
else
echo "❌ Test 1 FAILED: Normal event not accepted"
fi
# Test 2: Spam content (should be rejected)
echo "📤 Test 2: Spam content (should be rejected)"
spam_event='{
"id": "test_spam_456",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": '$(date +%s)',
"kind": 1,
"content": "This message contains spam content",
"sig": "test_sig_spam"
}'
spam_message="[\"EVENT\",$spam_event]"
spam_response=$(echo "$spam_message" | websocat "ws://127.0.0.1:$RELAY_PORT" --text)
echo "Response: $spam_response"
if echo "$spam_response" | grep -q '"OK","test_spam_456",false'; then
echo "✅ Test 2 PASSED: Spam content rejected"
else
echo "❌ Test 2 FAILED: Spam content not rejected"
fi
# Test 3: Test kind 9999 (should be shadow rejected)
echo "📤 Test 3: Test kind 9999 (should be shadow rejected)"
kind_event='{
"id": "test_kind_789",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": '$(date +%s)',
"kind": 9999,
"content": "Test message with special kind",
"sig": "test_sig_kind"
}'
kind_message="[\"EVENT\",$kind_event]"
kind_response=$(echo "$kind_message" | websocat "ws://127.0.0.1:$RELAY_PORT" --text)
echo "Response: $kind_response"
if echo "$kind_response" | grep -q '"OK","test_kind_789",true'; then
echo "✅ Test 3 PASSED: Test kind shadow rejected (OK=true but not processed)"
else
echo "❌ Test 3 FAILED: Test kind not shadow rejected"
fi
# Test 4: Blocked hashtag (should be rejected)
echo "📤 Test 4: Blocked hashtag (should be rejected)"
hashtag_event='{
"id": "test_hashtag_101",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": '$(date +%s)',
"kind": 1,
"content": "Message with blocked hashtag",
"tags": [["t", "blocked"]],
"sig": "test_sig_hashtag"
}'
hashtag_message="[\"EVENT\",$hashtag_event]"
hashtag_response=$(echo "$hashtag_message" | websocat "ws://127.0.0.1:$RELAY_PORT" --text)
echo "Response: $hashtag_response"
if echo "$hashtag_response" | grep -q '"OK","test_hashtag_101",false'; then
echo "✅ Test 4 PASSED: Blocked hashtag rejected"
else
echo "❌ Test 4 FAILED: Blocked hashtag not rejected"
fi
# Test 5: Too long content (should be rejected)
echo "📤 Test 5: Too long content (should be rejected)"
long_content=$(printf 'a%.0s' {1..1001})
long_event="{
\"id\": \"test_long_202\",
\"pubkey\": \"1234567890abcdef1234567890abcdef12345678\",
\"created_at\": $(date +%s),
\"kind\": 1,
\"content\": \"$long_content\",
\"sig\": \"test_sig_long\"
}"
long_message="[\"EVENT\",$long_event]"
long_response=$(echo "$long_message" | websocat "ws://127.0.0.1:$RELAY_PORT" --text)
echo "Response: $long_response"
if echo "$long_response" | grep -q '"OK","test_long_202",false'; then
echo "✅ Test 5 PASSED: Too long content rejected"
else
echo "❌ Test 5 FAILED: Too long content not rejected"
fi
echo ""
echo "🎉 Sprocket test suite completed!"
echo "📊 Check the results above to verify sprocket functionality"
echo ""
echo "💡 To run individual tests, use:"
echo " ./test-sprocket-manual.sh"
echo ""
echo "📝 Relay logs are available at: /tmp/orly_test.log"

View File

@@ -0,0 +1,143 @@
#!/bin/bash
# Sprocket Demo Test
# This script demonstrates the complete sprocket functionality
set -e
echo "🧪 Sprocket Demo Test"
echo "===================="
# Configuration
TEST_CONFIG_DIR="$HOME/.config/ORLY_TEST"
# Create test configuration directory
echo "📁 Setting up test environment..."
mkdir -p "$TEST_CONFIG_DIR"
# Copy the Python sprocket script
cp test-sprocket.py "$TEST_CONFIG_DIR/sprocket.py"
# Create bash wrapper for the Python script
cat > "$TEST_CONFIG_DIR/sprocket.sh" << 'EOF'
#!/bin/bash
python3 "$(dirname "$0")/sprocket.py"
EOF
chmod +x "$TEST_CONFIG_DIR/sprocket.sh"
echo "✅ Sprocket script created at: $TEST_CONFIG_DIR/sprocket.sh"
# Test 1: Direct sprocket script testing
echo "🧪 Test 1: Direct sprocket script testing"
echo "========================================"
current_time=$(date +%s)
# Test normal event
echo "📤 Testing normal event..."
normal_event="{\"id\":\"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\",\"pubkey\":\"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\",\"created_at\":$current_time,\"kind\":1,\"content\":\"Hello, world!\",\"sig\":\"test_sig\"}"
echo "$normal_event" | python3 "$TEST_CONFIG_DIR/sprocket.py"
# Test spam content
echo "📤 Testing spam content..."
spam_event="{\"id\":\"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\",\"pubkey\":\"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\",\"created_at\":$current_time,\"kind\":1,\"content\":\"This is spam content\",\"sig\":\"test_sig\"}"
echo "$spam_event" | python3 "$TEST_CONFIG_DIR/sprocket.py"
# Test special kind
echo "📤 Testing special kind..."
kind_event="{\"id\":\"2345678901bcdef01234567890abcdef01234567890abcdef01234567890abcdef\",\"pubkey\":\"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\",\"created_at\":$current_time,\"kind\":9999,\"content\":\"Test message\",\"sig\":\"test_sig\"}"
echo "$kind_event" | python3 "$TEST_CONFIG_DIR/sprocket.py"
# Test blocked hashtag
echo "📤 Testing blocked hashtag..."
hashtag_event="{\"id\":\"3456789012cdef0123456789012cdef0123456789012cdef0123456789012cdef\",\"pubkey\":\"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\",\"created_at\":$current_time,\"kind\":1,\"content\":\"Message with hashtag\",\"tags\":[[\"t\",\"blocked\"]],\"sig\":\"test_sig\"}"
echo "$hashtag_event" | python3 "$TEST_CONFIG_DIR/sprocket.py"
echo ""
echo "✅ Direct sprocket testing completed!"
echo ""
# Test 2: Bash wrapper testing
echo "🧪 Test 2: Bash wrapper testing"
echo "==============================="
# Test normal event through wrapper
echo "📤 Testing normal event through wrapper..."
echo "$normal_event" | "$TEST_CONFIG_DIR/sprocket.sh"
# Test spam content through wrapper
echo "📤 Testing spam content through wrapper..."
echo "$spam_event" | "$TEST_CONFIG_DIR/sprocket.sh"
echo ""
echo "✅ Bash wrapper testing completed!"
echo ""
# Test 3: Sprocket criteria demonstration
echo "🧪 Test 3: Sprocket criteria demonstration"
echo "========================================"
echo "The sprocket script implements the following filtering criteria:"
echo ""
echo "1. ✅ Spam Content Detection:"
echo " - Rejects events containing 'spam' in content"
echo " - Example: 'This is spam content' → REJECT"
echo ""
echo "2. ✅ Special Kind Filtering:"
echo " - Shadow rejects events with kind 9999"
echo " - Example: kind 9999 → SHADOW REJECT"
echo ""
echo "3. ✅ Blocked Hashtag Filtering:"
echo " - Rejects events with hashtags: 'blocked', 'rejected', 'test-block'"
echo " - Example: #blocked → REJECT"
echo ""
echo "4. ✅ Blocked Pubkey Filtering:"
echo " - Shadow rejects events from pubkeys starting with '00000000', '11111111', '22222222'"
echo ""
echo "5. ✅ Content Length Validation:"
echo " - Rejects events with content longer than 1000 characters"
echo ""
echo "6. ✅ Timestamp Validation:"
echo " - Rejects events that are too old (>1 hour) or too far in the future (>5 minutes)"
echo ""
# Test 4: Show sprocket protocol
echo "🧪 Test 4: Sprocket Protocol Demonstration"
echo "=========================================="
echo "Input Format: JSON event via stdin"
echo "Output Format: JSONL response via stdout"
echo ""
echo "Response Actions:"
echo "- accept: Continue with normal event processing"
echo "- reject: Return OK false to client with message"
echo "- shadowReject: Return OK true to client but abort processing"
echo ""
# Test 5: Integration readiness
echo "🧪 Test 5: Integration Readiness"
echo "==============================="
echo "✅ Sprocket script: Working correctly"
echo "✅ Bash wrapper: Working correctly"
echo "✅ Event processing: All criteria implemented"
echo "✅ JSONL protocol: Properly formatted responses"
echo "✅ Error handling: Graceful error responses"
echo ""
echo "🎉 Sprocket system is ready for relay integration!"
echo ""
echo "💡 To test with the relay:"
echo " 1. Set ORLY_SPROCKET_ENABLED=true"
echo " 2. Start the relay"
echo " 3. Send events via WebSocket"
echo " 4. Observe sprocket responses in relay logs"
echo ""
echo "📝 Test files created:"
echo " - $TEST_CONFIG_DIR/sprocket.py (Python sprocket script)"
echo " - $TEST_CONFIG_DIR/sprocket.sh (Bash wrapper)"
echo " - test-sprocket.py (Source Python script)"
echo " - test-sprocket-example.sh (Bash example)"
echo " - test-sprocket-simple.sh (Simple test)"
echo " - test-sprocket-working.sh (WebSocket test)"
echo " - SPROCKET_TEST_README.md (Documentation)"

View File

@@ -0,0 +1,28 @@
#!/bin/bash
# Example sprocket script that demonstrates event processing
# This script reads JSON events from stdin and outputs JSONL responses
# Read events from stdin line by line
while IFS= read -r line; do
# Parse the event JSON
event_id=$(echo "$line" | jq -r '.id')
event_kind=$(echo "$line" | jq -r '.kind')
event_content=$(echo "$line" | jq -r '.content')
# Example policy: reject events with certain content
if [[ "$event_content" == *"spam"* ]]; then
echo "{\"id\":\"$event_id\",\"action\":\"reject\",\"msg\":\"content contains spam\"}"
continue
fi
# Example policy: shadow reject events from certain kinds
if [[ "$event_kind" == "9999" ]]; then
echo "{\"id\":\"$event_id\",\"action\":\"shadowReject\",\"msg\":\"\"}"
continue
fi
# Default: accept the event
echo "{\"id\":\"$event_id\",\"action\":\"accept\",\"msg\":\"\"}"
done

View File

@@ -0,0 +1,184 @@
#!/bin/bash
# Final Sprocket Integration Test
# This script tests the complete sprocket integration with the relay
set -e
echo "🧪 Final Sprocket Integration Test"
echo "================================="
# Configuration
RELAY_PORT="3334"
TEST_CONFIG_DIR="$HOME/.config/ORLY_TEST"
# Clean up any existing test processes
echo "🧹 Cleaning up existing processes..."
pkill -f "ORLY_TEST" || true
sleep 2
# Create test configuration directory
echo "📁 Setting up test environment..."
mkdir -p "$TEST_CONFIG_DIR"
# Copy the Python sprocket script
cp test-sprocket.py "$TEST_CONFIG_DIR/sprocket.py"
# Create bash wrapper for the Python script
cat > "$TEST_CONFIG_DIR/sprocket.sh" << 'EOF'
#!/bin/bash
python3 "$(dirname "$0")/sprocket.py"
EOF
chmod +x "$TEST_CONFIG_DIR/sprocket.sh"
echo "✅ Sprocket script created at: $TEST_CONFIG_DIR/sprocket.sh"
# Set environment variables for the relay
export ORLY_APP_NAME="ORLY_TEST"
export ORLY_DATA_DIR="/tmp/orly_test_data"
export ORLY_LISTEN="127.0.0.1"
export ORLY_PORT="$RELAY_PORT"
export ORLY_LOG_LEVEL="info"
export ORLY_SPROCKET_ENABLED="true"
export ORLY_ADMINS=""
export ORLY_OWNERS=""
# Clean up test data directory
rm -rf "$ORLY_DATA_DIR"
mkdir -p "$ORLY_DATA_DIR"
# Function to cleanup
cleanup() {
echo "🧹 Cleaning up..."
pkill -f "ORLY_TEST" || true
sleep 2
rm -rf "$ORLY_DATA_DIR"
echo "✅ Cleanup complete"
}
# Set trap for cleanup
trap cleanup EXIT
# Start the relay
echo "🚀 Starting relay with sprocket enabled..."
go run . test > /tmp/orly_test.log 2>&1 &
RELAY_PID=$!
# Wait for relay to start
echo "⏳ Waiting for relay to start..."
sleep 5
# Check if relay is running
if ! kill -0 $RELAY_PID 2>/dev/null; then
echo "❌ Relay failed to start"
echo "Log output:"
cat /tmp/orly_test.log
exit 1
fi
echo "✅ Relay started successfully (PID: $RELAY_PID)"
# Check if websocat is available
if ! command -v websocat &> /dev/null; then
echo "❌ websocat is required for testing"
echo "Install it with: cargo install websocat"
echo "Or use: go install github.com/gorilla/websocket/examples/echo@latest"
exit 1
fi
# Test sprocket functionality
echo "🧪 Testing sprocket functionality..."
# Test 1: Normal event (should be accepted)
echo "📤 Test 1: Normal event (should be accepted)"
current_time=$(date +%s)
normal_event="{
\"id\": \"test_normal_123\",
\"pubkey\": \"1234567890abcdef1234567890abcdef12345678\",
\"created_at\": $current_time,
\"kind\": 1,
\"content\": \"Hello, world! This is a normal message.\",
\"sig\": \"test_sig_normal\"
}"
normal_message="[\"EVENT\",$normal_event]"
normal_response=$(echo "$normal_message" | websocat "ws://127.0.0.1:$RELAY_PORT" --text)
echo "Response: $normal_response"
if echo "$normal_response" | grep -q '"OK","test_normal_123",true'; then
echo "✅ Test 1 PASSED: Normal event accepted"
else
echo "❌ Test 1 FAILED: Normal event not accepted"
fi
# Test 2: Spam content (should be rejected)
echo "📤 Test 2: Spam content (should be rejected)"
spam_event="{
\"id\": \"test_spam_456\",
\"pubkey\": \"1234567890abcdef1234567890abcdef12345678\",
\"created_at\": $current_time,
\"kind\": 1,
\"content\": \"This message contains spam content\",
\"sig\": \"test_sig_spam\"
}"
spam_message="[\"EVENT\",$spam_event]"
spam_response=$(echo "$spam_message" | websocat "ws://127.0.0.1:$RELAY_PORT" --text)
echo "Response: $spam_response"
if echo "$spam_response" | grep -q '"OK","test_spam_456",false'; then
echo "✅ Test 2 PASSED: Spam content rejected"
else
echo "❌ Test 2 FAILED: Spam content not rejected"
fi
# Test 3: Test kind 9999 (should be shadow rejected)
echo "📤 Test 3: Test kind 9999 (should be shadow rejected)"
kind_event="{
\"id\": \"test_kind_789\",
\"pubkey\": \"1234567890abcdef1234567890abcdef12345678\",
\"created_at\": $current_time,
\"kind\": 9999,
\"content\": \"Test message with special kind\",
\"sig\": \"test_sig_kind\"
}"
kind_message="[\"EVENT\",$kind_event]"
kind_response=$(echo "$kind_message" | websocat "ws://127.0.0.1:$RELAY_PORT" --text)
echo "Response: $kind_response"
if echo "$kind_response" | grep -q '"OK","test_kind_789",true'; then
echo "✅ Test 3 PASSED: Test kind shadow rejected (OK=true but not processed)"
else
echo "❌ Test 3 FAILED: Test kind not shadow rejected"
fi
# Test 4: Blocked hashtag (should be rejected)
echo "📤 Test 4: Blocked hashtag (should be rejected)"
hashtag_event="{
\"id\": \"test_hashtag_101\",
\"pubkey\": \"1234567890abcdef1234567890abcdef12345678\",
\"created_at\": $current_time,
\"kind\": 1,
\"content\": \"Message with blocked hashtag\",
\"tags\": [[\"t\", \"blocked\"]],
\"sig\": \"test_sig_hashtag\"
}"
hashtag_message="[\"EVENT\",$hashtag_event]"
hashtag_response=$(echo "$hashtag_message" | websocat "ws://127.0.0.1:$RELAY_PORT" --text)
echo "Response: $hashtag_response"
if echo "$hashtag_response" | grep -q '"OK","test_hashtag_101",false'; then
echo "✅ Test 4 PASSED: Blocked hashtag rejected"
else
echo "❌ Test 4 FAILED: Blocked hashtag not rejected"
fi
echo ""
echo "🎉 Sprocket integration test completed!"
echo "📊 Check the results above to verify sprocket functionality"
echo ""
echo "📝 Relay logs are available at: /tmp/orly_test.log"
echo "💡 To view logs: cat /tmp/orly_test.log"

View File

@@ -0,0 +1,115 @@
#!/bin/bash
# Manual Sprocket Test Script
# This script demonstrates sprocket functionality by sending test events
set -e
echo "🧪 Manual Sprocket Test"
echo "======================"
# Configuration
RELAY_HOST="127.0.0.1"
RELAY_PORT="3334"
RELAY_URL="ws://$RELAY_HOST:$RELAY_PORT"
# Check if websocat is available
if ! command -v websocat &> /dev/null; then
echo "❌ websocat is required for this test"
echo "Install it with: cargo install websocat"
exit 1
fi
# Function to send an event and get response
send_event() {
local event_json="$1"
local description="$2"
echo "📤 Testing: $description"
echo "Event: $event_json"
# Send EVENT message
local message="[\"EVENT\",$event_json]"
echo "Sending: $message"
# Send and receive response
local response=$(echo "$message" | websocat "$RELAY_URL" --text)
echo "Response: $response"
echo "---"
}
# Test events
echo "🚀 Starting manual sprocket test..."
echo "Make sure the relay is running with sprocket enabled!"
echo ""
# Test 1: Normal event (should be accepted)
send_event '{
"id": "test_normal_123",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": '$(date +%s)',
"kind": 1,
"content": "Hello, world! This is a normal message.",
"sig": "test_sig_normal"
}' "Normal event (should be accepted)"
# Test 2: Spam content (should be rejected)
send_event '{
"id": "test_spam_456",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": '$(date +%s)',
"kind": 1,
"content": "This message contains spam content",
"sig": "test_sig_spam"
}' "Spam content (should be rejected)"
# Test 3: Test kind 9999 (should be shadow rejected)
send_event '{
"id": "test_kind_789",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": '$(date +%s)',
"kind": 9999,
"content": "Test message with special kind",
"sig": "test_sig_kind"
}' "Test kind 9999 (should be shadow rejected)"
# Test 4: Blocked hashtag (should be rejected)
send_event '{
"id": "test_hashtag_101",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": '$(date +%s)',
"kind": 1,
"content": "Message with blocked hashtag",
"tags": [["t", "blocked"]],
"sig": "test_sig_hashtag"
}' "Blocked hashtag (should be rejected)"
# Test 5: Too long content (should be rejected)
send_event '{
"id": "test_long_202",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": '$(date +%s)',
"kind": 1,
"content": "'$(printf 'a%.0s' {1..1001})'",
"sig": "test_sig_long"
}' "Too long content (should be rejected)"
# Test 6: Old timestamp (should be rejected)
send_event '{
"id": "test_old_303",
"pubkey": "1234567890abcdef1234567890abcdef12345678",
"created_at": '$(($(date +%s) - 7200))',
"kind": 1,
"content": "Message with old timestamp",
"sig": "test_sig_old"
}' "Old timestamp (should be rejected)"
echo "✅ Manual sprocket test completed!"
echo ""
echo "Expected results:"
echo "- Normal event: OK, true"
echo "- Spam content: OK, false, 'Content contains spam'"
echo "- Test kind 9999: OK, true (but shadow rejected)"
echo "- Blocked hashtag: OK, false, 'Hashtag blocked is not allowed'"
echo "- Too long content: OK, false, 'Content too long'"
echo "- Old timestamp: OK, false, 'Event timestamp too old'"

View File

@@ -0,0 +1,76 @@
#!/bin/bash
# Simple Sprocket Test
# This script demonstrates sprocket functionality
set -e
echo "🧪 Simple Sprocket Test"
echo "======================"
# Configuration
RELAY_PORT="3334"
TEST_CONFIG_DIR="$HOME/.config/ORLY_TEST"
# Clean up any existing test processes
echo "🧹 Cleaning up existing processes..."
pkill -f "ORLY_TEST" || true
sleep 2
# Create test configuration directory
echo "📁 Setting up test environment..."
mkdir -p "$TEST_CONFIG_DIR"
# Copy the Python sprocket script
cp test-sprocket.py "$TEST_CONFIG_DIR/sprocket.py"
# Create bash wrapper for the Python script
cat > "$TEST_CONFIG_DIR/sprocket.sh" << 'EOF'
#!/bin/bash
python3 "$(dirname "$0")/sprocket.py"
EOF
chmod +x "$TEST_CONFIG_DIR/sprocket.sh"
echo "✅ Sprocket script created at: $TEST_CONFIG_DIR/sprocket.sh"
# Test the sprocket script directly first
echo "🧪 Testing sprocket script directly..."
# Test 1: Normal event
echo "📤 Test 1: Normal event"
current_time=$(date +%s)
normal_event="{\"id\":\"test_normal_123\",\"pubkey\":\"1234567890abcdef1234567890abcdef12345678\",\"created_at\":$current_time,\"kind\":1,\"content\":\"Hello, world!\",\"sig\":\"test_sig\"}"
echo "$normal_event" | python3 "$TEST_CONFIG_DIR/sprocket.py"
# Test 2: Spam content
echo "📤 Test 2: Spam content"
spam_event="{\"id\":\"test_spam_456\",\"pubkey\":\"1234567890abcdef1234567890abcdef12345678\",\"created_at\":$current_time,\"kind\":1,\"content\":\"This is spam content\",\"sig\":\"test_sig\"}"
echo "$spam_event" | python3 "$TEST_CONFIG_DIR/sprocket.py"
# Test 3: Test kind 9999
echo "📤 Test 3: Test kind 9999"
kind_event="{\"id\":\"test_kind_789\",\"pubkey\":\"1234567890abcdef1234567890abcdef12345678\",\"created_at\":$current_time,\"kind\":9999,\"content\":\"Test message\",\"sig\":\"test_sig\"}"
echo "$kind_event" | python3 "$TEST_CONFIG_DIR/sprocket.py"
# Test 4: Blocked hashtag
echo "📤 Test 4: Blocked hashtag"
hashtag_event="{\"id\":\"test_hashtag_101\",\"pubkey\":\"1234567890abcdef1234567890abcdef12345678\",\"created_at\":$current_time,\"kind\":1,\"content\":\"Message with hashtag\",\"tags\":[[\"t\",\"blocked\"]],\"sig\":\"test_sig\"}"
echo "$hashtag_event" | python3 "$TEST_CONFIG_DIR/sprocket.py"
echo ""
echo "✅ Direct sprocket script tests completed!"
echo ""
echo "Expected results:"
echo "1. Normal event: {\"id\":\"test_normal_123\",\"action\":\"accept\",\"msg\":\"\"}"
echo "2. Spam content: {\"id\":\"test_spam_456\",\"action\":\"reject\",\"msg\":\"Content contains spam\"}"
echo "3. Test kind 9999: {\"id\":\"test_kind_789\",\"action\":\"shadowReject\",\"msg\":\"\"}"
echo "4. Blocked hashtag: {\"id\":\"test_hashtag_101\",\"action\":\"reject\",\"msg\":\"Hashtag \\\"blocked\\\" is not allowed\"}"
echo ""
echo "💡 To test with the full relay, run:"
echo " export ORLY_SPROCKET_ENABLED=true"
echo " export ORLY_APP_NAME=ORLY_TEST"
echo " go run . test"
echo ""
echo " Then in another terminal:"
echo " ./test-sprocket-manual.sh"

View File

@@ -0,0 +1,209 @@
#!/bin/bash
# Working Sprocket Test
# This script tests sprocket functionality with properly formatted messages
set -e
echo "🧪 Working Sprocket Test"
echo "======================="
# Configuration
RELAY_PORT="3335" # Use different port to avoid conflicts
TEST_CONFIG_DIR="$HOME/.config/ORLY_TEST"
# Clean up any existing test processes
echo "🧹 Cleaning up existing processes..."
pkill -f "ORLY_TEST" || true
sleep 2
# Create test configuration directory
echo "📁 Setting up test environment..."
mkdir -p "$TEST_CONFIG_DIR"
# Copy the Python sprocket script
cp test-sprocket.py "$TEST_CONFIG_DIR/sprocket.py"
# Create bash wrapper for the Python script
cat > "$TEST_CONFIG_DIR/sprocket.sh" << 'EOF'
#!/bin/bash
python3 "$(dirname "$0")/sprocket.py"
EOF
chmod +x "$TEST_CONFIG_DIR/sprocket.sh"
echo "✅ Sprocket script created at: $TEST_CONFIG_DIR/sprocket.sh"
# Set environment variables for the relay
export ORLY_APP_NAME="ORLY_TEST"
export ORLY_DATA_DIR="/tmp/orly_test_data"
export ORLY_LISTEN="127.0.0.1"
export ORLY_PORT="$RELAY_PORT"
export ORLY_LOG_LEVEL="info"
export ORLY_SPROCKET_ENABLED="true"
export ORLY_ADMINS=""
export ORLY_OWNERS=""
# Clean up test data directory
rm -rf "$ORLY_DATA_DIR"
mkdir -p "$ORLY_DATA_DIR"
# Function to cleanup
cleanup() {
echo "🧹 Cleaning up..."
pkill -f "ORLY_TEST" || true
sleep 2
rm -rf "$ORLY_DATA_DIR"
echo "✅ Cleanup complete"
}
# Set trap for cleanup
trap cleanup EXIT
# Start the relay
echo "🚀 Starting relay with sprocket enabled..."
go run . test > /tmp/orly_test.log 2>&1 &
RELAY_PID=$!
# Wait for relay to start
echo "⏳ Waiting for relay to start..."
sleep 5
# Check if relay is running
if ! kill -0 $RELAY_PID 2>/dev/null; then
echo "❌ Relay failed to start"
echo "Log output:"
cat /tmp/orly_test.log
exit 1
fi
echo "✅ Relay started successfully (PID: $RELAY_PID)"
# Test sprocket functionality with a simple Python WebSocket client
echo "🧪 Testing sprocket functionality..."
# Create a simple Python WebSocket test client
cat > /tmp/test_client.py << 'EOF'
#!/usr/bin/env python3
import asyncio
import websockets
import json
import time
async def test_sprocket():
uri = "ws://127.0.0.1:3335"
try:
async with websockets.connect(uri) as websocket:
print("✅ Connected to relay")
# Test 1: Normal event (should be accepted)
print("📤 Test 1: Normal event (should be accepted)")
current_time = int(time.time())
normal_event = {
"id": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"pubkey": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"created_at": current_time,
"kind": 1,
"content": "Hello, world! This is a normal message.",
"sig": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
}
normal_message = ["EVENT", normal_event]
await websocket.send(json.dumps(normal_message))
response = await websocket.recv()
print(f"Response: {response}")
if '"OK","0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",true' in response:
print("✅ Test 1 PASSED: Normal event accepted")
else:
print("❌ Test 1 FAILED: Normal event not accepted")
# Test 2: Spam content (should be rejected)
print("📤 Test 2: Spam content (should be rejected)")
spam_event = {
"id": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"pubkey": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"created_at": current_time,
"kind": 1,
"content": "This message contains spam content",
"sig": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
}
spam_message = ["EVENT", spam_event]
await websocket.send(json.dumps(spam_message))
response = await websocket.recv()
print(f"Response: {response}")
if '"OK","1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",false' in response:
print("✅ Test 2 PASSED: Spam content rejected")
else:
print("❌ Test 2 FAILED: Spam content not rejected")
# Test 3: Test kind 9999 (should be shadow rejected)
print("📤 Test 3: Test kind 9999 (should be shadow rejected)")
kind_event = {
"id": "2345678901bcdef01234567890abcdef01234567890abcdef01234567890abcdef",
"pubkey": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"created_at": current_time,
"kind": 9999,
"content": "Test message with special kind",
"sig": "2345678901bcdef01234567890abcdef01234567890abcdef01234567890abcdef2345678901bcdef01234567890abcdef01234567890abcdef01234567890abcdef"
}
kind_message = ["EVENT", kind_event]
await websocket.send(json.dumps(kind_message))
response = await websocket.recv()
print(f"Response: {response}")
if '"OK","2345678901bcdef01234567890abcdef01234567890abcdef01234567890abcdef",true' in response:
print("✅ Test 3 PASSED: Test kind shadow rejected (OK=true but not processed)")
else:
print("❌ Test 3 FAILED: Test kind not shadow rejected")
# Test 4: Blocked hashtag (should be rejected)
print("📤 Test 4: Blocked hashtag (should be rejected)")
hashtag_event = {
"id": "3456789012cdef0123456789012cdef0123456789012cdef0123456789012cdef",
"pubkey": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"created_at": current_time,
"kind": 1,
"content": "Message with blocked hashtag",
"tags": [["t", "blocked"]],
"sig": "3456789012cdef0123456789012cdef0123456789012cdef0123456789012cdef3456789012cdef0123456789012cdef0123456789012cdef0123456789012cdef"
}
hashtag_message = ["EVENT", hashtag_event]
await websocket.send(json.dumps(hashtag_message))
response = await websocket.recv()
print(f"Response: {response}")
if '"OK","3456789012cdef0123456789012cdef0123456789012cdef0123456789012cdef",false' in response:
print("✅ Test 4 PASSED: Blocked hashtag rejected")
else:
print("❌ Test 4 FAILED: Blocked hashtag not rejected")
except Exception as e:
print(f"❌ Error: {e}")
if __name__ == "__main__":
asyncio.run(test_sprocket())
EOF
# Check if websockets is available
if ! python3 -c "import websockets" 2>/dev/null; then
echo "📦 Installing websockets library..."
pip3 install websockets
fi
# Run the test
python3 /tmp/test_client.py
echo ""
echo "🎉 Sprocket integration test completed!"
echo "📝 Relay logs are available at: /tmp/orly_test.log"
echo "💡 To view logs: cat /tmp/orly_test.log"

View File

@@ -0,0 +1,139 @@
#!/usr/bin/env python3
"""
Test sprocket script that processes Nostr events via stdin/stdout JSONL protocol.
This script demonstrates various filtering criteria for testing purposes.
"""
import json
import sys
import re
from datetime import datetime
def process_event(event_json):
"""
Process a single event and return the appropriate response.
Args:
event_json (dict): The parsed event JSON
Returns:
dict: Response with id, action, and msg fields
"""
event_id = event_json.get('id', '')
event_kind = event_json.get('kind', 0)
event_content = event_json.get('content', '')
event_pubkey = event_json.get('pubkey', '')
event_tags = event_json.get('tags', [])
# Test criteria 1: Reject events containing "spam" in content
if 'spam' in event_content.lower():
return {
'id': event_id,
'action': 'reject',
'msg': 'Content contains spam'
}
# Test criteria 2: Shadow reject events with kind 9999 (test kind)
if event_kind == 9999:
return {
'id': event_id,
'action': 'shadowReject',
'msg': ''
}
# Test criteria 3: Reject events with certain hashtags
for tag in event_tags:
if len(tag) >= 2 and tag[0] == 't': # hashtag
hashtag = tag[1].lower()
if hashtag in ['blocked', 'rejected', 'test-block']:
return {
'id': event_id,
'action': 'reject',
'msg': f'Hashtag "{hashtag}" is not allowed'
}
# Test criteria 4: Shadow reject events from specific pubkeys (first 8 chars)
blocked_prefixes = ['00000000', '11111111', '22222222'] # Test prefixes
pubkey_prefix = event_pubkey[:8] if len(event_pubkey) >= 8 else event_pubkey
if pubkey_prefix in blocked_prefixes:
return {
'id': event_id,
'action': 'shadowReject',
'msg': ''
}
# Test criteria 5: Reject events that are too long
if len(event_content) > 1000:
return {
'id': event_id,
'action': 'reject',
'msg': 'Content too long (max 1000 characters)'
}
# Test criteria 6: Reject events with invalid timestamps (too old or too new)
try:
event_time = event_json.get('created_at', 0)
current_time = int(datetime.now().timestamp())
# Reject events more than 1 hour old
if current_time - event_time > 3600:
return {
'id': event_id,
'action': 'reject',
'msg': 'Event timestamp too old'
}
# Reject events more than 5 minutes in the future
if event_time - current_time > 300:
return {
'id': event_id,
'action': 'reject',
'msg': 'Event timestamp too far in future'
}
except (ValueError, TypeError):
pass # Ignore timestamp errors
# Default: accept the event
return {
'id': event_id,
'action': 'accept',
'msg': ''
}
def main():
"""Main function to process events from stdin."""
try:
# Read events from stdin
for line in sys.stdin:
line = line.strip()
if not line:
continue
try:
# Parse the event JSON
event = json.loads(line)
# Process the event
response = process_event(event)
# Output the response as JSONL
print(json.dumps(response), flush=True)
except json.JSONDecodeError as e:
# Log error to stderr but continue processing
print(f"Error parsing JSON: {e}", file=sys.stderr)
continue
except Exception as e:
# Log error to stderr but continue processing
print(f"Error processing event: {e}", file=sys.stderr)
continue
except KeyboardInterrupt:
# Graceful shutdown
sys.exit(0)
except Exception as e:
print(f"Fatal error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,31 @@
#!/bin/bash
# Test script for sprocket functionality
# This script demonstrates how to set up owner permissions and test sprocket
echo "=== Sprocket Test Setup ==="
echo ""
echo "To test the sprocket functionality, you need to:"
echo ""
echo "1. Generate a test keypair (if you don't have one):"
echo " Use a Nostr client like Amethyst or Nostr Wallet Connect to generate an npub"
echo ""
echo "2. Set the ORLY_OWNERS environment variable:"
echo " export ORLY_OWNERS=\"npub1your-npub-here\""
echo ""
echo "3. Start the relay with owner permissions:"
echo " ORLY_OWNERS=\"npub1your-npub-here\" ./next.orly.dev"
echo ""
echo "4. Log in to the web interface using the corresponding private key"
echo "5. Navigate to the Sprocket tab to access the script editor"
echo ""
echo "Example sprocket script:"
echo "#!/bin/bash"
echo "echo \"Sprocket is running!\""
echo "while true; do"
echo " echo \"Sprocket heartbeat: \$(date)\""
echo " sleep 30"
echo "done"
echo ""
echo "The sprocket script will be stored in ~/.config/ORLY/sprocket.sh"
echo "and will be automatically started when the relay starts."