# Phase 1 Testing & Validation Guide This guide provides comprehensive testing procedures for validating all Phase 1 event streaming functionality. ## Table of Contents 1. [Prerequisites](#prerequisites) 2. [Starting the Sample Application](#starting-the-sample-application) 3. [Test 1: Workflow Start Semantics](#test-1-workflow-start-semantics) 4. [Test 2: Event Consumer (Broadcast Mode)](#test-2-event-consumer-broadcast-mode) 5. [Test 3: Ephemeral Streams](#test-3-ephemeral-streams) 6. [Test 4: gRPC Streaming](#test-4-grpc-streaming) 7. [Test 5: Existing Features (Regression)](#test-5-existing-features-regression) 8. [Expected Results Summary](#expected-results-summary) --- ## Prerequisites **Required Tools:** - .NET 10 SDK - `curl` (for HTTP testing) - `grpcurl` (for gRPC testing) - Install: `brew install grpcurl` (macOS) or download from https://github.com/fullstorydev/grpcurl **Optional Tools:** - Postman or similar REST client - gRPC UI or BloomRPC for visual gRPC testing --- ## Starting the Sample Application ```bash cd Svrnty.Sample dotnet run ``` **Expected Output:** ``` === Svrnty CQRS Sample with Event Streaming === gRPC (HTTP/2): http://localhost:6000 - CommandService, QueryService, DynamicQueryService - EventService (bidirectional streaming) HTTP API (HTTP/1.1): http://localhost:6001 - Commands: POST /api/command/* - Queries: GET/POST /api/query/* - Swagger UI: http://localhost:6001/swagger Event Streams Configured: - UserWorkflow stream (ephemeral, at-least-once) - InvitationWorkflow stream (ephemeral, at-least-once) Subscriptions Active: - user-analytics (broadcast mode) - invitation-processor (exclusive mode) info: EventConsumerBackgroundService[0] Event consumer starting... info: EventConsumerBackgroundService[0] Subscribing to 'user-analytics' subscription (broadcast mode)... ``` ✅ **Verify:** Application starts without errors and background consumer logs appear. --- ## Test 1: Workflow Start Semantics **Objective:** Verify that commands create workflow instances with correlation IDs. ### Test 1.1: Add User Command (HTTP) ```bash curl -X POST http://localhost:6001/api/command/addUser \ -H "Content-Type: application/json" \ -d '{ "name": "John Doe", "email": "john@example.com" }' ``` **Expected Response:** ```json 5432 ``` (Returns the generated user ID) **Expected Console Output:** ``` info: EventConsumerBackgroundService[0] [ANALYTICS] Received event: UserAddedEvent (EventId: , CorrelationId: , OccurredAt: ) info: EventConsumerBackgroundService[0] [ANALYTICS] User added: UserId=5432, Name=John Doe ``` ✅ **Verify:** - Command returns user ID - EventConsumerBackgroundService logs show event received - CorrelationId is present and is a GUID (workflow ID) ### Test 1.2: Invite User Command (Multi-Step Workflow) **Step 1: Send Invitation** ```bash curl -X POST http://localhost:6001/api/command/inviteUser \ -H "Content-Type: application/json" \ -d '{ "email": "jane@example.com", "inviterName": "Admin" }' ``` **Expected Response:** ```json "" ``` **Expected Console Output:** ``` info: EventConsumerBackgroundService[0] [ANALYTICS] Received event: UserInvitedEvent (EventId: , CorrelationId: , ...) info: EventConsumerBackgroundService[0] [ANALYTICS] User invited: InvitationId=, Email=jane@example.com, Inviter=Admin ``` **Step 2: Accept Invitation** ```bash curl -X POST http://localhost:6001/api/command/acceptInvite \ -H "Content-Type: application/json" \ -d '{ "invitationId": "", "email": "jane@example.com", "name": "Jane Doe" }' ``` **Expected Response:** ```json 7891 ``` (Returns the generated user ID) **Expected Console Output:** ``` info: EventConsumerBackgroundService[0] [ANALYTICS] Received event: UserInviteAcceptedEvent (EventId: , CorrelationId: , ...) info: EventConsumerBackgroundService[0] [ANALYTICS] Invitation accepted: InvitationId=, UserId=7891, Name=Jane Doe ``` ✅ **Verify:** - Both commands complete successfully - Events are emitted for both steps - Each command creates its own workflow instance (Phase 1 behavior) - Different CorrelationIds for invite vs accept (Phase 1 limitation - Phase 2 will support continuation) --- ## Test 2: Event Consumer (Broadcast Mode) **Objective:** Verify that broadcast subscription delivers events to all consumers. ### Test 2.1: Multiple Events Execute multiple commands and observe that EventConsumerBackgroundService receives all events: ```bash # Add multiple users for i in {1..5}; do curl -X POST http://localhost:6001/api/command/addUser \ -H "Content-Type: application/json" \ -d "{\"name\": \"User $i\", \"email\": \"user$i@example.com\"}" sleep 1 done ``` **Expected Console Output:** ``` info: EventConsumerBackgroundService[0] [ANALYTICS] Received event: UserAddedEvent (EventId: , ...) info: EventConsumerBackgroundService[0] [ANALYTICS] User added: UserId=, Name=User 1 info: EventConsumerBackgroundService[0] [ANALYTICS] Received event: UserAddedEvent (EventId: , ...) info: EventConsumerBackgroundService[0] [ANALYTICS] User added: UserId=, Name=User 2 ... ``` ✅ **Verify:** - All 5 events are received by the consumer - Events appear in order - No events are missed (broadcast mode guarantees all consumers get all events) --- ## Test 3: Ephemeral Streams **Objective:** Verify ephemeral stream behavior (message queue semantics). ### Test 3.1: Event Visibility Timeout Ephemeral streams use visibility timeouts. Events that aren't acknowledged within the timeout are automatically requeued. **Current Behavior (Phase 1.4):** - EventSubscriptionClient automatically acknowledges events after processing - Visibility timeout is set to 30 seconds by default - Events are deleted after acknowledgment (ephemeral semantics) **Manual Test:** 1. Send a command to generate an event 2. Observe that the event is delivered to the consumer 3. Event is automatically acknowledged and removed from the stream ```bash curl -X POST http://localhost:6001/api/command/addUser \ -H "Content-Type: application/json" \ -d '{"name": "Test User", "email": "test@example.com"}' ``` ✅ **Verify:** - Event is delivered once to the consumer - No duplicate deliveries (event is removed after acknowledgment) - If you stop and restart the app, previous events are gone (ephemeral semantics) ### Test 3.2: Application Restart (Ephemeral Behavior) 1. Send several commands to generate events 2. Stop the application (Ctrl+C) 3. Restart the application 4. Observe that previous events are NOT replayed (ephemeral streams don't persist data) ```bash # While app is running curl -X POST http://localhost:6001/api/command/addUser \ -H "Content-Type: application/json" \ -d '{"name": "Before Restart", "email": "before@example.com"}' # Stop app (Ctrl+C) # Restart app # No events from before restart are delivered ``` ✅ **Verify:** - After restart, no historical events are delivered - Only new events (after restart) are received - This confirms ephemeral stream behavior (data is not persisted) --- ## Test 4: gRPC Streaming **Objective:** Verify gRPC EventService bidirectional streaming. ### Test 4.1: List gRPC Services ```bash grpcurl -plaintext localhost:6000 list ``` **Expected Output:** ``` grpc.reflection.v1.ServerReflection grpc.reflection.v1alpha.ServerReflection svrnty.cqrs.CommandService svrnty.cqrs.DynamicQueryService svrnty.cqrs.QueryService svrnty.cqrs.events.EventService ``` ✅ **Verify:** `svrnty.cqrs.events.EventService` is listed ### Test 4.2: Inspect EventService ```bash grpcurl -plaintext localhost:6000 describe svrnty.cqrs.events.EventService ``` **Expected Output:** ``` svrnty.cqrs.events.EventService is a service: service EventService { rpc Subscribe ( stream .svrnty.cqrs.events.SubscriptionRequest ) returns ( stream .svrnty.cqrs.events.EventMessage ); } ``` ✅ **Verify:** Subscribe method is available with bidirectional streaming ### Test 4.3: Subscribe to Events via gRPC This test requires a separate terminal window for the gRPC client: **Terminal 1: Start gRPC subscription (leave this running)** ```bash grpcurl -plaintext -d @ localhost:6000 svrnty.cqrs.events.EventService.Subscribe <", "eventType": "UserAddedEvent", "eventId": "", "sequence": 1, "occurredAt": "2025-12-09T...", "placeholder": { "data": "Event data placeholder" } } } ``` ✅ **Verify:** - gRPC client receives event in real-time - Event contains correct metadata (eventType, correlationId, etc.) - Phase 1 uses placeholder for event data (Phase 2 will add full event payloads) ### Test 4.4: gRPC with Event Type Filtering ```bash grpcurl -plaintext -d @ localhost:6000 svrnty.cqrs.events.EventService.Subscribe < /F ``` ### Issue: Event consumer not logging events **Check:** 1. Application started successfully 2. EventConsumerBackgroundService is registered in Program.cs 3. Subscription "user-analytics" matches configured subscription ID 4. Check application logs for errors ### Issue: grpcurl not found **Solution:** ```bash # macOS brew install grpcurl # Linux wget https://github.com/fullstorydev/grpcurl/releases/download/v1.8.9/grpcurl_1.8.9_linux_x86_64.tar.gz tar -xvf grpcurl_1.8.9_linux_x86_64.tar.gz sudo mv grpcurl /usr/local/bin/ # Windows choco install grpcurl ``` --- ## Next Steps After completing Phase 1 testing: 1. **Phase 2: Persistent Streams & Event Sourcing** - Add EventStoreDB or similar persistent storage - Implement stream replay capabilities - Add snapshot support 2. **Phase 3: Advanced Features** - Consumer groups (Kafka-style partitioning) - Dead letter queues - Retry policies - Circuit breakers 3. **Production Readiness** - Add comprehensive unit tests - Add integration tests - Performance benchmarking - Monitoring and observability