// smoke: POST /api/v1/auth/password/{forgot,reset} // // Covers forgot: // happy — active platform 帳號寄重設 OTP // 404 — member 不存在(28301000) // 403 — 未驗證帳號(28505000) // 400 — 缺少 email // 429 — OTP 重送冷卻(29604000) // // Covers reset: // 403 — OTP 錯誤(29505000) // 404 — challenge 不存在(29301000) // 403 — purpose 不符(用 registration challenge)(29505000) // 400 — 新密碼太短 import { sleep } from 'k6'; import { post, checkError } from '../lib/http.js'; import { cfg } from '../lib/config.js'; import { makeIdentity, registerEmail, registerAndConfirm, passwordForgot, } from '../lib/auth.js'; export const options = { vus: 1, iterations: 1, thresholds: { checks: ['rate==1.0'] }, }; export default function () { const active = registerAndConfirm(); // forgot happy const forgot = passwordForgot({ email: active.identity.email }); if (!forgot.challenge_id) { throw new Error('password/forgot happy: missing challenge_id'); } // forgot member not found checkError( post('/api/v1/auth/password/forgot', { tenant_slug: cfg.tenantSlug, email: `no-such-${Date.now()}@k6.local`, }), 'POST /auth/password/forgot (member not found)', 404, 28301000, ); // forgot unverified account const pending = makeIdentity('pwd-unverified'); registerEmail({ email: pending.email, password: pending.password, displayName: pending.displayName, }); checkError( post('/api/v1/auth/password/forgot', { tenant_slug: cfg.tenantSlug, email: pending.email, }), 'POST /auth/password/forgot (unverified)', 403, 28505000, ); // forgot missing email const missingForgot = post('/api/v1/auth/password/forgot', { tenant_slug: cfg.tenantSlug }); if (missingForgot.status !== 400) { throw new Error(`password/forgot missing email: expected 400 got ${missingForgot.status}`); } // forgot resend cooldown checkError( post('/api/v1/auth/password/forgot', { tenant_slug: cfg.tenantSlug, email: active.identity.email, }), 'POST /auth/password/forgot (resend cooldown)', 429, 29604000, ); sleep(cfg.resendCooldownSeconds + 1); passwordForgot({ email: active.identity.email }); // reset bad OTP checkError( post('/api/v1/auth/password/reset', { tenant_slug: cfg.tenantSlug, challenge_id: forgot.challenge_id, code: '000000', new_password: 'K6-NewPass-2!', }), 'POST /auth/password/reset (bad otp)', 403, 29505000, ); // reset unknown challenge — Redis miss 目前回 500(29201000),非 404 checkError( post('/api/v1/auth/password/reset', { tenant_slug: cfg.tenantSlug, challenge_id: '00000000-0000-0000-0000-000000000000', code: '123456', new_password: 'K6-NewPass-2!', }), 'POST /auth/password/reset (unknown challenge)', 500, 29201000, ); // reset purpose mismatch — registration challenge on password reset endpoint const regOnly = makeIdentity('pwd-purpose'); const reg = registerEmail({ email: regOnly.email, password: regOnly.password, displayName: regOnly.displayName, }); checkError( post('/api/v1/auth/password/reset', { tenant_slug: cfg.tenantSlug, challenge_id: reg.challenge_id, code: '123456', new_password: 'K6-NewPass-2!', }), 'POST /auth/password/reset (purpose mismatch)', 403, 29505000, ); // reset weak password (validate min=8) const weak = post('/api/v1/auth/password/reset', { tenant_slug: cfg.tenantSlug, challenge_id: forgot.challenge_id, code: '123456', new_password: 'short', }); if (weak.status !== 400) { throw new Error(`password/reset weak password: expected 400 got ${weak.status}`); } }