fix(e2e): submit login once with rate-limit backoff, surface the hub banner #445

Merged
nrupard merged 1 commit from fix/PMS-654-e2e-login-backoff into main 2026-07-13 18:27:09 +02:00
Owner

What

Fixes the recurring E2E failure where the bunyip hub login "re-renders with empty fields" (reported as PMS-592) and every spec fails at global setup. Root cause (PMS-654): the login helper's own retry burst self-trips the hub's 5-request/60s per-email login rate limit, and it swallows the hub's error banner so CI cannot see why.

Root cause (evidence-based)

  • submitCredentials retried the fill+submit up to 4x on ONE page load with no backoff => up to 4 POST /login in seconds; with global.setup.ts retries: 1, up to 8. The hub rate-limits login at 5/60s per email (bunyip RateLimitConfig::LOGIN, checked before credential verification), and the suite shares one account, so the burst blows the window. The hub then rejects every subsequent POST, which the loop misread as the chromium re-render race and retried harder.
  • Verified live 2026-07-13: staging OP/hub is frozen at commit 0226b8c (build 2026-07-08), so this is NOT a hub regression. The login markup, the email/password/"Sign In" selectors, redirect handling, and CSRF/Origin behavior are all healthy (a same-origin browser POST passes; a no-Origin curl gets the expected 403).
  • E2E has been red on main and every PR since the 2026-07-09/10 merge burst raised login frequency on the shared account.

Fix

Ports bunyip's own harness pattern into e2e/lib/login.ts:

  • submitCredentialsOnce: fill via DOM-set value, submit ONCE, then race the redirect to /login/2fa against the .text-destructive error banner so a rejection is seen in about a second. Returns advanced, the banner text, or null (no banner and no navigation = a stalled POST / PMS-148, or a fill lost to a re-render).
  • loginViaSpa: retry only after waiting out the interval the hub names on a rate-limit rejection (rateLimitBackoffMs), or with a fresh goto('/login') on an unclear submit; throw immediately with the hub's banner text on any other rejection. At most one login POST per attempt, spaced under the 5/min cap.
  • 2FA and /oauth2/consent handling unchanged.

Effect

  • Removes the self-inflicted rate-limit amplification.
  • The next failure (if any) names the actual cause in the log: a rate-limit message (backs off and passes), "The email or password you entered is incorrect." (=> the E2E account secret needs rotating, an ops action), or "no error banner and no navigation" (=> PMS-148 client stall). It is correct under all three.

Testing

  • bun x tsc --noEmit strict: clean.
  • Runtime behavior is exercised by the E2E job against staging (this suite); watch this PR's E2E run for the now-surfaced banner.

Relates PMS-592 (superseded diagnosis), PMS-148.

#PMS-654

## What Fixes the recurring E2E failure where the bunyip hub login "re-renders with empty fields" (reported as PMS-592) and every spec fails at global setup. Root cause (PMS-654): the login helper's own retry burst self-trips the hub's 5-request/60s per-email login rate limit, and it swallows the hub's error banner so CI cannot see why. ## Root cause (evidence-based) - `submitCredentials` retried the fill+submit up to 4x on ONE page load with no backoff => up to 4 `POST /login` in seconds; with `global.setup.ts` `retries: 1`, up to 8. The hub rate-limits login at 5/60s per email (bunyip `RateLimitConfig::LOGIN`, checked before credential verification), and the suite shares one account, so the burst blows the window. The hub then rejects every subsequent POST, which the loop misread as the chromium re-render race and retried harder. - Verified live 2026-07-13: staging OP/hub is frozen at commit `0226b8c` (build 2026-07-08), so this is NOT a hub regression. The login markup, the email/password/"Sign In" selectors, redirect handling, and CSRF/Origin behavior are all healthy (a same-origin browser POST passes; a no-`Origin` curl gets the expected 403). - E2E has been red on `main` and every PR since the 2026-07-09/10 merge burst raised login frequency on the shared account. ## Fix Ports bunyip's own harness pattern into `e2e/lib/login.ts`: - `submitCredentialsOnce`: fill via DOM-set value, submit ONCE, then race the redirect to `/login/2fa` against the `.text-destructive` error banner so a rejection is seen in about a second. Returns `advanced`, the banner text, or `null` (no banner and no navigation = a stalled POST / PMS-148, or a fill lost to a re-render). - `loginViaSpa`: retry only after waiting out the interval the hub names on a rate-limit rejection (`rateLimitBackoffMs`), or with a fresh `goto('/login')` on an unclear submit; throw immediately with the hub's banner text on any other rejection. At most one login POST per attempt, spaced under the 5/min cap. - 2FA and `/oauth2/consent` handling unchanged. ## Effect - Removes the self-inflicted rate-limit amplification. - The next failure (if any) names the actual cause in the log: a rate-limit message (backs off and passes), "The email or password you entered is incorrect." (=> the E2E account secret needs rotating, an ops action), or "no error banner and no navigation" (=> PMS-148 client stall). It is correct under all three. ## Testing - `bun x tsc --noEmit` strict: clean. - Runtime behavior is exercised by the E2E job against staging (this suite); watch this PR's E2E run for the now-surfaced banner. Relates PMS-592 (superseded diagnosis), PMS-148. #PMS-654
fix(e2e): submit login once with rate-limit backoff, surface the hub banner
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 1m28s
Integration / integration tests (pull_request) Successful in 5m13s
E2E / Playwright against staging (pull_request) Successful in 1m2s
Create release / Gate (release-branch merges only) (pull_request) Successful in 1s
Create release / Create release from merged PR (pull_request) Has been skipped
4f62cc002d
The E2E login helper retried the fill+submit up to four times on ONE page load with no backoff, firing four POST /login requests in seconds. The bunyip hub rate-limits login at 5 requests / 60s per email (checked before credential verification), and the suite shares a single account, so the setup's own retry burst exhausted the window; the hub then re-rendered /login with a rate-limit banner for every subsequent POST, which the loop misread as the old chromium form-re-render race (PMS-592) and retried harder. Because loginViaSpa never read the hub error banner, CI could not tell a 429 rate-limit from bad credentials or a stalled POST (PMS-148). Verified 2026-07-13: staging OP/hub is frozen at commit 0226b8c (build 2026-07-08), so this is not a hub regression and the login markup, selectors, and CSRF/Origin behavior are all healthy; E2E has been red on main and every PR since the 2026-07-09/10 merge burst raised login frequency.

Port bunyip's own harness pattern: submit ONCE per attempt (submitCredentialsOnce), race the redirect to /login/2fa against the .text-destructive error banner so a rejection is seen in about a second, and let loginViaSpa retry only after waiting out the interval the hub names on a rate-limit rejection (rateLimitBackoffMs) or on an unclear submit (no banner and no navigation, retried with a fresh goto rather than a same-page re-POST). Any other rejection now throws immediately with the hub's rendered banner text, so bad credentials or a stalled POST are self-diagnosing in the CI log. Net: at most one login POST per attempt, spaced under the 5/min cap, and no swallowed reason. Strict tsc --noEmit clean.

#PMS-654
nrupard deleted branch fix/PMS-654-e2e-login-backoff 2026-07-13 18:27:10 +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/mokosh-server!445
No description provided.