package org.jboss.arquillian.drone.webdriver.binary.downloading.source;
import com.google.common.base.Charsets;
import com.google.common.io.CharStreams;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import io.specto.hoverfly.junit.dsl.ResponseBuilder;
import io.specto.hoverfly.junit.rule.HoverflyRule;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpStatus;
import org.assertj.core.api.JUnitSoftAssertions;
import org.jboss.arquillian.drone.webdriver.binary.downloading.ExternalBinary;
import org.jboss.arquillian.drone.webdriver.utils.GitHubLastUpdateCache;
import org.jboss.arquillian.drone.webdriver.utils.HttpClient;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import static io.specto.hoverfly.junit.core.SimulationSource.dsl;
import static io.specto.hoverfly.junit.dsl.HoverflyDsl.service;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
public class GitHubSourceLatestReleaseFromTagsTestCase {
private static final String CACHED_CONTENT = "{\"lastModified\":\"Tue, 28 Mar 2017 05:23:15 GMT\"," +
"\"asset\":" +
"{\"version\":\"2.1.1\"," +
"\"url\":\"https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2\"}" +
"}";
@ClassRule
public static HoverflyRule hoverflyRule = HoverflyRule.inSimulationMode();
@Rule
public final TemporaryFolder folder = new TemporaryFolder();
@Rule
public final JUnitSoftAssertions softly = new JUnitSoftAssertions();
private final String RESPONSE_BODY;
private File tmpFolder;
private PhantomJSSourceForLatestRelease phantomJSSource;
private HttpClient httpClientSpy;
private GitHubLastUpdateCache cacheSpy;
public GitHubSourceLatestReleaseFromTagsTestCase() throws IOException {
this.RESPONSE_BODY = loadResponseBody("hoverfly/gh.simulation.ariya@phantomjs.tags.json");
}
@Before
public void wireComponentsUnderTest() throws IOException {
this.tmpFolder = folder.newFolder();
this.httpClientSpy = spy(new HttpClient());
this.cacheSpy = spy(new GitHubLastUpdateCache(tmpFolder));
this.phantomJSSource = new PhantomJSSourceForLatestRelease(httpClientSpy, cacheSpy);
}
@Test
public void should_load_release_information_from_gh_and_store_in_cache() throws Exception {
// given
hoverflyRule.simulate(dsl(service("https://api.github.com")
.get("/repos/ariya/phantomjs/tags")
.queryParam("page", 1)
.willReturn(
ResponseBuilder.response()
.header("Last-Modified", "Tue, 28 Mar 2017 05:23:15 GMT")
.body(RESPONSE_BODY)
)));
final String expectedVersion = "2.1.1";
// when
final ExternalBinary latestRelease = phantomJSSource.getLatestRelease();
// then
softly.assertThat(latestRelease.getVersion()).isEqualTo(expectedVersion);
softly.assertThat(tmpFolder.listFiles()).hasSize(1);
softly.assertThat(tmpFolder.listFiles()[0]).isFile().hasContent(CACHED_CONTENT);
verify(httpClientSpy).get(anyString(), anyMap());
verify(cacheSpy, times(0)).load(anyString(), any());
verify(cacheSpy, times(1)).store(any(), anyString(), any());
}
@Test
public void should_load_release_information_from_cache_when_not_changed() throws Exception {
// given
hoverflyRule.simulate(dsl(service("https://api.github.com")
.get("/repos/ariya/phantomjs/tags")
.header(HttpHeaders.IF_MODIFIED_SINCE, "Tue, 28 Mar 2017 05:23:15 GMT")
.queryParam("page", 1)
.willReturn(
ResponseBuilder.response().status(HttpStatus.SC_NOT_MODIFIED)
)));
createCacheFile("gh.cache.ariya@phantomjs.json", CACHED_CONTENT);
final String expectedVersion = "2.1.1";
// when
final ExternalBinary latestRelease = phantomJSSource.getLatestRelease();
// then
softly.assertThat(latestRelease.getVersion()).isEqualTo(expectedVersion);
softly.assertThat(tmpFolder.listFiles()).hasSize(1);
softly.assertThat(tmpFolder.listFiles()[0]).isFile().hasContent(CACHED_CONTENT);
verify(httpClientSpy).get(anyString(), anyMap());
verify(cacheSpy, times(1)).load(anyString(), any());
verify(cacheSpy, times(0)).store(any(), anyString(), any());
}
@Test
// With this test we make sure that the internal date is converted to RFC 2126 with GMT prior to GH call
public void should_load_release_information_from_gh_and_overwrite_cache_when_last_modified_changed()
throws Exception {
// given
String cached_content =
"{\"lastModified\":\"Sun, 01 Jan 2017 18:16:07 +0100\"," + // Here we store the date with the offset
"\"asset\":" +
"{\"version\":\"2.1.0\"," +
"\"url\":\"https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.0-linux-x86_64.tar.bz2\"}"
+
"}";
hoverflyRule.simulate(dsl(service("https://api.github.com")
.get("/repos/ariya/phantomjs/tags")
.header(HttpHeaders.IF_MODIFIED_SINCE,
"Sun, 01 Jan 2017 17:16:07 GMT") // But we expect it to be sent in GMT, as this is how we match the request
.queryParam("page", 1)
.willReturn(
ResponseBuilder.response()
.header("Last-Modified", "Tue, 28 Mar 2017 05:23:15 GMT")
.body(RESPONSE_BODY)
)));
createCacheFile("gh.cache.ariya@phantomjs.json", cached_content);
final String expectedVersion = "2.1.1";
// when
final ExternalBinary latestRelease = phantomJSSource.getLatestRelease();
// then
softly.assertThat(latestRelease.getVersion()).isEqualTo(expectedVersion);
softly.assertThat(tmpFolder.listFiles()).hasSize(1);
softly.assertThat(tmpFolder.listFiles()[0]).isFile().hasContent(CACHED_CONTENT);
verify(httpClientSpy).get(anyString(), anyMap());
verify(cacheSpy, times(0)).load(anyString(), any());
verify(cacheSpy, times(1)).store(any(), anyString(), any());
}
private String loadResponseBody(String path) throws IOException {
String response;
ClassLoader classLoader = getClass().getClassLoader();
try (
final InputStreamReader inputStreamReader = new InputStreamReader(classLoader.getResourceAsStream(path),
Charsets.UTF_8)) {
response = CharStreams.toString(inputStreamReader);
}
return response;
}
private void createCacheFile(String fileName, String content) throws FileNotFoundException {
try (final PrintWriter printWriter = new PrintWriter(new File(tmpFolder.getAbsolutePath() + "/" + fileName))) {
printWriter.print(content);
printWriter.flush();
}
}
class PhantomJSSourceForLatestRelease extends PhantomJSGitHubBitbucketSource {
private String TAGS_URL = "/tags";
private String TAG_NAME = "name";
PhantomJSSourceForLatestRelease(HttpClient httpClient,
GitHubLastUpdateCache gitHubLastUpdateCache) {
super(httpClient, gitHubLastUpdateCache);
}
public ExternalBinary getLatestRelease() throws Exception {
final HttpClient.Response response =
sentGetRequestWithPagination(getProjectUrl() + TAGS_URL, 1, lastModificationHeader());
final ExternalBinary latestPhantomJSBinary;
if (response.hasPayload()) {
JsonArray releaseTags = getGson().fromJson(response.getPayload(), JsonElement.class).getAsJsonArray();
if (releaseTags.size() == 0) {
return null;
}
String version = releaseTags.get(0).getAsJsonObject().get(TAG_NAME).getAsString();
latestPhantomJSBinary = new ExternalBinary(version);
latestPhantomJSBinary.setUrl(getUrlForVersion(version));
getCache().store(latestPhantomJSBinary, getUniqueKey(), extractModificationDate(response));
} else {
latestPhantomJSBinary = getCache().load(getUniqueKey(), ExternalBinary.class);
}
return latestPhantomJSBinary;
}
}
}