/** * Scénario combiné: health, auth, tracks, playlists, marketplace * Usage: k6 run loadtests/backend/full.js */ import http from 'k6/http'; import { check, sleep } from 'k6'; import { randomString } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js'; const BASE_URL = __ENV.BASE_URL || __ENV.API_ORIGIN || 'http://localhost:8080'; const TEST_EMAIL_PREFIX = __ENV.TEST_EMAIL_PREFIX || 'user+full'; const TEST_EMAIL_DOMAIN = __ENV.TEST_EMAIL_DOMAIN || 'example.com'; const TEST_PASSWORD_PREFIX = __ENV.TEST_PASSWORD_PREFIX || 'V3za!full-'; function generateTestUser() { const rand = randomString(8); const pwd = `${TEST_PASSWORD_PREFIX}${rand}`; return { email: `${TEST_EMAIL_PREFIX}${rand}@${TEST_EMAIL_DOMAIN}`, password: pwd, password_confirmation: pwd, username: `fu${rand}`, }; } export const options = { scenarios: { health: { executor: 'constant-vus', vus: 5, duration: '1m', startTime: '0s', exec: 'healthScenario', }, auth: { executor: 'ramping-vus', stages: [ { duration: '30s', target: 10 }, { duration: '1m', target: 10 }, { duration: '30s', target: 0 }, ], startTime: '10s', exec: 'authScenario', }, tracks: { executor: 'ramping-vus', stages: [ { duration: '30s', target: 15 }, { duration: '2m', target: 15 }, { duration: '30s', target: 0 }, ], startTime: '20s', exec: 'tracksScenario', }, playlists: { executor: 'ramping-vus', stages: [ { duration: '30s', target: 5 }, { duration: '1m', target: 5 }, { duration: '30s', target: 0 }, ], startTime: '30s', exec: 'playlistsScenario', }, marketplace: { executor: 'ramping-vus', stages: [ { duration: '30s', target: 5 }, { duration: '1m', target: 5 }, { duration: '30s', target: 0 }, ], startTime: '40s', exec: 'marketplaceScenario', }, }, thresholds: { http_req_duration: ['p(95)<1000', 'p(99)<2000'], http_req_failed: ['rate<0.15'], }, }; export function setup() { const users = []; const baseURL = `${BASE_URL}/api/v1/auth`; for (let i = 0; i < 25; i++) { const user = generateTestUser(); const registerRes = http.post(`${baseURL}/register`, JSON.stringify(user), { headers: { 'Content-Type': 'application/json' }, }); if (registerRes.status === 201) { const loginRes = http.post(`${baseURL}/login`, JSON.stringify({ email: user.email, password: user.password }), { headers: { 'Content-Type': 'application/json' }, }); if (loginRes.status === 200) { try { const body = JSON.parse(loginRes.body); const token = body.data?.token?.access_token; if (token) users.push({ ...user, token }); } catch (e) {} } } } return { users }; } export function healthScenario() { const healthRes = http.get(`${BASE_URL}/health`); check(healthRes, { 'health 200': (r) => r.status === 200 }); sleep(0.5); const readyzRes = http.get(`${BASE_URL}/readyz`); check(readyzRes, { 'readyz 200': (r) => r.status === 200 }); sleep(1); } export function authScenario(data) { const { users } = data; if (users.length === 0) return; const user = users[Math.floor(Math.random() * users.length)]; const loginRes = http.post( `${BASE_URL}/api/v1/auth/login`, JSON.stringify({ email: user.email, password: user.password }), { headers: { 'Content-Type': 'application/json' } } ); check(loginRes, { 'login 200': (r) => r.status === 200 }); sleep(1); } export function tracksScenario(data) { const { users } = data; const headers = users.length > 0 ? { Authorization: `Bearer ${users[Math.floor(Math.random() * users.length)].token}` } : {}; const tracksRes = http.get(`${BASE_URL}/api/v1/tracks`, { headers }); check(tracksRes, { 'tracks 200 or 401': (r) => r.status === 200 || r.status === 401 }); sleep(0.5); const searchRes = http.get(`${BASE_URL}/api/v1/tracks/search?q=test`, { headers }); check(searchRes, { 'search 200 or 401': (r) => r.status === 200 || r.status === 401 }); sleep(1); } export function playlistsScenario(data) { const { users } = data; if (users.length === 0) return; const user = users[Math.floor(Math.random() * users.length)]; const headers = { Authorization: `Bearer ${user.token}` }; const listRes = http.get(`${BASE_URL}/api/v1/playlists`, { headers }); check(listRes, { 'playlists 200': (r) => r.status === 200 }); sleep(1); } export function marketplaceScenario() { const productsRes = http.get(`${BASE_URL}/api/v1/marketplace/products`); check(productsRes, { 'products 200': (r) => r.status === 200 }); sleep(1); }