fix(e2e): harden reset_progress journey — Eval + 5s budget (bookshelf-ydjo9) #1342
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "bd-bookshelf-ydjo9"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Root cause
journey_reset_progress_test.gohad twoEventuallyblocks (the kebab menu-open check, lines 99–104 and 115–120) that usedpage.MustEval()as the polling function with a 3-second budget.Two independent problems:
MustEvalinsideEventually:MustEvalpanics on a transient CDP error (e.g.context deadline exceededunder CI runner starvation). That panic propagates OUT of theEventuallycallback instead of being caught and retried — the spec fails with a panic rather than a timeout assertion failure.3s budget too tight: The refresh-cover sibling uses 5s for the identical menu-open check. Under runner starvation, 3s is not enough margin for the Chromium click+Stimulus DOM update round-trip.
The file was skipped by the #1331 eu18m sweep, which targeted
page.Element()calls INSIDEEventuallybodies. This file hadpage.MustEval()insideEventually— the same class of panic-on-error problem but a different call site.Fix
MustEval-inside-Eventuallycalls to the non-Mustpage.Evalvariant (returningfalseon error so the loop retries), matching the pattern already used by the "confirming the dialog" step in the same file.Test plan
go build -tags e2e ./e2e/browser/...— compiles cleanmake e2e-policy-check— passes (all Describes are Ordered journey containers)Closes bead bookshelf-ydjo9 on merge.
Code Review — bookshelf-ydjo9 / PR #1342
CI status:
success(verified via/commits/{sha}/statusrollup)Mergeable:
TruePhase 1 — Spec Compliance
Bead description: convert
MustEval-inside-Eventuallyto non-Mustpage.Eval+ error-check on 3 occurrences; raise two 3s budgets to 5s. The diff delivers exactly this — 3 conversions, two budget bumps — and nothing extra. Spec is fully satisfied.Phase 2 — Code Quality
MustEval → Eval conversion correctness
All 3 converted blocks follow the same pattern:
page.Evalreturns a non-nilerr; the lambda returnsfalseandEventuallyretries — correct.result.Value.Bool()propagates the JS return value — correct.Eventually(...).Should(BeTrue())still fails loudly with a Gomega timeout message if the budget is exhausted without the condition becoming true — the assertion is not weakened.errpath converts to a retry signal, not silence;Eventually's own deadline is the outer bound that surfaces a real failure.Budget bumps (3s → 5s)
Two menus-open Eventually blocks go 3s → 5s. The third (
.apd-overlayvisibility) was already 5s onorigin/mainand is unchanged. The bumps are explicitly justified by the comment citing the refresh-cover sibling (which uses 5s for the identical DOM check). These are secondary to the panic-elimination fix and do not mask a hang: the real fix is the non-panic retry path.Consistency with eu18m non-blocking pattern
The converted pattern is identical to the approach used in the broader E2E browser sweep. The sibling
journey_refresh_cover_test.gostill usesMustEvalinsideEventually(a pre-existing gap), but that is out of scope for this bead and is a pre-existing condition onorigin/main.Remaining MustEval-inside-Eventually in this file
There are none remaining in
journey_reset_progress_test.goafter this PR — all 3 occurrences are converted.Black-box package
File declares
package browser_test— correct.No product code touched — confirmed, single test file changed.
No golangci exclusions added — confirmed.
No findings.
REVIEW VERDICT: 0 blocker, 0 major, 0 minor
Security Review — PR #1342 (bookshelf-ydjo9)
Scope: single file,
e2e/browser/journey_reset_progress_test.go. No production code is touched.Findings
(1) Production-code impact
Zero. The diff is entirely confined to the e2e browser test file. No handler, service, SQL query, middleware, or template is modified. No new routes are registered and no authorization or business logic changes.
(2) MustEval → Eval error-swallowing and security-relevant assertion coverage
The change converts three
page.MustEval(...).Bool()calls insideEventuallyloops topage.Eval(...)withif err != nil { return false }, and extends the corresponding poll budgets from 3 s to 5 s.What the swallowed error represents: a CDP (Chrome DevTools Protocol) transport or protocol error — e.g. the tab crashed, the CDP session timed out, or the JS evaluation itself threw. On
MustEval, any such error immediately panics the spec. OnEvalwithreturn false, theEventuallyloop retries for up to 5 s before failing the spec normally. This is the standard flake-fix pattern for go-rod under CI runner starvation.Security-relevant assertions and whether they are weakened:
The three converted
Evalcalls check:menu.classList.contains("is-open")— asserts the kebab menu opened after a click.!overlay.hasAttribute("hidden")— asserts the AppDialog confirm modal appeared.document.body.innerText.includes("Reading progress has been reset")— asserts the post-reset flash message is visible.None of these are security assertions in themselves. The security-relevant property — that the DELETE request resets only the current user's progress and sessions — is established by the fixture setup in
BeforeAll(rows seeded foruser_id = 1) and is implicitly verified when the journey succeeds end-to-end: the test authenticates as that user viasetAuthCookies, the Stimulus controller fires a real fetch DELETE, and the server processes it under the stub user's session. The flash message assertion proves the server returned a success response that the controller acted on.The error-swallow path (
return false) causesEventuallyto keep polling until the budget is exhausted, then fail theItwith a clear Gomega timeout error. It cannot produce a false-positive pass:Eventually(...).Should(BeTrue())requires the function to returntrue, not merely to stop returning errors. A CDP error silently looping then timing out is a worse flake outcome thanMustEvalpanicking immediately, not a security bypass.There is no DB-level assertion after the reset confirming that only
user_id = 1rows were deleted and no cross-user rows were touched. However, that gap is pre-existing on main and is not introduced or worsened by this PR. The PR does not remove any existing assertion; it only changes the error-handling discipline on pre-existing DOM-state polling insideEventually.(3) Secrets / PII in fixtures
Fixtures insert
user_id = 1(the stub user), a synthetic book title ("Reset Progress Test Book"),epub_progress_percent = 0.42, and a reading session with no personal data fields. No tokens, passwords, real email addresses, or PII are present.Summary
All three review questions are nil risk. The change is a mechanical flake fix (MustEval → Eval + 3 s → 5 s poll budget) that does not alter, weaken, or remove any security assertion. The no-cross-user-DB-assertion gap is pre-existing and out of scope for this PR.
REVIEW VERDICT: 0 blocker, 0 major, 0 minor