Skip to content

Commit fc597ba

Browse files
committed
Add test to verify retryCondition can use the abort signal
1 parent 53697b7 commit fc597ba

File tree

1 file changed

+83
-0
lines changed

1 file changed

+83
-0
lines changed

packages/toolkit/src/query/tests/retry.test.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,4 +465,87 @@ describe('configuration', () => {
465465

466466
expect(baseBaseQuery).toHaveBeenCalledOnce()
467467
})
468+
469+
test('retryCondition receives abort signal and stops retrying when cache entry is removed', async () => {
470+
let capturedSignal: AbortSignal | undefined
471+
let retryAttempts = 0
472+
473+
const baseBaseQuery = vi.fn<
474+
Parameters<BaseQueryFn>,
475+
ReturnType<BaseQueryFn>
476+
>()
477+
478+
// Always return an error to trigger retries
479+
baseBaseQuery.mockResolvedValue({ error: 'network error' })
480+
481+
let retryConditionCalled = false
482+
483+
const baseQuery = retry(baseBaseQuery, {
484+
retryCondition: (error, args, { attempt, baseQueryApi }) => {
485+
retryConditionCalled = true
486+
retryAttempts = attempt
487+
capturedSignal = baseQueryApi.signal
488+
489+
// Stop retrying if the signal is aborted
490+
if (baseQueryApi.signal.aborted) {
491+
return false
492+
}
493+
494+
// Otherwise, retry up to 10 times
495+
return attempt <= 10
496+
},
497+
backoff: async () => {
498+
// Short backoff for faster test
499+
await new Promise((resolve) => setTimeout(resolve, 10))
500+
},
501+
})
502+
503+
const api = createApi({
504+
baseQuery,
505+
endpoints: (build) => ({
506+
getTest: build.query<string, number>({
507+
query: (id) => ({ url: `test/${id}` }),
508+
keepUnusedDataFor: 0.01, // Very short timeout (10ms)
509+
}),
510+
}),
511+
})
512+
513+
const storeRef = setupApiStore(api, undefined, {
514+
withoutTestLifecycles: true,
515+
})
516+
517+
// Start the query
518+
const queryPromise = storeRef.store.dispatch(
519+
api.endpoints.getTest.initiate(1),
520+
)
521+
522+
// Wait for the first retry to happen so we capture the signal
523+
await loopTimers(2)
524+
525+
// Verify the retry condition was called and we have a signal
526+
expect(retryConditionCalled).toBe(true)
527+
expect(capturedSignal).toBeDefined()
528+
expect(capturedSignal!.aborted).toBe(false)
529+
530+
// Unsubscribe to trigger cache removal
531+
queryPromise.unsubscribe()
532+
533+
// Wait for the cache entry to be removed (keepUnusedDataFor: 0.01s = 10ms)
534+
await vi.advanceTimersByTimeAsync(50)
535+
536+
// Allow some time for more retries to potentially happen
537+
await loopTimers(3)
538+
539+
// The signal should now be aborted
540+
expect(capturedSignal!.aborted).toBe(true)
541+
542+
// We should have stopped retrying early due to the abort signal
543+
// If abort signal wasn't working, we'd see many more retry attempts
544+
expect(retryAttempts).toBeLessThan(10)
545+
546+
// The base query should have been called at least once (initial attempt)
547+
// but not the full 10+ times it would without abort signal
548+
expect(baseBaseQuery).toHaveBeenCalled()
549+
expect(baseBaseQuery.mock.calls.length).toBeLessThan(10)
550+
})
468551
})

0 commit comments

Comments
 (0)