import http from 'k6/http'; import { check, sleep } from 'k6'; import { Trend } from 'k6/metrics'; import { BASE_URL, SLA_THRESHOLDS, registerTestUser, authHeaders } from '../lib/config.js'; const searchDuration = new Trend('listings_search_duration', true); const detailDuration = new Trend('listings_detail_duration', true); export const options = { scenarios: { listings_load: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '30s', target: 100 }, { duration: '2m', target: 500 }, // peak: 500 concurrent { duration: '30s', target: 0 }, ], gracefulRampDown: '10s', }, }, thresholds: { ...SLA_THRESHOLDS, listings_search_duration: ['p(95)<500'], listings_detail_duration: ['p(95)<300'], }, }; export function setup() { // Register a user and create some listings for search/detail tests const tokens = registerTestUser(http); if (!tokens) return { accessToken: null, listingIds: [] }; const listingIds = []; const cities = ['Hà Nội', 'TP. Hồ Chí Minh', 'Đà Nẵng']; const types = ['APARTMENT', 'HOUSE', 'LAND']; for (let i = 0; i < 10; i++) { const listing = { title: `K6 Load Test Listing ${i}`, description: `Performance test listing #${i} for load testing`, transactionType: i % 2 === 0 ? 'SALE' : 'RENT', propertyType: types[i % types.length], address: `${100 + i} Đường Tải Thử`, ward: 'Phường 1', district: 'Quận 1', city: cities[i % cities.length], latitude: 10.7769 + (i * 0.001), longitude: 106.7009 + (i * 0.001), area: 50 + (i * 10), bedrooms: 1 + (i % 4), bathrooms: 1 + (i % 3), floors: 1 + (i % 3), priceVND: 1000000000 + (i * 500000000), direction: 'EAST', }; const res = http.post( `${BASE_URL}/listings`, JSON.stringify(listing), { headers: authHeaders(tokens.accessToken), tags: { name: 'setup_create_listing' } }, ); if (res.status === 201 || res.status === 200) { try { listingIds.push(JSON.parse(res.body).id); } catch (_) { /* skip */ } } } return { accessToken: tokens.accessToken, listingIds }; } export default function (data) { const iter = __ITER; if (iter % 3 === 0 && data.listingIds.length > 0) { // Detail view — pick a random listing const id = data.listingIds[iter % data.listingIds.length]; const res = http.get(`${BASE_URL}/listings/${id}`, { tags: { name: 'GET /listings/:id' }, }); detailDuration.add(res.timings.duration); check(res, { 'detail: status 200': (r) => r.status === 200, 'detail: has id': (r) => { try { return JSON.parse(r.body).id !== undefined; } catch { return false; } }, }); } else { // Search listings with various filters const queries = [ '?limit=20&offset=0', '?propertyType=APARTMENT&limit=10', '?transactionType=SALE&limit=10', '?city=Hà Nội&limit=10', '?limit=20&offset=20', ]; const query = queries[iter % queries.length]; const res = http.get(`${BASE_URL}/listings${query}`, { tags: { name: 'GET /listings (search)' }, }); searchDuration.add(res.timings.duration); check(res, { 'search: status 200': (r) => r.status === 200, 'search: has data array': (r) => { try { return Array.isArray(JSON.parse(r.body).data); } catch { return false; } }, }); } sleep(Math.random() * 1 + 0.3); }