dotnet-cqrs/test-phase2-event-streaming.sh

491 lines
17 KiB
Bash
Executable File

#!/bin/bash
# ============================================================================
# Phase 2.8: Event Streaming Testing Script (InMemory Provider)
# ============================================================================
# Tests all Phase 2 features: persistent streams, append/read, metadata,
# event replay, and stress testing.
# ============================================================================
set -e # Exit on error
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Test configuration
GRPC_HOST="localhost:6000"
TEST_STREAM="test-persistent-stream"
STRESS_STREAM="stress-test-stream"
# Counters
TESTS_PASSED=0
TESTS_FAILED=0
# ============================================================================
# Helper Functions
# ============================================================================
print_header() {
echo -e "\n${BLUE}========================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}========================================${NC}\n"
}
print_test() {
echo -e "${YELLOW}▶ Test: $1${NC}"
}
print_pass() {
echo -e "${GREEN}✓ PASS${NC}: $1"
((TESTS_PASSED++))
}
print_fail() {
echo -e "${RED}✗ FAIL${NC}: $1"
((TESTS_FAILED++))
}
print_summary() {
echo -e "\n${BLUE}========================================${NC}"
echo -e "${BLUE}Test Summary${NC}"
echo -e "${BLUE}========================================${NC}"
echo -e "Tests Passed: ${GREEN}$TESTS_PASSED${NC}"
echo -e "Tests Failed: ${RED}$TESTS_FAILED${NC}"
echo -e "${BLUE}========================================${NC}\n"
if [ $TESTS_FAILED -eq 0 ]; then
echo -e "${GREEN}All tests passed!${NC}"
exit 0
else
echo -e "${RED}Some tests failed!${NC}"
exit 1
fi
}
check_grpcurl() {
if ! command -v grpcurl &> /dev/null; then
echo -e "${RED}Error: grpcurl is not installed${NC}"
echo "Install with: brew install grpcurl (macOS) or go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest"
exit 1
fi
}
wait_for_service() {
echo -e "${YELLOW}Waiting for gRPC service to be ready...${NC}"
for i in {1..30}; do
if grpcurl -plaintext $GRPC_HOST list > /dev/null 2>&1; then
echo -e "${GREEN}Service is ready!${NC}"
return 0
fi
echo -n "."
sleep 1
done
echo -e "${RED}Service did not become ready in time${NC}"
exit 1
}
# ============================================================================
# Phase 2.8.1: Test Persistent Stream Append/Read
# ============================================================================
test_persistent_append_read() {
print_header "Phase 2.8.1: Persistent Stream Append/Read"
# Test 1: Append single event
print_test "Append single event to persistent stream"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$TEST_STREAM"'",
"events": [{
"eventType": "TestEvent",
"eventId": "evt-001",
"correlationId": "corr-001",
"eventData": "{\"test\":\"data-001\"}",
"occurredAt": "2025-12-10T00:00:00Z"
}]
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/AppendToStream 2>&1)
if echo "$RESPONSE" | grep -q '"offsets"'; then
print_pass "Event appended successfully"
else
print_fail "Failed to append event: $RESPONSE"
fi
# Test 2: Append multiple events in batch
print_test "Append multiple events in batch"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$TEST_STREAM"'",
"events": [
{
"eventType": "TestEvent",
"eventId": "evt-002",
"correlationId": "corr-002",
"eventData": "{\"test\":\"data-002\"}",
"occurredAt": "2025-12-10T00:01:00Z"
},
{
"eventType": "TestEvent",
"eventId": "evt-003",
"correlationId": "corr-003",
"eventData": "{\"test\":\"data-003\"}",
"occurredAt": "2025-12-10T00:02:00Z"
},
{
"eventType": "TestEvent",
"eventId": "evt-004",
"correlationId": "corr-004",
"eventData": "{\"test\":\"data-004\"}",
"occurredAt": "2025-12-10T00:03:00Z"
}
]
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/AppendToStream 2>&1)
if echo "$RESPONSE" | grep -q '"offsets"'; then
print_pass "Batch append successful"
else
print_fail "Failed to append batch: $RESPONSE"
fi
# Test 3: Read stream from beginning
print_test "Read stream from offset 0"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$TEST_STREAM"'",
"fromOffset": "0",
"maxCount": 100
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/ReadStream 2>&1)
if echo "$RESPONSE" | grep -q '"eventId": "evt-001"' && \
echo "$RESPONSE" | grep -q '"eventId": "evt-004"'; then
print_pass "Read stream successful - all events present"
else
print_fail "Failed to read stream or events missing: $RESPONSE"
fi
# Test 4: Read stream from specific offset
print_test "Read stream from offset 2"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$TEST_STREAM"'",
"fromOffset": "2",
"maxCount": 100
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/ReadStream 2>&1)
if echo "$RESPONSE" | grep -q '"eventId": "evt-003"' && \
echo "$RESPONSE" | grep -q '"eventId": "evt-004"' && \
! echo "$RESPONSE" | grep -q '"eventId": "evt-001"'; then
print_pass "Read from specific offset successful"
else
print_fail "Failed to read from specific offset: $RESPONSE"
fi
# Test 5: Get stream length
print_test "Get stream length"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$TEST_STREAM"'"
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/GetStreamLength 2>&1)
if echo "$RESPONSE" | grep -q '"length": "4"'; then
print_pass "Stream length is correct (4 events)"
else
print_fail "Incorrect stream length: $RESPONSE"
fi
# Test 6: Get stream metadata
print_test "Get stream metadata"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$TEST_STREAM"'"
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/GetStreamMetadata 2>&1)
if echo "$RESPONSE" | grep -q '"streamName"' && \
echo "$RESPONSE" | grep -q '"length"'; then
print_pass "Stream metadata retrieved successfully"
else
print_fail "Failed to get stream metadata: $RESPONSE"
fi
}
# ============================================================================
# Phase 2.8.4: Test Event Replay from Various Positions
# ============================================================================
test_event_replay() {
print_header "Phase 2.8.4: Event Replay from Various Positions"
# Create a new stream for replay testing
REPLAY_STREAM="replay-test-stream"
# Append 10 events
print_test "Creating stream with 10 events for replay testing"
for i in {1..10}; do
grpcurl -d '{
"streamName": "'"$REPLAY_STREAM"'",
"events": [{
"eventType": "ReplayTestEvent",
"eventId": "replay-evt-'$i'",
"correlationId": "replay-corr-'$i'",
"eventData": "{\"index\":'$i'}",
"occurredAt": "2025-12-10T00:0'$i':00Z"
}]
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/AppendToStream > /dev/null 2>&1
done
print_pass "Created stream with 10 events"
# Test 1: Replay from beginning
print_test "Replay from beginning (offset 0)"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$REPLAY_STREAM"'",
"fromOffset": "0",
"maxCount": 5
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/ReadStream 2>&1)
EVENT_COUNT=$(echo "$RESPONSE" | grep -o '"eventId"' | wc -l | tr -d ' ')
if [ "$EVENT_COUNT" -eq "5" ]; then
print_pass "Replay from beginning returned 5 events (limited by maxCount)"
else
print_fail "Expected 5 events, got $EVENT_COUNT"
fi
# Test 2: Replay from middle
print_test "Replay from middle (offset 5)"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$REPLAY_STREAM"'",
"fromOffset": "5",
"maxCount": 100
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/ReadStream 2>&1)
if echo "$RESPONSE" | grep -q '"eventId": "replay-evt-6"' && \
echo "$RESPONSE" | grep -q '"eventId": "replay-evt-10"' && \
! echo "$RESPONSE" | grep -q '"eventId": "replay-evt-1"'; then
print_pass "Replay from middle successful"
else
print_fail "Failed to replay from middle: $RESPONSE"
fi
# Test 3: Replay from near end
print_test "Replay from near end (offset 8)"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$REPLAY_STREAM"'",
"fromOffset": "8",
"maxCount": 100
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/ReadStream 2>&1)
EVENT_COUNT=$(echo "$RESPONSE" | grep -o '"eventId"' | wc -l | tr -d ' ')
if [ "$EVENT_COUNT" -eq "2" ]; then
print_pass "Replay from near end returned 2 events (offsets 8 and 9)"
else
print_fail "Expected 2 events, got $EVENT_COUNT"
fi
# Test 4: Read entire stream
print_test "Read entire stream (maxCount 100)"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$REPLAY_STREAM"'",
"fromOffset": "0",
"maxCount": 100
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/ReadStream 2>&1)
EVENT_COUNT=$(echo "$RESPONSE" | grep -o '"eventId"' | wc -l | tr -d ' ')
if [ "$EVENT_COUNT" -eq "10" ]; then
print_pass "Read entire stream successfully (10 events)"
else
print_fail "Expected 10 events, got $EVENT_COUNT"
fi
}
# ============================================================================
# Phase 2.8.6: Stress Test with Large Event Volumes
# ============================================================================
test_stress_large_volumes() {
print_header "Phase 2.8.6: Stress Test with Large Event Volumes"
# Test 1: Append 1000 events
print_test "Appending 1000 events in batches of 100"
START_TIME=$(date +%s)
for batch in {1..10}; do
BATCH_START=$(( (batch - 1) * 100 + 1 ))
EVENTS_JSON=""
for i in $(seq $BATCH_START $((BATCH_START + 99))); do
EVENT_JSON='{
"eventType": "StressTestEvent",
"eventId": "stress-evt-'$i'",
"correlationId": "stress-corr-'$i'",
"eventData": "{\"index\":'$i',\"data\":\"Lorem ipsum dolor sit amet\"}",
"occurredAt": "2025-12-10T00:00:00Z"
}'
if [ -z "$EVENTS_JSON" ]; then
EVENTS_JSON="$EVENT_JSON"
else
EVENTS_JSON="$EVENTS_JSON,$EVENT_JSON"
fi
done
grpcurl -d '{
"streamName": "'"$STRESS_STREAM"'",
"events": ['"$EVENTS_JSON"']
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/AppendToStream > /dev/null 2>&1
echo -n "."
done
echo ""
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
print_pass "Appended 1000 events in $DURATION seconds"
# Test 2: Verify stream length
print_test "Verify stream length is 1000"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$STRESS_STREAM"'"
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/GetStreamLength 2>&1)
if echo "$RESPONSE" | grep -q '"length": "1000"'; then
print_pass "Stream length verified: 1000 events"
else
print_fail "Incorrect stream length: $RESPONSE"
fi
# Test 3: Read large batch from stream
print_test "Reading 500 events from stream (offset 0)"
START_TIME=$(date +%s)
RESPONSE=$(grpcurl -d '{
"streamName": "'"$STRESS_STREAM"'",
"fromOffset": "0",
"maxCount": 500
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/ReadStream 2>&1)
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
EVENT_COUNT=$(echo "$RESPONSE" | grep -o '"eventId"' | wc -l | tr -d ' ')
if [ "$EVENT_COUNT" -eq "500" ]; then
print_pass "Read 500 events in $DURATION seconds"
else
print_fail "Expected 500 events, got $EVENT_COUNT"
fi
# Test 4: Read from middle of large stream
print_test "Reading events from middle of stream (offset 500)"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$STRESS_STREAM"'",
"fromOffset": "500",
"maxCount": 100
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/ReadStream 2>&1)
if echo "$RESPONSE" | grep -q '"eventId": "stress-evt-501"'; then
print_pass "Successfully read from middle of large stream"
else
print_fail "Failed to read from middle: $RESPONSE"
fi
# Test 5: Performance test - multiple concurrent reads
print_test "Concurrent read performance (10 simultaneous reads)"
START_TIME=$(date +%s)
for i in {1..10}; do
grpcurl -d '{
"streamName": "'"$STRESS_STREAM"'",
"fromOffset": "0",
"maxCount": 100
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/ReadStream > /dev/null 2>&1 &
done
wait # Wait for all background processes to complete
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
print_pass "Completed 10 concurrent reads in $DURATION seconds"
}
# ============================================================================
# Test Ephemeral Streams (verify backward compatibility)
# ============================================================================
test_ephemeral_streams() {
print_header "Backward Compatibility: Ephemeral Streams"
EPHEMERAL_STREAM="ephemeral-test-queue"
# Test 1: Enqueue events
print_test "Enqueue events to ephemeral stream"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$EPHEMERAL_STREAM"'",
"events": [
{
"eventType": "EphemeralEvent",
"eventId": "eph-evt-001",
"correlationId": "eph-corr-001",
"eventData": "{\"message\":\"test\"}",
"occurredAt": "2025-12-10T00:00:00Z"
}
]
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/EnqueueEvents 2>&1)
if echo "$RESPONSE" | grep -q '{}' || echo "$RESPONSE" | grep -q 'OK'; then
print_pass "Enqueued event to ephemeral stream"
else
print_fail "Failed to enqueue: $RESPONSE"
fi
# Test 2: Dequeue event
print_test "Dequeue event from ephemeral stream"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$EPHEMERAL_STREAM"'",
"consumerId": "test-consumer",
"visibilityTimeout": "30s",
"maxCount": 1
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/DequeueEvents 2>&1)
if echo "$RESPONSE" | grep -q '"eventId": "eph-evt-001"'; then
print_pass "Dequeued event successfully"
else
print_fail "Failed to dequeue: $RESPONSE"
fi
# Test 3: Acknowledge event
print_test "Acknowledge dequeued event"
RESPONSE=$(grpcurl -d '{
"streamName": "'"$EPHEMERAL_STREAM"'",
"eventId": "eph-evt-001",
"consumerId": "test-consumer"
}' -plaintext $GRPC_HOST svrnty.cqrs.events.EventStreamService/AcknowledgeEvent 2>&1)
if echo "$RESPONSE" | grep -q '{}' || echo "$RESPONSE" | grep -q '"success": true' || ! echo "$RESPONSE" | grep -q 'error'; then
print_pass "Event acknowledged successfully"
else
print_fail "Failed to acknowledge: $RESPONSE"
fi
}
# ============================================================================
# Main Test Execution
# ============================================================================
main() {
echo -e "${BLUE}"
echo "╔═══════════════════════════════════════════════════════════╗"
echo "║ Phase 2.8: Event Streaming Testing (InMemory Provider) ║"
echo "╚═══════════════════════════════════════════════════════════╝"
echo -e "${NC}"
# Prerequisite checks
check_grpcurl
wait_for_service
# Run all test suites
test_persistent_append_read
test_event_replay
test_stress_large_volumes
test_ephemeral_streams
# Print summary
print_summary
}
# Run main function
main