/** * Load Test Configuration for PRIX * Simulates 500 concurrent users hitting the webhook endpoint * * Prerequisites: * npm install -g k6 * k6 login cloud (optional for trending) * * Usage: * k6 run scripts/load-test.js * k6 run --vus 500 --duration 60s scripts/load-test.js * k6 run --stage rps:10-50:30s,rps:50-100:60s scripts/load-test.js */ import http from 'k6/http'; import { check, sleep, group } from 'k6'; import { Rate, Trend } from 'k6/metrics'; // Custom metrics const errorRate = new Rate('errors'); const responseTime = new Trend('response_time'); const webhookSuccess = new Trend('webhook_success'); const healthCheckTime = new Trend('health_check_time'); // Test configuration export const options = { scenarios: { // Warmup scenario warmup: { executor: 'ramping-arrival-rate', duration: '30s', preAllocatedVUs: 50, maxVUs: 100, stages: [ { duration: '10s', target: 10 }, { duration: '20s', target: 50 }, ], }, // Main load test - 500 concurrent users load_test: { executor: 'ramping-vus', duration: '2m', maxVUs: 500, stages: [ { duration: '30s', target: 100 }, { duration: '30s', target: 250 }, { duration: '30s', target: 500 }, { duration: '30s', target: 500 }, // Hold at 500 { duration: '30s', target: 0 }, // Ramp down ], }, }, thresholds: { http_req_duration: ['p(95)<5000'], // 95% of requests under 5s http_req_failed: ['rate<0.1'], // Less than 10% failure errors: ['rate<0.05'], // Less than 5% error rate webhook_success: ['p(90)<10000'], // 90% of webhooks complete in 10s }, }; // Test data generation function generateMockGitHubPayload(prNumber) { const timestamp = Date.now(); return { action: 'opened', number: prNumber, pull_request: { number: prNumber, title: `Load test PR #${prNumber} - ${timestamp}`, body: 'This is a load test pull request for performance testing', state: 'open', user: { login: `loadtest-user-${prNumber % 100}`, id: 1000 + (prNumber % 100), }, base: { ref: 'main', sha: '136795cc20e35ffa9a345771e8eeaaf4bce69ae2', }, head: { ref: `feature/load-test-${prNumber}`, sha: `${timestamp.toString(16)}`, }, }, repository: { id: 123456, name: 'TEST-PRIX', full_name: 'Rachit-Tiwari-7/TEST-PRIX', owner: { login: 'Rachit-Tiwari-7', id: 789, }, }, installation: { id: 12345, }, }; } // Simulated PR file changes function generateMockFileChanges() { return [ { filename: 'test-file.js', status: 'modified', additions: 5, deletions: 2, changes: 7, patch: `@@ -1,3 +1,6 @@ const x = 1; const y = 2; +const z = 3; +const w = 4; function calculate() { return x + y; } +function extra() { return z + w; }`, }, { filename: 'calc.py', status: 'modified', additions: 2, deletions: 1, changes: 3, patch: `@@ -1,5 +1,6 @@ if __name__ == "__main__": print(f"Addition: {add(10, 5)}") - print(f"Division: {divide(10, 5)}") + print(f"Division: {divide(10, 5)}") + print("Load test line") result = add(5, 3)`, }, ]; } // Default function - main test loop export default function () { const baseUrl = __ENV.TARGET_URL || 'http://localhost:7860'; const githubToken = __ENV.GITHUB_TOKEN || 'test-token'; // Health check group('Health Check', () => { const healthStart = Date.now(); const healthRes = http.get(`${baseUrl}/ping`, { headers: { 'Content-Type': 'application/json' }, }); healthCheckTime.add(Date.now() - healthStart); check(healthRes, { 'health check status is 200': (r) => r.status === 200, 'health check has correct response': (r) => r.body.includes('pong') || r.status === 200, }); }); // Simulate webhook payload group('Webhook Delivery', () => { const prNumber = 10000 + Math.floor(Math.random() * 1000); const payload = generateMockGitHubPayload(prNumber); const files = generateMockFileChanges(); // Mock GitHub API responses const mockGitHubResponses = { 'repos/compareCommits': { files: files, commits: [{ sha: 'commit-1' }, { sha: 'commit-2' }], }, 'repos/getContent': { content: Buffer.from('const existing = true;').toString('base64'), encoding: 'base64', }, }; const webhookStart = Date.now(); // Note: In real load test against production, you'd hit the actual webhook endpoint // For local testing, we simulate the webhook processing const webhookRes = http.post( `${baseUrl}/api/github/webhooks`, JSON.stringify(payload), { headers: { 'Content-Type': 'application/json', 'X-GitHub-Event': 'pull_request', 'X-GitHub-Delivery': `${Date.now()}`, 'X-Hub-Signature-256': 'sha256=test-signature', }, } ); webhookSuccess.add(Date.now() - webhookStart); responseTime.add(Date.now() - webhookStart); const isSuccess = check(webhookRes, { 'webhook status is 200': (r) => r.status === 200, 'webhook response time < 5s': (r) => r.timings.duration < 5000, }); errorRate.add(!isSuccess); }); // Random think time between requests (1-3 seconds) sleep(Math.random() * 2 + 1); } // Setup function - runs once before test export function setup() { console.log('Starting PRIX Load Test'); console.log(`Target URL: ${__ENV.TARGET_URL || 'http://localhost:7860'}`); console.log('Simulating 500 concurrent users...'); return { startTime: Date.now() }; } // Teardown function - runs once after test export function teardown(data) { console.log('Load Test Complete'); console.log(`Total Duration: ${(Date.now() - data.startTime) / 1000}s`); }