fix(e2e-browser): per-op deadline reset kills shared-budget flake (bookshelf-rpr4) #892
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "bd-bookshelf-rpr4"
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?
Summary
Root cause (confirmed):
waitForStimulusControllerand multi-stepJustBeforeEach/Itblocks shared a single 60s absolute go-rod deadline set by onerefreshPageTimeoutcall. On the chart-heavy/library-statspage under CI load,MustNavigate+MustWaitStableconsumed nearly the full 60s budget, leavingwaitForStimulusController(andMustElementafter it) to start against a near-expired context — panicking with "context deadline exceeded". Proof: deadline reset at 00:49:21, panic at 00:50:21 (exactly 60s, run 5662/#885).Fix (durable):
waitForStimulusControllernow resets the page absolute deadline at its start viarefreshPageTimeoutand returns the updated*rod.Page. Every subsequent go-rod op after a controller wait therefore starts with a fresh 60s budget, regardless of how long the preceding navigate+wait chain took.Primary fix site:
journey_library_stats_test.goJustBeforeEach— the confirmed panic location.Systemic audit: all 19 call sites of
waitForStimulusControlleracross 11 files updated topage = waitForStimulusController(...). Additional multi-op chains hardened:openDedupModal(7-op chain inbrowser_seed_helpers_test.go) — both controller waits reset the deadlinejourney_a11y_keyboard_test.go— 4Itblocks navigating to/library-stats(chart-heavy) before a controller waitjourney_stats_distributions_test.go,journey_organize_modal_test.go,journey_book_file_replace_test.go,bookdrop_*.go(5 files)Test plan
go vet -tags e2e ./e2e/browser/passes (verified locally, no type errors from changed signature)make testpasses (verified locally)Closes bead bookshelf-rpr4 on merge.
Recompute Match Score — kebab open screenshot (recompute-match-score-kebab-open)
Workflow Detail page screenshot (wf-detail-older-execution)
Older completed ContinueAsNew epoch detail — execution ID and state visible, Cancel absent.
Root cause (bookshelf-rpr4): waitForStimulusController held shared page context and was called AFTER MustNavigate+MustWaitStable, leaving the click/Eval ops with an exhausted 60s budget on slow CI. Fix: - Revert waitForStimulusController to void (no return, no internal reset) so it never cancels the page context and invalidates captured elements. - Add an explicit `page = refreshPageTimeout(page)` before each blocking op that follows a heavy navigate+wait sequence: MustNavigate → [break chain] → refreshPageTimeout → MustWaitStable → refreshPageTimeout → waitForStimulusController → refreshPageTimeout → MustElement/MustClick - journey_library_stats_test.go JustBeforeEach: break MustNavigate(...).MustWaitStable() chain with an inter-op reset (the confirmed panic site: 62.559 s with MustWaitStable under full 60s budget). - journey_stats_distributions_test.go: remove the erroneous page= assignment; the page is pre-loaded so the 60s budget is ample. - All other callers (bookdrop_*, organize_modal, book_file_replace, axe, keyboard, openDedupModal): add refreshPageTimeout before the controller poll and drop the (now-illegal) assignment. Closes bead bookshelf-rpr4 on merge.Workflow Detail page screenshot (wf-detail-older-execution)
Older completed ContinueAsNew epoch detail — execution ID and state visible, Cancel absent.
Recompute Match Score — kebab open screenshot (recompute-match-score-kebab-open)
Code Review — PR #883 (bookshelf-ry66)
Diff reviewed: origin/main...origin/bd-bookshelf-ry66 (head
87e9ea8eb5)Files: internal/books/dto.go, internal/books/metadata_service.go, internal/books/metadata_candidates_test.go, static/js/controllers/metadata_fetch_controller.js, static/js/test/metadata_fetch_controller.test.js
Phase 0: DEMO Verification
No DEMO block in the bead completion comment. CI is confirmed green via Forgejo commit-status API (state: success) and PR is mergeable. Per the review task the CI green state serves as functional verification; findings below are diff-based.
Phase 1: Spec Compliance
age_rating 0 (All Ages) on the Go side - PASS.
age_rating 0 on the JS side - PASS.
Locks - PASS. SQL upsert in metadata.sql.go:120 uses IF(COALESCE(age_rating_locked, 0) = 0 AND COALESCE(all_fields_locked, 0) = 0, VALUES(age_rating), age_rating). Lock enforcement at DB level, unchanged.
Canonical validation - PASS. Service rejects non-canonical age_rating values (metadata_service.go:374-379). Go json.Unmarshal into *int32 rejects floats at decode time. IsCanonicalContentRating gate at line 382 unmodified.
Serialisation field names - PASS. JS sends age_rating (number) and content_rating (string), matching SaveMetadataRequest fields.
Scope vs PR 882 (7mzg) - PASS. This PR adds to fieldLabel/_editorFieldID/displayValue/_saveFromModal. It does NOT touch buildComicCompareRow, _comicEditorFieldID, comicFieldLabel, or comicFieldOrder. Non-conflicting sections; clean rebase expected.
Bulk enrich path - PASS. No changes to persistInTxWithComic or wfengine bulk enrichment activities. Bulk path writes AgeRating/ContentRating via metadataToUpsertParams unchanged.
Black-box tests - PASS. metadata_candidates_test.go declares package books_test. No unexported symbols referenced.
CSP / inline style= - PASS. No new style= attributes in the diff.
Phase 2: Code Quality Findings
[MINOR] static/js/test/metadata_fetch_controller.test.js — missing save-path test for age_rating=0
The JS test suite tests copy-and-save with age_rating 13 but never exercises the All Ages path (age_rating 0) through to the POST body. The code is correct (v !== "" passes for "0", Number("0") = 0), and coverage is maintained because the same branch handles both values. But age_rating 0 is the critical case motivating this fix; a future regression that special-cases 0 in the save path would go undetected. Suggested addition: open modal with METRON_CANDIDATE_ALL_AGES, copy age_rating, save, assert capturedBody.age_rating === 0 (number, not string, not absent).
[MINOR] static/js/controllers/metadata_fetch_controller.js:308-315 — compare row shows raw "0" not "All Ages" in fetched column
displayValue("age_rating", {age_rating: 0}) returns "0". The modal fetched column shows 0 while the main form select shows the label "All Ages". Not a correctness bug — the value copies and saves correctly — but potentially confusing to users. Consider mapping numeric buckets to labels inside displayValue when key === "age_rating".
REVIEW VERDICT: 0 blocker, 0 major, 2 minor
5e5962a454eb98a466edRecompute Match Score — kebab open screenshot (recompute-match-score-kebab-open)
Workflow Detail page screenshot (wf-detail-older-execution)
Older completed ContinueAsNew epoch detail — execution ID and state visible, Cancel absent.
eb98a466ed70a499fa58Recompute Match Score — kebab open screenshot (recompute-match-score-kebab-open)
Workflow Detail page screenshot (wf-detail-older-execution)
Older completed ContinueAsNew epoch detail — execution ID and state visible, Cancel absent.