Skip to content

fix(auctions): handle auction close races without stale action buttons - #989

Open
Olajcodes wants to merge 1 commit into
Creditra:mainfrom
Olajcodes:fix/auction-close-race-stale-buttons
Open

fix(auctions): handle auction close races without stale action buttons#989
Olajcodes wants to merge 1 commit into
Creditra:mainfrom
Olajcodes:fix/auction-close-race-stale-buttons

Conversation

@Olajcodes

Copy link
Copy Markdown

Summary

Fixes auction close race conditions where stale action buttons remain interactive after an auction has logically ended. This prevents users from attempting purchases on ended auctions and ensures deterministic UI state transitions.

Problem

When a Dutch auction's endTime passes while the card component is mounted, two races occur:

  1. The setInterval for price/countdown continues running unnecessarily (leaked timer)
  2. The "Purchase Now" button stays visible and clickable despite the auction being over
  3. The stored status: 'Active' is not reconciled against the actual clock

Changes

src/types/dutchAuction.ts

  • Added computeEffectiveStatus(auction, nowMs) — resolves the effective status by comparing endTime against wall-clock time. Returns Completed when an Active auction's deadline has passed, preventing stale "Active" states.

src/components/DutchAuctionCard.tsx

  • Dynamic status: Computes effectiveStatus on each 1-second tick instead of relying on the static auction.status prop
  • Interval cleanup: Clears the setInterval as soon as effectiveStatus transitions to non-Active (no leaked timers)
  • Stale button guard: handlePurchase checks effectiveStatus !== 'Active' before firing; uses isPurchasing + purchaseAttemptRef to prevent double-click races
  • Proper PendingButton usage: Passes pending and pendingLabel props to PendingButton for correct aria-busy and disabled behavior
  • Ended notice: Shows an accessible role="status" + aria-live="polite" message when an Active auction expires while mounted

src/pages/DutchAuctions.tsx

  • Effective-status filtering: Filters auctions using computeEffectiveStatus() so time-expired auctions move to the "completed" tab automatically
  • Purchase in-flight guard: Uses a Set<string> ref to prevent duplicate purchase submissions for the same auction ID

Acceptance Criteria Mapping

Criterion How addressed
Deterministic behavior for valid/invalid/duplicate/boundary inputs computeEffectiveStatus handles exact boundary (now >= endTime), pre-boundary, and terminal statuses deterministically
Authorization, validation, and state-transition invariants enforced No status can skip terminal states; Active → Completed only via clock
Retries, partial failure, and concurrent execution safe purchaseAttemptRef ensures only one in-flight purchase per card; purchaseInFlightRef guards the page-level handler
Focused tests cover success, rejection, boundary, and regression 18 tests: 8 unit tests for computeEffectiveStatus, 10 component tests for card close-race behavior
Existing callers remain compatible No public interface changes; DutchAuctionCardProps and DutchAuctions API unchanged
Relevant logs, metrics, or user-visible errors role="status" + aria-live="polite" announces auction end to screen readers

Test Plan

  • computeEffectiveStatus: Active with future endTime → Active
  • computeEffectiveStatus: Active with past endTime → Completed
  • computeEffectiveStatus: Exact boundary (now === endTime) → Completed
  • computeEffectiveStatus: Terminal statuses (Completed/Cancelled) pass through unchanged
  • Card shows Active status and Purchase button when live
  • Card removes Purchase button and shows Completed when auction ends mid-tick
  • Card shows "This auction has ended" notice on Active→Completed transition
  • Interval is cleared after auction ends (no leaked timers)
  • Purchase button shows pending state and re-enables after completion
  • Double-click purchase is prevented during in-flight operation
  • Purchase does not fire after auction has ended
  • Time Left section hides after auction expires
  • Countdown decrements while auction is active
  • Existing DutchAuctions empty-state tests pass (5/5)
  • Build passes (npm run build)

Security & Failure-Mode Handling

  • No sensitive data exposed in error states or accessibility announcements
  • purchaseAttemptRef pattern ensures stale async completions don't re-enable the wrong button
  • computeEffectiveStatus is pure/deterministic — safe for concurrent calls
  • No weakening of existing validation or safeguards

Closes #942

- Add computeEffectiveStatus() to resolve Active status past endTime
- Fix DutchAuctionCard interval to clear when auction ends mid-tick
- Guard Purchase button against stale clicks via async pending state
- Filter auctions by effective status on the listing page
- Prevent duplicate purchase submissions with in-flight ref guard
- Show accessible 'This auction has ended' notice on transition
- Add 18 tests covering close races, boundary conditions, and purchase guards

Closes Creditra#942
@drips-wave

drips-wave Bot commented Sep 1, 2026

Copy link
Copy Markdown

@Olajcodes Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Quality-2][High] Handle auction close races without stale action buttons

2 participants