From c3be7acfd6920fe310839a306a9ae6ee79e93b8d Mon Sep 17 00:00:00 2001
From: Wonsuk Choi <sukvvon@gmail.com>
Date: Thu, 4 Jun 2026 10:51:37 +0900
Subject: [PATCH 1/5] test(query-persist-client-core/persist): add test for
 hydrating a valid persisted cache in 'persistQueryClientRestore' (#10872)

---
 .../src/__tests__/persist.test.ts             | 25 ++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/packages/query-persist-client-core/src/__tests__/persist.test.ts b/packages/query-persist-client-core/src/__tests__/persist.test.ts
index c831c8b8b0..779d887f52 100644
--- a/packages/query-persist-client-core/src/__tests__/persist.test.ts
+++ b/packages/query-persist-client-core/src/__tests__/persist.test.ts
@@ -1,5 +1,5 @@
 import { describe, expect, it, vi } from 'vitest'
-import { QueriesObserver, QueryClient } from '@tanstack/query-core'
+import { QueriesObserver, QueryClient, dehydrate } from '@tanstack/query-core'
 import {
   persistQueryClientRestore,
   persistQueryClientSubscribe,
@@ -155,4 +155,27 @@ describe('persistQueryClientRestore', () => {
       }),
     ).rejects.toBe(removeError)
   })
+
+  it('should hydrate the query client when the persisted cache is valid', async () => {
+    const sourceClient = new QueryClient()
+    sourceClient.setQueryData(['key'], 'data')
+
+    const queryClient = new QueryClient()
+    const persister = createSpyPersister()
+
+    persister.restoreClient = () =>
+      Promise.resolve({
+        buster: '',
+        clientState: dehydrate(sourceClient),
+        timestamp: Date.now(),
+      })
+
+    await persistQueryClientRestore({
+      queryClient,
+      persister,
+    })
+
+    expect(persister.removeClient).not.toHaveBeenCalled()
+    expect(queryClient.getQueryData(['key'])).toBe('data')
+  })
 })

