// k6 equivalent of KneeStepsSimulation: open model, 30 -> 240 requests/s in 8 steps, // each held 6 s and separated by 2 s ramps. Every request is tagged with the step it was sent in. import http from 'k6/http'; import exec from 'k6/execution'; import { check } from 'k6'; import { SharedArray } from 'k6/data'; const BASE = __ENV.APP_URL || 'http://localhost:18300'; const STEPS = 8, RATE_STEP = 30, HOLD_S = 6, RAMP_S = 2; // the feeder: same CSV Gatling reads const symbols = new SharedArray('symbols', () => open('../src/test/resources/data/symbols.csv').trim().split('\n').slice(1)); // k6 treats only these statuses as success, so a 503 counts in http_req_failed http.setResponseCallback(http.expectedStatuses(200)); const stages = []; for (let i = 0; i < STEPS; i++) { if (i > 0) stages.push({ duration: `${RAMP_S}s`, target: RATE_STEP * (i + 1) }); stages.push({ duration: `${HOLD_S}s`, target: RATE_STEP * (i + 1) }); } export const options = { scenarios: { knee: { executor: 'ramping-arrival-rate', // open model: iterations start on a schedule startRate: RATE_STEP, timeUnit: '1s', preAllocatedVUs: 200, maxVUs: 1000, stages, }, }, }; function stepLabel() { const elapsed = Date.now() - exec.scenario.startTime; const slot = Math.floor(elapsed / ((HOLD_S + RAMP_S) * 1000)); const into = elapsed % ((HOLD_S + RAMP_S) * 1000); if (into >= HOLD_S * 1000 || slot >= STEPS) return 'ramp'; return `rate ${String(RATE_STEP * (slot + 1)).padStart(3, '0')}/s`; } export default function () { const symbol = symbols[Math.floor(Math.random() * symbols.length)]; const res = http.get(`${BASE}/api/quote/${symbol}`, { tags: { step: stepLabel() } }); check(res, { 'status is 200': (r) => r.status === 200 }); }