Claude-skill-registry e2e-test
Run E2E test scenarios against running services. Use for happy path testing, unhappy flows, debugging, or when user says "otestuj", "proved test", "zkus flow".
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/e2e-test-cooper538-eshop-demo" ~/.claude/skills/majiayu000-claude-skill-registry-e2e-test && rm -rf "$T"
skills/data/e2e-test-cooper538-eshop-demo/SKILL.mdE2E Test Runner
Execute end-to-end test scenarios against running microservices with automatic debugging and reporting.
Usage
/e2e-test # Interactive - asks what to test /e2e-test happy # Happy path: create order flow /e2e-test unhappy # Unhappy path: out of stock, invalid data /e2e-test cancel # Cancel order flow /e2e-test debug # Just show service status and debug info /e2e-test trace <corr-id> # Trace request across services by CorrelationId /e2e-test <custom scenario> # Describe what you want to test
Input
- Test scenario to run or empty for interactive mode$ARGUMENTS
Architecture Reference
Services
| Service | Purpose | API Base | gRPC |
|---|---|---|---|
| Gateway | Reverse proxy (YARP) | | - |
| Product API | Product catalog & stock | | |
| Order API | Order management | | - |
| Notification | Email notifications | - (consumer only) | - |
| Analytics | Order tracking | - (consumer only) | - |
Databases (PostgreSQL)
| Database | Tables | Purpose |
|---|---|---|
| , , , , | Product catalog & stock |
| , , , , | Orders + outbox |
| | Inbox pattern (note: plural "Messages") |
Stock Behavior (IMPORTANT)
Stock quantity is NOT decreased when orders are created. Instead:
- A
record is created withStockReservation
(Active)Status=0 - When order is cancelled, reservation changes to
(Released)Status=1 - The
field represents total inventory, not available inventoryStock.Quantity
Message Flow
Order API → [gRPC] → Product API (ReserveStock) ↓ OrderConfirmedEvent → [RabbitMQ] → Notification + Analytics
CRITICAL: Gateway-First Rule
ALL API calls in E2E tests MUST go through the Gateway. This is non-negotiable.
✓ CORRECT: curl http://localhost:$GATEWAY_PORT/api/products ✗ WRONG: curl http://localhost:$PRODUCT_PORT/api/products
Why:
- Gateway is the only entry point in production
- E2E tests must simulate real-world usage
- Tests the full stack: YARP proxy, correlation ID propagation, routing
- Catches Gateway-specific issues (routing, headers, timeouts)
When direct service access is allowed:
scenario only - to compare Gateway vs direct responsedebug- Troubleshooting when Gateway returns error but you need to verify backend works
- Never in
,happy
,unhappy
scenarioscancel
Process
Phase 0: Service Mode Selection
ALWAYS start here. Use
AskUserQuestion to ask the user how they want to manage services:
Question: "How should I manage services for this E2E test?"
Options:
-
Manual (Recommended) - "I'll run the services myself or they're already running"
- User controls AppHost in their own terminal
- Skill only discovers existing services
- User responsible for starting/stopping
-
Automatic - "Start AppHost as a background process"
- Skill starts
in background usingdotnet run --project src/AppHostrun_in_background: true - Skill waits for services to be healthy
- Skill offers to stop AppHost at the end using
TaskStop
- Skill starts
Based on selection:
- Manual mode: Proceed to Phase 1 (Environment Discovery)
- Automatic mode:
- Start AppHost in background:
Usedotnet run --project src/AppHost
parameterrun_in_background: true - Wait 15-30 seconds for services to start
- Run discovery to verify services are healthy
- If services not healthy after 60s, show logs and ask user what to do
- Remember the task_id - you'll need it for cleanup at the end
- Start AppHost in background:
Phase 1: Environment Discovery
Run
./tools/e2e-test/discover.sh to get:
- Running services - ports dynamically assigned by Aspire
- Database connection - PostgreSQL container and credentials
- Message broker - RabbitMQ management URL
- Health status - all service health endpoints
If services are not running:
- Manual mode: Inform user:
Services not detected. Start with: dotnet run --project src/AppHost - Automatic mode: Check AppHost logs for errors, report to user
Phase 2: Scenario Planning
Based on
$ARGUMENTS, plan the test scenario:
happy
- Order Happy Path
happyAll API calls via Gateway (
$GATEWAY_PORT):
- GET
via Gateway → pick one with stock > 0/api/products - POST
via Gateway → create order (see API Contract below)/api/orders - Verify: Order status = Confirmed (via Gateway GET
)/api/orders/{id} - Verify: StockReservation created (Status=0) - DB query
- Verify: Notification inbox has record (
table) - DB queryProcessedMessages - Check logs for complete flow
API Contract - Create Order:
{ "customerId": "guid", "customerEmail": "email@example.com", "items": [{ "productId": "guid", "quantity": 2 }] }
unhappy
- Failure Scenarios
unhappyAll API calls via Gateway (
$GATEWAY_PORT):
- Out of stock: POST
via Gateway with quantity > available/api/orders - Invalid product: POST
via Gateway with non-existent productId/api/orders - Invalid data: POST
via Gateway with missing required fields/api/orders - Verify: Appropriate error responses from Gateway
- Verify: No side effects (stock unchanged, no events) - DB queries
cancel
- Order Cancellation
cancelAll API calls via Gateway (
$GATEWAY_PORT):
- Create order first via Gateway (happy path steps 1-2)
- POST
via Gateway (see API Contract below)/api/orders/{orderId}/cancel - Verify: Order status = Cancelled (via Gateway GET
)/api/orders/{id} - Verify: StockReservation status changed to 1 (Released) - DB query
- Verify: Cancellation notification processed (
) - DB queryOrderCancelledConsumer
API Contract - Cancel Order:
POST /api/orders/{orderId}/cancel Content-Type: application/json {"reason": "Customer requested cancellation"} # Body is REQUIRED!
debug
- Service Debug Info
debugThis is the only scenario where direct service access is allowed (for comparison/troubleshooting).
- Show all service ports and URLs (Gateway + backend services)
- Show database connection info
- Show recent logs (last 20 lines per service)
- Show message queue status (RabbitMQ)
- Show gRPC connectivity status
- Check service discovery configuration
- (Optional) Compare Gateway response vs direct service response
trace <correlation-id>
- Distributed Request Tracing
trace <correlation-id>- Run
./tools/e2e-test/trace-correlation.sh <correlation-id> - Display chronologically sorted logs from all services
- Highlight errors and warnings
- Show service flow visualization
Options:
- Search all log files, not just latest--all-logs
- Output as JSON for further processing--json
Example:
/e2e-test trace 228617a4-175a-4384-a8e2-ade916a78c3f
Output shows:
- Service-colored log entries (gateway=cyan, order=green, product=yellow, etc.)
- Chronological order across all services
- Error highlighting in red, warnings in yellow
Phase 3: Execution
Execute scenario step by step. After each step:
- Log the action taken
- Verify expected outcome
- If failure detected → STOP and consult user
REMEMBER: All API calls go through Gateway! (see Gateway-First Rule above)
Use these helpers:
# Service discovery - saves ports to .env file ./tools/e2e-test/discover.sh source ./tools/e2e-test/.env # API calls - ALWAYS use Gateway port! curl -s "http://localhost:$GATEWAY_PORT/api/products" | jq '.' curl -s "http://localhost:$GATEWAY_PORT/api/orders" | jq '.' curl -s -X POST "http://localhost:$GATEWAY_PORT/api/orders" -H "Content-Type: application/json" -d '...' # WRONG - never call services directly in E2E tests: # curl -s "http://localhost:$PRODUCT_PORT/api/products" # ✗ DON'T DO THIS # curl -s "http://localhost:$ORDER_PORT/api/orders" # ✗ DON'T DO THIS # Database queries - get password first, then query PG_PASS=$(docker exec <container> printenv POSTGRES_PASSWORD) docker exec -e PGPASSWORD="$PG_PASS" <container> psql -U postgres -d <db> -c '<SQL>' # Example: PG_PASS=$(docker exec postgres-4cdf07e3 printenv POSTGRES_PASSWORD) docker exec -e PGPASSWORD="$PG_PASS" postgres-4cdf07e3 psql -U postgres -d productdb -c 'SELECT * FROM "StockReservation";' # Log inspection ./tools/e2e-test/logs.sh <service> [lines] # Trace correlation ID ./tools/e2e-test/trace-correlation.sh <correlation-id>
Finding Service Ports Manually
If
discover.sh fails to find services, use this:
# List all dotnet processes with ports lsof -i -P -n | grep -E "EShop\.(Ord|Pro|Gat)" | grep LISTEN # Typical output: # EShop.Ord 45956 ... TCP 127.0.0.1:49814 (LISTEN) <- Order API HTTP # EShop.Pro 45955 ... TCP 127.0.0.1:49815 (LISTEN) <- Product API HTTP # EShop.Gat 45954 ... TCP 127.0.0.1:49818 (LISTEN) <- Gateway HTTP
Phase 4: Reporting
Generate structured report:
═══════════════════════════════════════════════════════ E2E TEST REPORT: <scenario name> ═══════════════════════════════════════════════════════ ENVIRONMENT Gateway: http://localhost:XXXXX ✓ ← All API calls go here PostgreSQL: localhost:XXXXX ✓ RabbitMQ: localhost:XXXXX ✓ Backend services (for reference only): Order API: http://localhost:XXXXX ✓ Product API: http://localhost:XXXXX ✓ SCENARIO: <description> STEPS EXECUTED [✓] Step 1: Get products → Found 10 products, selected "Cable Management Kit" (stock: 100) [✓] Step 2: Create order → POST /api/orders → 201 Created → OrderId: abc-123-def [✓] Step 3: Verify order status → DB: Order.Status = 1 (Confirmed) [✗] Step 4: Verify stock decreased → Expected: 98, Actual: 100 → FAILURE: Stock not reserved LOGS (relevant entries) [Order.API 21:00:10] Creating order for customer X [Order.API 21:00:10] ERROR: gRPC call failed - No address resolver DIAGNOSIS Problem: gRPC service discovery not configured Location: src/Common/EShop.ServiceClients/Extensions/ServiceCollectionExtensions.cs Fix: Add .AddServiceDiscovery() to gRPC client registration RESULT: FAILED (Step 4) ═══════════════════════════════════════════════════════
Error Handling
When an error blocks the scenario:
- STOP execution immediately
- Gather diagnostic info:
- Recent logs from affected service
- Database state
- Error response details
- Present to user with options:
⚠️ Test blocked by error at Step X Error: <description> Service: <service name> Log excerpt: <relevant log lines> Options: 1. Attempt to fix the issue (I'll suggest a fix) 2. Skip this step and continue 3. Abort test and show partial report 4. Debug mode - show all diagnostic info
Wait for user decision before proceeding.
Key Validation Points
Note: All API calls in these validation points go through Gateway.
Order Creation (Happy Path)
| Check | Query/Command | Expected |
|---|---|---|
| API Response | | 200, |
| Get Order | | |
| Order in DB | | (Confirmed) |
| Reservation created | | 1 row, |
| Stock unchanged | | Same as before (stock is NOT decreased) |
| Outbox processed | | 0 pending (processed) |
| Notification inbox | | row |
Order Cancellation
| Check | Query/Command | Expected |
|---|---|---|
| API Response | | 200, |
| Get Order | | |
| Order in DB | | (Cancelled) |
| Reservation released | | (Released) |
| Notification inbox | | row |
Stock Low Alert
| Check | Query/Command | Expected |
|---|---|---|
| Triggered when | Reservation makes available < threshold | published |
| Notification inbox | | row |
Log Patterns to Verify
| Service | Pattern | Meaning |
|---|---|---|
| Order.API | | Command received |
| Order.API | | Event dispatched |
| Product.API | | gRPC call arrived |
| Product.API | | Reservation complete |
| Notification | | Event received |
| Notification | | Email triggered |
RabbitMQ Diagnostics
Use
./tools/e2e-test/rabbitmq.sh for message broker debugging:
./tools/e2e-test/rabbitmq.sh status # Overview ./tools/e2e-test/rabbitmq.sh queues # Queue status with message counts ./tools/e2e-test/rabbitmq.sh connections # Active service connections ./tools/e2e-test/rabbitmq.sh consumers # Consumer registrations ./tools/e2e-test/rabbitmq.sh messages # Pending message analysis
RabbitMQ Validation Points
| Check | What to look for | Issue if... |
|---|---|---|
| Messages Ready | Should be 0 after processing | > 0 = stuck messages, consumer issue |
| Messages Unacked | Should be 0 or low | High = slow consumer or stuck processing |
| Connections | Order, Notification, Analytics | Missing = service not connected |
| Consumers | At least 2 per event type | 0 = no one listening |
| Dead Letter | Should be empty | Messages = repeated failures |
Expected Queues (after first message)
order-confirmed - OrderConfirmedEvent consumers order-rejected - OrderRejectedEvent consumers order-cancelled - OrderCancelledEvent consumers stock-low - StockLowEvent consumers
gRPC Diagnostics
Use
./tools/e2e-test/grpc.sh for inter-service communication debugging:
./tools/e2e-test/grpc.sh status # Port and connectivity check ./tools/e2e-test/grpc.sh list # List services (requires grpcurl) ./tools/e2e-test/grpc.sh test # Test gRPC calls ./tools/e2e-test/grpc.sh discovery # Service discovery configuration
gRPC Services
| Service | Proto | Methods |
|---|---|---|
| | , , |
gRPC Validation Points
| Check | How | Expected |
|---|---|---|
| Product service reachable | | HTTP 200 on health, gRPC port open |
| Service discovery | | configured |
| Proto matches | Compare proto file | Same version client/server |
Cleanup After Testing
After completing tests, offer cleanup options based on the mode selected in Phase 0:
Ask User About Cleanup
Manual mode:
Test completed. Cleanup options: 1. Keep everything running (for further testing) 2. Reset test data (purge queues, clear orders) Note: Stop services manually with Ctrl+C in your AppHost terminal. What would you like to do?
Automatic mode (AppHost started by skill):
Test completed. Cleanup options: 1. Keep AppHost running (for further testing) 2. Stop AppHost (terminate background process) 3. Reset test data only (keep services running) 4. Full cleanup (stop AppHost + reset data) What would you like to do?
Use
TaskStop with the saved task_id to stop the background AppHost process.
Cleanup Commands
Use
./tools/e2e-test/cleanup.sh for easy cleanup:
./tools/e2e-test/cleanup.sh status # See what needs cleaning ./tools/e2e-test/cleanup.sh data # Clear test orders, reservations ./tools/e2e-test/cleanup.sh queues # Purge RabbitMQ queues ./tools/e2e-test/cleanup.sh logs # Remove logs older than 7 days ./tools/e2e-test/cleanup.sh env # Remove generated .env ./tools/e2e-test/cleanup.sh services # Kill all running EShop services ./tools/e2e-test/cleanup.sh all # Full cleanup (except services)
Kill Services
Use
./tools/e2e-test/kill-services.sh to kill all running EShop services:
./tools/e2e-test/kill-services.sh # Show running services ./tools/e2e-test/kill-services.sh kill # Kill with confirmation ./tools/e2e-test/kill-services.sh --force # Kill without confirmation
This script finds and terminates all processes matching:
(AppHost, common libs)EShop.*
,Order.API
,Products.APIGateway.API
,Notification.APIAnalytics.APIDatabaseMigration
Or manually:
# Stop Aspire (if started by this session) # Note: AppHost runs in foreground, Ctrl+C stops it # Reset databases (clear test orders, keep products) ./tools/reset-db.sh # Purge RabbitMQ queues (remove stuck messages) ./tools/e2e-test/rabbitmq.sh purge <queue-name>
Test Data Cleanup SQL
-- Clear orders (orderdb) DELETE FROM "OrderItem"; DELETE FROM "Order"; DELETE FROM "OutboxMessage"; DELETE FROM "OutboxState"; DELETE FROM "InboxState"; -- Clear stock reservations (productdb) DELETE FROM "StockReservation"; -- Note: Stock.Quantity doesn't need reset - it's not modified by orders -- Clear processed messages (notificationdb) - note plural "Messages" DELETE FROM "ProcessedMessages";
When to Clean Up
| Scenario | Recommended Cleanup |
|---|---|
| Single test run | Option 1 (keep running) |
| End of testing session | Option 2 (stop services) |
| Tests created bad data | Option 3 (reset data) |
| Fresh start needed | Option 4 (full cleanup) |
Important Notes
- Never auto-cleanup without asking user
- Preserve logs - useful for debugging issues found during tests
- Database reset uses
which re-seeds products./tools/reset-db.sh - RabbitMQ queues auto-recreate when services restart
- Automatic mode cleanup - if you started AppHost in background, ALWAYS offer to stop it at the end using
with the savedTaskStoptask_id - Manual mode - user manages services, don't attempt to stop them
Files Reference
| File | Purpose |
|---|---|
| Service discovery (ports, credentials) |
| Database queries |
| Log inspection |
| API call helper |
| RabbitMQ diagnostics |
| gRPC diagnostics |
| Distributed tracing by CorrelationId |
| Test cleanup (default: all) |
| Kill all running EShop services |
| Database reset script |
| Service log files |
| API documentation |
Technical Notes
StockReservation Table Schema
- timestamp when reservation was madeReservedAt
- when reservation expiresExpiresAt
: 0 = Active, 1 = ReleasedStatus
Process Names in lsof
Aspire services use truncated names:
/EShop.OrdOrder.API
/EShop.ProProducts.
/EShop.GatGateway.A
The
discover.sh script searches for both naming patterns.