7.1 KiB
E2E Tests for Notifier Service
This directory contains end-to-end (E2E) tests for the Notifier service using testcontainers for isolated Docker-based testing.
Overview
The E2E tests validate real-world scenarios using:
- testcontainers-go: Isolated containerized service instances
- REST client library: Type-safe API client (pkg/client)
- Real notifications: Actual in-memory notification processing
CRITICAL-1 Test Scenarios
The following tests validate the notification retention policy implementation:
1. TTL-Based Cleanup (TestCRITICAL1_TTLBasedCleanup)
- Purpose: Verify old notifications are removed after TTL expires
- Configuration: TTL=2s, check frequency=500ms
- Steps:
- Send notification
- Wait for TTL + cleanup interval
- Verify notification count decreased
- Expected Result: ✅ Old notifications cleaned up
2. Max Size Enforcement (TestCRITICAL1_MaxSizeEnforcement)
- Purpose: Verify max_size limit is enforced
- Configuration: max_size=5, TTL=24h
- Steps:
- Send 10 notifications
- Wait for cleanup
- Verify only 5 remain
- Expected Result: ✅ Excess notifications removed
3. Cleanup Disabled (TestCRITICAL1_CleanupDisabled)
- Purpose: Verify cleanup doesn't run when disabled
- Configuration: enabled=false
- Steps:
- Send 10 notifications
- Wait for time cleanup would run
- Verify all 10 still exist
- Expected Result: ✅ No notifications removed
4. Concurrent Sends (TestCRITICAL1_ConcurrentSends)
- Purpose: Verify concurrent client access is safe
- Configuration: Default retention
- Steps:
- Send 10 notifications concurrently
- Verify all succeeded
- Check stats
- Expected Result: ✅ All 10 notifications processed
5. Oldest Removed First (TestCRITICAL1_OldestRemovedFirst)
- Purpose: Verify oldest are removed when exceeding max_size
- Configuration: max_size=3
- Steps:
- Send 5 notifications with delays
- Wait for cleanup
- Verify oldest 2 removed
- Expected Result: ✅ Newest 3 remain
6. Memory Bounded (TestCRITICAL1_MemoryBounded)
- Purpose: Verify memory stays bounded over time
- Configuration: max_size=50, TTL=5s
- Steps:
- Send 30 notifications in batches
- Wait for cleanup between batches
- Verify count stays under max_size
- Expected Result: ✅ Memory usage stays bounded
7. Service Health (TestCRITICAL1_ServiceHealthy)
- Purpose: Verify service stays responsive during cleanup
- Configuration: Default retention
- Steps:
- Send 20 notifications, check health
- Repeat 5 times
- Verify health check always succeeds
- Expected Result: ✅ Service remains responsive
Prerequisites
- Docker: For running containerized tests
- Go 1.21+: For building and running tests
- Docker daemon: Must be running
Running Tests
All E2E Tests
go test -v ./tests/e2e -timeout 600s
Single Test
go test -v ./tests/e2e -timeout 120s -run TestCRITICAL1_TTLBasedCleanup
Skip Long-Running Tests
go test -v ./tests/e2e -short
With Debug Output
go test -v ./tests/e2e -timeout 600s -count=1 -race
Test Execution Flow
-
Build Docker Image
- Uses existing Dockerfile to build
notifier:testimage - Compiles service with all retention features
- Uses existing Dockerfile to build
-
Create Container
- testcontainers spins up isolated container
- Exposes port 8080 internally
- Sets environment variables for retention config
-
Wait for Readiness
- Polls
/healthendpoint until ready (max 30s) - Waits for service to fully initialize
- Polls
-
Run Test Scenarios
- Send notifications via REST client
- Wait for cleanup to run
- Verify expected behavior
-
Cleanup
- Stops and removes container
- Cleans up Docker resources
Configuration
Each test can customize retention settings via environment variables:
retention := "NOTIFIER_RETENTION_ENABLED=true"
retention += "|NOTIFIER_RETENTION_TTL=2s"
retention += "|NOTIFIER_RETENTION_CHECK_FREQUENCY=500ms"
retention += "|NOTIFIER_RETENTION_MAX_SIZE=50"
suite := SetupSuite(t, retention)
Available Settings
NOTIFIER_RETENTION_ENABLED: Turn cleanup on/offNOTIFIER_RETENTION_TTL: Time-to-live (e.g., "2s", "24h")NOTIFIER_RETENTION_CHECK_FREQUENCY: Cleanup interval (e.g., "500ms", "1h")NOTIFIER_RETENTION_MAX_SIZE: Max notifications (e.g., 50, 100000)
Client Library
Tests use the REST client library in pkg/client/:
// Create client
cfg := client.ClientConfig{
BaseURL: "http://localhost:8080",
Timeout: 30 * time.Second,
}
c := client.NewRESTClient(cfg)
// Send notification
resp, err := c.Send(ctx, client.NotificationRequest{
Type: "stdout",
Subject: "Test",
Body: "Message",
Recipients: []string{"test@example.com"},
})
// Get stats
stats, err := c.GetStats(ctx)
Debugging
View Container Logs
Tests automatically capture logs on failure. Access via:
logs := suite.GetLogs(context.Background())
fmt.Println(logs)
Inspect Running Container
# Find container ID
docker ps | grep notifier:test
# View logs
docker logs <container-id>
# Connect to container
docker exec -it <container-id> /bin/sh
Common Issues
Issue: Container fails to build
Error: Failed to build docker image
Solution: Ensure Dockerfile exists and Docker daemon is running
Issue: Port already in use
Error: Failed to listen on
Solution: Stop other services on port 8080 or retry
Issue: Timeout waiting for service
Error: Service failed to become ready
Solution: Check Docker logs, may need more CPU/memory
Performance Expectations
Cleanup Performance
- Cleanup 5000 notifications: ~1.4ms
- Per-item overhead: <1μs
- No impact on normal operations
Test Duration
- Single test: 5-10 seconds
- Full suite: 45-60 seconds (running sequentially)
- CI/CD recommendation: Run with
-timeout 600s
CI/CD Integration
GitHub Actions Example
- name: Run E2E Tests
run: |
go test -v ./tests/e2e -timeout 600s
Docker-in-Docker
services:
docker:
image: docker:dind
options: --privileged
Test Matrix
| Scenario | TTL | Frequency | Max Size | Expected |
|---|---|---|---|---|
| TTL Cleanup | 2s | 500ms | 10k | Old removed |
| Max Size | 24h | 500ms | 5 | Size capped |
| Disabled | - | - | - | No cleanup |
| Concurrent | 24h | 1s | 1k | All succeed |
| Oldest First | 24h | 500ms | 3 | Newest kept |
| Bounded | 5s | 500ms | 50 | Size bounded |
| Health | 5s | 500ms | 1k | Always healthy |
Future Enhancements
- gRPC client library + tests
- Load testing scenarios (10k+ notifications)
- Memory profiling validation
- Performance benchmarking
- Stress testing (high throughput)
- Multi-container orchestration tests
- Kubernetes integration tests