Skip to content

No test coverage exists for the boost allocation flow — write tests alongside implementing the missing BoostModal UI #92

Description

@prodbycorne

Problem

As described in the related issue about the missing Boost UI, SorobanService.setBoost and useSetBoost are fully implemented but have no test coverage anywhere in the repo (a search of all *.test.ts/*.test.tsx files turns up zero references to setBoost, useSetBoost, openBoost, or BoostConfig), and no UI component consumes them at all yet. This is a distinct, standalone testing gap from the UI-implementation gap: even the already-written setBoost contract-interaction code (allocation-percentage validation, validateSimulationAuth security check for the set_boost function, fee-bump handling) has never been exercised by a single test, unlike its siblings lockAssets/unlockAssets, which have dedicated coverage (soroban.test.ts, soroban.partial-unlock.test.ts, soroban.feebump.test.ts).

Acceptance Criteria

  • Add unit tests for SorobanService.setBoost covering: valid allocation (0-100), out-of-range allocation rejection, simulation failure, validateSimulationAuth rejecting a mismatched auth entry for set_boost, and fee-bump sponsorship, mirroring the structure of soroban.feebump.test.ts/soroban.partial-unlock.test.ts.
  • Add tests for useSetBoost's mutation success/error toast behavior and cache invalidation, mirroring useLockAssets/useUnlockAssets hook test patterns if any exist, or establishing the pattern if not.
  • Once the BoostModal UI exists (see the companion issue), add component tests for its validation and step-machine states following UnlockModal's test coverage as a template.
  • Ensure new tests run under the existing vitest run script with no additional configuration required.

Relevant Files

  • src/lib/soroban.ts — SorobanService.setBoost (~L1159) has zero test coverage
  • src/hooks/useSorobanQuery.ts — useSetBoost (~L255) has zero test coverage
  • src/lib/soroban.feebump.test.ts — the closest existing test pattern (fee-bump path) new boost tests should follow

Further Investigation

Confirmed: the UI genuinely does not exist, and its interaction model is unspecified anywhere

A repo-wide search (find src -iname "*boost*") turns up zero BoostModal, BoostConfig component, or any file whose name references boost UI. The only front-end trace of "boost" is:

  • src/app/farm/EarningRow.tsx L145-158 — a permanently-disabled Button with no onClick at all:
    <Button
      ...
      isDisabled={isNetworkMismatch || boostUnavailable}
      opacity={0.6}
      cursor="not-allowed"
      _hover={{ opacity: 0.6 }}
      ...
    >
      Boost
    </Button>
    There is no handler wired to open a modal — this is dead markup, not a stubbed-out entry point.
  • src/store/farmStore.ts L5-39 — FarmModal already has a "boost" variant and openBoost(position) action fully implemented in the zustand store, exactly mirroring openUnlock/openDeposit. So the state plumbing for a boost modal exists and works, but nothing calls openBoost, and nothing renders when activeModal === "boost" (compare to how activeModal === "unlock" presumably renders <UnlockModal> somewhere in farm/page.tsx — worth double-checking that render-switch during implementation, since it may need a new case/conditional branch added).
  • src/lib/soroban.ts L1159-1230 (SorobanService.setBoost) and src/hooks/useSorobanQuery.ts L255-297 (useSetBoost) are fully implemented and functionally complete: allocation-percentage bounds validation (allocationPercentage < 0 || > 100, soroban.ts L1166-1171), validateSimulationAuth check scoped to the set_boost function (L1198-1203), fee-bump-compatible transaction flow, and success/error toasts with cache invalidation for USER_POSITION/USER_CREDITS in the mutation's onSuccess (useSorobanQuery.ts ~L271-286). None of this is in question — the data layer is done and correct-looking.

What's genuinely undetermined, and why this warrants spike rather than a straightforward "write the missing test" task:

  1. Input model: is boost allocation set via a slider (0-100%), a numeric input, preset buttons (25/50/75/100%), or something else? setBoost's signature (allocationPercentage: number) doesn't constrain the UI — any of these are valid callers.
  2. Relationship to existing modals: should BoostModal follow UnlockModal's step-machine pattern (confirm → sign → pending → success, see src/components/UnlockModal/UnlockModal.tsx) or is boost config simple enough for a single-step form? UnlockModal also does partial-unlock preview math (computePartialUnlockPreview, tested in soroban.partial-unlock.test.ts) — does boost need an analogous "preview effect of this allocation on my daily rate" calculation, or is it a blind percentage-in/percentage-out?
  3. What does "boost" even do to the user's position? The store/service layer names it but nothing in the reachable UI explains the feature's effect (no tooltip, no copy, no docs found in a repo-wide grep for "boost" outside code). Writing a meaningful component test requires first deciding what the component should communicate and validate — that's a product/design decision, not just an engineering one, which is the hallmark of a spike per this batch's triage criteria.
  4. boostUnavailable (referenced at EarningRow.tsx L151 alongside isNetworkMismatch) — trace where this flag is computed and what conditions gate it; the eventual modal will need to respect the same gating and probably surface why boost is unavailable (e.g. position too new, already at max allocation) rather than a silently-disabled button, which is the same UX anti-pattern issue UnlockModal's autofocus uses a fragile document-wide querySelector instead of a ref, with no ARIA-live status region #85 (UnlockModal's ARIA gap) and Leaderboard's "You are rank X" banner only checks the currently-fetched page, not the user's true global rank #78 (leaderboard rank ambiguity) flag elsewhere in this batch — worth a consistent fix, not a one-off.

Two viable approaches, with tradeoffs

Approach A — Minimal modal, mirror UnlockModal's skeleton exactly. Copy UnlockModal's structure (Chakra Modal, step state, isNetworkMismatch gating, toast wiring already present in useSetBoost) and reduce the form to a single percentage input/slider with client-side 0-100 validation matching setBoost's own bounds check. Fastest to ship, lowest design risk, but risks being product-wrong if "boost" allocation actually needs preview/impact math the team hasn't specified (see point 2 above) — could require a follow-up rework once real requirements surface.

Approach B — Spike first: write a short design note capturing the intended UX (input type, preview math if any, copy/tooltip explaining what boost does, relationship to boostUnavailable), get it reviewed/approved, then implement BoostModal + tests together. Slower to first PR, but avoids building the wrong thing twice. Given openBoost/FarmModal were clearly scaffolded in anticipation of this UI (farmStore.ts already has the "boost" variant sitting unused), it's likely someone already had an intended design that never got written down or got dropped — worth checking with the repo owner / issue #81 before assuming from scratch.

Approach C — Treat this purely as a testing issue and defer the UI entirely to #81. Write the SorobanService.setBoost/useSetBoost unit tests now (these have zero ambiguity — the contract is fully specified by existing code), and leave the BoostModal component-test AC as blocked/deferred until #81 lands. This decouples the "well-specified, low-risk" test-writing work (service + hook layer) from the "needs design decisions" work (component), letting a contributor claim partial value now instead of blocking on a UX decision.

Recommendation: do the SorobanService.setBoost/useSetBoost tests immediately (well-specified, see below) and treat the BoostModal component itself as the actual spike, tracked jointly with #81.

Testing strategy for the parts that ARE well-specified today

  • New test file src/lib/soroban.setboost.test.ts, mirroring src/lib/soroban.feebump.test.ts's structure (describe/it blocks, vi.mock for the RPC server per that file's existing pattern) and soroban.partial-unlock.test.ts for pure-function-style assertions where applicable.
  • Cases to cover for SorobanService.setBoost (soroban.ts L1159-1230): valid allocation (e.g. 50) happy path through simulate→sign→submit; allocation -1 and 101 rejected before any RPC call (assert rpcServer.simulateTransaction/getAccount were never called — this exercises the early-return at L1167-1172); simulation returning { error } (L1188-1193); validateSimulationAuth throwing SecurityError for a mismatched set_boost auth entry (mirror however soroban.test.ts tests this for lockAssets/unlockAssets); and submissionResult.status === 'ERROR' (L1219-1224).
  • For useSetBoost (useSorobanQuery.ts ~L255-297): assert mutationFn throws 'Wallet not connected' when walletApi/publicKey are absent; assert onSuccess toast fires with the correct allocation percentage in its description and that invalidateQueries is called with [QUERY_KEYS.USER_POSITION, poolId] and [QUERY_KEYS.USER_CREDITS, poolId]; assert onError toast fires on a rejected mutation. Check whether any existing hook test (useSorobanQuery.test.ts if present) already establishes a wrapper/renderHook pattern for mutations to reuse rather than inventing a new one.
  • Defer BoostModal component tests (the UnlockModal-template AC) until the UI itself is designed and built — attempting to write these tests first, without a settled interaction model, would produce tests that lock in an arbitrary/wrong UI and need to be rewritten anyway.

Cross-reference

Directly duplicative scope with #81 ("Boost allocation is fully wired in the data layer but has no UI — the Boost button is permanently disabled dead code"), which is the implementation-side issue for the exact same gap described here. These two issues should be assigned/worked together — #81 for the UI decision, this issue for the tests that follow it — rather than independently, to avoid one PR reinventing what the other already decided.

Metadata

Metadata

Assignees

No one assigned

    Labels

    GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official CampaignOfficial Campaign | FWC26Campaign: Official Campaign | FWC26farmFarming/staking flow — deposit, lock, unlock, creditstestingUnit, integration, or e2e test coveragevery hardExtremely hard — deep expertise, careful design, and significant time required

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions