273 lines
7.1 KiB
Markdown
273 lines
7.1 KiB
Markdown
# 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**:
|
|
1. Send notification
|
|
2. Wait for TTL + cleanup interval
|
|
3. 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**:
|
|
1. Send 10 notifications
|
|
2. Wait for cleanup
|
|
3. 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**:
|
|
1. Send 10 notifications
|
|
2. Wait for time cleanup would run
|
|
3. 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**:
|
|
1. Send 10 notifications concurrently
|
|
2. Verify all succeeded
|
|
3. 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**:
|
|
1. Send 5 notifications with delays
|
|
2. Wait for cleanup
|
|
3. 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**:
|
|
1. Send 30 notifications in batches
|
|
2. Wait for cleanup between batches
|
|
3. 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**:
|
|
1. Send 20 notifications, check health
|
|
2. Repeat 5 times
|
|
3. 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
|
|
```bash
|
|
go test -v ./tests/e2e -timeout 600s
|
|
```
|
|
|
|
### Single Test
|
|
```bash
|
|
go test -v ./tests/e2e -timeout 120s -run TestCRITICAL1_TTLBasedCleanup
|
|
```
|
|
|
|
### Skip Long-Running Tests
|
|
```bash
|
|
go test -v ./tests/e2e -short
|
|
```
|
|
|
|
### With Debug Output
|
|
```bash
|
|
go test -v ./tests/e2e -timeout 600s -count=1 -race
|
|
```
|
|
|
|
## Test Execution Flow
|
|
|
|
1. **Build Docker Image**
|
|
- Uses existing Dockerfile to build `notifier:test` image
|
|
- Compiles service with all retention features
|
|
|
|
2. **Create Container**
|
|
- testcontainers spins up isolated container
|
|
- Exposes port 8080 internally
|
|
- Sets environment variables for retention config
|
|
|
|
3. **Wait for Readiness**
|
|
- Polls `/health` endpoint until ready (max 30s)
|
|
- Waits for service to fully initialize
|
|
|
|
4. **Run Test Scenarios**
|
|
- Send notifications via REST client
|
|
- Wait for cleanup to run
|
|
- Verify expected behavior
|
|
|
|
5. **Cleanup**
|
|
- Stops and removes container
|
|
- Cleans up Docker resources
|
|
|
|
## Configuration
|
|
|
|
Each test can customize retention settings via environment variables:
|
|
|
|
```go
|
|
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/off
|
|
- `NOTIFIER_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/`:
|
|
|
|
```go
|
|
// 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:
|
|
|
|
```go
|
|
logs := suite.GetLogs(context.Background())
|
|
fmt.Println(logs)
|
|
```
|
|
|
|
### Inspect Running Container
|
|
```bash
|
|
# 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
|
|
```yaml
|
|
- name: Run E2E Tests
|
|
run: |
|
|
go test -v ./tests/e2e -timeout 600s
|
|
```
|
|
|
|
### Docker-in-Docker
|
|
```yaml
|
|
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
|
|
|
|
## References
|
|
|
|
- [testcontainers-go](https://github.com/testcontainers/testcontainers-go)
|
|
- [Client Library](../../pkg/client/)
|
|
- [CRITICAL-1 Implementation](../../docs/CRITICAL_1_IMPLEMENTATION.md)
|
|
- [Retention Configuration](../../docs/CRITICAL_1_QUICK_START.md)
|