import http from 'k6/http'; import { check, group, sleep } from 'k6'; import { Rate, Trend, Counter } from 'k6/metrics'; import { randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; // Custom metrics const errorRate = new Rate('errors'); const responseTime = new Trend('response_time'); const throughput = new Counter('throughput'); const loginFailures = new Counter('login_failures'); // Test configuration export const options = { scenarios: { // Smoke test - low load to verify system works smoke: { executor: 'constant-vus', vus: 10, duration: '1m', tags: { test_type: 'smoke' }, }, // Load test - 100 concurrent users load_100: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '2m', target: 100 }, { duration: '5m', target: 100 }, { duration: '2m', target: 0 }, ], tags: { test_type: 'load_100' }, }, // Load test - 500 concurrent users load_500: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '3m', target: 500 }, { duration: '10m', target: 500 }, { duration: '3m', target: 0 }, ], tags: { test_type: 'load_500' }, }, // Load test - 1000 concurrent users load_1000: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '5m', target: 1000 }, { duration: '15m', target: 1000 }, { duration: '5m', target: 0 }, ], tags: { test_type: 'load_1000' }, }, }, thresholds: { // Performance requirements http_req_duration: ['p(95)<200'], // 95th percentile < 200ms http_req_duration: ['p(50)<100'], // 50th percentile < 100ms http_req_failed: ['rate<0.01'], // Error rate < 1% errors: ['rate<0.01'], // Throughput requirements throughput: ['count>1000'], }, }; const BASE_URL = __ENV.BASE_URL || 'http://localhost:8000'; const API_V1 = `${BASE_URL}/api/v1`; // Test data testData = { username: `loadtest_${randomIntBetween(1, 10000)}@test.com`, password: 'TestPassword123!', scenarioName: `LoadTest_Scenario_${randomIntBetween(1, 1000)}`, }; export function setup() { console.log('Starting load test setup...'); // Health check const healthCheck = http.get(`${BASE_URL}/health`); check(healthCheck, { 'health check status is 200': (r) => r.status === 200, }); // Register test user const registerRes = http.post(`${API_V1}/auth/register`, JSON.stringify({ email: testData.username, password: testData.password, full_name: 'Load Test User', }), { headers: { 'Content-Type': 'application/json' }, }); let authToken = null; if (registerRes.status === 201) { // Login to get token const loginRes = http.post(`${API_V1}/auth/login`, JSON.stringify({ username: testData.username, password: testData.password, }), { headers: { 'Content-Type': 'application/json' }, }); if (loginRes.status === 200) { authToken = JSON.parse(loginRes.body).access_token; } } return { authToken, testData }; } export default function(data) { const params = { headers: { 'Content-Type': 'application/json', ...(data.authToken && { 'Authorization': `Bearer ${data.authToken}` }), }, }; group('API Health & Info', () => { // Health endpoint const healthRes = http.get(`${BASE_URL}/health`, params); const healthCheck = check(healthRes, { 'health status is 200': (r) => r.status === 200, 'health response time < 100ms': (r) => r.timings.duration < 100, }); errorRate.add(!healthCheck); responseTime.add(healthRes.timings.duration); throughput.add(1); // API docs const docsRes = http.get(`${BASE_URL}/docs`, params); check(docsRes, { 'docs status is 200': (r) => r.status === 200, }); }); group('Authentication', () => { // Login endpoint - high frequency const loginRes = http.post(`${API_V1}/auth/login`, JSON.stringify({ username: data.testData.username, password: data.testData.password, }), params); const loginCheck = check(loginRes, { 'login status is 200': (r) => r.status === 200, 'login response time < 500ms': (r) => r.timings.duration < 500, 'login returns access_token': (r) => r.json('access_token') !== undefined, }); if (!loginCheck) { loginFailures.add(1); } errorRate.add(!loginCheck); responseTime.add(loginRes.timings.duration); throughput.add(1); }); group('Scenarios API', () => { // List scenarios const listRes = http.get(`${API_V1}/scenarios?page=1&page_size=20`, params); const listCheck = check(listRes, { 'list scenarios status is 200': (r) => r.status === 200, 'list scenarios response time < 200ms': (r) => r.timings.duration < 200, }); errorRate.add(!listCheck); responseTime.add(listRes.timings.duration); throughput.add(1); // Create scenario (20% of requests) if (Math.random() < 0.2) { const createRes = http.post(`${API_V1}/scenarios`, JSON.stringify({ name: `${data.testData.scenarioName}_${randomIntBetween(1, 10000)}`, description: 'Load test scenario', region: 'us-east-1', tags: ['load-test', 'performance'], }), params); const createCheck = check(createRes, { 'create scenario status is 201': (r) => r.status === 201, 'create scenario response time < 500ms': (r) => r.timings.duration < 500, }); errorRate.add(!createCheck); responseTime.add(createRes.timings.duration); throughput.add(1); } }); group('Metrics API', () => { // Get dashboard metrics const metricsRes = http.get(`${API_V1}/metrics/dashboard`, params); const metricsCheck = check(metricsRes, { 'metrics status is 200': (r) => r.status === 200, 'metrics response time < 300ms': (r) => r.timings.duration < 300, }); errorRate.add(!metricsCheck); responseTime.add(metricsRes.timings.duration); throughput.add(1); }); group('Ingest API', () => { // Simulate log ingestion const ingestRes = http.post(`${BASE_URL}/ingest`, JSON.stringify({ message: `Load test log entry ${randomIntBetween(1, 1000000)}`, source: 'load-test', level: 'INFO', metadata: { service: 'load-test-service', request_id: `req_${randomIntBetween(1, 1000000)}`, }, }), { ...params, headers: { ...params.headers, 'X-Scenario-ID': `scenario_${randomIntBetween(1, 100)}`, }, }); const ingestCheck = check(ingestRes, { 'ingest status is 200 or 202': (r) => r.status === 200 || r.status === 202, 'ingest response time < 100ms': (r) => r.timings.duration < 100, }); errorRate.add(!ingestCheck); responseTime.add(ingestRes.timings.duration); throughput.add(1); }); group('Reports API', () => { // List reports const reportsRes = http.get(`${API_V1}/reports?page=1&page_size=10`, params); const reportsCheck = check(reportsRes, { 'reports list status is 200': (r) => r.status === 200, 'reports list response time < 300ms': (r) => r.timings.duration < 300, }); errorRate.add(!reportsCheck); responseTime.add(reportsRes.timings.duration); throughput.add(1); }); // Random sleep between 1-3 seconds to simulate real user behavior sleep(randomIntBetween(1, 3)); } export function teardown(data) { console.log('Load test completed. Cleaning up...'); // Cleanup test data if needed if (data.authToken) { const params = { headers: { 'Authorization': `Bearer ${data.authToken}`, 'Content-Type': 'application/json', }, }; // Delete test scenarios created during load test http.del(`${API_V1}/scenarios/cleanup-load-test`, null, params); } console.log('Cleanup completed.'); }