import http from 'k6/http'; import { check, sleep, Trend } from 'k6'; import { Rate } from 'k6/metrics'; export let options = { stages: [ { duration: '1m', target: 20 }, // warm-up { duration: '3m', target: 50 }, // peak load { duration: '1m', target: 0 }, // ramp down ], thresholds: { http_req_duration: [ { threshold: 'p(95)<800', abortOnFail: true }, { threshold: 'p(99)<2000', abortOnFail: false }, ], 'api success rate': ['rate>0.99'], }, noConnectionReuse: false, maxRedirects: 5, }; const BASE_URL = __ENV.BASE_URL || 'http://localhost:8000'; const ADMIN_TOKEN = __ENV.ADMIN_TOKEN || ''; // optional // Custom metrics const loginTrend = new Trend('login_duration'); const registerTrend = new Trend('register_duration'); const jobsTrend = new Trend('jobs_list_duration'); const leadsTrend = new Trend('leads_list_duration'); // Helper: get auth headers function authHeaders() { return ADMIN_TOKEN ? { Authorization: `Bearer ${ADMIN_TOKEN}` } : {}; } export default function () { // 1. Login flow (simulate admin) let loginRes = http.post( `${BASE_URL}/api/auth/login`, JSON.stringify({ email: 'admin@example.com', password: 'test123', loginTarget: 'admin', }), { headers: { 'Content-Type': 'application/json', Accept: 'application/json', 'x-portal-target': 'admin', }, } ); loginTrend.add(loginRes.timings.duration); let success = check(loginRes, { 'login 200': (r) => r.status === 200, 'login has token': (r) => { const t = r.json('token'); return t !== undefined && t !== null; }, }); if (success) { const body = loginRes.json(); const token = body.token; const headers = { ...authHeaders(), Authorization: `Bearer ${token}` }; // 2. List companies let res = http.get(`${BASE_URL}/api/admin/companies`, { headers }); check(res, { 'companies 200': (r) => r.status === 200 }); jobsTrend.add(res.timings.duration); // 3. List jobs (via companies service) res = http.get(`${BASE_URL}/api/admin/companies/jobs`, { headers }); check(res, { 'jobs 200': (r) => r.status === 200 }); leadsTrend.add(res.timings.duration); // 4. Get single company detail if (res.json().length > 0) { const firstJob = res.json()[0]; if (firstJob.id) { http.get(`${BASE_URL}/api/admin/companies/jobs/${firstJob.id}`, { headers }); } } // 5. List leads res = http.get(`${BASE_URL}/api/admin/leads`, { headers }); check(res, { 'leads 200': (r) => r.status === 200 }); leadsTrend.add(res.timings.duration); } sleep(2); } // Summarize metrics at end export function teardown() { console.log(`Login avg: ${loginTrend.avg.toFixed(2)}ms`); console.log(`Jobs list avg: ${jobsTrend.avg.toFixed(2)}ms`); console.log(`Leads list avg: ${leadsTrend.avg.toFixed(2)}ms`); }