test(core): add getResponseMeta-paths integration test (non-GC angle)#3929
Conversation
The previous integration test poked GCPolicy['entityCount'] directly to prove the journey-mutation bug. Replace it with a test that asserts the public-API consequence the bug creates outside of GC: every subscriber to the same endpoint must observe the same expiresAt from Controller.getResponseMeta(). This is the property the bug actually broke for non-GC users: with the buggy paths.shift(), entityExpiresAt(paths, …) iterates a progressively-shorter list, dropping the entity with the earliest expiry first. Subscriber 2 observes a too-late expiresAt; subscriber 3+ observe Infinity and never refetch. Fires under ImmortalGCPolicy too, since entityExpiresAt is unconditional whenever the endpoint has no top-level meta.expiresAt — typical for state populated via controller.set(Entity, …), SSR hydration, or useQuery. Verified the assertion fails on the buggy paths.shift() (m3.expiresAt returns FOO_2_EXPIRY instead of FOO_1_EXPIRY) and passes on the fix.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## fix/journey-mutation-on-result-cache-hit-scalar #3929 +/- ##
================================================================================
Coverage 98.21% 98.21%
================================================================================
Files 154 154
Lines 3018 3018
Branches 604 604
================================================================================
Hits 2964 2964
Misses 11 11
Partials 43 43 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Restore the GC-side getResponseMeta-countRef.ts integration test that the prior commit replaced. The two tests cover the journey-mutation bug from complementary angles: - getResponseMeta-countRef.ts: GC consumer of paths (entityCount under-counting → premature reaping under default GCPolicy). - getResponseMeta-paths.ts: non-GC consumer of paths (entityExpiresAt → suppressed entity-expiry refetch; fires under ImmortalGCPolicy too). Both pass on the fix; both fail on the buggy paths.shift().
05e1298
into
fix/journey-mutation-on-result-cache-hit-scalar
* chore: rebase PR #3925 onto latest master * pkg: Bump peerdeps of @data-client/react to support 0.17 (#3927) * pkg: Bump peerdeps of @data-client/react to support 0.17 * pkg: Update yarn.lock for peerdep bump * perf(normalizr): store consumer-facing journey at write time (#3928) Move the per-hit paths.slice(1) (and the hasStringDeps filter loop) out of GlobalCache.getResults and into the cache write. GlobalCache.paths() already produces the placeholder-free, function-free shape every consumer needs. Hand it to WeakDependencyMap.set as the journey, and the cache-hit branch can return that array by reference - no per-hit allocation, no per-hit typeof === 'function' walk. Safety: paths is now a shared reference held by every subsequent hit. The contract that consumers must not mutate it was established by the journey-mutation fix (PR #3925) and is exercised by the existing globalCache.test.ts regression test. * fix: Scalar reversion * test(core): add getResponseMeta-paths integration test (non-GC angle) (#3929) * test(core): replace getResponseMeta-countRef with getResponseMeta-paths The previous integration test poked GCPolicy['entityCount'] directly to prove the journey-mutation bug. Replace it with a test that asserts the public-API consequence the bug creates outside of GC: every subscriber to the same endpoint must observe the same expiresAt from Controller.getResponseMeta(). This is the property the bug actually broke for non-GC users: with the buggy paths.shift(), entityExpiresAt(paths, …) iterates a progressively-shorter list, dropping the entity with the earliest expiry first. Subscriber 2 observes a too-late expiresAt; subscriber 3+ observe Infinity and never refetch. Fires under ImmortalGCPolicy too, since entityExpiresAt is unconditional whenever the endpoint has no top-level meta.expiresAt — typical for state populated via controller.set(Entity, …), SSR hydration, or useQuery. Verified the assertion fails on the buggy paths.shift() (m3.expiresAt returns FOO_2_EXPIRY instead of FOO_1_EXPIRY) and passes on the fix. * test(core): keep existing countRef integration test alongside paths test Restore the GC-side getResponseMeta-countRef.ts integration test that the prior commit replaced. The two tests cover the journey-mutation bug from complementary angles: - getResponseMeta-countRef.ts: GC consumer of paths (entityCount under-counting → premature reaping under default GCPolicy). - getResponseMeta-paths.ts: non-GC consumer of paths (entityExpiresAt → suppressed entity-expiry refetch; fires under ImmortalGCPolicy too). Both pass on the fix; both fail on the buggy paths.shift().
Stacks on #3925.
Adds
packages/core/src/controller/__tests__/getResponseMeta-paths.tsalongside the existinggetResponseMeta-countRef.tsto cover the journey-mutation bug from a second, non-GC angle.The two tests are complementary:
pathsImmortalGCPolicy?getResponseMeta-countRef.ts(existing)GCPolicy.createCountRef({key, paths})→entityCountgetResponseMeta-paths.ts(new)entityExpiresAt(paths, state.entitiesMeta)→expiresAt→ hooks' refetch gateexpiresAtand never triggers entity-expiry refetchThe new test asserts a property a normal hook user can observe — every subscriber to the same endpoint must receive the same
expiresAtfromController.getResponseMeta()— without poking implementation-detail state likegcPolicy['entityCount']:The state shape (endpoints slot populated, no top-level
meta.expiresAt) corresponds to real-world cases whereentityExpiresAt(paths, …)is the only thing computing the endpoint's effectiveexpiresAt: SSR hydration,controller.set(Entity, …), oruseQuery. The resultingexpiresAtflows directly intouseSuspense/useCache/useFetch/useDLE/useLive's refetch gateif (Date.now() <= expiresAt && !forceFetch) return;, so a too-late value silently suppresses refetch on the 2nd+ subscriber.Verification
paths.shift()(and remove the journey-at-write change):m3.expiresAtreturns1_000_000(FOO_2_EXPIRY) instead of100(FOO_1_EXPIRY) — the new test fails at line 81; the existing countRef test also fails as it did pre-fix.yarn jest --selectProjects ReactDOM --testPathPatterns "packages/(normalizr|core|endpoint)"— passes.No production code changes.