TimeCurveProtocolWarbowRefreshSection: missing Vitest coverage for permissionless write path #175

Closed
opened 2026-05-07 02:02:51 +00:00 by Brouie · 6 comments
Brouie commented 2026-05-07 02:02:51 +00:00 (Migrated from gitlab.com)

Severity: P4 — test coverage

split out of #160 follow-up per @PlasticDigits — "all of these findings need to have issues opened for them".

context

TimeCurveProtocolWarbowRefreshSection.tsx (frontend/src/pages/timecurve/) is a permissionless write path covering candidate-load + on-chain podium refresh. landed via a648283 (#160 / EcoStrategy M-04) plus fff7946 (#170 P3 polish) plus #170 follow-up adjustments.

no Vitest test file exists for this component at HEAD 284c15c:

$ ls /srv/qa/repos/yieldomega/frontend/src/pages/timecurve/ | grep -iE "warbow.*refresh"
TimeCurveProtocolWarbowRefreshSection.tsx

sister flows in the same directory have tests (TimeCurveSimplePodiumSection.test.tsx, TimeCurveStakeAtLaunchSection.test.tsx, TimeCurveSubnav.test.tsx).

why it matters

permissionless write path with non-trivial state machine — fresh-load reset, pagination accumulation, distinct_sql_cap_hit OR-aggregation across pages, dedup vs podium hints + connected wallet, sale-ended early return, wrong-chain barrier, error UX. exactly the kind of surface where regressions ride in via unrelated refactors.

related #160 follow-up findings (separate tickets): #173 cosmetic double-call, #174 silent truncation, #172 CRITICAL gas-blowup design. each of these would benefit from regression coverage that does not exist today.

suggested coverage

minimum useful set:

  • loadFromIndexer happy path: paginates across next_offset, accumulates candidates, sets extras (apiTotal, podiumHintCount, distinct_sql_cap_hit OR-folded across pages)
  • loadFromIndexer indexer-returns-null branch (line 102): clears candidates + extras, sets loadErr
  • loadFromIndexer exception branch (line 131): friendlyRevertFromUnknown(e), candidates + extras cleared
  • loadFromIndexer malformed chunk.candidates (non-array): does not throw, ends with empty load + appropriate error state (covers #174 + the Array.isArray hardening if it lands)
  • loadFromIndexer reset-on-fresh-load: prior successful state cleared at top of new load (relevant when #160 finding 2 / "P3 reset-on-reload" lands)
  • runRefresh sale-ended branch: early return with #149 message
  • runRefresh chain-mismatch branch: early return with helper message, single invocation of chainMismatchWriteMessage (covers #173 once that fix lands)
  • runRefresh no-candidates branch: prompt to load first
  • runRefresh happy path: warBowRefreshCandidateAddresses calldata, writeContractAsync invocation, refetch hooks called on success

acceptance criteria

  • new test file frontend/src/pages/timecurve/TimeCurveProtocolWarbowRefreshSection.test.tsx
  • minimum cases listed above covered (or fewer, with rationale)
  • cd frontend && npm test -- --run passes including new file
  • no flake on repeat invocations

origin

split from #160 follow-up notes ("P4 — no Vitest unit/component test ships with the new section"). filing now per @PlasticDigits.

sister flow useTimeCurveArenaModel.tsx:2615-2641 is similarly untested per the original note. that is a separate scope and not covered by this ticket.

cc @PlasticDigits

**Severity:** P4 — test coverage split out of #160 follow-up per @PlasticDigits — "all of these findings need to have issues opened for them". ## context `TimeCurveProtocolWarbowRefreshSection.tsx` (`frontend/src/pages/timecurve/`) is a permissionless write path covering candidate-load + on-chain podium refresh. landed via `a648283` (#160 / EcoStrategy M-04) plus `fff7946` (#170 P3 polish) plus #170 follow-up adjustments. no Vitest test file exists for this component at HEAD `284c15c`: ``` $ ls /srv/qa/repos/yieldomega/frontend/src/pages/timecurve/ | grep -iE "warbow.*refresh" TimeCurveProtocolWarbowRefreshSection.tsx ``` sister flows in the same directory have tests (`TimeCurveSimplePodiumSection.test.tsx`, `TimeCurveStakeAtLaunchSection.test.tsx`, `TimeCurveSubnav.test.tsx`). ## why it matters permissionless write path with non-trivial state machine — fresh-load reset, pagination accumulation, `distinct_sql_cap_hit` OR-aggregation across pages, dedup vs podium hints + connected wallet, sale-ended early return, wrong-chain barrier, error UX. exactly the kind of surface where regressions ride in via unrelated refactors. related #160 follow-up findings (separate tickets): #173 cosmetic double-call, #174 silent truncation, #172 CRITICAL gas-blowup design. each of these would benefit from regression coverage that does not exist today. ## suggested coverage minimum useful set: - `loadFromIndexer` happy path: paginates across `next_offset`, accumulates candidates, sets extras (apiTotal, podiumHintCount, distinct_sql_cap_hit OR-folded across pages) - `loadFromIndexer` indexer-returns-null branch (line 102): clears candidates + extras, sets loadErr - `loadFromIndexer` exception branch (line 131): `friendlyRevertFromUnknown(e)`, candidates + extras cleared - `loadFromIndexer` malformed `chunk.candidates` (non-array): does not throw, ends with empty load + appropriate error state (covers #174 + the Array.isArray hardening if it lands) - `loadFromIndexer` reset-on-fresh-load: prior successful state cleared at top of new load (relevant when #160 finding 2 / "P3 reset-on-reload" lands) - `runRefresh` sale-ended branch: early return with #149 message - `runRefresh` chain-mismatch branch: early return with helper message, single invocation of `chainMismatchWriteMessage` (covers #173 once that fix lands) - `runRefresh` no-candidates branch: prompt to load first - `runRefresh` happy path: `warBowRefreshCandidateAddresses` calldata, `writeContractAsync` invocation, refetch hooks called on success ## acceptance criteria - [ ] new test file `frontend/src/pages/timecurve/TimeCurveProtocolWarbowRefreshSection.test.tsx` - [ ] minimum cases listed above covered (or fewer, with rationale) - [ ] `cd frontend && npm test -- --run` passes including new file - [ ] no flake on repeat invocations ## origin split from #160 follow-up notes ("P4 — no Vitest unit/component test ships with the new section"). filing now per @PlasticDigits. sister flow `useTimeCurveArenaModel.tsx:2615-2641` is similarly untested per the original note. that is a separate scope and not covered by this ticket. cc @PlasticDigits
Brouie commented 2026-05-07 02:06:00 +00:00 (Migrated from gitlab.com)

mentioned in issue #176

mentioned in issue #176
Brouie commented 2026-05-07 02:09:58 +00:00 (Migrated from gitlab.com)

mentioned in issue #160

mentioned in issue #160
Brouie commented 2026-05-07 03:19:40 +00:00 (Migrated from gitlab.com)

mentioned in merge request !377

mentioned in merge request !377
Brouie commented 2026-05-07 04:13:13 +00:00 (Migrated from gitlab.com)

mentioned in merge request !378

mentioned in merge request !378
Brouie commented 2026-05-07 23:32:21 +00:00 (Migrated from gitlab.com)

@PlasticDigits — did a deep-dive on the test scope before writing any code. surfaced one finding worth surfacing before i commit to a path.

the finding

there is no in-repo precedent for testing a wagmi-coupled component. every existing .test.tsx uses renderToStaticMarkup (server-side, no hooks). zero hits for vi.mock("wagmi", @testing-library/react, or @vitest-environment per-file switches across the whole frontend.

closest analogue is referralRegisterWalletSession.test.ts:16-22 which mocks ONE function from wagmi/actions. TimeCurveProtocolWarbowRefreshSection would need the full wagmi hook surface (useAccount, useChainId, useReadContracts, useWriteContract) plus wagmi/actions.waitForTransactionReceipt plus the writeContractWithGasBuffer helper plus a per-file env switch to happy-dom or jsdom to fire button clicks. that's net-new test infrastructure for this codebase.

branch inventory at HEAD efb6e0b

loadFromIndexer (lines 91-141) — 9 distinct branches: happy single-page, happy multi-page pagination + apiTotal/podiumHintCount captured on first chunk + capHit OR-aggregated, indexer-returns-null, malformed candidates non-array, pagination guard cap at 50, throw-in-loop with friendlyRevertFromUnknown, finally-clears-loading, undefined-vs-null next_offset both break, empty-pages-after-success.

runRefresh (lines 143-191) — 8 distinct branches: saleEnded short-circuit (#149), chain-mismatch short-circuit, address-undefined short-circuit, wbReads-not-loaded short-circuit, no-candidates-loaded short-circuit, happy submit with calldata + softCap 12_000_000n + onEstimateRevert "rethrow" + waitForTransactionReceipt + dual refetch, write rejection with friendlyRevertFromUnknown, useEffect-clears-txErr-on-chain-correction.

plus 6 render-side branches (warbowPodiumFinalized fallback, idxExtras conditional rows, saleEnded placeholder, refresh button disabled cascade, load button label flip, error StatusMessage variants).

20 test cases mapped end-to-end. 7-8 reachable today via renderToStaticMarkup. the rest depend on testing-library + dom env, OR a refactor.

three paths

(A) refactor. extract loadFromIndexer and runRefresh into pure dependency-injected helpers in a sibling .ts file (deps: fetchTimecurveWarbowRefreshCandidates, writeContractWithGasBuffer, waitForTransactionReceipt, the friendly-error helper, etc.). component file becomes thinner — just hook glue + render. unit-test the helpers like writeContractWithGasBuffer.test.ts does. consistent with existing patterns. no new dev dependencies. larger PR diff, but adjacent code stays clean.

(B) add testing infrastructure. install @testing-library/react, set per-file env via // @vitest-environment happy-dom, mock the full wagmi hook surface in one block, write component-integration tests. smaller diff, but introduces a new testing pattern that diverges from the rest of the repo. one new dev dependency.

(C) defer. park #175 until either path A or B comes up naturally. ticket stays open as a known coverage gap. P4 severity supports this.

my read

(A) is the cleanest fit — same idiom as writeContractWithGasBuffer which we just landed in !378. extracted helpers are reusable for future work and trivial to mock at the boundary. (B) introduces test-shape divergence in the codebase. (C) is fine if you want to push #175 down the priority list, but worth flagging that the ticket as filed assumed (B) shape.

happy to ship (A) — would land as a refactor MR (component split + helpers + ~12-15 helper unit tests + ~7-8 render tests for the component shell). ETA reasonable from here. flag if you'd rather (B) or (C).

@PlasticDigits — did a deep-dive on the test scope before writing any code. surfaced one finding worth surfacing before i commit to a path. ## the finding there is no in-repo precedent for testing a wagmi-coupled component. every existing `.test.tsx` uses `renderToStaticMarkup` (server-side, no hooks). zero hits for `vi.mock("wagmi"`, `@testing-library/react`, or `@vitest-environment` per-file switches across the whole frontend. closest analogue is `referralRegisterWalletSession.test.ts:16-22` which mocks ONE function from `wagmi/actions`. `TimeCurveProtocolWarbowRefreshSection` would need the full wagmi hook surface (`useAccount`, `useChainId`, `useReadContracts`, `useWriteContract`) plus `wagmi/actions.waitForTransactionReceipt` plus the `writeContractWithGasBuffer` helper plus a per-file env switch to `happy-dom` or `jsdom` to fire button clicks. that's net-new test infrastructure for this codebase. ## branch inventory at HEAD efb6e0b `loadFromIndexer` (lines 91-141) — 9 distinct branches: happy single-page, happy multi-page pagination + apiTotal/podiumHintCount captured on first chunk + capHit OR-aggregated, indexer-returns-null, malformed candidates non-array, pagination guard cap at 50, throw-in-loop with friendlyRevertFromUnknown, finally-clears-loading, undefined-vs-null next_offset both break, empty-pages-after-success. `runRefresh` (lines 143-191) — 8 distinct branches: saleEnded short-circuit (#149), chain-mismatch short-circuit, address-undefined short-circuit, wbReads-not-loaded short-circuit, no-candidates-loaded short-circuit, happy submit with calldata + softCap 12_000_000n + onEstimateRevert "rethrow" + waitForTransactionReceipt + dual refetch, write rejection with friendlyRevertFromUnknown, useEffect-clears-txErr-on-chain-correction. plus 6 render-side branches (warbowPodiumFinalized fallback, idxExtras conditional rows, saleEnded placeholder, refresh button disabled cascade, load button label flip, error StatusMessage variants). 20 test cases mapped end-to-end. 7-8 reachable today via `renderToStaticMarkup`. the rest depend on testing-library + dom env, OR a refactor. ## three paths (A) **refactor.** extract `loadFromIndexer` and `runRefresh` into pure dependency-injected helpers in a sibling `.ts` file (deps: `fetchTimecurveWarbowRefreshCandidates`, `writeContractWithGasBuffer`, `waitForTransactionReceipt`, the friendly-error helper, etc.). component file becomes thinner — just hook glue + render. unit-test the helpers like `writeContractWithGasBuffer.test.ts` does. consistent with existing patterns. no new dev dependencies. larger PR diff, but adjacent code stays clean. (B) **add testing infrastructure.** install `@testing-library/react`, set per-file env via `// @vitest-environment happy-dom`, mock the full wagmi hook surface in one block, write component-integration tests. smaller diff, but introduces a new testing pattern that diverges from the rest of the repo. one new dev dependency. (C) **defer.** park #175 until either path A or B comes up naturally. ticket stays open as a known coverage gap. P4 severity supports this. ## my read (A) is the cleanest fit — same idiom as `writeContractWithGasBuffer` which we just landed in !378. extracted helpers are reusable for future work and trivial to mock at the boundary. (B) introduces test-shape divergence in the codebase. (C) is fine if you want to push #175 down the priority list, but worth flagging that the ticket as filed assumed (B) shape. happy to ship (A) — would land as a refactor MR (component split + helpers + ~12-15 helper unit tests + ~7-8 render tests for the component shell). ETA reasonable from here. flag if you'd rather (B) or (C).
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-09 04:56:17 +00:00
PlasticDigits commented 2026-05-09 04:56:18 +00:00 (Migrated from gitlab.com)

Closing as we are redesigning the warbow podium to be set by trusted governance instead of onchain due to issues from warbow point decreases

Closing as we are redesigning the warbow podium to be set by trusted governance instead of onchain due to issues from warbow point decreases
Sign in to join this conversation.
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
code/yieldomega#175
No description provided.