/** * k6 load test — Token Issuance * * Scenario : POST /api/v1/token (OAuth2 client_credentials grant) * VUs : 1000 * Duration : 60 seconds * Thresholds: * p95 response time < 500 ms * HTTP error rate < 1 % * * Usage: * BASE_URL=http://localhost:3000 \ * CLIENT_ID=your-client-id \ * CLIENT_SECRET=your-secret \ * k6 run tests/load/token-issuance.js */ import http from 'k6/http'; import { check, sleep } from 'k6'; import { Rate, Trend } from 'k6/metrics'; // ── Custom metrics ───────────────────────────────────────────────────────────── const errorRate = new Rate('error_rate'); const tokenIssuanceDuration = new Trend('token_issuance_duration_ms', true); // ── Configuration ────────────────────────────────────────────────────────────── export const options = { vus: 1000, duration: '60s', thresholds: { http_req_duration: ['p(95)<500'], error_rate: ['rate<0.01'], }, }; const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000'; const CLIENT_ID = __ENV.CLIENT_ID || 'load-test-client-id'; const CLIENT_SECRET = __ENV.CLIENT_SECRET || 'load-test-client-secret'; // ── Default function (executed per VU iteration) ─────────────────────────────── export default function tokenIssuance() { const url = `${BASE_URL}/api/v1/token`; // OAuth2 client_credentials grant — application/x-www-form-urlencoded body const payload = { grant_type: 'client_credentials', client_id: CLIENT_ID, client_secret: CLIENT_SECRET, scope: 'agents:read agents:write', }; const params = { headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json', }, timeout: '10s', }; const response = http.post(url, payload, params); tokenIssuanceDuration.add(response.timings.duration); const success = check(response, { 'status is 200': (r) => r.status === 200, 'response has access_token': (r) => { try { const body = JSON.parse(r.body); return typeof body.access_token === 'string' && body.access_token.length > 0; } catch { return false; } }, 'token_type is Bearer': (r) => { try { const body = JSON.parse(r.body); return body.token_type === 'Bearer'; } catch { return false; } }, 'response time < 500ms': (r) => r.timings.duration < 500, }); errorRate.add(!success); // Minimal think-time — token issuance is typically called without delays sleep(0.05); }