Add k6 load testing infrastructure for Darkwatch service
- Create load test directory structure (infra/load-tests/) - Implement k6 script for Darkwatch endpoints (darkwatch.js) - Tests watchlist, scan, exposure, and alert operations - Configured for 500 req/s sustained load with P99 < 200ms - Includes error rate metrics and threshold validation - Add documentation and usage guide (README.md) Related: [FRE-4807](/FRE/issues/FRE-4807) Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
61
infra/load-tests/README.md
Normal file
61
infra/load-tests/README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# ShieldAI Load Tests
|
||||
|
||||
k6 load testing suite for ShieldAI services.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- k6 v0.45+ installed
|
||||
- Target services running on staging environment
|
||||
- Authentication tokens for API access
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Local Execution
|
||||
|
||||
```bash
|
||||
# Run against local development environment
|
||||
k6 run --env BASE_URL=http://localhost:3000 --env AUTH_TOKEN=dev-token src/darkwatch.js
|
||||
|
||||
# Run with results output
|
||||
k6 run --out json=results.json src/darkwatch.js
|
||||
```
|
||||
|
||||
### CI/CD Execution
|
||||
|
||||
```bash
|
||||
# Run on staging environment
|
||||
k6 run --env BASE_URL=https://staging-api.freno.me --env AUTH_TOKEN=$STAGING_AUTH_TOKEN src/darkwatch.js
|
||||
```
|
||||
|
||||
## Test Configuration
|
||||
|
||||
Each test script includes:
|
||||
|
||||
- **Stages**: Ramp-up, sustained load, ramp-down
|
||||
- **Thresholds**: P99 latency and error rate limits
|
||||
- **Metrics**: Custom metrics for error tracking
|
||||
|
||||
### Current Thresholds
|
||||
|
||||
| Service | P99 Latency | Error Rate |
|
||||
|---------|-------------|------------|
|
||||
| Darkwatch | < 200ms | < 1% |
|
||||
|
||||
## Metrics Collection
|
||||
|
||||
Run with output options:
|
||||
|
||||
```bash
|
||||
# JSON output for analysis
|
||||
k6 run --out json=darkwatch-results.json src/darkwatch.js
|
||||
|
||||
# InfluxDB for visualization
|
||||
k6 run --out influxdb=http://influxdb:8086/k6 src/darkwatch.js
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Create load test scripts for Spamshield and Voiceprint
|
||||
2. Integrate with GitHub Actions CI pipeline
|
||||
3. Set up metrics visualization dashboard
|
||||
4. Configure alerting on threshold breaches
|
||||
112
infra/load-tests/src/darkwatch.js
Normal file
112
infra/load-tests/src/darkwatch.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import http from 'k6/http';
|
||||
import { check, group } from 'k6';
|
||||
import { Rate } from 'k6/metrics';
|
||||
|
||||
// Custom metrics
|
||||
const errorRate = new Rate('errors');
|
||||
|
||||
// Test configuration
|
||||
export const options = {
|
||||
stages: [
|
||||
{ duration: '30s', target: 100 }, // Ramp up to 100 users
|
||||
{ duration: '2m', target: 500 }, // Ramp to 500 req/s
|
||||
{ duration: '3m', target: 500 }, // Stay at 500 req/s for 3 minutes
|
||||
{ duration: '30s', target: 0 }, // Ramp down to 0
|
||||
],
|
||||
thresholds: {
|
||||
http_req_duration: ['p(99)<200'], // P99 latency < 200ms
|
||||
errors: ['rate<0.01'], // Error rate < 1%
|
||||
},
|
||||
};
|
||||
|
||||
const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000';
|
||||
|
||||
export default function () {
|
||||
group('Watchlist Operations', function () {
|
||||
// GET /watchlist
|
||||
const watchlistRes = http.get(`${BASE_URL}/watchlist`, {
|
||||
headers: { 'Authorization': `Bearer ${getAuthToken()}` },
|
||||
});
|
||||
|
||||
check(watchlistRes, {
|
||||
'watchlist GET status is 200': (r) => r.status === 200,
|
||||
'watchlist GET P99 < 100ms': (r) => r.timings.duration < 100,
|
||||
});
|
||||
errorRate.add(watchlistRes.status !== 200);
|
||||
|
||||
// POST /watchlist
|
||||
const newItemRes = http.post(
|
||||
`${BASE_URL}/watchlist`,
|
||||
JSON.stringify({ type: 'email', value: `test${Date()}@example.com` }),
|
||||
{
|
||||
headers: {
|
||||
'Authorization': `Bearer ${getAuthToken()}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
check(newItemRes, {
|
||||
'watchlist POST status is 201': (r) => r.status === 201,
|
||||
'watchlist POST P99 < 200ms': (r) => r.timings.duration < 200,
|
||||
});
|
||||
errorRate.add(newItemRes.status !== 201);
|
||||
});
|
||||
|
||||
group('Scan Operations', function () {
|
||||
// POST /scan
|
||||
const scanRes = http.post(
|
||||
`${BASE_URL}/scan`,
|
||||
{},
|
||||
{
|
||||
headers: { 'Authorization': `Bearer ${getAuthToken()}` },
|
||||
}
|
||||
);
|
||||
|
||||
check(scanRes, {
|
||||
'scan POST status is 200': (r) => r.status === 200,
|
||||
'scan POST P99 < 150ms': (r) => r.timings.duration < 150,
|
||||
});
|
||||
errorRate.add(scanRes.status !== 200);
|
||||
|
||||
// GET /scan/schedule
|
||||
const scheduleRes = http.get(`${BASE_URL}/scan/schedule`, {
|
||||
headers: { 'Authorization': `Bearer ${getAuthToken()}` },
|
||||
});
|
||||
|
||||
check(scheduleRes, {
|
||||
'schedule GET status is 200': (r) => r.status === 200,
|
||||
'schedule GET P99 < 100ms': (r) => r.timings.duration < 100,
|
||||
});
|
||||
errorRate.add(scheduleRes.status !== 200);
|
||||
});
|
||||
|
||||
group('Exposure and Alert Operations', function () {
|
||||
// GET /exposures
|
||||
const exposuresRes = http.get(`${BASE_URL}/exposures`, {
|
||||
headers: { 'Authorization': `Bearer ${getAuthToken()}` },
|
||||
});
|
||||
|
||||
check(exposuresRes, {
|
||||
'exposures GET status is 200': (r) => r.status === 200,
|
||||
'exposures GET P99 < 150ms': (r) => r.timings.duration < 150,
|
||||
});
|
||||
errorRate.add(exposuresRes.status !== 200);
|
||||
|
||||
// GET /alerts
|
||||
const alertsRes = http.get(`${BASE_URL}/alerts`, {
|
||||
headers: { 'Authorization': `Bearer ${getAuthToken()}` },
|
||||
});
|
||||
|
||||
check(alertsRes, {
|
||||
'alerts GET status is 200': (r) => r.status === 200,
|
||||
'alerts GET P99 < 150ms': (r) => r.timings.duration < 150,
|
||||
});
|
||||
errorRate.add(alertsRes.status !== 200);
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function to get auth token (replace with actual token retrieval)
|
||||
function getAuthToken() {
|
||||
return __ENV.AUTH_TOKEN || 'test-token';
|
||||
}
|
||||
Reference in New Issue
Block a user