From a6fda44c0de0830c8fed36b76b26e693531128bf Mon Sep 17 00:00:00 2001
From: Wonsuk Choi <sukvvon@gmail.com>
Date: Thu, 4 Jun 2026 11:19:24 +0900
Subject: [PATCH 2/5] test(query-persist-client-core/persist): hoist
 'queryClient' and 'persister' setup into 'beforeEach' and clear in
 'afterEach' (#10874)

---
 .../src/__tests__/persist.test.ts             | 28 +++++++++----------
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/packages/query-persist-client-core/src/__tests__/persist.test.ts b/packages/query-persist-client-core/src/__tests__/persist.test.ts
index 779d887f52..4a7f552849 100644
--- a/packages/query-persist-client-core/src/__tests__/persist.test.ts
+++ b/packages/query-persist-client-core/src/__tests__/persist.test.ts
@@ -1,4 +1,4 @@
-import { describe, expect, it, vi } from 'vitest'
+import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
 import { QueriesObserver, QueryClient, dehydrate } from '@tanstack/query-core'
 import {
   persistQueryClientRestore,
@@ -64,6 +64,18 @@ describe('persistQueryClientSave', () => {
 })
 
 describe('persistQueryClientRestore', () => {
+  let queryClient: QueryClient
+  let persister: ReturnType<typeof createSpyPersister>
+
+  beforeEach(() => {
+    queryClient = new QueryClient()
+    persister = createSpyPersister()
+  })
+
+  afterEach(() => {
+    queryClient.clear()
+  })
+
   it('should rethrow exceptions in `restoreClient`', async () => {
     const consoleMock = vi
       .spyOn(console, 'error')
@@ -73,12 +85,8 @@ describe('persistQueryClientRestore', () => {
       .spyOn(console, 'warn')
       .mockImplementation(() => undefined)
 
-    const queryClient = new QueryClient()
-
     const restoreError = new Error('Error restoring client')
 
-    const persister = createSpyPersister()
-
     persister.restoreClient = () => Promise.reject(restoreError)
 
     await expect(
@@ -105,13 +113,9 @@ describe('persistQueryClientRestore', () => {
       .spyOn(console, 'warn')
       .mockImplementation(() => undefined)
 
-    const queryClient = new QueryClient()
-
     const restoreError = new Error('Error restoring client')
     const removeError = new Error('Error removing client')
 
-    const persister = createSpyPersister()
-
     persister.restoreClient = () => Promise.reject(restoreError)
     persister.removeClient = () => Promise.reject(removeError)
 
@@ -131,9 +135,6 @@ describe('persistQueryClientRestore', () => {
   })
 
   it('should rethrow error in `removeClient`', async () => {
-    const queryClient = new QueryClient()
-
-    const persister = createSpyPersister()
     const removeError = new Error('Error removing client')
 
     persister.removeClient = () => Promise.reject(removeError)
@@ -160,9 +161,6 @@ describe('persistQueryClientRestore', () => {
     const sourceClient = new QueryClient()
     sourceClient.setQueryData(['key'], 'data')
 
-    const queryClient = new QueryClient()
-    const persister = createSpyPersister()
-
     persister.restoreClient = () =>
       Promise.resolve({
         buster: '',

From e6c26c066b43fdfb3a8c394a5dfa7b6d94a0624c Mon Sep 17 00:00:00 2001
From: Wonsuk Choi <sukvvon@gmail.com>
Date: Thu, 4 Jun 2026 12:06:37 +0900
Subject: [PATCH 3/5] test(query-persist-client-core/persist): wrap the suites
 in a top-level 'persist' describe and hoist the shared 'queryClient' setup
 (#10875)

---
 .../src/__tests__/persist.test.ts             | 268 +++++++++---------
 1 file changed, 135 insertions(+), 133 deletions(-)

diff --git a/packages/query-persist-client-core/src/__tests__/persist.test.ts b/packages/query-persist-client-core/src/__tests__/persist.test.ts
index 4a7f552849..efa9fd9cb1 100644
--- a/packages/query-persist-client-core/src/__tests__/persist.test.ts
+++ b/packages/query-persist-client-core/src/__tests__/persist.test.ts
@@ -6,174 +6,176 @@ import {
 } from '../persist'
 import { createMockPersister, createSpyPersister } from './utils'
 
-describe('persistQueryClientSubscribe', () => {
-  it('should persist mutations', async () => {
-    const queryClient = new QueryClient()
+describe('persist', () => {
+  let queryClient: QueryClient
 
-    const persister = createMockPersister()
+  beforeEach(() => {
+    queryClient = new QueryClient()
+  })
 
-    const unsubscribe = persistQueryClientSubscribe({
-      queryClient,
-      persister,
-      dehydrateOptions: { shouldDehydrateMutation: () => true },
-    })
+  afterEach(() => {
+    queryClient.clear()
+  })
 
-    queryClient.getMutationCache().build(queryClient, {
-      mutationFn: (text: string) => Promise.resolve(text),
-    })
+  describe('persistQueryClientSubscribe', () => {
+    it('should persist mutations', async () => {
+      const persister = createMockPersister()
 
-    const result = await persister.restoreClient()
+      const unsubscribe = persistQueryClientSubscribe({
+        queryClient,
+        persister,
+        dehydrateOptions: { shouldDehydrateMutation: () => true },
+      })
 
-    expect(result?.clientState.mutations).toHaveLength(1)
+      queryClient.getMutationCache().build(queryClient, {
+        mutationFn: (text: string) => Promise.resolve(text),
+      })
+
+      const result = await persister.restoreClient()
+
+      expect(result?.clientState.mutations).toHaveLength(1)
 
-    unsubscribe()
+      unsubscribe()
+    })
   })
-})
 
-describe('persistQueryClientSave', () => {
-  it('should not be triggered on observer type events', () => {
-    const queryClient = new QueryClient()
+  describe('persistQueryClientSave', () => {
+    it('should not be triggered on observer type events', () => {
+      const persister = createSpyPersister()
 
-    const persister = createSpyPersister()
+      const unsubscribe = persistQueryClientSubscribe({
+        queryClient,
+        persister,
+      })
 
-    const unsubscribe = persistQueryClientSubscribe({
-      queryClient,
-      persister,
+      const queryKey = ['test']
+      const queryFn = vi.fn().mockReturnValue(1)
+      const observer = new QueriesObserver(queryClient, [{ queryKey, queryFn }])
+      const unsubscribeObserver = observer.subscribe(vi.fn())
+      observer
+        .getObservers()[0]
+        ?.setOptions({ queryKey, refetchOnWindowFocus: false })
+      unsubscribeObserver()
+
+      queryClient.setQueryData(queryKey, 2)
+
+      // persistClient should be called 3 times:
+      // 1. When query is added
+      // 2. When queryFn is resolved
+      // 3. When setQueryData is called
+      // All events fired by manipulating observers are ignored
+      expect(persister.persistClient).toHaveBeenCalledTimes(3)
+
+      unsubscribe()
     })
-
-    const queryKey = ['test']
-    const queryFn = vi.fn().mockReturnValue(1)
-    const observer = new QueriesObserver(queryClient, [{ queryKey, queryFn }])
-    const unsubscribeObserver = observer.subscribe(vi.fn())
-    observer
-      .getObservers()[0]
-      ?.setOptions({ queryKey, refetchOnWindowFocus: false })
-    unsubscribeObserver()
-
-    queryClient.setQueryData(queryKey, 2)
-
-    // persistClient should be called 3 times:
-    // 1. When query is added
-    // 2. When queryFn is resolved
-    // 3. When setQueryData is called
-    // All events fired by manipulating observers are ignored
-    expect(persister.persistClient).toHaveBeenCalledTimes(3)
-
-    unsubscribe()
   })
-})
 
-describe('persistQueryClientRestore', () => {
-  let queryClient: QueryClient
-  let persister: ReturnType<typeof createSpyPersister>
+  describe('persistQueryClientRestore', () => {
+    let persister: ReturnType<typeof createSpyPersister>
 
-  beforeEach(() => {
-    queryClient = new QueryClient()
-    persister = createSpyPersister()
-  })
+    beforeEach(() => {
+      persister = createSpyPersister()
+    })
 
-  afterEach(() => {
-    queryClient.clear()
-  })
+    it('should rethrow exceptions in `restoreClient`', async () => {
+      const consoleMock = vi
+        .spyOn(console, 'error')
+        .mockImplementation(() => undefined)
 
-  it('should rethrow exceptions in `restoreClient`', async () => {
-    const consoleMock = vi
-      .spyOn(console, 'error')
-      .mockImplementation(() => undefined)
+      const consoleWarn = vi
+        .spyOn(console, 'warn')
+        .mockImplementation(() => undefined)
 
-    const consoleWarn = vi
-      .spyOn(console, 'warn')
-      .mockImplementation(() => undefined)
+      const restoreError = new Error('Error restoring client')
 
-    const restoreError = new Error('Error restoring client')
+      persister.restoreClient = () => Promise.reject(restoreError)
 
-    persister.restoreClient = () => Promise.reject(restoreError)
+      await expect(
+        persistQueryClientRestore({
+          queryClient,
+          persister,
+        }),
+      ).rejects.toBe(restoreError)
 
-    await expect(
-      persistQueryClientRestore({
-        queryClient,
-        persister,
-      }),
-    ).rejects.toBe(restoreError)
+      expect(consoleMock).toHaveBeenCalledTimes(1)
+      expect(consoleWarn).toHaveBeenCalledTimes(1)
+      expect(consoleMock).toHaveBeenNthCalledWith(1, restoreError)
 
-    expect(consoleMock).toHaveBeenCalledTimes(1)
-    expect(consoleWarn).toHaveBeenCalledTimes(1)
-    expect(consoleMock).toHaveBeenNthCalledWith(1, restoreError)
+      consoleMock.mockRestore()
+      consoleWarn.mockRestore()
+    })
 
-    consoleMock.mockRestore()
-    consoleWarn.mockRestore()
-  })
+    it('should rethrow exceptions in `removeClient` before `restoreClient`', async () => {
+      const consoleMock = vi
+        .spyOn(console, 'error')
+        .mockImplementation(() => undefined)
 
-  it('should rethrow exceptions in `removeClient` before `restoreClient`', async () => {
-    const consoleMock = vi
-      .spyOn(console, 'error')
-      .mockImplementation(() => undefined)
+      const consoleWarn = vi
+        .spyOn(console, 'warn')
+        .mockImplementation(() => undefined)
 
-    const consoleWarn = vi
-      .spyOn(console, 'warn')
-      .mockImplementation(() => undefined)
+      const restoreError = new Error('Error restoring client')
+      const removeError = new Error('Error removing client')
 
-    const restoreError = new Error('Error restoring client')
-    const removeError = new Error('Error removing client')
+      persister.restoreClient = () => Promise.reject(restoreError)
+      persister.removeClient = () => Promise.reject(removeError)
 
-    persister.restoreClient = () => Promise.reject(restoreError)
-    persister.removeClient = () => Promise.reject(removeError)
+      await expect(
+        persistQueryClientRestore({
+          queryClient,
+          persister,
+        }),
+      ).rejects.toBe(removeError)
 
-    await expect(
-      persistQueryClientRestore({
-        queryClient,
-        persister,
-      }),
-    ).rejects.toBe(removeError)
+      expect(consoleMock).toHaveBeenCalledTimes(1)
+      expect(consoleWarn).toHaveBeenCalledTimes(1)
+      expect(consoleMock).toHaveBeenNthCalledWith(1, restoreError)
 
-    expect(consoleMock).toHaveBeenCalledTimes(1)
-    expect(consoleWarn).toHaveBeenCalledTimes(1)
-    expect(consoleMock).toHaveBeenNthCalledWith(1, restoreError)
+      consoleMock.mockRestore()
+      consoleWarn.mockRestore()
+    })
 
-    consoleMock.mockRestore()
-    consoleWarn.mockRestore()
-  })
+    it('should rethrow error in `removeClient`', async () => {
+      const removeError = new Error('Error removing client')
+
+      persister.removeClient = () => Promise.reject(removeError)
+      persister.restoreClient = () => {
+        return Promise.resolve({
+          buster: 'random-buster',
+          clientState: {
+            mutations: [],
+            queries: [],
+          },
+          timestamp: new Date().getTime(),
+        })
+      }
+
+      await expect(
+        persistQueryClientRestore({
+          queryClient,
+          persister,
+        }),
+      ).rejects.toBe(removeError)
+    })
 
-  it('should rethrow error in `removeClient`', async () => {
-    const removeError = new Error('Error removing client')
-
-    persister.removeClient = () => Promise.reject(removeError)
-    persister.restoreClient = () => {
-      return Promise.resolve({
-        buster: 'random-buster',
-        clientState: {
-          mutations: [],
-          queries: [],
-        },
-        timestamp: new Date().getTime(),
-      })
-    }
+    it('should hydrate the query client when the persisted cache is valid', async () => {
+      const sourceClient = new QueryClient()
+      sourceClient.setQueryData(['key'], 'data')
 
-    await expect(
-      persistQueryClientRestore({
+      persister.restoreClient = () =>
+        Promise.resolve({
+          buster: '',
+          clientState: dehydrate(sourceClient),
+          timestamp: Date.now(),
+        })
+
+      await persistQueryClientRestore({
         queryClient,
         persister,
-      }),
-    ).rejects.toBe(removeError)
-  })
-
-  it('should hydrate the query client when the persisted cache is valid', async () => {
-    const sourceClient = new QueryClient()
-    sourceClient.setQueryData(['key'], 'data')
-
-    persister.restoreClient = () =>
-      Promise.resolve({
-        buster: '',
-        clientState: dehydrate(sourceClient),
-        timestamp: Date.now(),
       })
 
-    await persistQueryClientRestore({
-      queryClient,
-      persister,
+      expect(persister.removeClient).not.toHaveBeenCalled()
+      expect(queryClient.getQueryData(['key'])).toBe('data')
     })
-
-    expect(persister.removeClient).not.toHaveBeenCalled()
-    expect(queryClient.getQueryData(['key'])).toBe('data')
   })
 })

From 89bae56573156bb676dd38f17b96706277a6878b Mon Sep 17 00:00:00 2001
From: Wonsuk Choi <sukvvon@gmail.com>
Date: Thu, 4 Jun 2026 13:34:04 +0900
Subject: [PATCH 4/5] test(query-persist-client-core/persist): add tests for
 removing an expired or busted cache in 'persistQueryClientRestore' (#10876)

---
 .../src/__tests__/persist.test.ts             | 34 +++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/packages/query-persist-client-core/src/__tests__/persist.test.ts b/packages/query-persist-client-core/src/__tests__/persist.test.ts
index efa9fd9cb1..ded1c53ac0 100644
--- a/packages/query-persist-client-core/src/__tests__/persist.test.ts
+++ b/packages/query-persist-client-core/src/__tests__/persist.test.ts
@@ -177,5 +177,39 @@ describe('persist', () => {
       expect(persister.removeClient).not.toHaveBeenCalled()
       expect(queryClient.getQueryData(['key'])).toBe('data')
     })
+
+    it('should remove the client when the persisted cache is expired', async () => {
+      persister.restoreClient = () =>
+        Promise.resolve({
+          buster: '',
+          clientState: { mutations: [], queries: [] },
+          timestamp: Date.now() - 1000,
+        })
+
+      await persistQueryClientRestore({
+        queryClient,
+        persister,
+        maxAge: 100,
+      })
+
+      expect(persister.removeClient).toHaveBeenCalledTimes(1)
+    })
+
+    it('should remove the client when the buster does not match', async () => {
+      persister.restoreClient = () =>
+        Promise.resolve({
+          buster: 'old-buster',
+          clientState: { mutations: [], queries: [] },
+          timestamp: Date.now(),
+        })
+
+      await persistQueryClientRestore({
+        queryClient,
+        persister,
+        buster: 'new-buster',
+      })
+
+      expect(persister.removeClient).toHaveBeenCalledTimes(1)
+    })
   })
 })

From 5fcb4d76a42cd668a7c54bf7ea27268412ac4cca Mon Sep 17 00:00:00 2001
From: Wonsuk Choi <sukvvon@gmail.com>
Date: Thu, 4 Jun 2026 13:48:36 +0900
Subject: [PATCH 5/5] test(query-persist-client-core/persist): add test for
 removing a cache without a timestamp in 'persistQueryClientRestore' (#10877)

---
 .../src/__tests__/persist.test.ts                | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/packages/query-persist-client-core/src/__tests__/persist.test.ts b/packages/query-persist-client-core/src/__tests__/persist.test.ts
index ded1c53ac0..3138abc972 100644
--- a/packages/query-persist-client-core/src/__tests__/persist.test.ts
+++ b/packages/query-persist-client-core/src/__tests__/persist.test.ts
@@ -211,5 +211,21 @@ describe('persist', () => {
 
       expect(persister.removeClient).toHaveBeenCalledTimes(1)
     })
+
+    it('should remove the client when the persisted cache has no timestamp', async () => {
+      persister.restoreClient = () =>
+        Promise.resolve({
+          buster: '',
+          clientState: { mutations: [], queries: [] },
+          timestamp: 0,
+        })
+
+      await persistQueryClientRestore({
+        queryClient,
+        persister,
+      })
+
+      expect(persister.removeClient).toHaveBeenCalledTimes(1)
+    })
   })
 })
