// smoke: member endpoints (Bearer) // // Covers (11 endpoints): // GET /api/v1/members/me // PATCH /api/v1/members/me // POST /api/v1/members/me/verifications/email/start // POST /api/v1/members/me/verifications/email/confirm (negative: invalid code) // POST /api/v1/members/me/verifications/phone/start // POST /api/v1/members/me/verifications/phone/confirm (negative: invalid code) // GET /api/v1/members/me/totp (status before enroll) // POST /api/v1/members/me/totp/enroll-start // POST /api/v1/members/me/totp/enroll-confirm (negative: invalid code) // POST /api/v1/members/me/totp/verify (negative: not enrolled) // POST /api/v1/members/me/totp/backup-codes (negative: not enrolled) // DELETE /api/v1/members/me/totp (negative: not enrolled / 404) // // Happy paths for TOTP and verification end-to-end live in journeys/. import { get, post, patch, del, checkEnvelope, checkError } from '../lib/http.js'; import { registerAndConfirm } from '../lib/auth.js'; import { unique } from '../lib/config.js'; export const options = { vus: 1, iterations: 1, thresholds: { checks: ['rate==1.0'] }, }; export default function () { const { tokens } = registerAndConfirm(); const bearer = { Authorization: `Bearer ${tokens.access_token}` }; // 1. GET /me const me = checkEnvelope(get('/api/v1/members/me', bearer), 'GET /members/me').data; if (me.uid !== tokens.uid) throw new Error('me.uid mismatch'); // 2. PATCH /me const newName = `smoke-${unique('m')}`; const patched = checkEnvelope(patch('/api/v1/members/me', { display_name: newName }, bearer), 'PATCH /members/me').data; if (patched.display_name !== newName) throw new Error('display_name not updated'); // 3. POST /me/verifications/email/start const eStart = checkEnvelope( post('/api/v1/members/me/verifications/email/start', { target: `verify-${unique('e')}@k6.local` }, bearer), 'POST /me/verifications/email/start', ).data; if (!eStart.challenge_id) throw new Error('email start: missing challenge_id'); // 4. POST /me/verifications/email/confirm (negative — wrong code 000000) checkError( post('/api/v1/members/me/verifications/email/confirm', { challenge_id: eStart.challenge_id, code: '000000' }, bearer), 'POST /me/verifications/email/confirm (bad code)', 403, 29505000, ); // 5. POST /me/verifications/phone/start const pStart = checkEnvelope( post('/api/v1/members/me/verifications/phone/start', { target: '+886912000001' }, bearer), 'POST /me/verifications/phone/start', ).data; if (!pStart.challenge_id) throw new Error('phone start: missing challenge_id'); // 6. POST /me/verifications/phone/confirm (negative) checkError( post('/api/v1/members/me/verifications/phone/confirm', { challenge_id: pStart.challenge_id, code: '000000' }, bearer), 'POST /me/verifications/phone/confirm (bad code)', 403, 29505000, ); // 7. GET /me/totp (status — not enrolled) const totpStatus = checkEnvelope(get('/api/v1/members/me/totp', bearer), 'GET /me/totp').data; if (totpStatus.enrolled !== false) throw new Error('totp should not be enrolled'); // 8. POST /me/totp/enroll-start (happy) const enroll = checkEnvelope(post('/api/v1/members/me/totp/enroll-start', null, bearer), 'POST /me/totp/enroll-start').data; if (!enroll.otpauth_url) throw new Error('enroll-start: missing otpauth_url'); // 9. POST /me/totp/enroll-confirm (negative — bad code) checkError( post('/api/v1/members/me/totp/enroll-confirm', { code: '000000' }, bearer), 'POST /me/totp/enroll-confirm (bad code)', 403, 29505000, ); // 10. POST /me/totp/verify (negative — not enrolled) checkError( post('/api/v1/members/me/totp/verify', { code: '000000' }, bearer), 'POST /me/totp/verify (not enrolled)', 409, 29309000, ); // 11. POST /me/totp/backup-codes (negative — not enrolled) checkError( post('/api/v1/members/me/totp/backup-codes', null, bearer), 'POST /me/totp/backup-codes (not enrolled)', 409, 29309000, ); // 12. DELETE /me/totp — when not enrolled the contract returns 200 (idempotent disable). // Accept either 200 success envelope or 404 (member-not-found edge case). const delRes = del('/api/v1/members/me/totp', null, bearer); if (delRes.status !== 200 && delRes.status !== 404) { throw new Error(`DELETE /me/totp unexpected status ${delRes.status}: ${delRes.body}`); } }