package com.apollographql.apollo;
import com.apollographql.android.impl.httpcache.AllPlanets;
import com.apollographql.android.impl.normalizer.CharacterDetails;
import com.apollographql.android.impl.normalizer.CharacterNameById;
import com.apollographql.android.impl.normalizer.EpisodeHeroName;
import com.apollographql.android.impl.normalizer.HeroAndFriendsNames;
import com.apollographql.android.impl.normalizer.HeroAndFriendsNamesWithIDForParentOnly;
import com.apollographql.android.impl.normalizer.HeroAndFriendsNamesWithIDs;
import com.apollographql.android.impl.normalizer.HeroAndFriendsWithFragments;
import com.apollographql.android.impl.normalizer.HeroAppearsIn;
import com.apollographql.android.impl.normalizer.HeroParentTypeDependentField;
import com.apollographql.android.impl.normalizer.HeroTypeDependentAliasedField;
import com.apollographql.android.impl.normalizer.SameHeroTwice;
import com.apollographql.android.impl.normalizer.fragment.HeroWithFriendsFragment;
import com.apollographql.android.impl.normalizer.fragment.HumanWithIdFragment;
import com.apollographql.android.impl.normalizer.type.Episode;
import com.apollographql.apollo.api.Operation;
import com.apollographql.apollo.api.Response;
import com.apollographql.apollo.cache.normalized.CacheControl;
import com.apollographql.apollo.cache.normalized.CacheKey;
import com.apollographql.apollo.cache.normalized.lru.EvictionPolicy;
import com.apollographql.apollo.cache.normalized.lru.LruNormalizedCacheFactory;
import com.apollographql.apollo.exception.ApolloException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import okhttp3.OkHttpClient;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import static com.google.common.truth.Truth.assertThat;
public class NormalizedCacheTestCase {
private ApolloClient apolloClient;
private MockWebServer server;
@Before public void setUp() {
server = new MockWebServer();
OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
apolloClient = ApolloClient.builder()
.serverUrl(server.url("/"))
.okHttpClient(okHttpClient)
.normalizedCache(new LruNormalizedCacheFactory(EvictionPolicy.NO_EVICTION), new IdFieldCacheKeyResolver())
.dispatcher(Utils.immediateExecutorService())
.build();
}
@After public void tearDown() {
try {
server.shutdown();
} catch (IOException ignored) {
}
}
private MockResponse mockResponse(String fileName) throws IOException {
return new MockResponse().setChunkedBody(Utils.readFileToString(getClass(), "/" + fileName), 32);
}
@Test public void episodeHeroName() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroNameResponse.json"));
EpisodeHeroName query = EpisodeHeroName.builder().episode(Episode.EMPIRE).build();
Response<EpisodeHeroName.Data> body = apolloClient.query(query).execute();
assertThat(body.hasErrors()).isFalse();
body = apolloClient.query(query).cacheControl(CacheControl.CACHE_ONLY).execute();
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().name()).isEqualTo("R2-D2");
}
@Test public void heroAndFriendsNameResponse() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroAndFriendsNameResponse.json"));
HeroAndFriendsNames query = HeroAndFriendsNames.builder().episode(Episode.JEDI).build();
Response<HeroAndFriendsNames.Data> body = apolloClient.query(query).execute();
assertThat(body.hasErrors()).isFalse();
body = apolloClient.query(query).cacheControl(CacheControl.CACHE_ONLY).execute();
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().name()).isEqualTo("R2-D2");
assertThat(body.data().hero().friends()).hasSize(3);
assertThat(body.data().hero().friends().get(0).name()).isEqualTo("Luke Skywalker");
assertThat(body.data().hero().friends().get(1).name()).isEqualTo("Han Solo");
assertThat(body.data().hero().friends().get(2).name()).isEqualTo("Leia Organa");
}
@Test public void heroAndFriendsNamesWithIDs() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroAndFriendsNameWithIdsResponse.json"));
HeroAndFriendsNamesWithIDs query = HeroAndFriendsNamesWithIDs.builder().episode(Episode.NEWHOPE).build();
Response<HeroAndFriendsNamesWithIDs.Data> body = apolloClient.query(query).execute();
assertThat(body.hasErrors()).isFalse();
body = apolloClient.query(query).cacheControl(CacheControl.CACHE_ONLY).execute();
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().id()).isEqualTo("2001");
assertThat(body.data().hero().name()).isEqualTo("R2-D2");
assertThat(body.data().hero().friends()).hasSize(3);
assertThat(body.data().hero().friends().get(0).id()).isEqualTo("1000");
assertThat(body.data().hero().friends().get(0).name()).isEqualTo("Luke Skywalker");
assertThat(body.data().hero().friends().get(1).id()).isEqualTo("1002");
assertThat(body.data().hero().friends().get(1).name()).isEqualTo("Han Solo");
assertThat(body.data().hero().friends().get(2).id()).isEqualTo("1003");
assertThat(body.data().hero().friends().get(2).name()).isEqualTo("Leia Organa");
}
@Test public void heroAndFriendsNameWithIdsForParentOnly() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroAndFriendsNameWithIdsParentOnlyResponse.json"));
HeroAndFriendsNamesWithIDForParentOnly query = HeroAndFriendsNamesWithIDForParentOnly.builder()
.episode(Episode.NEWHOPE).build();
Response<HeroAndFriendsNamesWithIDForParentOnly.Data> body = apolloClient.query(query).execute();
assertThat(body.hasErrors()).isFalse();
body = apolloClient.query(query).cacheControl(CacheControl.CACHE_ONLY).execute();
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().id()).isEqualTo("2001");
assertThat(body.data().hero().name()).isEqualTo("R2-D2");
assertThat(body.data().hero().friends()).hasSize(3);
assertThat(body.data().hero().friends().get(0).name()).isEqualTo("Luke Skywalker");
assertThat(body.data().hero().friends().get(1).name()).isEqualTo("Han Solo");
assertThat(body.data().hero().friends().get(2).name()).isEqualTo("Leia Organa");
}
@Test public void heroAppearsInResponse() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroAppearsInResponse.json"));
HeroAppearsIn query = new HeroAppearsIn();
Response<HeroAppearsIn.Data> body = apolloClient.query(query).execute();
assertThat(body.hasErrors()).isFalse();
body = apolloClient.query(query).cacheControl(CacheControl.CACHE_ONLY).execute();
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().appearsIn()).hasSize(3);
assertThat(body.data().hero().appearsIn().get(0).name()).isEqualTo("NEWHOPE");
assertThat(body.data().hero().appearsIn().get(1).name()).isEqualTo("EMPIRE");
assertThat(body.data().hero().appearsIn().get(2).name()).isEqualTo("JEDI");
}
@Test public void heroParentTypeDependentField() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroParentTypeDependentFieldDroidResponse.json"));
HeroParentTypeDependentField query = HeroParentTypeDependentField.builder().episode(Episode.NEWHOPE).build();
Response<HeroParentTypeDependentField.Data> body = apolloClient.query(query).execute();
assertThat(body.hasErrors()).isFalse();
body = apolloClient.query(query).cacheControl(CacheControl.CACHE_ONLY).execute();
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().name()).isEqualTo("R2-D2");
assertThat(body.data().hero().asDroid().name()).isEqualTo("R2-D2");
assertThat(body.data().hero().asDroid().friends()).hasSize(3);
assertThat(body.data().hero().asDroid().friends().get(0).name()).isEqualTo("Luke Skywalker");
assertThat(body.data().hero().asDroid().friends().get(0).asHuman().name()).isEqualTo("Luke Skywalker");
assertThat(body.data().hero().asDroid().friends().get(0).asHuman().height()).isWithin(1.72);
}
@Test public void heroTypeDependentAliasedField() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroTypeDependentAliasedFieldResponse.json"));
HeroTypeDependentAliasedField query = HeroTypeDependentAliasedField.builder().episode(Episode.NEWHOPE).build();
Response<HeroTypeDependentAliasedField.Data> body = apolloClient.query(query).execute();
assertThat(body.hasErrors()).isFalse();
body = apolloClient.query(query).cacheControl(CacheControl.CACHE_ONLY).execute();
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().asHuman()).isNull();
assertThat(body.data().hero().asDroid().property()).isEqualTo("Astromech");
server.enqueue(mockResponse("HeroTypeDependentAliasedFieldResponseHuman.json"));
body = apolloClient.query(query).cacheControl(CacheControl.NETWORK_ONLY).execute();
assertThat(body.hasErrors()).isFalse();
body = apolloClient.query(query).cacheControl(CacheControl.CACHE_ONLY).execute();
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().asDroid()).isNull();
assertThat(body.data().hero().asHuman().property()).isEqualTo("Tatooine");
}
@Test public void sameHeroTwice() throws IOException, ApolloException {
server.enqueue(mockResponse("SameHeroTwiceResponse.json"));
SameHeroTwice query = new SameHeroTwice();
Response<SameHeroTwice.Data> body = apolloClient.query(query).execute();
assertThat(body.hasErrors()).isFalse();
body = apolloClient.query(query).cacheControl(CacheControl.CACHE_ONLY).execute();
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().name()).isEqualTo("R2-D2");
assertThat(body.data().r2().appearsIn()).hasSize(3);
assertThat(body.data().r2().appearsIn().get(0).name()).isEqualTo("NEWHOPE");
assertThat(body.data().r2().appearsIn().get(1).name()).isEqualTo("EMPIRE");
assertThat(body.data().r2().appearsIn().get(2).name()).isEqualTo("JEDI");
}
@Test public void cacheFirst() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroNameResponse.json"));
EpisodeHeroName query = EpisodeHeroName.builder().episode(Episode.EMPIRE).build();
Response<EpisodeHeroName.Data> body = apolloClient.query(query).execute();
assertThat(body.hasErrors()).isFalse();
body = apolloClient.query(query).cacheControl(CacheControl.CACHE_FIRST).execute();
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().name()).isEqualTo("R2-D2");
}
@Test public void cacheOnly() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroNameResponse.json"));
EpisodeHeroName query = EpisodeHeroName.builder().episode(Episode.EMPIRE).build();
Response<EpisodeHeroName.Data> body = apolloClient.query(query).execute();
assertThat(body.hasErrors()).isFalse();
body = apolloClient.query(query).cacheControl(CacheControl.CACHE_ONLY).execute();
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().name()).isEqualTo("R2-D2");
}
@Test public void networkFirst() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroNameResponse.json"));
EpisodeHeroName query = EpisodeHeroName.builder().episode(Episode.EMPIRE).build();
Response<EpisodeHeroName.Data> body = apolloClient.query(query).execute();
assertThat(body.hasErrors()).isFalse();
server.enqueue(mockResponse("HeroNameResponse.json"));
body = apolloClient.query(query).cacheControl(CacheControl.NETWORK_FIRST).execute();
assertThat(server.getRequestCount()).isEqualTo(2);
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().name()).isEqualTo("R2-D2");
server.enqueue(new MockResponse().setResponseCode(504).setBody(""));
body = apolloClient.query(query).cacheControl(CacheControl.NETWORK_FIRST).execute();
assertThat(server.getRequestCount()).isEqualTo(3);
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().name()).isEqualTo("R2-D2");
}
@Test public void networkOnly() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroNameResponse.json"));
EpisodeHeroName query = EpisodeHeroName.builder().episode(Episode.EMPIRE).build();
Response<EpisodeHeroName.Data> body = apolloClient.query(query).execute();
assertThat(body.hasErrors()).isFalse();
server.enqueue(mockResponse("HeroNameResponse.json"));
body = apolloClient.query(query).cacheControl(CacheControl.NETWORK_ONLY).execute();
assertThat(server.getRequestCount()).isEqualTo(2);
assertThat(body.hasErrors()).isFalse();
assertThat(body.data().hero().name()).isEqualTo("R2-D2");
}
@Test public void masterDetailSuccess() throws Exception {
server.enqueue(mockResponse("HeroAndFriendsNameWithIdsResponse.json"));
HeroAndFriendsNamesWithIDs query = HeroAndFriendsNamesWithIDs.builder().episode(Episode.NEWHOPE).build();
apolloClient.query(query).cacheControl(CacheControl.NETWORK_ONLY).execute();
CharacterNameById character = CharacterNameById.builder().id("1002").build();
CharacterNameById.Data characterData = apolloClient.query(character).cacheControl(CacheControl.CACHE_ONLY)
.execute().data();
assertThat(characterData).isNotNull();
assertThat(characterData.character()).isNotNull();
assertThat(characterData.character().asHuman().name()).isEqualTo("Han Solo");
}
@Test public void masterDetailFailIncomplete() throws Exception {
server.enqueue(mockResponse("HeroAndFriendsNameWithIdsResponse.json"));
HeroAndFriendsNamesWithIDs query = HeroAndFriendsNamesWithIDs.builder().episode(Episode.NEWHOPE).build();
apolloClient.query(query).cacheControl(CacheControl.NETWORK_ONLY).execute();
CharacterDetails character = CharacterDetails.builder().id("1002").build();
CharacterDetails.Data characterData = apolloClient.query(character).cacheControl(CacheControl.CACHE_ONLY)
.execute().data();
assertThat(characterData).isNull();
}
@Test public void independentQueriesGoToNetworkWhenCacheMiss() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroNameResponse.json"));
EpisodeHeroName query = EpisodeHeroName.builder().episode(Episode.EMPIRE).build();
Response<EpisodeHeroName.Data> body = apolloClient.query(query).execute();
assertThat(body.hasErrors()).isFalse();
assertThat(body.data()).isNotNull();
server.enqueue(mockResponse("AllPlanetsNullableField.json"));
AllPlanets allPlanetsQuery = new AllPlanets();
final Response<AllPlanets.Data> allPlanetsResponse = apolloClient.query(allPlanetsQuery).execute();
assertThat(allPlanetsResponse.hasErrors()).isFalse();
assertThat(allPlanetsResponse.data().allPlanets()).isNotNull();
}
@Test public void cacheOnlyMissReturnsNullData() throws IOException, ApolloException {
EpisodeHeroName query = EpisodeHeroName.builder().episode(Episode.EMPIRE).build();
Response<EpisodeHeroName.Data> body = apolloClient.query(query).cacheControl(CacheControl.CACHE_ONLY).execute();
assertThat(body.data()).isNull();
}
@Test public void cacheResponseWithNullableFields() throws IOException, ApolloException {
server.enqueue(mockResponse("AllPlanetsNullableField.json"));
AllPlanets query = new AllPlanets();
Response<AllPlanets.Data> body = apolloClient.query(query).cacheControl(CacheControl.NETWORK_ONLY).execute();
assertThat(body).isNotNull();
assertThat(body.hasErrors()).isFalse();
body = apolloClient.query(query).cacheControl(CacheControl.CACHE_ONLY).execute();
assertThat(body).isNotNull();
assertThat(body.hasErrors()).isFalse();
}
@Test public void readOperationFromStore() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroAndFriendsNameWithIdsResponse.json"));
HeroAndFriendsNamesWithIDs query = HeroAndFriendsNamesWithIDs.builder().episode(Episode.NEWHOPE).build();
apolloClient.query(query).execute();
HeroAndFriendsNamesWithIDs.Data data = apolloClient.apolloStore().read(query);
assertThat(data.hero().id()).isEqualTo("2001");
assertThat(data.hero().name()).isEqualTo("R2-D2");
assertThat(data.hero().friends()).hasSize(3);
assertThat(data.hero().friends().get(0).id()).isEqualTo("1000");
assertThat(data.hero().friends().get(0).name()).isEqualTo("Luke Skywalker");
assertThat(data.hero().friends().get(1).id()).isEqualTo("1002");
assertThat(data.hero().friends().get(1).name()).isEqualTo("Han Solo");
assertThat(data.hero().friends().get(2).id()).isEqualTo("1003");
assertThat(data.hero().friends().get(2).name()).isEqualTo("Leia Organa");
}
@Test public void readFragmentFromStore() throws IOException, ApolloException {
server.enqueue(mockResponse("HeroAndFriendsWithFragmentResponse.json"));
HeroAndFriendsNamesWithIDs query = HeroAndFriendsNamesWithIDs.builder().episode(Episode.NEWHOPE).build();
apolloClient.query(query).execute();
HeroWithFriendsFragment heroWithFriendsFragment = apolloClient.apolloStore().read(
new HeroWithFriendsFragment.Mapper(), CacheKey.from("2001"), Operation.EMPTY_VARIABLES);
assertThat(heroWithFriendsFragment.id()).isEqualTo("2001");
assertThat(heroWithFriendsFragment.name()).isEqualTo("R2-D2");
assertThat(heroWithFriendsFragment.friends()).hasSize(3);
assertThat(heroWithFriendsFragment.friends().get(0).fragments().humanWithIdFragment().id()).isEqualTo("1000");
assertThat(heroWithFriendsFragment.friends().get(0).fragments().humanWithIdFragment().name()).isEqualTo("Luke Skywalker");
assertThat(heroWithFriendsFragment.friends().get(1).fragments().humanWithIdFragment().id()).isEqualTo("1002");
assertThat(heroWithFriendsFragment.friends().get(1).fragments().humanWithIdFragment().name()).isEqualTo("Han Solo");
assertThat(heroWithFriendsFragment.friends().get(2).fragments().humanWithIdFragment().id()).isEqualTo("1003");
assertThat(heroWithFriendsFragment.friends().get(2).fragments().humanWithIdFragment().name()).isEqualTo("Leia Organa");
HumanWithIdFragment fragment = apolloClient.apolloStore().read(new HumanWithIdFragment.Mapper(),
CacheKey.from("1000"), Operation.EMPTY_VARIABLES);
assertThat(fragment.id()).isEqualTo("1000");
assertThat(fragment.name()).isEqualTo("Luke Skywalker");
fragment = apolloClient.apolloStore().read(new HumanWithIdFragment.Mapper(), CacheKey.from("1002"),
Operation.EMPTY_VARIABLES);
assertThat(fragment.id()).isEqualTo("1002");
assertThat(fragment.name()).isEqualTo("Han Solo");
fragment = apolloClient.apolloStore().read(new HumanWithIdFragment.Mapper(), CacheKey.from("1003"),
Operation.EMPTY_VARIABLES);
assertThat(fragment.id()).isEqualTo("1003");
assertThat(fragment.name()).isEqualTo("Leia Organa");
}
}