184 lines
6.0 KiB
JavaScript

/**
* 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 };