package com.apollographql.apollo;
import com.apollographql.android.impl.httpcache.AllPlanets;
import com.apollographql.android.impl.normalizer.EpisodeHeroName;
import com.apollographql.android.impl.normalizer.type.Episode;
import com.apollographql.apollo.cache.http.DiskLruHttpCacheStore;
import com.apollographql.apollo.cache.http.HttpCachePolicy;
import com.apollographql.apollo.exception.ApolloCanceledException;
import com.apollographql.apollo.exception.ApolloException;
import com.apollographql.apollo.internal.cache.http.HttpCache;
import com.apollographql.apollo.internal.interceptor.ApolloServerInterceptor;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nonnull;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.internal.io.FileSystem;
import okhttp3.internal.io.InMemoryFileSystem;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.fail;
public class ApolloPrefetchTest {
private static final long TIME_OUT_SECONDS = 3;
private ApolloClient apolloClient;
private MockWebServer server;
@Rule public InMemoryFileSystem inMemoryFileSystem = new InMemoryFileSystem();
private okhttp3.Request lastHttRequest;
private okhttp3.Response lastHttResponse;
private MockHttpCacheStore cacheStore;
private OkHttpClient okHttpClient;
@Before
public void setup() {
server = new MockWebServer();
cacheStore = new MockHttpCacheStore();
cacheStore.delegate = new DiskLruHttpCacheStore(inMemoryFileSystem, new File("/cache/"), Integer.MAX_VALUE);
okHttpClient = new OkHttpClient.Builder()
.addInterceptor(new Interceptor() {
@Override public okhttp3.Response intercept(Chain chain) throws IOException {
lastHttRequest = chain.request();
lastHttResponse = chain.proceed(lastHttRequest);
return lastHttResponse;
}
})
.build();
apolloClient = ApolloClient.builder()
.serverUrl(server.url("/"))
.okHttpClient(okHttpClient)
.httpCacheStore(cacheStore)
.build();
}
@After public void tearDown() {
try {
apolloClient.clearHttpCache();
server.shutdown();
} catch (Exception ignore) {
}
}
@Test
public void ApolloPrefetchNotCalled_WhenCanceled() throws IOException, InterruptedException {
final NamedCountDownLatch responseLatch = new NamedCountDownLatch("ApolloPrefetchNotCalled_WhenCanceled", 1);
EpisodeHeroName query = EpisodeHeroName.builder().episode(Episode.EMPIRE).build();
server.enqueue(mockResponse("EpisodeHeroNameResponseWithId.json"));
ApolloPrefetch prefetch = apolloClient.prefetch(query);
prefetch.cancel();
prefetch.enqueue(new ApolloPrefetch.Callback() {
@Override public void onSuccess() {
responseLatch.countDown();
if (responseLatch.getCount() == 0) {
Assert.fail("Received callback, although apollo prefetch has already been canceled");
}
}
@Override public void onFailure(@Nonnull ApolloException e) {
responseLatch.countDown();
Assert.fail(e.getMessage());
}
});
responseLatch.await(TIME_OUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void apolloCanceledExceptionEnqueue() throws Exception {
final NamedCountDownLatch responseLatch = new NamedCountDownLatch("apolloCanceledExceptionEnqueue", 1);
EpisodeHeroName query = EpisodeHeroName.builder().episode(Episode.EMPIRE).build();
server.enqueue(mockResponse("EpisodeHeroNameResponseWithId.json")
.setBodyDelay(TIME_OUT_SECONDS, TimeUnit.SECONDS));
final AtomicReference<ApolloException> errorRef = new AtomicReference<>();
ApolloPrefetch apolloCall = apolloClient.prefetch(query);
apolloCall.enqueue(new ApolloPrefetch.Callback() {
@Override public void onSuccess() {
responseLatch.countDown();
}
@Override public void onFailure(@Nonnull ApolloException e) {
errorRef.set(e);
responseLatch.countDown();
}
});
Thread.sleep(500);
apolloCall.cancel();
responseLatch.await(TIME_OUT_SECONDS, TimeUnit.SECONDS);
assertThat(errorRef.get()).isInstanceOf(ApolloCanceledException.class);
}
@Test
public void apolloCanceledExceptionExecute() throws Exception {
final NamedCountDownLatch responseLatch = new NamedCountDownLatch("apolloCanceledExceptionExecute", 1);
EpisodeHeroName query = EpisodeHeroName.builder().episode(Episode.EMPIRE).build();
server.enqueue(mockResponse("EpisodeHeroNameResponseWithId.json")
.setBodyDelay(TIME_OUT_SECONDS, TimeUnit.SECONDS));
final AtomicReference<ApolloException> errorRef = new AtomicReference<>();
final ApolloPrefetch apolloCall = apolloClient.prefetch(query);
new Thread(new Runnable() {
@Override public void run() {
try {
apolloCall.execute();
} catch (ApolloException e) {
errorRef.set(e);
}
responseLatch.countDown();
}
}).start();
Thread.sleep(500);
apolloCall.cancel();
responseLatch.await(TIME_OUT_SECONDS, TimeUnit.HOURS);
assertThat(errorRef.get()).isInstanceOf(ApolloCanceledException.class);
}
@Test public void prefetchDefault() throws IOException, ApolloException {
enqueueResponse("HttpCacheTestAllPlanets.json");
apolloClient.prefetch(new AllPlanets()).execute();
checkCachedResponse("HttpCacheTestAllPlanets.json");
assertThat(apolloClient.query(new AllPlanets())
.httpCachePolicy(HttpCachePolicy.CACHE_ONLY.expireAfter(2, TimeUnit.SECONDS)).execute()
.hasErrors()).isFalse();
}
@Test public void prefetchNoCacheStore() throws IOException, ApolloException {
ApolloClient apolloClient = ApolloClient.builder()
.serverUrl(server.url("/"))
.okHttpClient(okHttpClient)
.build();
enqueueResponse("HttpCacheTestAllPlanets.json");
apolloClient.prefetch(new AllPlanets()).execute();
enqueueResponse("HttpCacheTestAllPlanets.json");
assertThat(apolloClient.query(new AllPlanets()).execute().hasErrors()).isFalse();
}
@Test public void prefetchFileSystemWriteFailure() throws IOException {
FaultyHttpCacheStore faultyCacheStore = new FaultyHttpCacheStore(FileSystem.SYSTEM);
cacheStore.delegate = faultyCacheStore;
enqueueResponse("HttpCacheTestAllPlanets.json");
faultyCacheStore.failStrategy(FaultyHttpCacheStore.FailStrategy.FAIL_HEADER_WRITE);
try {
apolloClient.prefetch(new AllPlanets()).execute();
fail("exception expected");
} catch (Exception expected) {
}
checkNoCachedResponse();
enqueueResponse("HttpCacheTestAllPlanets.json");
faultyCacheStore.failStrategy(FaultyHttpCacheStore.FailStrategy.FAIL_BODY_WRITE);
try {
apolloClient.prefetch(new AllPlanets()).execute();
fail("exception expected");
} catch (Exception expected) {
}
checkNoCachedResponse();
}
private void enqueueResponse(String fileName) throws IOException {
server.enqueue(mockResponse(fileName));
}
private void checkCachedResponse(String fileName) throws IOException {
String cacheKey = ApolloServerInterceptor.cacheKey(lastHttRequest.body());
okhttp3.Response response = apolloClient.cachedHttpResponse(cacheKey);
assertThat(response).isNotNull();
assertThat(response.body().source().readUtf8()).isEqualTo(Utils.readFileToString(getClass(), "/" + fileName));
response.body().source().close();
}
private void checkNoCachedResponse() throws IOException {
String cacheKey = lastHttRequest.header(HttpCache.CACHE_KEY_HEADER);
okhttp3.Response cachedResponse = apolloClient.cachedHttpResponse(cacheKey);
assertThat(cachedResponse).isNull();
}
private MockResponse mockResponse(String fileName) throws IOException {
return new MockResponse().setChunkedBody(Utils.readFileToString(getClass(), "/" + fileName), 32);
}
}