Skip to content

test(query-core/query): add test for refetch after a previous fetch errored#10529

Closed
KimHyeongRae0 wants to merge 2 commits intoTanStack:mainfrom
KimHyeongRae0:test/query-core-query-fetch-after-rejected-retryer
Closed

test(query-core/query): add test for refetch after a previous fetch errored#10529
KimHyeongRae0 wants to merge 2 commits intoTanStack:mainfrom
KimHyeongRae0:test/query-core-query-fetch-after-rejected-retryer

Conversation

@KimHyeongRae0
Copy link
Copy Markdown
Contributor

@KimHyeongRae0 KimHyeongRae0 commented Apr 20, 2026

🎯 Changes

Adds a behavioral test for Query.fetch that covers the case where the previous retryer has ended in a rejected state. After an initial fetch fails with retry: false, calling query.fetch(...) directly must fall through the early-return guard in query.ts:401-417 (specifically the this.#retryer?.status() !== 'rejected' condition) and create a fresh retryer. The test also asserts the resulting status transition and that the new queryFn runs.

This complements the existing "fetch should not dispatch 'fetch' query is already fetching" test (query.test.tsx:819), which only validates the in-flight concurrent-fetch path, not the post-rejection recovery path.

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with npx nx run @tanstack/query-core:test:lib and npx nx run @tanstack/query-core:test:types.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Tests
    • Added test coverage validating a query that initially fails then later succeeds: confirms error state is recorded, advancing timers and a subsequent fetch resolves successfully, call counts and retry behavior update, and the query state transitions from error to success.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b9d9c8dd-8b40-4a50-9a7a-10d4e9428bd2

📥 Commits

Reviewing files that changed from the base of the PR and between 47d908c and 762c163.

📒 Files selected for processing (1)
  • packages/query-core/src/__tests__/query.test.tsx
✅ Files skipped from review due to trivial changes (1)
  • packages/query-core/src/tests/query.test.tsx

📝 Walkthrough

Walkthrough

Added a test that prefetches a Query whose queryFn first rejects (retry: false), waits until the Query reaches status: 'error', then replaces the queryFn to succeed, calls query.fetch(...), and asserts the query resolves and transitions to status: 'success'. (≤50 words)

Changes

Cohort / File(s) Summary
Query Error Recovery Test
packages/query-core/src/__tests__/query.test.tsx
New test that prefetches a query which fails, verifies error state and call count, swaps to a succeeding queryFn, invokes query.fetch(...), and asserts the returned promise resolves and the Query moves to status: 'success'.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

I stumbled once on test-time's hill,
Timers ticked, the error held me still.
A swapped-in hop, a second try,
Fetch resolved — I bounded high! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a test for refetch after a previous fetch errored, which matches the test addition in query.test.tsx.
Description check ✅ Passed The PR description includes all required sections from the template: detailed explanation of changes, completed checklist items, and proper release impact classification.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/query-core/src/__tests__/query.test.tsx`:
- Around line 863-877: The test must exercise the Query.fetch early-return guard
path where an existing retryer is present and its status() === 'rejected' while
fetchStatus !== 'idle'; modify the setup so the second call to query.fetch
happens while the rejected retryer is still attached (e.g., start the failing
prefetch with queryClient.prefetchQuery({ queryKey: key, queryFn, retry: false
}), advance timers only enough to make the retryer reach rejected but do not let
the Query reset fetchStatus to 'idle', then call query.fetch({ queryKey: key,
queryFn, retry: false }) and assert the guard path is taken), or if that is
brittle, change the test title and behavior to a plain “refetch after error”
scenario that does not attempt to cover the rejected-retryer guard; target
symbols: queryClient.prefetchQuery, query.fetch, Query.fetch, this.#retryer,
fetchStatus.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3880683d-2327-4cd7-9041-14637cf79e7b

📥 Commits

Reviewing files that changed from the base of the PR and between 799952f and 99a484f.

📒 Files selected for processing (1)
  • packages/query-core/src/__tests__/query.test.tsx

Comment thread packages/query-core/src/__tests__/query.test.tsx Outdated
KimHyeongRae0 added a commit to KimHyeongRae0/query that referenced this pull request Apr 21, 2026
The previous title ('fetch should start a new fetch when the previous
retryer has rejected') claimed coverage of the
`this.#retryer?.status() !== 'rejected'` branch in Query.fetch, but that
guard sits behind `state.fetchStatus !== 'idle'`. Once the prior fetch has
rejected and state.status becomes 'error', fetchStatus is already back to
'idle', so the first predicate short-circuits and the retryer.status()
check is never evaluated. Removing the rejected-retryer exception would
not flip this assertion.

Rename and trim to what the test actually exercises: a rejected fetch
followed by a successful refetch via Query.fetch. Per CodeRabbit review
on PR TanStack#10529.
@KimHyeongRae0 KimHyeongRae0 changed the title test(query-core/query): add test for fetch starting a new fetch after the previous retryer has rejected test(query-core/query): add test for refetch after a previous fetch errored Apr 21, 2026
The previous title ('fetch should start a new fetch when the previous
retryer has rejected') claimed coverage of the
`this.#retryer?.status() !== 'rejected'` branch in Query.fetch, but that
guard sits behind `state.fetchStatus !== 'idle'`. Once the prior fetch has
rejected and state.status becomes 'error', fetchStatus is already back to
'idle', so the first predicate short-circuits and the retryer.status()
check is never evaluated. Removing the rejected-retryer exception would
not flip this assertion.

Rename and trim to what the test actually exercises: a rejected fetch
followed by a successful refetch via Query.fetch. Per CodeRabbit review
on PR TanStack#10529.
@KimHyeongRae0 KimHyeongRae0 force-pushed the test/query-core-query-fetch-after-rejected-retryer branch from 47d908c to 762c163 Compare April 21, 2026 10:06
@sukvvon
Copy link
Copy Markdown
Collaborator

sukvvon commented Apr 23, 2026

I think the claim in the description doesn't actually hold.

After prefetchQuery errors out with retry: false, the reducer sets state.fetchStatus: 'idle' on the 'error' case. I verified this empirically by logging inside Query.fetch:

[fetch] fetchStatus= idle  retryerStatus= undefined   // initial prefetchQuery
[fetch] fetchStatus= idle  retryerStatus= rejected    // the manual query.fetch(...)

So when query.fetch(...) is called the second time, the if (this.state.fetchStatus !== 'idle' && ...) block is short-circuited by the first condition, and the this.#retryer?.status() !== 'rejected' check is never reached. The test therefore validates "error → success on refetch" in general, but not the specific retryer.status() === 'rejected' branch.

That rejected-retryer guard was added in #9565 to fix a StrictMode race where fetchStatus was still 'fetching' while the retryer had already rejected. To hit that branch specifically, you'd need to reproduce that race (i.e. state.fetchStatus: 'fetching' at the moment of the second fetch() call).

Could you either adjust the scenario to cover that race, or reword the description so it doesn't claim to cover the retryer rejected guard?

@KimHyeongRae0
Copy link
Copy Markdown
Contributor Author

You're right — without the retryer-rejected angle, this duplicates existing refetch-after-error coverage. Closing this one.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants