This guide provides step-by-step instructions for testing the RabbitMQ Publish/Subscribe pattern implementation in OrderFlow.Core.
Before testing, ensure:
- ✅ Docker Desktop is running
- ✅ Services are started:
docker-compose up -d - ✅ All containers are healthy:
docker-compose ps
# Check service status
docker-compose ps
# Expected output:
# NAME IMAGE STATUS
# orderflow-rabbitmq rabbitmq:3-management Up (healthy)
# orderflow-core orderflow-core Up- Swagger UI: http://localhost:8080/swagger
- RabbitMQ Management: http://localhost:15672 (admin/admin123)
- Health Check: http://localhost:8080/health
- Open http://localhost:8080/swagger
- Expand any endpoint (e.g.,
POST /api/orders) - Click "Try it out"
- Enter test data
- Click "Execute"
- View the response
Use the http OrderFlow.Core.http file in Visual Studio or VS Code with REST Client extension.
See examples in each test scenario below.
Purpose: Test basic Pub/Sub flow with multiple subscribers
curl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{
"customerName": "Alice Smith",
"productName": "Wireless Mouse",
"quantity": 2,
"totalAmount": 49.98
}'Invoke-RestMethod -Method Post -Uri "http://localhost:8080/api/orders" `
-ContentType "application/json" `
-Body (@{
customerName = "Alice Smith"
productName = "Wireless Mouse"
quantity = 2
totalAmount = 49.98
} | ConvertTo-Json){
"success": true,
"message": "Order created and published successfully",
"data": {
"order": {
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"customerName": "Alice Smith",
"productName": "Wireless Mouse",
"quantity": 2,
"totalAmount": 49.98,
"status": "Created",
"createdAt": "2024-01-20T10:30:00Z"
}
},
"errors": null,
"timestamp": "2024-01-20T10:30:00Z"
}- ✅ OrderProcessingSubscriber (routing key:
order.created) - ✅ NotificationSubscriber (routing key:
order.*)
docker-compose logs orderflow-core --tail 50 | grep "order.created"[OrdersController] Published message with routing key order.created. OrderId: <guid>
[OrderProcessingSubscriber] Received message from queue order_processing_queue
[OrderProcessingSubscriber] Processing order for Alice Smith - Wireless Mouse
[NotificationSubscriber] Received message from queue notification_queue
[NotificationSubscriber] Sending Order Confirmation notification for order <guid>
Purpose: Test wildcard routing pattern (payment.*)
# Linux/Mac
ORDER_ID=$(curl -s -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{
"customerName": "Bob Johnson",
"productName": "Keyboard",
"quantity": 1,
"totalAmount": 79.99
}' | jq -r '.data.order.id')
echo "Order ID: $ORDER_ID"# PowerShell
$response = Invoke-RestMethod -Method Post -Uri "http://localhost:8080/api/orders" `
-ContentType "application/json" `
-Body (@{
customerName = "Bob Johnson"
productName = "Keyboard"
quantity = 1
totalAmount = 79.99
} | ConvertTo-Json)
$ORDER_ID = $response.data.order.id
Write-Host "Order ID: $ORDER_ID"# Replace {orderId} with the GUID from step 1
curl -X POST http://localhost:8080/api/orders/{orderId}/payment# PowerShell
Invoke-RestMethod -Method Post -Uri "http://localhost:8080/api/orders/$ORDER_ID/payment"{
"success": true,
"message": "Payment verification event published successfully",
"data": {
"orderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"eventType": "payment.verified"
},
"errors": null,
"timestamp": "2024-01-20T10:35:00Z"
}- ✅ PaymentVerificationSubscriber (routing key:
payment.*) - ❌ OrderProcessingSubscriber (no match)
- ❌ ShippingSubscriber (no match)
- ❌ NotificationSubscriber (no match - doesn't match
order.*)
docker-compose logs orderflow-core --tail 50 | grep "payment"[OrdersController] Published message with routing key payment.verified
[PaymentVerificationSubscriber] Received message from queue payment_verification_queue
[PaymentVerificationSubscriber] Verifying payment for order <guid>
[PaymentVerificationSubscriber] Payment verification completed successfully
Purpose: Test specific routing to multiple subscribers
# Replace {orderId} with a valid order GUID
curl -X POST http://localhost:8080/api/orders/{orderId}/shipInvoke-RestMethod -Method Post -Uri "http://localhost:8080/api/orders/$ORDER_ID/ship"{
"success": true,
"message": "Shipping event published successfully",
"data": {
"orderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"eventType": "order.shipped"
},
"errors": null,
"timestamp": "2024-01-20T10:40:00Z"
}- ✅ ShippingSubscriber (routing key:
order.shipped) - ✅ NotificationSubscriber (routing key:
order.*)
docker-compose logs orderflow-core --tail 50 | grep "shipped"[OrdersController] Published message with routing key order.shipped
[ShippingSubscriber] Received message from queue shipping_queue
[ShippingSubscriber] Processing shipping for order <guid>
[NotificationSubscriber] Sending Order Shipped notification
Purpose: Test routing to single subscriber via wildcard
curl -X POST http://localhost:8080/api/orders/{orderId}/deliverInvoke-RestMethod -Method Post -Uri "http://localhost:8080/api/orders/$ORDER_ID/deliver"{
"success": true,
"message": "Delivery event published successfully",
"data": {
"orderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"eventType": "order.delivered"
},
"errors": null,
"timestamp": "2024-01-20T10:45:00Z"
}- ✅ NotificationSubscriber (routing key:
order.*) - ❌ All other subscribers (no matching routing keys)
docker-compose logs orderflow-core --tail 50 | grep "delivered"Purpose: Test DELETE endpoint with event publishing
curl -X DELETE http://localhost:8080/api/orders/{orderId}Invoke-RestMethod -Method Delete -Uri "http://localhost:8080/api/orders/$ORDER_ID"{
"success": true,
"message": "Order cancellation event published successfully",
"data": {
"orderId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"eventType": "order.cancelled"
},
"errors": null,
"timestamp": "2024-01-20T10:50:00Z"
}- ✅ NotificationSubscriber (routing key:
order.*)
docker-compose logs orderflow-core --tail 50 | grep "cancelled"- Navigate to: http://localhost:15672
- Login: admin / admin123
- Go to "Exchanges" tab
- Find
order_exchange(type: topic) - Click on it to see bindings
| Queue Name | Routing Key | Expected Messages |
|---|---|---|
order_processing_queue |
order.created |
New orders only |
payment_verification_queue |
payment.* |
Payment events only |
shipping_queue |
order.shipped |
Shipping events only |
notification_queue |
order.* |
ALL order events |
- Click on "Queues" tab
- For each queue, observe:
- Ready: Messages waiting to be consumed
- Unacked: Messages being processed
- Total: Total messages processed
- Message rates: Messages/second
Real-time monitoring:
# All logs
docker-compose logs -f orderflow-core
# Specific subscriber
docker-compose logs -f orderflow-core | grep "OrderProcessingSubscriber"
# Specific event type
docker-compose logs -f orderflow-core | grep "order.created"
# Publisher only
docker-compose logs -f orderflow-core | grep "Published message"Structured log analysis:
# Count messages by event type
docker-compose logs orderflow-core | grep "Published message" | grep -o "routing key [a-z.]*" | sort | uniq -c
# Count messages by subscriber
docker-compose logs orderflow-core | grep "Received message from queue" | grep -o "queue [a-z_]*" | sort | uniq -c# Check application and RabbitMQ health
curl http://localhost:8080/health | jqExpected output:
{
"status": "Healthy",
"totalDuration": "00:00:00.0118102",
"entries": {
"rabbitmq": {
"status": "Healthy",
"duration": "00:00:00.0094042"
}
}
}Test the system's ability to handle multiple simultaneous requests.
for i in {1..10}; do
curl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d "{
\"customerName\": \"Customer $i\",
\"productName\": \"Product $i\",
\"quantity\": 1,
\"totalAmount\": 99.99
}" &
done
wait1..10 | ForEach-Object {
Start-Job -ScriptBlock {
param($num)
Invoke-RestMethod -Method Post -Uri "http://localhost:8080/api/orders" `
-ContentType "application/json" `
-Body (@{
customerName = "Customer $num"
productName = "Product $num"
quantity = 1
totalAmount = 99.99
} | ConvertTo-Json)
} -ArgumentList $_
}
Get-Job | Wait-Job | Receive-Job
Get-Job | Remove-JobVerification:
- Check RabbitMQ message rates in management UI
- Verify all messages were processed
- Check for errors in logs
Test that messages survive application restarts.
# 1. Publish messages
curl -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{
"customerName": "Test Persistence",
"productName": "Test Product",
"quantity": 1,
"totalAmount": 100.00
}'
# 2. Stop subscribers (restart app)
docker-compose restart orderflow-core
# 3. Wait for app to restart
Start-Sleep -Seconds 10
# 4. Verify messages were redelivered
docker-compose logs orderflow-core --tail 100 | grep "Test Persistence"Test a complete order from creation to delivery.
# 1. Create Order
ORDER_ID=$(curl -s -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{
"customerName": "John Doe",
"productName": "Complete Lifecycle Test",
"quantity": 1,
"totalAmount": 199.99
}' | jq -r '.data.order.id')
echo "Order Created: $ORDER_ID"
Start-Sleep -Seconds 2
# 2. Verify Payment
curl -s -X POST "http://localhost:8080/api/orders/$ORDER_ID/payment"
echo "Payment Verified"
Start-Sleep -Seconds 2
# 3. Ship Order
curl -s -X POST "http://localhost:8080/api/orders/$ORDER_ID/ship"
echo "Order Shipped"
Start-Sleep -Seconds 2
# 4. Deliver Order
curl -s -X POST "http://localhost:8080/api/orders/$ORDER_ID/deliver"
echo "Order Delivered"
# 5. Check logs for complete flow
docker-compose logs orderflow-core | grep "$ORDER_ID"# 1. Create Order
$response = Invoke-RestMethod -Method Post -Uri "http://localhost:8080/api/orders" `
-ContentType "application/json" `
-Body (@{
customerName = "John Doe"
productName = "Complete Lifecycle Test"
quantity = 1
totalAmount = 199.99
} | ConvertTo-Json)
$ORDER_ID = $response.data.order.id
Write-Host "Order Created: $ORDER_ID"
Start-Sleep -Seconds 2
# 2. Verify Payment
Invoke-RestMethod -Method Post -Uri "http://localhost:8080/api/orders/$ORDER_ID/payment"
Write-Host "Payment Verified"
Start-Sleep -Seconds 2
# 3. Ship Order
Invoke-RestMethod -Method Post -Uri "http://localhost:8080/api/orders/$ORDER_ID/ship"
Write-Host "Order Shipped"
Start-Sleep -Seconds 2
# 4. Deliver Order
Invoke-RestMethod -Method Post -Uri "http://localhost:8080/api/orders/$ORDER_ID/deliver"
Write-Host "Order Delivered"
# 5. Check logs
docker-compose logs orderflow-core | Select-String $ORDER_IDVerify all routing patterns work correctly.
#!/bin/bash
echo "=========================================="
echo "Routing Pattern Verification Test"
echo "=========================================="
echo ""
# Create order
ORDER_ID=$(curl -s -X POST http://localhost:8080/api/orders \
-H "Content-Type: application/json" \
-d '{
"customerName": "Routing Test",
"productName": "Test Product",
"quantity": 1,
"totalAmount": 10.00
}' | jq -r '.data.order.id')
echo "Order ID: $ORDER_ID"
sleep 3
# Test 1: order.created -> 2 subscribers
echo ""
echo "Test 1: order.created routing"
echo "------------------------------"
PROCESSING_COUNT=$(docker-compose logs orderflow-core | grep -c "order_processing_queue.*$ORDER_ID")
NOTIFICATION_COUNT=$(docker-compose logs orderflow-core | grep -c "notification_queue.*$ORDER_ID")
echo " OrderProcessingSubscriber: $PROCESSING_COUNT messages"
echo " NotificationSubscriber: $NOTIFICATION_COUNT messages"
# Test 2: payment.verified -> 1 subscriber
curl -s -X POST "http://localhost:8080/api/orders/$ORDER_ID/payment" > /dev/null
sleep 3
echo ""
echo "Test 2: payment.verified routing"
echo "---------------------------------"
PAYMENT_COUNT=$(docker-compose logs orderflow-core | grep -c "payment_verification_queue.*$ORDER_ID")
echo " PaymentVerificationSubscriber: $PAYMENT_COUNT messages"
# Test 3: order.shipped -> 2 subscribers
curl -s -X POST "http://localhost:8080/api/orders/$ORDER_ID/ship" > /dev/null
sleep 3
echo ""
echo "Test 3: order.shipped routing"
echo "------------------------------"
SHIPPING_COUNT=$(docker-compose logs orderflow-core | grep -c "shipping_queue.*$ORDER_ID")
SHIPPING_NOTIF_COUNT=$(docker-compose logs orderflow-core | grep -c "notification_queue.*shipped.*$ORDER_ID")
echo " ShippingSubscriber: $SHIPPING_COUNT messages"
echo " NotificationSubscriber: $SHIPPING_NOTIF_COUNT messages"
echo ""
echo "=========================================="
echo "Routing Verification Complete"
echo "=========================================="Diagnosis:
# Check if exchange exists
docker exec orderflow-rabbitmq rabbitmqctl list_exchanges
# Check if queues exist and have bindings
docker exec orderflow-rabbitmq rabbitmqctl list_queues name messages_ready messages_unacknowledged
# Check bindings
docker exec orderflow-rabbitmq rabbitmqctl list_bindingsSolutions:
- Verify subscribers are running: Check logs for "Subscriber started" messages
- Check routing keys match exactly
- Verify exchange and queue declarations in code
Diagnosis:
# Check queue details
docker exec orderflow-rabbitmq rabbitmqctl list_queues name messages consumers
# Check consumer connections
docker exec orderflow-rabbitmq rabbitmqctl list_consumersSolutions:
- Check if subscribers are connected
- Look for processing errors in logs
- Verify message format is correct (JSON deserialization)
Symptoms:
- HTTP 500 errors
- "Unable to connect to RabbitMQ" in logs
Diagnosis:
# Check RabbitMQ health
docker-compose ps rabbitmq
# Check RabbitMQ logs
docker-compose logs rabbitmq --tail 50
# Test connection from app container
docker exec orderflow-core ping rabbitmqSolutions:
- Ensure RabbitMQ is healthy:
docker-compose ps - Verify network connectivity between containers
- Check credentials in docker-compose.yml match RabbitMQ settings
Diagnosis:
- Check if multiple instances of the same subscriber are running
- Verify consumer tags are unique
Solutions:
- Ensure only one instance of each subscriber service is running
- Check for restart loops in logs:
docker-compose logs orderflow-core | grep "Subscriber started"
Use this checklist to ensure comprehensive testing:
- Create order event published and consumed
- Payment event routed to correct subscriber
- Shipping event received by multiple subscribers
- Delivery event processed
- Cancel event processed
-
order.createdreaches 2 subscribers -
payment.*wildcard works -
order.*wildcard catches all order events - Non-matching routing keys are filtered correctly
- Messages persist through app restart
- Failed messages are requeued
- Acknowledgments work correctly
- Connection recovery works
- Concurrent message publishing works
- No message loss under load
- Subscribers process messages efficiently
- RabbitMQ management UI shows healthy metrics
- Application logs show publish/consume events
- RabbitMQ UI shows correct exchange/queue setup
- Health endpoint reports healthy status
- No error messages in logs
{
"customerName": "Jane Doe",
"productName": "Ergonomic Chair",
"quantity": 1,
"totalAmount": 299.99
}[
{
"customerName": "Customer 1",
"productName": "Product A",
"quantity": 2,
"totalAmount": 199.98
},
{
"customerName": "Customer 2",
"productName": "Product B",
"quantity": 1,
"totalAmount": 49.99
},
{
"customerName": "Customer 3",
"productName": "Product C",
"quantity": 5,
"totalAmount": 499.95
}
]| Event Type | Routing Key | Subscribers That Process It |
|---|---|---|
| Create Order | order.created |
OrderProcessingSubscriber, NotificationSubscriber |
| Verify Payment | payment.verified |
PaymentVerificationSubscriber |
| Ship Order | order.shipped |
ShippingSubscriber, NotificationSubscriber |
| Deliver Order | order.delivered |
NotificationSubscriber |
| Cancel Order | order.cancelled |
NotificationSubscriber |
- Main README - Application overview and setup
- API Response Pattern - API contract documentation
- Docker Deployment Guide - Docker-specific instructions
- RabbitMQ Topic Exchange Tutorial
If tests fail or you encounter issues:
- Check application logs:
docker-compose logs orderflow-core - Check RabbitMQ logs:
docker-compose logs rabbitmq - Verify health endpoint:
curl http://localhost:8080/health - Check RabbitMQ Management UI: http://localhost:15672
- Refer to DOCKER-DEPLOYMENT.md for troubleshooting
Happy Testing! 🚀