Skip to content

Commit

Permalink
New test of re-reading incomplete network results from cache.
Browse files Browse the repository at this point in the history
  • Loading branch information
benjamn committed Sep 13, 2023
1 parent 7f9c5ac commit a201111
Showing 1 changed file with 156 additions and 0 deletions.
156 changes: 156 additions & 0 deletions src/cache/inmemory/__tests__/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// This file contains InMemoryCache-specific tests that exercise the
// ApolloClient class. Other test modules in this directory only test
// InMemoryCache and related utilities, without involving ApolloClient.

import { ApolloClient, WatchQueryFetchPolicy, gql } from "../../../core";
import { ApolloLink } from "../../../link/core";
import { Observable } from "../../../utilities";
import { InMemoryCache } from "../..";
import { subscribeAndCount } from "../../../testing";

describe("InMemoryCache tests exercising ApolloClient", () => {
it.each<WatchQueryFetchPolicy>([
"cache-first",
"network-only",
"cache-and-network",
"cache-only",
"no-cache",
])("results should be read from cache even when incomplete (fetchPolicy %s)", fetchPolicy => {
const dateString = new Date().toISOString();

const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
date: {
read(existing) {
return new Date(existing || dateString);
},
},
},
},
},
});

const client = new ApolloClient({
link: new ApolloLink(operation => new Observable(observer => {
observer.next({
data: {
// This raw string should be converted to a Date by the Query.date
// read function passed to InMemoryCache below.
date: dateString,
// Make sure we don't accidentally return fields not mentioned in
// the query just because the result is incomplete.
ignored: "irrelevant to the subscribed query",
// Note the Query.missing field is, well, missing.
},
});
setTimeout(() => {
observer.complete();
}, 10);
})),
cache,
});

const query = gql`
query {
date
missing
}
`;

const observable = client.watchQuery({
query,
fetchPolicy, // Varies with each test iteration
returnPartialData: true,
});

return new Promise<void>((resolve, reject) => {
subscribeAndCount(reject, observable, (handleCount, result) => {
let adjustedCount = handleCount;
if (
fetchPolicy === "network-only" ||
fetchPolicy === "no-cache" ||
fetchPolicy === "cache-only"
) {
// The network-only, no-cache, and cache-only fetch policies do not
// deliver a loading:true result initially, so we adjust the
// handleCount to skip that case.
++adjustedCount;
}

// The only fetch policy that does not re-read results from the cache is
// the "no-cache" policy. In this test, that means the Query.date field
// will remain as a raw string rather than being converted to a Date by
// the read function.
const expectedDate =
fetchPolicy === "no-cache" ? dateString : new Date(dateString);

if (adjustedCount === 1) {
expect(result.loading).toBe(true);
expect(result.data).toEqual({
date: expectedDate,
});

} else if (adjustedCount === 2) {
expect(result.loading).toBe(false);
expect(result.data).toEqual({
date: expectedDate,
// The no-cache fetch policy does return extraneous fields from the
// raw network result that were not requested in the query, since
// the cache is not consulted.
...(fetchPolicy === "no-cache" ? {
ignored: "irrelevant to the subscribed query"
} : null),
});

if (fetchPolicy === "no-cache") {
// The "no-cache" fetch policy does not receive updates from the
// cache, so we finish the test early (passing).
setTimeout(() => resolve(), 20);

} else {
cache.writeQuery({
query: gql`query { missing }`,
data: {
missing: "not missing anymore",
},
});
}

} else if (adjustedCount === 3) {
expect(result.loading).toBe(false);
expect(result.data).toEqual({
date: expectedDate,
missing: "not missing anymore",
});

expect(cache.extract()).toEqual({
ROOT_QUERY: {
__typename: "Query",
// The cache-only fetch policy does not receive updates from the
// network, so it never ends up writing the date field into the
// cache explicitly, though Query.date can still be synthesized by
// the read function.
...(fetchPolicy === "cache-only" ? null : {
// Make sure this field is stored internally as a raw string.
date: dateString,
}),
// Written explicitly with cache.writeQuery above.
missing: "not missing anymore",
// The ignored field is never written to the cache, because it is
// not included in the query.
},
});

// Wait 20ms to give the test a chance to fail if there are unexpected
// additional results.
setTimeout(() => resolve(), 20);

} else {
reject(new Error(`Unexpected count ${adjustedCount}`));
}
});
});
});
});

0 comments on commit a201111

Please sign in to comment.