- Fix search.js: replace URLSearchParams (unsupported in K6) with string interpolation - Add baseline performance report with latency benchmarks across all 4 suites - Add load-tests/results/*.json to .gitignore (large raw output files) Note: pre-existing test failure in create-listing.handler.spec.ts (eventBus.publish mock) — unrelated to this change. Co-Authored-By: Paperclip <noreply@paperclip.ing>
88 lines
2.6 KiB
JavaScript
88 lines
2.6 KiB
JavaScript
import http from 'k6/http';
|
|
import { check, sleep } from 'k6';
|
|
import { Trend } from 'k6/metrics';
|
|
import { BASE_URL, SLA_THRESHOLDS } from '../lib/config.js';
|
|
|
|
const textSearchDuration = new Trend('text_search_duration', true);
|
|
const geoSearchDuration = new Trend('geo_search_duration', true);
|
|
|
|
export const options = {
|
|
scenarios: {
|
|
search_load: {
|
|
executor: 'ramping-vus',
|
|
startVUs: 0,
|
|
stages: [
|
|
{ duration: '30s', target: 50 },
|
|
{ duration: '2m', target: 200 }, // peak: 200 concurrent
|
|
{ duration: '30s', target: 0 },
|
|
],
|
|
gracefulRampDown: '10s',
|
|
},
|
|
},
|
|
thresholds: {
|
|
...SLA_THRESHOLDS,
|
|
text_search_duration: ['p(95)<500'],
|
|
geo_search_duration: ['p(95)<500'],
|
|
},
|
|
};
|
|
|
|
const TEXT_QUERIES = [
|
|
'căn hộ',
|
|
'nhà phố quận 1',
|
|
'đất nền',
|
|
'chung cư',
|
|
'biệt thự',
|
|
'apartment Ho Chi Minh',
|
|
'house Hanoi',
|
|
];
|
|
|
|
const GEO_SEARCHES = [
|
|
{ lat: 10.7769, lng: 106.7009, radius: 5000 }, // HCM center
|
|
{ lat: 21.0285, lng: 105.8542, radius: 5000 }, // Hanoi center
|
|
{ lat: 16.0544, lng: 108.2022, radius: 10000 }, // Da Nang
|
|
{ lat: 10.8231, lng: 106.6297, radius: 3000 }, // HCM Tan Binh
|
|
{ lat: 21.0067, lng: 105.8400, radius: 2000 }, // Hanoi Ba Dinh
|
|
];
|
|
|
|
export default function () {
|
|
const iter = __ITER;
|
|
|
|
if (iter % 2 === 0) {
|
|
// Full-text search
|
|
const query = TEXT_QUERIES[iter % TEXT_QUERIES.length];
|
|
const url = `${BASE_URL}/search?q=${encodeURIComponent(query)}&limit=20&offset=0`;
|
|
|
|
const res = http.get(url, {
|
|
tags: { name: 'GET /search (text)' },
|
|
});
|
|
|
|
textSearchDuration.add(res.timings.duration);
|
|
check(res, {
|
|
'text search: status 200|503': (r) => r.status === 200 || r.status === 503,
|
|
'text search: valid response': (r) => {
|
|
if (r.status === 503) return true; // Typesense unavailable — acceptable
|
|
try { return JSON.parse(r.body).data !== undefined; } catch { return false; }
|
|
},
|
|
});
|
|
} else {
|
|
// Geo search
|
|
const geo = GEO_SEARCHES[iter % GEO_SEARCHES.length];
|
|
const url = `${BASE_URL}/search/geo?lat=${geo.lat}&lng=${geo.lng}&radius=${geo.radius}&limit=20`;
|
|
|
|
const res = http.get(url, {
|
|
tags: { name: 'GET /search/geo' },
|
|
});
|
|
|
|
geoSearchDuration.add(res.timings.duration);
|
|
check(res, {
|
|
'geo search: status 200|503': (r) => r.status === 200 || r.status === 503,
|
|
'geo search: valid response': (r) => {
|
|
if (r.status === 503) return true;
|
|
try { return JSON.parse(r.body).data !== undefined; } catch { return false; }
|
|
},
|
|
});
|
|
}
|
|
|
|
sleep(Math.random() * 1 + 0.5);
|
|
}
|