/** * SCENARIO: timeline-query.js * Timeline analytics endpoints — Guardian dashboard, location history, activity logs. * * Simulasi Guardian yang aktif monitoring: buka dashboard, lihat peta, * scroll history, cek obstacle logs. Query-heavy read scenario. * * Target: p95 < 1000ms (analytics boleh lebih lambat), error rate < 1% */ 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: { timeline_analytics: { executor: "ramping-vus", startVUs: 0, stages: [ { duration: "20s", target: 10 }, { duration: "90s", target: 30 }, { duration: "30s", target: 0 }, ], gracefulRampDown: "15s", }, }, thresholds: { ...DEFAULT_THRESHOLDS, walkguide_timeline_latency_ms: ["p(95)<1000", "p(99)<2000"], http_req_duration: ["p(95)<1000"], }, }; // ── Per-VU state ────────────────────────────────────────────────────────────── let guardianToken = null; let userToken = null; export default function timelineQuery() { // ── Init: setup both roles ───────────────────────────────────────────────── if (!guardianToken || !userToken) { const suffix = `tl_${__VU}_${Date.now()}`; const gRes = api.register(testData.guardianRegisterPayload(suffix)); if (gRes.status === 200) { const b = api.parseBody(gRes); if (b && b.data) guardianToken = b.data.accessToken; } const uRes = api.register(testData.userRegisterPayload(suffix)); if (uRes.status === 200) { const b = api.parseBody(uRes); if (b && b.data) userToken = b.data.accessToken; } if (!guardianToken || !userToken) { sleep(2); return; } // Seed beberapa location updates agar ada data untuk query for (let i = 0; i < 3; i++) { const coord = testData.randomSurabayaCoord(0.02); api.updateLocation(userToken, coord.lat, coord.lng, 5.0, 1.2, 90.0); sleep(0.1); } // Seed obstacle logs for (let i = 0; i < 5; i++) { api.logObstacle(userToken, testData.obstacleLogPayload()); sleep(0.1); } sleep(0.5); } // ── Guardian Dashboard Simulation ───────────────────────────────────────── // Step 1: Guardian dashboard (overview semua data) const dashRes = api.getGuardianDashboard(guardianToken); check(dashRes, { "dashboard: 2xx": (r) => r.status >= 200 && r.status < 300, "dashboard: fast": (r) => r.timings.duration < 1000, "dashboard: has data": (r) => { const b = api.parseBody(r); return b && b.data !== undefined; }, }); sleep(0.5); // Step 2: Get user location (paling sering dipanggil di Guardian map screen) const locRes = api.getUserLocation(guardianToken); check(locRes, { "user-location: 2xx": (r) => r.status >= 200 && r.status < 300, "user-location: fast": (r) => r.timings.duration < 500, }); sleep(0.3); // Step 3: Location history (scroll timeline peta seperti Google Maps Timeline) const pages = [0, 1, 2]; const pageSizes = [20, 50, 100]; for (const page of pages) { const size = pageSizes[page % pageSizes.length]; const histRes = api.getLocationHistory(guardianToken, page, size); check(histRes, { [`location-history page${page}: 2xx`]: (r) => r.status >= 200 && r.status < 300, [`location-history page${page}: latency`]: (r) => r.timings.duration < 1500, }); sleep(0.3); } // Step 4: Obstacle logs (Guardian ingin tahu apa saja yang terdeteksi) const obsRes = api.getObstacleLogs(guardianToken, 0, 20); check(obsRes, { "obstacle-logs: 2xx": (r) => r.status >= 200 && r.status < 300, "obstacle-logs: fast": (r) => r.timings.duration < 800, }); sleep(0.3); // Step 5: Activity logs (timeline aktivitas User) const actRes = api.getActivityLogsGuardian(guardianToken, 0, 20); check(actRes, { "activity-logs: 2xx": (r) => r.status >= 200 && r.status < 300, "activity-logs: fast": (r) => r.timings.duration < 1000, }); sleep(0.3); // Step 6: SOS events history const sosRes = api.getSosEvents(guardianToken, 0, 10); check(sosRes, { "sos-events: 2xx": (r) => r.status >= 200 && r.status < 300, }); sleep(0.2); // Step 7: AI config (Guardian mau lihat setting YOLO) const aiRes = api.getAiConfig(guardianToken); check(aiRes, { "ai-config: 2xx": (r) => r.status >= 200 && r.status < 300, }); sleep(0.2); // Step 8: Geofence config const geoRes = api.getGeofenceConfig(guardianToken); check(geoRes, { "geofence: 2xx": (r) => r.status >= 200 && r.status < 300, }); // ── User side: check own activity ───────────────────────────────────────── sleep(0.5); const uActRes = api.getActivityLogs(userToken, 0, 10); check(uActRes, { "user-activity: 2xx": (r) => r.status >= 200 && r.status < 300, }); const uSettRes = api.getUserSettings(userToken); check(uSettRes, { "user-settings: 2xx": (r) => r.status >= 200 && r.status < 300, }); const uAiRes = api.getUserAiConfig(userToken); check(uAiRes, { "user-ai-config: 2xx": (r) => r.status >= 200 && r.status < 300, }); // Think time — Guardian tidak refresh setiap detik sleep(testData.randomFloat(2.0, 5.0, 1)); } export function setup() { const res = api.ping(); if (res.status !== 200) throw new Error(`Backend not reachable: ${res.status}`); console.log("📊 Timeline analytics test starting."); } export { handleSummary };