/** * SCENARIO: notification-send.js * Mass notification broadcast — Guardian kirim pesan ke User. * * Simulasi banyak Guardian mengirim notifikasi serentak + User membaca. * Termasuk test mark-read dan unread-count endpoints. * * Target: p95 < 500ms, 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: { // Scenario A: Guardian send notifications guardian_send: { executor: "constant-vus", vus: 25, duration: "120s", exec: "guardianSend", tags: { scenario: "guardian_send" }, }, // Scenario B: User read notifications (mulai setelah 10s) user_read: { executor: "constant-vus", vus: 25, duration: "100s", startTime: "10s", exec: "userRead", tags: { scenario: "user_read" }, }, }, thresholds: { ...DEFAULT_THRESHOLDS, walkguide_notif_latency_ms: ["p(95)<500", "p(99)<1000"], }, }; // ── Shared VU states ────────────────────────────────────────────────────────── let guardianVuToken = null; let userVuToken = null; // ── EXEC: Guardian send notifications ──────────────────────────────────────── export function guardianSend() { if (!guardianVuToken) { const suffix = `notif_g_${__VU}_${Date.now()}`; const res = api.register(testData.guardianRegisterPayload(suffix)); if (res.status === 200) { const b = api.parseBody(res); if (b && b.data) guardianVuToken = b.data.accessToken; } if (!guardianVuToken) { sleep(2); return; } sleep(0.3); } // Kirim 1-3 notifikasi per iterasi const count = testData.randomInt(1, 3); for (let i = 0; i < count; i++) { const payload = testData.sendNotificationPayload(); const res = api.sendNotification(guardianVuToken, payload); check(res, { "send-notif: status 2xx": (r) => r.status >= 200 && r.status < 300, "send-notif: latency < 500": (r) => r.timings.duration < 500, }); sleep(0.2); } // Cek activity log guardian if (__ITER % 5 === 0) { const logsRes = api.getActivityLogsGuardian(guardianVuToken); check(logsRes, { "guardian-logs: 2xx": (r) => r.status >= 200 && r.status < 300, }); } sleep(testData.randomFloat(0.5, 1.5, 1)); } // ── EXEC: User read notifications ───────────────────────────────────────────── export function userRead() { if (!userVuToken) { const suffix = `notif_u_${__VU}_${Date.now()}`; const res = api.register(testData.userRegisterPayload(suffix)); if (res.status === 200) { const b = api.parseBody(res); if (b && b.data) userVuToken = b.data.accessToken; } if (!userVuToken) { sleep(2); return; } sleep(0.3); } // ── Step 1: Cek unread count ───────────────────────────────────────────── const unreadRes = api.getUnreadCount(userVuToken); check(unreadRes, { "unread-count: 2xx": (r) => r.status >= 200 && r.status < 300, "unread-count: has data": (r) => { const b = api.parseBody(r); return b && b.data !== undefined; }, }); sleep(0.2); // ── Step 2: Get notification list ──────────────────────────────────────── const page = __ITER % 3; // test pagination const notifRes = api.getNotifications(userVuToken, page, 20); check(notifRes, { "get-notifs: 2xx": (r) => r.status >= 200 && r.status < 300, "get-notifs: has list": (r) => { const b = api.parseBody(r); return b && b.data !== undefined; }, }); // Extract first notif id untuk test mark-read let firstNotifId = null; if (notifRes.status >= 200 && notifRes.status < 300) { const body = api.parseBody(notifRes); if ( body && body.data && Array.isArray(body.data.content) && body.data.content.length > 0 ) { firstNotifId = body.data.content[0].id; } } sleep(0.2); // ── Step 3: Mark individual notification read (jika ada) ───────────────── if (firstNotifId) { const markRes = api.markOneRead(userVuToken, firstNotifId); check(markRes, { "mark-one-read: 2xx": (r) => r.status >= 200 && r.status < 300, }); sleep(0.2); } // ── Step 4: Mark all read (setiap 5 iterasi) ───────────────────────────── if (__ITER % 5 === 0) { const markAllRes = api.markAllRead(userVuToken); check(markAllRes, { "mark-all-read: 2xx": (r) => r.status >= 200 && r.status < 300, }); } sleep(testData.randomFloat(0.5, 2.0, 1)); } export function setup() { const res = api.ping(); if (res.status !== 200) throw new Error(`Backend not reachable: ${res.status}`); console.log( "📬 Notification send/read test starting (2 parallel scenarios).", ); } export { handleSummary };