/** * k6 load test — concurrent players spinning. * k6 run -e BASE=https://www.spinza.dev -e VUS=100 -e DURATION=60s qa/load/spin.k6.js * Each VU registers a throwaway account (username lt__) and spins on neon-vault. * Never run against production without lowering the register rate limit awareness (10/10 min per IP): * use -e USERS_FILE with pre-created "username:password" lines to skip registration. */ import http from "k6/http"; import { check, sleep } from "k6"; import { uuidv4 } from "https://jslib.k6.io/k6-utils/1.4.0/index.js"; const BASE = __ENV.BASE || "http://localhost:8230"; export const options = { scenarios: { spins: { executor: "constant-vus", vus: Number(__ENV.VUS || 50), duration: __ENV.DURATION || "60s" }, }, thresholds: { http_req_failed: ["rate<0.01"], "http_req_duration{name:spin}": ["p(95)<250"], }, }; export function setup() { const users = []; if (__ENV.USERS_FILE) { for (const line of open(__ENV.USERS_FILE).split("\n")) { const [u, p] = line.trim().split(":"); if (u && p) users.push({ username: u, password: p }); } } return { users }; } export default function (data) { const jar = http.cookieJar(); let creds = data.users.length ? data.users[(__VU - 1) % data.users.length] : null; if (!creds) { creds = { username: `lt_${__VU}_${Math.floor(Math.random() * 1e6)}`.slice(0, 24), password: "loadtest-pass-123" }; const r = http.post(`${BASE}/api/auth/register`, JSON.stringify({ ...creds, confirmPassword: creds.password, ageConfirmed: true }), { headers: { "Content-Type": "application/json", Origin: BASE }, tags: { name: "register" } }); check(r, { registered: (res) => res.status === 200 }); } else { const r = http.post(`${BASE}/api/auth/login`, JSON.stringify(creds), { headers: { "Content-Type": "application/json", Origin: BASE }, tags: { name: "login" } }); check(r, { logged_in: (res) => res.status === 200 }); } for (let i = 0; i < 40; i++) { const r = http.post(`${BASE}/api/games/neon-vault/spin`, JSON.stringify({ bet: 10, clientRoundId: uuidv4() }), { headers: { "Content-Type": "application/json", Origin: BASE }, tags: { name: "spin" } }); check(r, { spin_ok: (res) => res.status === 200 || res.status === 402 }); if (r.status === 402) { http.post(`${BASE}/api/rewards/rescue/claim`, null, { headers: { Origin: BASE }, tags: { name: "rescue" } }); } sleep(0.3); } void jar; }