import http from 'k6/http'; import { check, sleep } from 'k6'; import { Rate, Trend } from 'k6/metrics'; import { BASE_URL, SLA_THRESHOLDS, registerTestUser, authHeaders } from '../lib/config.js'; const paymentCreateDuration = new Trend('payment_create_duration', true); const paymentListDuration = new Trend('payment_list_duration', true); const paymentFailRate = new Rate('payment_failures'); export const options = { scenarios: { payments_load: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '30s', target: 20 }, { duration: '1m', target: 50 }, // peak: 50 concurrent { duration: '30s', target: 0 }, ], gracefulRampDown: '10s', }, }, thresholds: { ...SLA_THRESHOLDS, payment_create_duration: ['p(95)<500'], payment_list_duration: ['p(95)<300'], payment_failures: ['rate<0.05'], }, }; export function setup() { // Register multiple users for payment tests const tokens = []; for (let i = 0; i < 10; i++) { const phone = `0920${String(i).padStart(6, '0')}`; const t = registerTestUser(http, phone); if (t) tokens.push(t.accessToken); } return { tokens }; } export default function (data) { if (!data.tokens.length) return; const token = data.tokens[__VU % data.tokens.length]; const headers = authHeaders(token); const iter = __ITER; if (iter % 3 === 0) { // List transactions const res = http.get(`${BASE_URL}/payments?limit=10&offset=0`, { headers, tags: { name: 'GET /payments (list)' }, }); paymentListDuration.add(res.timings.duration); check(res, { 'list payments: status 200': (r) => r.status === 200, 'list payments: has data': (r) => { try { return JSON.parse(r.body).data !== undefined; } catch { return false; } }, }); } else { // Create payment const amounts = [100000, 500000, 1000000, 2500000, 5000000]; const payload = JSON.stringify({ provider: 'VNPAY', type: 'LISTING_FEE', amountVND: amounts[iter % amounts.length], description: `K6 load test payment ${__VU}-${iter}`, returnUrl: 'http://localhost:3000/payments/callback', }); const res = http.post(`${BASE_URL}/payments`, payload, { headers, tags: { name: 'POST /payments (create)' }, }); paymentCreateDuration.add(res.timings.duration); // Payment gateway may not be available — 502/503 is acceptable in test const ok = check(res, { 'create payment: status 201|502|503': (r) => r.status === 201 || r.status === 502 || r.status === 503, 'create payment: has id or gateway error': (r) => { if (r.status >= 500) return true; // gateway unavailable try { return JSON.parse(r.body).id !== undefined; } catch { return false; } }, }); if (!ok) paymentFailRate.add(1); } sleep(Math.random() * 2 + 1); }