fix(e2e): verify the disposable account before change-email (BUNYIP-150) #226

Merged
nrupard merged 1 commit from fix/bunyip-150-change-email-verify-first into main 2026-06-25 17:28:44 +02:00
Owner

What

Fix the last failing BUNYIP-150 e2e spec: account/change-email. After #225 (registration unthrottled + JMAP origin) magic-link and password-reset went green, but change-email's waitForLink timed out for the /settings/confirm-email link that never arrived.

Root cause

request_email_change (crates/bunyip-domain/src/services/auth.rs) branches on email_verified:

  • verified account -> generates a token and emails the /settings/confirm-email link.
  • unverified account -> changes the email IMMEDIATELY in a transaction, with NO email sent (Ok((old_email, None))).

A freshly registered disposable account is unverified, so the change took the immediate path and the spec waited forever for a link that was never sent. magic-link and password-reset pass because they do not depend on verification state.

Fix

The spec now verifies the disposable account first via the existing verify-email flow (over the same JMAP sink), so the change takes the verified, link-confirmed path it is meant to exercise:

  1. POST /v1/users/me/email/verify (no body) -> read the /settings/verify-email link from the sink -> POST /v1/users/me/email/verify/confirm { token }.
  2. Then POST /v1/users/me/email to a new subaddress -> read the /settings/confirm-email link -> confirm -> assert the new email.

Adds the two verify routes to lib/api.ts, EMAIL_VERIFY_RE to lib/mail-sink.ts, and a dev-docs note. As a bonus it now also exercises the otherwise-untested verify-email flow.

Verification

Pure e2e change against existing deployed endpoints (no app deploy needed). tsc --noEmit clean. Full green confirmed by the staging e2e run on this PR.

🤖 Generated with Claude Code

## What Fix the last failing BUNYIP-150 e2e spec: `account/change-email`. After #225 (registration unthrottled + JMAP origin) magic-link and password-reset went green, but change-email's `waitForLink` timed out for the `/settings/confirm-email` link that never arrived. ## Root cause `request_email_change` (`crates/bunyip-domain/src/services/auth.rs`) branches on `email_verified`: - **verified** account -> generates a token and emails the `/settings/confirm-email` link. - **unverified** account -> changes the email IMMEDIATELY in a transaction, with NO email sent (`Ok((old_email, None))`). A freshly registered disposable account is unverified, so the change took the immediate path and the spec waited forever for a link that was never sent. magic-link and password-reset pass because they do not depend on verification state. ## Fix The spec now verifies the disposable account first via the existing verify-email flow (over the same JMAP sink), so the change takes the verified, link-confirmed path it is meant to exercise: 1. `POST /v1/users/me/email/verify` (no body) -> read the `/settings/verify-email` link from the sink -> `POST /v1/users/me/email/verify/confirm { token }`. 2. Then `POST /v1/users/me/email` to a new subaddress -> read the `/settings/confirm-email` link -> confirm -> assert the new email. Adds the two verify routes to `lib/api.ts`, `EMAIL_VERIFY_RE` to `lib/mail-sink.ts`, and a dev-docs note. As a bonus it now also exercises the otherwise-untested verify-email flow. ## Verification Pure e2e change against existing deployed endpoints (no app deploy needed). `tsc --noEmit` clean. Full green confirmed by the staging e2e run on this PR. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(e2e): verify the disposable account before change-email (BUNYIP-150)
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 51s
Check / fmt + clippy + build + tests (pull_request) Successful in 4m28s
Create release / Create release from merged PR (pull_request) Has been skipped
c17176173d
The change-email spec failed on staging: waitForLink timed out for the /settings/confirm-email link that never arrived. Root cause: request_email_change (crates/bunyip-domain/src/services/auth.rs) changes an UNVERIFIED account's email IMMEDIATELY with no confirmation email; only a VERIFIED account gets the emailed token link. A freshly registered disposable account is unverified, so the change took the immediate path and the spec waited forever for a link that was never sent. This is why magic-link and password-reset (which do not depend on verification state) passed while change-email did not.

Fix: the spec now verifies the disposable account first via the verify-email flow (POST /v1/users/me/email/verify -> read the /settings/verify-email link from the same sink -> POST .../verify/confirm), so the subsequent change takes the verified, link-confirmed path it is meant to exercise. Adds the two verify routes to lib/api.ts, EMAIL_VERIFY_RE to lib/mail-sink.ts, and a dev-docs note. Pure e2e change against existing deployed endpoints (no app deploy needed).

tsc --noEmit clean.

#BUNYIP-150

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nrupard scheduled this pull request to auto merge when all checks succeed 2026-06-25 17:26:33 +02:00
nrupard deleted branch fix/bunyip-150-change-email-verify-first 2026-06-25 17:28:44 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!226
No description provided.