Reorganize project: move docs to docs/ and test scripts to tests/
Moved 9 documentation .md files from root into docs/. Moved 4 test scripts from root into tests/. Updated cross-references in README.md and docs to reflect new paths. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
146
tests/test-session-end-websocket.js
Executable file
146
tests/test-session-end-websocket.js
Executable file
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Test script for session.ended WebSocket event
|
||||
*
|
||||
* This script:
|
||||
* 1. Connects to the WebSocket server
|
||||
* 2. Authenticates with a JWT token
|
||||
* 3. Subscribes to a session
|
||||
* 4. Listens for session.ended events
|
||||
*
|
||||
* Usage:
|
||||
* node test-session-end-websocket.js <session_id> <jwt_token>
|
||||
*
|
||||
* Example:
|
||||
* node test-session-end-websocket.js 17 your-jwt-token-here
|
||||
*/
|
||||
|
||||
const WebSocket = require('ws');
|
||||
|
||||
// Configuration
|
||||
const WS_URL = process.env.WS_URL || 'ws://localhost:5000/api/sessions/live';
|
||||
const SESSION_ID = process.argv[2] || '17';
|
||||
const JWT_TOKEN = process.argv[3];
|
||||
|
||||
if (!JWT_TOKEN) {
|
||||
console.error('❌ Error: JWT token is required');
|
||||
console.error('Usage: node test-session-end-websocket.js <session_id> <jwt_token>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log('🚀 Testing session.ended WebSocket event');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log(`📡 Connecting to: ${WS_URL}`);
|
||||
console.log(`🎮 Session ID: ${SESSION_ID}`);
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
||||
|
||||
// Create WebSocket connection
|
||||
const ws = new WebSocket(WS_URL);
|
||||
|
||||
ws.on('open', () => {
|
||||
console.log('✅ Connected to WebSocket server\n');
|
||||
|
||||
// Step 1: Authenticate
|
||||
console.log('🔐 Authenticating...');
|
||||
ws.send(JSON.stringify({
|
||||
type: 'auth',
|
||||
token: JWT_TOKEN
|
||||
}));
|
||||
});
|
||||
|
||||
ws.on('message', (data) => {
|
||||
try {
|
||||
const message = JSON.parse(data.toString());
|
||||
|
||||
switch (message.type) {
|
||||
case 'auth_success':
|
||||
console.log('✅ Authentication successful\n');
|
||||
|
||||
// Step 2: Subscribe to session
|
||||
console.log(`📻 Subscribing to session ${SESSION_ID}...`);
|
||||
ws.send(JSON.stringify({
|
||||
type: 'subscribe',
|
||||
sessionId: parseInt(SESSION_ID)
|
||||
}));
|
||||
break;
|
||||
|
||||
case 'subscribed':
|
||||
console.log(`✅ Subscribed to session ${message.sessionId}\n`);
|
||||
console.log('👂 Listening for session.ended events...');
|
||||
console.log(' (Close the session in the Picker to trigger the event)\n');
|
||||
break;
|
||||
|
||||
case 'session.ended':
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('🎉 SESSION.ENDED EVENT RECEIVED!');
|
||||
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
||||
console.log('\n📦 Event Data:');
|
||||
console.log(JSON.stringify(message, null, 2));
|
||||
console.log('\n✨ Event Details:');
|
||||
console.log(` Session ID: ${message.data.session.id}`);
|
||||
console.log(` Active: ${message.data.session.is_active === 1 ? 'Yes' : 'No'}`);
|
||||
console.log(` Games Played: ${message.data.session.games_played}`);
|
||||
console.log(` Timestamp: ${message.timestamp}`);
|
||||
console.log('\n✅ Test successful! The bot should now announce the session end.\n');
|
||||
|
||||
// Close connection after receiving the event
|
||||
setTimeout(() => {
|
||||
console.log('👋 Closing connection...');
|
||||
ws.close();
|
||||
}, 1000);
|
||||
break;
|
||||
|
||||
case 'auth_error':
|
||||
case 'error':
|
||||
console.error(`❌ Error: ${message.message}`);
|
||||
ws.close();
|
||||
process.exit(1);
|
||||
break;
|
||||
|
||||
case 'pong':
|
||||
// Ignore pong messages
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log(`📨 Received message: ${message.type}`);
|
||||
console.log(JSON.stringify(message, null, 2));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('❌ Failed to parse message:', err);
|
||||
console.error('Raw data:', data.toString());
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('error', (err) => {
|
||||
console.error('❌ WebSocket error:', err.message);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
ws.on('close', (code, reason) => {
|
||||
console.log(`\n🔌 Connection closed (code: ${code})`);
|
||||
if (reason) {
|
||||
console.log(` Reason: ${reason}`);
|
||||
}
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
// Handle Ctrl+C gracefully
|
||||
process.on('SIGINT', () => {
|
||||
console.log('\n\n👋 Shutting down...');
|
||||
ws.close();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
// Send periodic pings to keep connection alive
|
||||
const pingInterval = setInterval(() => {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: 'ping' }));
|
||||
}
|
||||
}, 30000);
|
||||
|
||||
// Clean up interval on close
|
||||
ws.on('close', () => {
|
||||
clearInterval(pingInterval);
|
||||
});
|
||||
|
||||
182
tests/test-webhook-simple.sh
Executable file
182
tests/test-webhook-simple.sh
Executable file
@@ -0,0 +1,182 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Simple Webhook Test Script for macOS
|
||||
# Uses webhook.site for easy testing
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
API_URL="${API_URL:-https://hso.cottongin.xyz/api}"
|
||||
|
||||
echo -e "\n${BLUE}╔════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Webhook Test Script (webhook.site) ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════════════════╝${NC}\n"
|
||||
|
||||
# Check if JWT_TOKEN is set
|
||||
if [ -z "$JWT_TOKEN" ]; then
|
||||
echo -e "${RED}❌ ERROR: JWT_TOKEN not set!${NC}\n"
|
||||
echo "Please set your JWT token:"
|
||||
echo " 1. Get token:"
|
||||
echo " curl -X POST https://hso.cottongin.xyz/api/auth/login \\"
|
||||
echo " -H \"Content-Type: application/json\" \\"
|
||||
echo " -d '{\"key\":\"YOUR_ADMIN_KEY\"}'"
|
||||
echo ""
|
||||
echo " 2. Export the token:"
|
||||
echo " export JWT_TOKEN=\"your_token_here\""
|
||||
echo ""
|
||||
echo " 3. Run this script:"
|
||||
echo " ./test-webhook-simple.sh"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓${NC} JWT_TOKEN is set"
|
||||
echo -e "${GREEN}✓${NC} API URL: $API_URL"
|
||||
echo ""
|
||||
|
||||
# Get a webhook.site URL
|
||||
echo -e "${BLUE}📡 Getting webhook.site URL...${NC}"
|
||||
WEBHOOK_RESPONSE=$(curl -s -X POST https://webhook.site/token)
|
||||
WEBHOOK_UUID=$(echo "$WEBHOOK_RESPONSE" | grep -o '"uuid":"[^"]*' | cut -d'"' -f4)
|
||||
|
||||
if [ -z "$WEBHOOK_UUID" ]; then
|
||||
echo -e "${RED}❌ Failed to get webhook.site URL${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WEBHOOK_URL="https://webhook.site/$WEBHOOK_UUID"
|
||||
WEBHOOK_SECRET="test_secret_$(date +%s)"
|
||||
|
||||
echo -e "${GREEN}✓${NC} Webhook URL: $WEBHOOK_URL"
|
||||
echo -e "${GREEN}✓${NC} View webhooks at: $WEBHOOK_URL"
|
||||
echo -e "${GREEN}✓${NC} Secret: $WEBHOOK_SECRET"
|
||||
echo ""
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
echo ""
|
||||
echo -e "${YELLOW}🧹 Cleaning up...${NC}"
|
||||
|
||||
if [ -n "$WEBHOOK_ID" ]; then
|
||||
echo " Deleting webhook $WEBHOOK_ID..."
|
||||
DELETE_RESPONSE=$(curl -s -X DELETE \
|
||||
"$API_URL/webhooks/$WEBHOOK_ID" \
|
||||
-H "Authorization: Bearer $JWT_TOKEN")
|
||||
|
||||
if echo "$DELETE_RESPONSE" | grep -q "deleted successfully"; then
|
||||
echo -e " ${GREEN}✓${NC} Webhook deleted"
|
||||
else
|
||||
echo -e " ${YELLOW}⚠${NC} Could not delete webhook (you may need to delete it manually)"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e " ${GREEN}✓${NC} Cleanup complete"
|
||||
echo ""
|
||||
echo -e "${BLUE}👋 Goodbye!${NC}\n"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Trap Ctrl+C
|
||||
trap cleanup SIGINT SIGTERM
|
||||
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE}Starting Webhook Tests${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}\n"
|
||||
|
||||
# Test 1: Create webhook
|
||||
echo -e "${YELLOW}📝 Test 1: Creating webhook...${NC}"
|
||||
CREATE_RESPONSE=$(curl -s -X POST \
|
||||
"$API_URL/webhooks" \
|
||||
-H "Authorization: Bearer $JWT_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"name\": \"Test Webhook\",
|
||||
\"url\": \"$WEBHOOK_URL\",
|
||||
\"secret\": \"$WEBHOOK_SECRET\",
|
||||
\"events\": [\"game.added\"]
|
||||
}")
|
||||
|
||||
if echo "$CREATE_RESPONSE" | grep -q '"id"'; then
|
||||
WEBHOOK_ID=$(echo "$CREATE_RESPONSE" | grep -o '"id":[0-9]*' | head -1 | cut -d':' -f2)
|
||||
echo -e "${GREEN}✓${NC} Webhook created with ID: $WEBHOOK_ID"
|
||||
else
|
||||
echo -e "${RED}✗${NC} Failed to create webhook"
|
||||
echo " Response: $CREATE_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 2: List webhooks
|
||||
echo -e "${YELLOW}📝 Test 2: Listing webhooks...${NC}"
|
||||
LIST_RESPONSE=$(curl -s "$API_URL/webhooks" \
|
||||
-H "Authorization: Bearer $JWT_TOKEN")
|
||||
|
||||
WEBHOOK_COUNT=$(echo "$LIST_RESPONSE" | grep -o '"id":' | wc -l | tr -d ' ')
|
||||
echo -e "${GREEN}✓${NC} Found $WEBHOOK_COUNT webhook(s)"
|
||||
echo ""
|
||||
|
||||
# Test 3: Send test webhook
|
||||
echo -e "${YELLOW}📝 Test 3: Sending test webhook...${NC}"
|
||||
TEST_RESPONSE=$(curl -s -X POST \
|
||||
"$API_URL/webhooks/test/$WEBHOOK_ID" \
|
||||
-H "Authorization: Bearer $JWT_TOKEN")
|
||||
|
||||
if echo "$TEST_RESPONSE" | grep -q "Test webhook sent"; then
|
||||
echo -e "${GREEN}✓${NC} Test webhook sent"
|
||||
else
|
||||
echo -e "${RED}✗${NC} Failed to send test webhook"
|
||||
echo " Response: $TEST_RESPONSE"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Wait for webhook delivery
|
||||
echo -e "${YELLOW}⏳ Waiting for webhook delivery (3 seconds)...${NC}"
|
||||
sleep 3
|
||||
echo ""
|
||||
|
||||
# Test 4: Check webhook logs
|
||||
echo -e "${YELLOW}📝 Test 4: Checking webhook logs...${NC}"
|
||||
LOGS_RESPONSE=$(curl -s "$API_URL/webhooks/$WEBHOOK_ID/logs?limit=10" \
|
||||
-H "Authorization: Bearer $JWT_TOKEN")
|
||||
|
||||
LOG_COUNT=$(echo "$LOGS_RESPONSE" | grep -o '"id":' | wc -l | tr -d ' ')
|
||||
echo -e "${GREEN}✓${NC} Found $LOG_COUNT log entries"
|
||||
|
||||
if [ "$LOG_COUNT" -gt 0 ]; then
|
||||
echo ""
|
||||
echo "Recent webhook deliveries:"
|
||||
echo "$LOGS_RESPONSE" | python3 -c "import sys, json; print(json.dumps(json.load(sys.stdin), indent=2))" 2>/dev/null || echo "$LOGS_RESPONSE"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE}Test Summary${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${GREEN}✓${NC} Webhook created: ID $WEBHOOK_ID"
|
||||
echo -e "${GREEN}✓${NC} Test webhook sent"
|
||||
echo -e "${GREEN}✓${NC} Webhook logs: $LOG_COUNT entries"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}\n"
|
||||
|
||||
echo -e "${GREEN}🎉 All tests completed!${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}💡 Next steps:${NC}"
|
||||
echo " 1. Visit $WEBHOOK_URL to see webhook deliveries"
|
||||
echo " 2. Add a game to an active session in the Picker page"
|
||||
echo " 3. Refresh webhook.site to see the real webhook"
|
||||
echo " 4. Press Ctrl+C to cleanup and exit"
|
||||
echo ""
|
||||
echo -e "${YELLOW}⏳ Press Ctrl+C when done to cleanup...${NC}"
|
||||
echo ""
|
||||
|
||||
# Wait for Ctrl+C
|
||||
while true; do
|
||||
sleep 1
|
||||
done
|
||||
|
||||
294
tests/test-webhook.js
Normal file
294
tests/test-webhook.js
Normal file
@@ -0,0 +1,294 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Webhook Test Script
|
||||
*
|
||||
* This script creates a local webhook receiver and tests the webhook system.
|
||||
*
|
||||
* Usage:
|
||||
* 1. Start your backend server (docker-compose up or npm run dev)
|
||||
* 2. Run this script: node test-webhook.js
|
||||
* 3. The script will:
|
||||
* - Start a local webhook receiver on port 3001
|
||||
* - Create a webhook in the API
|
||||
* - Send a test webhook
|
||||
* - Wait for incoming webhooks
|
||||
*/
|
||||
|
||||
const express = require('express');
|
||||
const crypto = require('crypto');
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
// Configuration
|
||||
const API_URL = process.env.API_URL || 'http://localhost:5000';
|
||||
const WEBHOOK_PORT = process.env.WEBHOOK_PORT || 3001;
|
||||
const WEBHOOK_SECRET = 'test_secret_' + Math.random().toString(36).substring(7);
|
||||
|
||||
// You need to set this - get it from: curl -X POST http://localhost:5000/api/auth/login -H "Content-Type: application/json" -d '{"key":"YOUR_ADMIN_KEY"}'
|
||||
const JWT_TOKEN = process.env.JWT_TOKEN || '';
|
||||
|
||||
if (!JWT_TOKEN) {
|
||||
console.error('\n❌ ERROR: JWT_TOKEN not set!');
|
||||
console.error('\nPlease set your JWT token:');
|
||||
console.error(' 1. Get token: curl -X POST http://localhost:5000/api/auth/login -H "Content-Type: application/json" -d \'{"key":"YOUR_ADMIN_KEY"}\'');
|
||||
console.error(' 2. Run: JWT_TOKEN="your_token_here" node test-webhook.js');
|
||||
console.error(' OR: export JWT_TOKEN="your_token_here" && node test-webhook.js\n');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let webhookId = null;
|
||||
let receivedWebhooks = [];
|
||||
|
||||
// Create Express app for webhook receiver
|
||||
const app = express();
|
||||
|
||||
// Important: Parse JSON and keep raw body for signature verification
|
||||
app.use(express.json({
|
||||
verify: (req, res, buf) => {
|
||||
req.rawBody = buf.toString('utf8');
|
||||
}
|
||||
}));
|
||||
|
||||
// Webhook receiver endpoint
|
||||
app.post('/webhook/jackbox', (req, res) => {
|
||||
console.log('\n📨 Webhook received!');
|
||||
console.log('Headers:', {
|
||||
'x-webhook-signature': req.headers['x-webhook-signature'],
|
||||
'x-webhook-event': req.headers['x-webhook-event'],
|
||||
'user-agent': req.headers['user-agent']
|
||||
});
|
||||
|
||||
const signature = req.headers['x-webhook-signature'];
|
||||
|
||||
// Verify signature
|
||||
if (!signature || !signature.startsWith('sha256=')) {
|
||||
console.log('❌ Missing or invalid signature format');
|
||||
return res.status(401).send('Missing or invalid signature');
|
||||
}
|
||||
|
||||
const expectedSignature = 'sha256=' + crypto
|
||||
.createHmac('sha256', WEBHOOK_SECRET)
|
||||
.update(req.rawBody)
|
||||
.digest('hex');
|
||||
|
||||
// Timing-safe comparison
|
||||
let isValid = false;
|
||||
try {
|
||||
isValid = crypto.timingSafeEqual(
|
||||
Buffer.from(signature),
|
||||
Buffer.from(expectedSignature)
|
||||
);
|
||||
} catch (err) {
|
||||
console.log('❌ Signature verification failed:', err.message);
|
||||
return res.status(401).send('Invalid signature');
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
console.log('❌ Signature mismatch!');
|
||||
console.log(' Expected:', expectedSignature);
|
||||
console.log(' Received:', signature);
|
||||
return res.status(401).send('Invalid signature');
|
||||
}
|
||||
|
||||
console.log('✅ Signature verified!');
|
||||
console.log('\nPayload:', JSON.stringify(req.body, null, 2));
|
||||
|
||||
if (req.body.event === 'game.added') {
|
||||
const game = req.body.data.game;
|
||||
console.log(`\n🎮 Game Added: ${game.title} from ${game.pack_name}`);
|
||||
console.log(` Players: ${game.min_players}-${game.max_players}`);
|
||||
console.log(` Session ID: ${req.body.data.session.id}`);
|
||||
console.log(` Games Played: ${req.body.data.session.games_played}`);
|
||||
}
|
||||
|
||||
receivedWebhooks.push(req.body);
|
||||
res.status(200).send('OK');
|
||||
});
|
||||
|
||||
// Health check
|
||||
app.get('/health', (req, res) => {
|
||||
res.json({
|
||||
status: 'ok',
|
||||
webhooksReceived: receivedWebhooks.length
|
||||
});
|
||||
});
|
||||
|
||||
// Start webhook receiver
|
||||
const server = app.listen(WEBHOOK_PORT, async () => {
|
||||
console.log(`\n🚀 Webhook receiver started on http://localhost:${WEBHOOK_PORT}`);
|
||||
console.log(`📍 Webhook endpoint: http://localhost:${WEBHOOK_PORT}/webhook/jackbox`);
|
||||
console.log(`🔐 Secret: ${WEBHOOK_SECRET}\n`);
|
||||
|
||||
// Wait a moment for server to be ready
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
// Run tests
|
||||
await runTests();
|
||||
});
|
||||
|
||||
async function runTests() {
|
||||
console.log('═══════════════════════════════════════════════════════');
|
||||
console.log('Starting Webhook Tests');
|
||||
console.log('═══════════════════════════════════════════════════════\n');
|
||||
|
||||
try {
|
||||
// Test 1: Create webhook
|
||||
console.log('📝 Test 1: Creating webhook...');
|
||||
const createResponse = await fetch(`${API_URL}/api/webhooks`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${JWT_TOKEN}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
name: 'Test Webhook',
|
||||
url: `http://localhost:${WEBHOOK_PORT}/webhook/jackbox`,
|
||||
secret: WEBHOOK_SECRET,
|
||||
events: ['game.added']
|
||||
})
|
||||
});
|
||||
|
||||
if (!createResponse.ok) {
|
||||
const error = await createResponse.text();
|
||||
throw new Error(`Failed to create webhook: ${createResponse.status} ${error}`);
|
||||
}
|
||||
|
||||
const webhook = await createResponse.json();
|
||||
webhookId = webhook.id;
|
||||
console.log(`✅ Webhook created with ID: ${webhookId}\n`);
|
||||
|
||||
// Test 2: List webhooks
|
||||
console.log('📝 Test 2: Listing webhooks...');
|
||||
const listResponse = await fetch(`${API_URL}/api/webhooks`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${JWT_TOKEN}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!listResponse.ok) {
|
||||
throw new Error(`Failed to list webhooks: ${listResponse.status}`);
|
||||
}
|
||||
|
||||
const webhooks = await listResponse.json();
|
||||
console.log(`✅ Found ${webhooks.length} webhook(s)\n`);
|
||||
|
||||
// Test 3: Send test webhook
|
||||
console.log('📝 Test 3: Sending test webhook...');
|
||||
const testResponse = await fetch(`${API_URL}/api/webhooks/test/${webhookId}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${JWT_TOKEN}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!testResponse.ok) {
|
||||
throw new Error(`Failed to send test webhook: ${testResponse.status}`);
|
||||
}
|
||||
|
||||
console.log('✅ Test webhook sent\n');
|
||||
|
||||
// Wait for webhook to be received
|
||||
console.log('⏳ Waiting for webhook delivery (5 seconds)...');
|
||||
await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
if (receivedWebhooks.length > 0) {
|
||||
console.log(`✅ Received ${receivedWebhooks.length} webhook(s)!\n`);
|
||||
} else {
|
||||
console.log('⚠️ No webhooks received yet. Check webhook logs.\n');
|
||||
}
|
||||
|
||||
// Test 4: Check webhook logs
|
||||
console.log('📝 Test 4: Checking webhook logs...');
|
||||
const logsResponse = await fetch(`${API_URL}/api/webhooks/${webhookId}/logs?limit=10`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${JWT_TOKEN}`
|
||||
}
|
||||
});
|
||||
|
||||
if (!logsResponse.ok) {
|
||||
throw new Error(`Failed to get webhook logs: ${logsResponse.status}`);
|
||||
}
|
||||
|
||||
const logs = await logsResponse.json();
|
||||
console.log(`✅ Found ${logs.length} log entries\n`);
|
||||
|
||||
if (logs.length > 0) {
|
||||
console.log('Recent webhook deliveries:');
|
||||
logs.forEach((log, i) => {
|
||||
console.log(` ${i + 1}. Event: ${log.event_type}, Status: ${log.response_status || 'pending'}, Time: ${log.created_at}`);
|
||||
if (log.error_message) {
|
||||
console.log(` Error: ${log.error_message}`);
|
||||
}
|
||||
});
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Summary
|
||||
console.log('═══════════════════════════════════════════════════════');
|
||||
console.log('Test Summary');
|
||||
console.log('═══════════════════════════════════════════════════════');
|
||||
console.log(`✅ Webhook created: ID ${webhookId}`);
|
||||
console.log(`✅ Webhooks received: ${receivedWebhooks.length}`);
|
||||
console.log(`✅ Webhook logs: ${logs.length} entries`);
|
||||
console.log('═══════════════════════════════════════════════════════\n');
|
||||
|
||||
console.log('🎉 All tests completed!');
|
||||
console.log('\n💡 Next steps:');
|
||||
console.log(' 1. Add a game to an active session in the Picker page');
|
||||
console.log(' 2. Watch for the webhook to be received here');
|
||||
console.log(' 3. Press Ctrl+C to cleanup and exit\n');
|
||||
|
||||
} catch (error) {
|
||||
console.error('\n❌ Test failed:', error.message);
|
||||
console.error('\nMake sure:');
|
||||
console.error(' - Backend server is running (http://localhost:5000)');
|
||||
console.error(' - JWT_TOKEN is valid');
|
||||
console.error(' - Port 3001 is available\n');
|
||||
await cleanup();
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanup() {
|
||||
console.log('\n🧹 Cleaning up...');
|
||||
|
||||
if (webhookId) {
|
||||
try {
|
||||
console.log(` Deleting webhook ${webhookId}...`);
|
||||
const deleteResponse = await fetch(`${API_URL}/api/webhooks/${webhookId}`, {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${JWT_TOKEN}`
|
||||
}
|
||||
});
|
||||
|
||||
if (deleteResponse.ok) {
|
||||
console.log(' ✅ Webhook deleted');
|
||||
} else {
|
||||
console.log(' ⚠️ Could not delete webhook (you may need to delete it manually)');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(' ⚠️ Error during cleanup:', err.message);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(' Stopping webhook receiver...');
|
||||
server.close(() => {
|
||||
console.log(' ✅ Server stopped');
|
||||
console.log('\n👋 Goodbye!\n');
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
// Handle Ctrl+C
|
||||
process.on('SIGINT', async () => {
|
||||
console.log('\n\n⚠️ Received interrupt signal');
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
// Handle errors
|
||||
process.on('uncaughtException', async (err) => {
|
||||
console.error('\n❌ Uncaught exception:', err);
|
||||
await cleanup();
|
||||
});
|
||||
|
||||
268
tests/test-webhook.sh
Executable file
268
tests/test-webhook.sh
Executable file
@@ -0,0 +1,268 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Webhook Test Script (Bash/curl version)
|
||||
# This script tests the webhook system using only curl and bash
|
||||
|
||||
set -e
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
API_URL="${API_URL:-https://hso.cottongin.xyz/api}"
|
||||
WEBHOOK_PORT="${WEBHOOK_PORT:-3001}"
|
||||
WEBHOOK_SECRET="test_secret_$(date +%s)"
|
||||
|
||||
echo -e "\n${BLUE}╔════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${BLUE}║ Webhook Test Script (curl) ║${NC}"
|
||||
echo -e "${BLUE}╚════════════════════════════════════════════════════╝${NC}\n"
|
||||
|
||||
# Check if JWT_TOKEN is set
|
||||
if [ -z "$JWT_TOKEN" ]; then
|
||||
echo -e "${RED}❌ ERROR: JWT_TOKEN not set!${NC}\n"
|
||||
echo "Please set your JWT token:"
|
||||
echo " 1. Get token:"
|
||||
echo " curl -X POST https://hso.cottongin.xyz/api/auth/login \\"
|
||||
echo " -H \"Content-Type: application/json\" \\"
|
||||
echo " -d '{\"key\":\"YOUR_ADMIN_KEY\"}'"
|
||||
echo ""
|
||||
echo " 2. Export the token:"
|
||||
echo " export JWT_TOKEN=\"your_token_here\""
|
||||
echo ""
|
||||
echo " 3. Run this script:"
|
||||
echo " ./test-webhook.sh"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if nc (netcat) is available
|
||||
if ! command -v nc &> /dev/null; then
|
||||
echo -e "${RED}❌ ERROR: 'nc' (netcat) command not found!${NC}"
|
||||
echo "Please install netcat to run this test."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓${NC} JWT_TOKEN is set"
|
||||
echo -e "${GREEN}✓${NC} API URL: $API_URL"
|
||||
echo -e "${GREEN}✓${NC} Webhook Port: $WEBHOOK_PORT"
|
||||
echo -e "${GREEN}✓${NC} Webhook Secret: $WEBHOOK_SECRET"
|
||||
echo ""
|
||||
|
||||
# Start a simple webhook receiver in the background
|
||||
echo -e "${BLUE}🚀 Starting webhook receiver on port $WEBHOOK_PORT...${NC}"
|
||||
|
||||
# Create a named pipe for communication
|
||||
FIFO="/tmp/webhook_test_$$"
|
||||
mkfifo "$FIFO"
|
||||
|
||||
# Simple HTTP server using netcat
|
||||
(
|
||||
while true; do
|
||||
{
|
||||
# Read the request
|
||||
read -r REQUEST
|
||||
|
||||
# Read headers until empty line
|
||||
CONTENT_LENGTH=0
|
||||
SIGNATURE=""
|
||||
EVENT=""
|
||||
while read -r HEADER; do
|
||||
HEADER=$(echo "$HEADER" | tr -d '\r')
|
||||
[ -z "$HEADER" ] && break
|
||||
|
||||
if [[ "$HEADER" =~ ^Content-Length:\ ([0-9]+) ]]; then
|
||||
CONTENT_LENGTH="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
if [[ "$HEADER" =~ ^X-Webhook-Signature:\ (.+) ]]; then
|
||||
SIGNATURE="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
if [[ "$HEADER" =~ ^X-Webhook-Event:\ (.+) ]]; then
|
||||
EVENT="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
done
|
||||
|
||||
# Read body if Content-Length is set
|
||||
BODY=""
|
||||
if [ "$CONTENT_LENGTH" -gt 0 ]; then
|
||||
BODY=$(dd bs=1 count="$CONTENT_LENGTH" 2>/dev/null)
|
||||
fi
|
||||
|
||||
# Log the webhook
|
||||
if [ -n "$BODY" ]; then
|
||||
echo "" >> "$FIFO"
|
||||
echo "📨 Webhook received!" >> "$FIFO"
|
||||
echo " Event: $EVENT" >> "$FIFO"
|
||||
echo " Signature: $SIGNATURE" >> "$FIFO"
|
||||
echo " Body: $BODY" >> "$FIFO"
|
||||
echo "" >> "$FIFO"
|
||||
fi
|
||||
|
||||
# Send response
|
||||
echo "HTTP/1.1 200 OK"
|
||||
echo "Content-Type: text/plain"
|
||||
echo "Content-Length: 2"
|
||||
echo ""
|
||||
echo "OK"
|
||||
} | nc -l -p "$WEBHOOK_PORT" -q 1
|
||||
done
|
||||
) &
|
||||
|
||||
WEBHOOK_PID=$!
|
||||
|
||||
# Display webhook output in background
|
||||
tail -f "$FIFO" &
|
||||
TAIL_PID=$!
|
||||
|
||||
# Give the server a moment to start
|
||||
sleep 2
|
||||
|
||||
echo -e "${GREEN}✓${NC} Webhook receiver started (PID: $WEBHOOK_PID)"
|
||||
echo -e "${GREEN}✓${NC} Listening on http://localhost:$WEBHOOK_PORT/webhook/jackbox"
|
||||
echo ""
|
||||
|
||||
# Cleanup function
|
||||
cleanup() {
|
||||
echo ""
|
||||
echo -e "${YELLOW}🧹 Cleaning up...${NC}"
|
||||
|
||||
if [ -n "$WEBHOOK_ID" ]; then
|
||||
echo " Deleting webhook $WEBHOOK_ID..."
|
||||
DELETE_RESPONSE=$(curl -s -w "\n%{http_code}" -X DELETE \
|
||||
"$API_URL/api/webhooks/$WEBHOOK_ID" \
|
||||
-H "Authorization: Bearer $JWT_TOKEN")
|
||||
|
||||
DELETE_CODE=$(echo "$DELETE_RESPONSE" | tail -n1)
|
||||
if [ "$DELETE_CODE" = "200" ]; then
|
||||
echo -e " ${GREEN}✓${NC} Webhook deleted"
|
||||
else
|
||||
echo -e " ${YELLOW}⚠${NC} Could not delete webhook (you may need to delete it manually)"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo " Stopping webhook receiver..."
|
||||
kill $WEBHOOK_PID 2>/dev/null || true
|
||||
kill $TAIL_PID 2>/dev/null || true
|
||||
rm -f "$FIFO"
|
||||
echo -e " ${GREEN}✓${NC} Cleanup complete"
|
||||
echo ""
|
||||
echo -e "${BLUE}👋 Goodbye!${NC}\n"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Trap Ctrl+C
|
||||
trap cleanup SIGINT SIGTERM
|
||||
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE}Starting Webhook Tests${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}\n"
|
||||
|
||||
# Test 1: Create webhook
|
||||
echo -e "${YELLOW}📝 Test 1: Creating webhook...${NC}"
|
||||
CREATE_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
|
||||
"$API_URL/api/webhooks" \
|
||||
-H "Authorization: Bearer $JWT_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"name\": \"Test Webhook\",
|
||||
\"url\": \"http://host.docker.internal:$WEBHOOK_PORT/webhook/jackbox\",
|
||||
\"secret\": \"$WEBHOOK_SECRET\",
|
||||
\"events\": [\"game.added\"]
|
||||
}")
|
||||
|
||||
CREATE_CODE=$(echo "$CREATE_RESPONSE" | tail -n1)
|
||||
CREATE_BODY=$(echo "$CREATE_RESPONSE" | head -n-1)
|
||||
|
||||
if [ "$CREATE_CODE" = "201" ]; then
|
||||
WEBHOOK_ID=$(echo "$CREATE_BODY" | grep -o '"id":[0-9]*' | grep -o '[0-9]*')
|
||||
echo -e "${GREEN}✓${NC} Webhook created with ID: $WEBHOOK_ID"
|
||||
echo " Response: $CREATE_BODY"
|
||||
else
|
||||
echo -e "${RED}✗${NC} Failed to create webhook (HTTP $CREATE_CODE)"
|
||||
echo " Response: $CREATE_BODY"
|
||||
cleanup
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 2: List webhooks
|
||||
echo -e "${YELLOW}📝 Test 2: Listing webhooks...${NC}"
|
||||
LIST_RESPONSE=$(curl -s -w "\n%{http_code}" \
|
||||
"$API_URL/api/webhooks" \
|
||||
-H "Authorization: Bearer $JWT_TOKEN")
|
||||
|
||||
LIST_CODE=$(echo "$LIST_RESPONSE" | tail -n1)
|
||||
LIST_BODY=$(echo "$LIST_RESPONSE" | head -n-1)
|
||||
|
||||
if [ "$LIST_CODE" = "200" ]; then
|
||||
WEBHOOK_COUNT=$(echo "$LIST_BODY" | grep -o '"id":' | wc -l)
|
||||
echo -e "${GREEN}✓${NC} Found $WEBHOOK_COUNT webhook(s)"
|
||||
else
|
||||
echo -e "${RED}✗${NC} Failed to list webhooks (HTTP $LIST_CODE)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 3: Send test webhook
|
||||
echo -e "${YELLOW}📝 Test 3: Sending test webhook...${NC}"
|
||||
TEST_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
|
||||
"$API_URL/api/webhooks/test/$WEBHOOK_ID" \
|
||||
-H "Authorization: Bearer $JWT_TOKEN")
|
||||
|
||||
TEST_CODE=$(echo "$TEST_RESPONSE" | tail -n1)
|
||||
|
||||
if [ "$TEST_CODE" = "200" ]; then
|
||||
echo -e "${GREEN}✓${NC} Test webhook sent"
|
||||
else
|
||||
echo -e "${RED}✗${NC} Failed to send test webhook (HTTP $TEST_CODE)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Wait for webhook delivery
|
||||
echo -e "${YELLOW}⏳ Waiting for webhook delivery (5 seconds)...${NC}"
|
||||
sleep 5
|
||||
echo ""
|
||||
|
||||
# Test 4: Check webhook logs
|
||||
echo -e "${YELLOW}📝 Test 4: Checking webhook logs...${NC}"
|
||||
LOGS_RESPONSE=$(curl -s -w "\n%{http_code}" \
|
||||
"$API_URL/api/webhooks/$WEBHOOK_ID/logs?limit=10" \
|
||||
-H "Authorization: Bearer $JWT_TOKEN")
|
||||
|
||||
LOGS_CODE=$(echo "$LOGS_RESPONSE" | tail -n1)
|
||||
LOGS_BODY=$(echo "$LOGS_RESPONSE" | head -n-1)
|
||||
|
||||
if [ "$LOGS_CODE" = "200" ]; then
|
||||
LOG_COUNT=$(echo "$LOGS_BODY" | grep -o '"id":' | wc -l)
|
||||
echo -e "${GREEN}✓${NC} Found $LOG_COUNT log entries"
|
||||
echo ""
|
||||
echo "Recent webhook deliveries:"
|
||||
echo "$LOGS_BODY" | python3 -m json.tool 2>/dev/null || echo "$LOGS_BODY"
|
||||
else
|
||||
echo -e "${RED}✗${NC} Failed to get webhook logs (HTTP $LOGS_CODE)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Summary
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE}Test Summary${NC}"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${GREEN}✓${NC} Webhook created: ID $WEBHOOK_ID"
|
||||
echo -e "${GREEN}✓${NC} Test webhook sent"
|
||||
echo -e "${GREEN}✓${NC} Webhook logs: $LOG_COUNT entries"
|
||||
echo -e "${BLUE}═══════════════════════════════════════════════════════${NC}\n"
|
||||
|
||||
echo -e "${GREEN}🎉 All tests completed!${NC}"
|
||||
echo ""
|
||||
echo -e "${BLUE}💡 Next steps:${NC}"
|
||||
echo " 1. Add a game to an active session in the Picker page"
|
||||
echo " 2. Watch for the webhook to be received above"
|
||||
echo " 3. Press Ctrl+C to cleanup and exit"
|
||||
echo ""
|
||||
echo -e "${YELLOW}⏳ Webhook receiver is still running...${NC}"
|
||||
echo ""
|
||||
|
||||
# Keep running until Ctrl+C
|
||||
wait $WEBHOOK_PID
|
||||
|
||||
Reference in New Issue
Block a user