template-monorepo/test/k6/journeys/phone_verify.js

69 lines
2.5 KiB
JavaScript
Raw Permalink Normal View History

2026-05-26 06:05:33 +00:00
// Journey: business phone verification end-to-end (SMS OTP via Redis)
//
// Endpoints exercised:
// POST /api/v1/auth/register
// POST /api/v1/auth/register/confirm
// POST /api/v1/members/me/verifications/phone/start
// POST /api/v1/members/me/verifications/phone/confirm
// GET /api/v1/members/me (verify business_phone_verified flag is true)
//
// SMS OTP source: the mock SMS sender (when WithMockRedis is wired by the
// notification factory in k6 mode) writes the SMS body to Redis at
// "dev:notification:last:sms:<phone>". fetchSMSOTP polls that key.
//
// k6/experimental/redis requires k6 v0.46+.
import { get, post, checkEnvelope } from '../lib/http.js';
import { registerAndConfirm } from '../lib/auth.js';
import { fetchSMSOTP } from '../lib/otp.js';
import { unique } from '../lib/config.js';
export const options = {
vus: 1,
iterations: 1,
thresholds: { checks: ['rate==1.0'] },
};
// Generate a deterministic-ish unique E.164 number per iteration to avoid
// collisions across concurrent runs. Use +886912 + 7 digits derived from the
// VU+iter+timestamp.
function uniquePhone() {
const ts = Date.now() % 1_000_000_0;
const vu = (typeof __VU !== 'undefined' && __VU) || 0;
const iter = (typeof __ITER !== 'undefined' && __ITER) || 0;
const suffix = String(ts + vu * 100 + iter).padStart(7, '0').slice(-7);
return `+886912${suffix}`;
}
export default async function () {
const { tokens } = registerAndConfirm();
const bearer = { Authorization: `Bearer ${tokens.access_token}` };
const phone = uniquePhone();
const startRes = post(
'/api/v1/members/me/verifications/phone/start',
{ target: phone },
bearer,
);
const start = checkEnvelope(startRes, 'POST /me/verifications/phone/start').data;
if (!start.challenge_id) throw new Error('phone/start: missing challenge_id');
const { code } = await fetchSMSOTP(phone);
if (!code) throw new Error(`could not extract SMS OTP for ${phone}`);
const confirmRes = post(
'/api/v1/members/me/verifications/phone/confirm',
{ challenge_id: start.challenge_id, code },
bearer,
);
checkEnvelope(confirmRes, 'POST /me/verifications/phone/confirm');
const me = checkEnvelope(get('/api/v1/members/me', bearer), 'GET /members/me (post-phone-verify)').data;
if (me.business_phone !== phone) {
throw new Error(`business_phone not set: got=${me.business_phone}`);
}
if (me.business_phone_verified !== true) {
throw new Error(`business_phone_verified should be true: got=${me.business_phone_verified}`);
}
}