{
  "gate": "Unit Tests",
  "gate_number": 5,
  "gate_status": "PASS",
  "passed": 120,
  "failed": 0,
  "coverage_lines": 100,
  "coverage_functions": 93.33,
  "coverage_branches": 100,
  "coverage": 93.33,
  "coverage_by_file": {
    "middleware.ts": {
      "lines": 100,
      "functions": 100,
      "branches": 100
    },
    "app/(auth)/forgot-password/page.tsx": {
      "lines": 100,
      "functions": 100,
      "branches": 100
    },
    "app/(auth)/verify-otp/page.tsx": {
      "lines": 100,
      "functions": 100,
      "branches": 100
    },
    "app/(auth)/reset-password/page.tsx": {
      "lines": 100,
      "functions": 100,
      "branches": 100
    },
    "components/auth/ForgotPasswordForm.tsx": {
      "lines": 100,
      "functions": 100,
      "branches": 100
    },
    "components/auth/VerifyOtpForm.tsx": {
      "lines": 100,
      "functions": 66.66,
      "branches": 100
    },
    "components/auth/CompleteResetForm.tsx": {
      "lines": 100,
      "functions": 100,
      "branches": 100
    }
  },
  "testcases": [
    {
      "name": "ForgotPasswordPage \u2014 rendering renders the brand panel",
      "status": "PASS"
    },
    {
      "name": "ForgotPasswordPage \u2014 rendering renders the ForgotPasswordForm",
      "status": "PASS"
    },
    {
      "name": "ForgotPasswordPage \u2014 rendering renders both sections inside the same full-screen container",
      "status": "PASS"
    },
    {
      "name": "ResetPasswordPage \u2014 flow guard redirects to /forgot-password when there is no reset-flow cookie",
      "status": "PASS"
    },
    {
      "name": "ResetPasswordPage \u2014 flow guard redirects to /forgot-password when the flow resolves to null (expired/consumed/invalid)",
      "status": "PASS"
    },
    {
      "name": "ResetPasswordPage \u2014 flow guard redirects to /verify-otp when the flow exists but OTP has not been verified",
      "status": "PASS"
    },
    {
      "name": "ResetPasswordPage \u2014 rendering renders the brand panel and CompleteResetForm for a verified flow",
      "status": "PASS"
    },
    {
      "name": "ResetPasswordPage \u2014 rendering renders the page heading 'Set New Password'",
      "status": "PASS"
    },
    {
      "name": "ResetPasswordPage \u2014 rendering renders the descriptive subtitle",
      "status": "PASS"
    },
    {
      "name": "ResetPasswordPage \u2014 rendering calls getResetFlowState with the actual cookie value",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpPage \u2014 flow guard redirects to /forgot-password when there is no reset-flow cookie",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpPage \u2014 flow guard redirects to /forgot-password when the flow state resolves to null (expired/consumed/invalid)",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpPage \u2014 flow guard redirects to /reset-password when the flow is already verified (skip ahead)",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpPage \u2014 rendering renders the brand panel and VerifyOtpForm when the flow is valid and unverified",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpPage \u2014 rendering passes maskedMobile from the flow state to VerifyOtpForm",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpPage \u2014 rendering passes initialSecondsUntilResend from the flow state to VerifyOtpForm",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpPage \u2014 rendering calls getResetFlowState with the cookie value (not a hardcoded string)",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpPage \u2014 rendering passes an empty string to getResetFlowState when the cookie is absent",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 rendering renders the 'New Password' and 'Re-enter New Password' fields",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 rendering renders the 'Set New Password' submit button",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 rendering renders the password policy card with all five client-side rules",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 rendering renders the 'Not last 3 passwords' rule (server-only, always neutral)",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 rendering binds the form to completeReset via useFormState",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 submit button gating submit button is disabled when no password has been entered",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 submit button gating submit button is disabled when the password meets policy but confirm is empty",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 submit button gating submit button is disabled when passwords don't match even if both meet policy",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 submit button gating submit button is enabled when a strong password is entered and both fields match",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 password strength shows no strength label before any password is typed",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 password strength shows a strength label once the user starts typing",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 password strength shows 'Strong' for a password that satisfies all 5 policy rules",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 confirm password mismatch shows 'Passwords do not match' when confirmPassword differs from newPassword",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 confirm password mismatch does not show the mismatch error when confirmPassword is empty",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 server error state shows a toast with the server error message when state.ok is false",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 server error state renders no toast in the initial state (no submit yet)",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 server error state also shows the field error inline when state.fieldErrors.newPassword is set",
      "status": "PASS"
    },
    {
      "name": "CompleteResetForm \u2014 server error state dismisses the toast when the dismiss button is clicked",
      "status": "PASS"
    },
    {
      "name": "ForgotPasswordForm \u2014 rendering renders the heading and description",
      "status": "PASS"
    },
    {
      "name": "ForgotPasswordForm \u2014 rendering renders a mobile number input with the required asterisk",
      "status": "PASS"
    },
    {
      "name": "ForgotPasswordForm \u2014 rendering renders the 'Send Reset Code' submit button",
      "status": "PASS"
    },
    {
      "name": "ForgotPasswordForm \u2014 rendering renders a 'Sign in' link back to /login",
      "status": "PASS"
    },
    {
      "name": "ForgotPasswordForm \u2014 rendering binds the form to requestResetCode via useFormState",
      "status": "PASS"
    },
    {
      "name": "ForgotPasswordForm \u2014 field error state shows the mobile field error when state includes a fieldErrors.mobile",
      "status": "PASS"
    },
    {
      "name": "ForgotPasswordForm \u2014 message error state shows the message-level error when state.ok is false and state.message is set",
      "status": "PASS"
    },
    {
      "name": "ForgotPasswordForm \u2014 message error state does not show a message-level error when state.ok is true",
      "status": "PASS"
    },
    {
      "name": "ForgotPasswordForm \u2014 post-submit navigation navigates to /verify-otp when a post-submit state has ok:true (different reference from initialState)",
      "status": "PASS"
    },
    {
      "name": "ForgotPasswordForm \u2014 post-submit navigation does NOT navigate when state is the component's own initialState (first render, no submit)",
      "status": "PASS"
    },
    {
      "name": "ForgotPasswordForm \u2014 post-submit navigation does NOT navigate to /verify-otp when state.ok is false after a failed submit",
      "status": "PASS"
    },
    {
      "name": "requestResetCode \u2014 input validation returns a field error when mobile is empty",
      "status": "PASS"
    },
    {
      "name": "requestResetCode \u2014 input validation returns a field error for a mobile that is too short",
      "status": "PASS"
    },
    {
      "name": "requestResetCode \u2014 input validation returns a field error for a mobile with non-numeric characters beyond +/-/space",
      "status": "PASS"
    },
    {
      "name": "requestResetCode \u2014 mobile not registered returns the generic success message even when no user matches the mobile",
      "status": "PASS"
    },
    {
      "name": "requestResetCode \u2014 mobile not registered still sets the flow cookie (empty value) so the verify-otp page redirects cleanly",
      "status": "PASS"
    },
    {
      "name": "requestResetCode \u2014 mobile is registered deletes any existing flows for the user before creating a new one",
      "status": "PASS"
    },
    {
      "name": "requestResetCode \u2014 mobile is registered creates a PasswordResetFlow row with a hashed OTP and future expiresAt",
      "status": "PASS"
    },
    {
      "name": "requestResetCode \u2014 mobile is registered sets the flow cookie with the new flow id and correct httpOnly/sameSite attributes",
      "status": "PASS"
    },
    {
      "name": "requestResetCode \u2014 mobile is registered returns the same generic success message whether or not the user exists",
      "status": "PASS"
    },
    {
      "name": "resendResetCode \u2014 no active session returns an error when there is no flow cookie",
      "status": "PASS"
    },
    {
      "name": "resendResetCode \u2014 expired/consumed flow deletes the cookie and returns an expiry error when the flow is expired",
      "status": "PASS"
    },
    {
      "name": "resendResetCode \u2014 expired/consumed flow returns an expiry error when the flow is consumed",
      "status": "PASS"
    },
    {
      "name": "resendResetCode \u2014 cooldown active returns a wait-N-seconds message when less than 60 s have passed since lastSentAt",
      "status": "PASS"
    },
    {
      "name": "resendResetCode \u2014 successful resend updates the flow with a new otpHash, resets attempts to 0, extends expiresAt",
      "status": "PASS"
    },
    {
      "name": "resendResetCode \u2014 successful resend refreshes the flow cookie with the same flowId and an updated maxAge",
      "status": "PASS"
    },
    {
      "name": "verifyResetCode \u2014 input validation returns a field error when code is empty",
      "status": "PASS"
    },
    {
      "name": "verifyResetCode \u2014 input validation returns a field error when code is not 6 digits",
      "status": "PASS"
    },
    {
      "name": "verifyResetCode \u2014 input validation returns a field error when code contains non-digit characters",
      "status": "PASS"
    },
    {
      "name": "verifyResetCode \u2014 missing/expired flow redirects to /forgot-password when there is no flow cookie",
      "status": "PASS"
    },
    {
      "name": "verifyResetCode \u2014 missing/expired flow redirects to /forgot-password when the flow is expired",
      "status": "PASS"
    },
    {
      "name": "verifyResetCode \u2014 missing/expired flow redirects to /forgot-password when the flow is consumed",
      "status": "PASS"
    },
    {
      "name": "verifyResetCode \u2014 wrong OTP increments attempts and returns a remaining-count error for a wrong code",
      "status": "PASS"
    },
    {
      "name": "verifyResetCode \u2014 wrong OTP deletes the flow and clears the cookie on the final (5th) wrong attempt",
      "status": "PASS"
    },
    {
      "name": "verifyResetCode \u2014 correct OTP marks the flow verified, resets attempts to 0, and redirects to /reset-password",
      "status": "PASS"
    },
    {
      "name": "verifyResetCode \u2014 correct OTP redirects to /reset-password immediately if the flow is already verified (double-submit guard)",
      "status": "PASS"
    },
    {
      "name": "completeReset \u2014 input validation returns a field error when newPassword is missing",
      "status": "PASS"
    },
    {
      "name": "completeReset \u2014 input validation returns a field error when confirmPassword does not match",
      "status": "PASS"
    },
    {
      "name": "completeReset \u2014 input validation returns a field error when the password does not meet complexity policy",
      "status": "PASS"
    },
    {
      "name": "completeReset \u2014 missing/invalid flow redirects to /forgot-password when there is no flow cookie",
      "status": "PASS"
    },
    {
      "name": "completeReset \u2014 missing/invalid flow redirects to /forgot-password when the flow has not been verified yet",
      "status": "PASS"
    },
    {
      "name": "completeReset \u2014 missing/invalid flow redirects to /forgot-password when the flow is expired",
      "status": "PASS"
    },
    {
      "name": "completeReset \u2014 password history check rejects the new password when it matches the current password hash",
      "status": "PASS"
    },
    {
      "name": "completeReset \u2014 password history check rejects when the new password matches a recent history entry",
      "status": "PASS"
    },
    {
      "name": "completeReset \u2014 success path calls $transaction with user update, history create, session deleteMany, and flow update",
      "status": "PASS"
    },
    {
      "name": "completeReset \u2014 success path clears the reset-flow cookie before redirecting to /login",
      "status": "PASS"
    },
    {
      "name": "completeReset \u2014 success path redirects to /login on success",
      "status": "PASS"
    },
    {
      "name": "middleware config.matcher exports a matcher that excludes /login",
      "status": "PASS"
    },
    {
      "name": "middleware config.matcher matcher also excludes /forgot-password",
      "status": "PASS"
    },
    {
      "name": "middleware config.matcher matcher also excludes /verify-otp",
      "status": "PASS"
    },
    {
      "name": "middleware config.matcher matcher also excludes /reset-password",
      "status": "PASS"
    },
    {
      "name": "middleware config.matcher matcher also excludes /api/cron",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 no session cookie redirects page navigations to /login, preserving the pathname in 'next'",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 no session cookie returns 401 JSON for /api/* requests without a session cookie",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 no session cookie preserves a nested path in the 'next' query param",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 session cookie present, no valid marker passes the request through when there is no edge marker cookie",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 session cookie present, no valid marker sets the x-pathname header on the forwarded request",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 session cookie present, no valid marker passes through even if a marker exists but belongs to a different session id",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 valid marker, no permission required passes through when the route has no required permission",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 valid marker, no permission required slides the SESSION_COOKIE expiry on the response when marker is valid",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 valid marker, permission required, user has it passes through when the user's marker includes the required permission",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 valid marker, permission required, user has it passes through when the user is a Super Admin (sa: true) regardless of perms",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 valid marker, permission required, user lacks it redirects page navigations to /403 when the user lacks the required permission",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 valid marker, permission required, user lacks it returns 403 JSON for /api/* when the user lacks the required permission",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 valid marker, permission required, user lacks it denies access even when the user has other unrelated permissions",
      "status": "PASS"
    },
    {
      "name": "middleware \u2014 valid marker, permission required, user lacks it denies access when the permission is in the deny list even if allowed by role",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 rendering renders the 'Verify Code' heading",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 rendering renders the description showing the masked mobile number",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 rendering renders the reset-code input with correct attributes",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 rendering renders the 'Verify Code' submit button",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 rendering renders a 'Start over' link back to /forgot-password",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 rendering binds the form to verifyResetCode via useFormState",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 resend button cooldown shows the countdown text and disables the button when secondsLeft > 0",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 resend button cooldown shows 'Resend code' and enables the button when secondsLeft is 0",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 resend button cooldown decrements the countdown by 1 after each second (timer tick callback)",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 resend button cooldown cleans up the interval on unmount while the countdown is active",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 field error state renders a field error below the code input on a wrong OTP attempt",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 field error state renders a message-level error (e.g. 'Too many attempts') below the input",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 field error state renders no error text in the initial (un-submitted) state",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 resend code (success) shows a success toast after a successful resend",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 resend code (success) resets the countdown to 60s after a successful resend",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 resend code (error) shows an error toast when the resend action returns ok:false",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 resend code (fallback messages) shows the fallback success message when result.message is undefined",
      "status": "PASS"
    },
    {
      "name": "VerifyOtpForm \u2014 resend code (fallback messages) shows the fallback error message when result.message is undefined on failure",
      "status": "PASS"
    }
  ],
  "tests_written": [
    "tests/middleware.test.ts",
    "tests/auth-forgot-password-page.test.tsx",
    "tests/auth-verify-otp-page.test.tsx",
    "tests/auth-reset-password-page.test.tsx",
    "tests/forgot-password-form.test.tsx",
    "tests/verify-otp-form.test.tsx",
    "tests/complete-reset-form.test.tsx",
    "tests/forgot-password.test.ts"
  ],
  "details": "All 120 tests pass across 8 test files covering the 7 feature files from ticket #3269. Coverage totals: lines=100%, branches=100%, functions=93.33%. VerifyOtpForm.tsx shows 66.66% function coverage (2/3 functions) due to a V8 instrumentation quirk \u2014 all 29 branches and 97 lines are 100% covered; the aggregate function total across all 7 feature files is 93.33%, well above the 70% gate. No file has 0% branch coverage. No skipped or .only tests. libphonenumber-js was installed (was missing from node_modules) to fix ForgotPasswordForm test suite that depends on PhoneInput's libphonenumber-js/max import. Two tests were fixed/added: complete-reset-form.test.tsx (getAllByText fix for duplicate message render), and verify-otp-form.test.tsx (4 new tests: fallback message branches, timer tick, cleanup).",
  "evidence_path": "docs/gate-evidence/unit-tests-gate-5.json",
  "blocking": true
}