155 lines
5.4 KiB
JavaScript
155 lines
5.4 KiB
JavaScript
/**
|
|
* SCENARIO: auth-flow.js
|
|
* Register → Login → Refresh Token → Logout flow
|
|
*
|
|
* Tujuan: Stress test auth endpoints yang dipakai setiap user saat app dibuka.
|
|
* Target: p95 auth latency < 800ms, error rate < 1%
|
|
*/
|
|
import { sleep, check } from "k6";
|
|
import {
|
|
DEFAULT_THRESHOLDS,
|
|
handleSummary,
|
|
pickThresholds,
|
|
} from "../modules/metrics-helper.js";
|
|
import * as api from "../modules/api-client.js";
|
|
import { testData } from "../modules/test-data.js";
|
|
import { registerAndLogin, doRefreshToken } from "../modules/auth-helper.js";
|
|
|
|
const RUN_ID = __ENV.RUN_ID || `${Date.now()}`;
|
|
|
|
// ── Konfigurasi test — override via --config atau env ───────────────────────
|
|
export const options = {
|
|
scenarios: {
|
|
auth_flow: {
|
|
executor: "ramping-vus",
|
|
startVUs: 0,
|
|
stages: [
|
|
{ duration: "30s", target: 10 }, // warm-up
|
|
{ duration: "60s", target: 30 }, // ramp-up
|
|
{ duration: "60s", target: 30 }, // sustain
|
|
{ duration: "30s", target: 0 }, // ramp-down
|
|
],
|
|
gracefulRampDown: "15s",
|
|
},
|
|
},
|
|
thresholds: pickThresholds(
|
|
__ENV.BENCH_PROFILE === "local"
|
|
? { walkguide_auth_latency_ms: ["p(95)<35000", "p(99)<40000"] }
|
|
: {
|
|
...DEFAULT_THRESHOLDS,
|
|
walkguide_auth_latency_ms: ["p(95)<800", "p(99)<1500"],
|
|
},
|
|
),
|
|
};
|
|
|
|
// ── Main VU function ──────────────────────────────────────────────────────────
|
|
|
|
export default function authFlow() {
|
|
const vuId = __VU;
|
|
|
|
// ── Step 1: Register user baru (setiap VU punya unique email) ───────────────
|
|
const role = vuId % 2 === 0 ? "ROLE_GUARDIAN" : "ROLE_USER";
|
|
const suffix = `${RUN_ID}_${vuId}_${__ITER}`;
|
|
const payload =
|
|
role === "ROLE_GUARDIAN"
|
|
? testData.guardianRegisterPayload(suffix)
|
|
: testData.userRegisterPayload(suffix);
|
|
|
|
const regRes = api.register(payload);
|
|
check(regRes, {
|
|
"register: status 200": (r) => r.status === 200,
|
|
"register: has accessToken": (r) => {
|
|
const b = api.parseBody(r);
|
|
return b && b.data && !!b.data.accessToken;
|
|
},
|
|
});
|
|
|
|
let tokens = null;
|
|
if (regRes.status === 200) {
|
|
const body = api.parseBody(regRes);
|
|
if (body && body.data) tokens = body.data;
|
|
}
|
|
|
|
sleep(0.5);
|
|
|
|
// ── Step 2: Login ulang dengan credentials yang sama ────────────────────────
|
|
const loginRes = api.login(payload.email, payload.password);
|
|
check(loginRes, {
|
|
"login: status 200": (r) => r.status === 200,
|
|
"login: accessToken set": (r) => {
|
|
const b = api.parseBody(r);
|
|
return b && b.data && !!b.data.accessToken;
|
|
},
|
|
"login: refreshToken set": (r) => {
|
|
const b = api.parseBody(r);
|
|
return b && b.data && !!b.data.refreshToken;
|
|
},
|
|
"login: role correct": (r) => {
|
|
const b = api.parseBody(r);
|
|
return b && b.data && b.data.role === role;
|
|
},
|
|
});
|
|
|
|
if (loginRes.status === 200) {
|
|
const body = api.parseBody(loginRes);
|
|
if (body && body.data) tokens = body.data;
|
|
}
|
|
|
|
sleep(0.3);
|
|
|
|
// ── Step 3: Refresh token (simulasi token expire) ───────────────────────────
|
|
if (tokens && tokens.refreshToken) {
|
|
const refreshRes = api.refreshToken(tokens.refreshToken);
|
|
check(refreshRes, {
|
|
"refresh: status 200": (r) => r.status === 200,
|
|
"refresh: new accessToken": (r) => {
|
|
const b = api.parseBody(r);
|
|
return b && b.data && !!b.data.accessToken;
|
|
},
|
|
});
|
|
|
|
// Update access token
|
|
if (refreshRes.status === 200) {
|
|
const body = api.parseBody(refreshRes);
|
|
if (body && body.data) tokens.accessToken = body.data.accessToken;
|
|
}
|
|
}
|
|
|
|
sleep(0.3);
|
|
|
|
// ── Step 4: Update FCM token (dilakukan tiap login di production) ───────────
|
|
if (tokens && tokens.accessToken) {
|
|
const fakeFcm = `fcm_${Math.random().toString(36).slice(2, 30)}:APA91b${Math.random().toString(36).slice(2, 50)}`;
|
|
const fcmRes = api.updateFcmToken(tokens.accessToken, fakeFcm);
|
|
check(fcmRes, {
|
|
"fcm-token: status 2xx": (r) => r.status >= 200 && r.status < 300,
|
|
});
|
|
}
|
|
|
|
sleep(0.2);
|
|
|
|
// ── Step 5: Logout ──────────────────────────────────────────────────────────
|
|
if (tokens && tokens.accessToken) {
|
|
const logoutRes = api.logout(tokens.accessToken);
|
|
check(logoutRes, {
|
|
"logout: status 2xx": (r) => r.status >= 200 && r.status < 300,
|
|
});
|
|
}
|
|
|
|
// ── Think time: user nyata butuh beberapa detik antar aksi ──────────────────
|
|
sleep(testData.randomFloat(0.5, 1.5, 1));
|
|
}
|
|
|
|
// ── Health check sebelum test dimulai ────────────────────────────────────────
|
|
export function setup() {
|
|
const res = api.ping();
|
|
if (res.status !== 200) {
|
|
throw new Error(
|
|
`Backend tidak reachable. Ping returned ${res.status}. URL: ${__ENV.BASE_URL || "http://202.46.28.160:8080"}`,
|
|
);
|
|
}
|
|
console.log("✅ Backend reachable. Auth flow test starting.");
|
|
}
|
|
|
|
export { handleSummary };
|