/** * SCENARIO: obstacle-logging.js * High-frequency obstacle detection log — simulasi YOLO inference results. * * WalkGuide menjalankan YOLO pada 5 FPS, setiap obstacle terdeteksi dikirim ke backend. * Ini endpoint write-heavy terbesar karena obstacle bisa sangat sering. * * Target: p95 < 400ms, throughput > 200 req/s pada 50 VUs */ import { sleep, check } from "k6"; import { DEFAULT_THRESHOLDS, handleSummary, } from "../modules/metrics-helper.js"; import * as api from "../modules/api-client.js"; import { testData } from "../modules/test-data.js"; export const options = { scenarios: { obstacle_burst: { executor: "ramping-vus", startVUs: 0, stages: [ { duration: "20s", target: 10 }, { duration: "60s", target: 50 }, { duration: "120s", target: 50 }, // sustain high load { duration: "20s", target: 0 }, ], gracefulRampDown: "10s", }, }, thresholds: { ...DEFAULT_THRESHOLDS, walkguide_obstacle_latency_ms: ["p(95)<400", "p(99)<800"], http_reqs: ["rate>100"], // throughput > 100 req/s }, }; // ── Per-VU state ────────────────────────────────────────────────────────────── let vuToken = null; export default function obstacleLogging() { // ── Init: register + login ───────────────────────────────────────────────── if (!vuToken) { const suffix = `obs_${__VU}_${Date.now()}`; const payload = testData.userRegisterPayload(suffix); const res = api.register(payload); if (res.status === 200) { const body = api.parseBody(res); if (body && body.data) vuToken = body.data.accessToken; } if (!vuToken) { sleep(2); return; } // Start walkguide api.startWalkguide(vuToken); sleep(0.3); } // ── Burst mode: kirim 5 obstacle logs (simulasi 5 FPS selama 1 detik) ────── const burst = testData.obstacleLogBurst(5); let allSuccess = true; for (const obstaclePayload of burst) { const res = api.logObstacle(vuToken, obstaclePayload); const ok = check(res, { "obstacle-log: status 2xx": (r) => r.status >= 200 && r.status < 300, "obstacle-log: fast response": (r) => r.timings.duration < 600, }); if (!ok) allSuccess = false; // Minimal sleep (5 FPS = 200ms per frame) sleep(0.2); } // ── Setiap 10 iteration, kirim obstacle dengan confidence tinggi ──────────── if (__ITER % 10 === 0) { const criticalObstacle = { label: "person", confidence: 0.95, direction: "CENTER", estimatedDist: "Very Close", lat: testData.randomSurabayaCoord().lat, lng: testData.randomSurabayaCoord().lng, }; const res = api.logObstacle(vuToken, criticalObstacle); check(res, { "critical-obstacle: 2xx": (r) => r.status >= 200 && r.status < 300, "critical-obstacle: fast": (r) => r.timings.duration < 300, }); } // ── Think time minimal — obstacle logging adalah high-frequency ───────────── sleep(testData.randomFloat(0.1, 0.5, 1)); } export function setup() { const res = api.ping(); if (res.status !== 200) throw new Error(`Backend not reachable: ${res.status}`); console.log("✅ Obstacle logging stress test starting."); } export { handleSummary };