/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.spring.initializr.web;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import io.spring.initializr.metadata.InitializrMetadata;
import io.spring.initializr.metadata.InitializrMetadataBuilder;
import io.spring.initializr.metadata.InitializrMetadataProvider;
import io.spring.initializr.metadata.InitializrProperties;
import io.spring.initializr.test.generator.ProjectAssert;
import io.spring.initializr.web.AbstractInitializrIntegrationTests.Config;
import io.spring.initializr.web.mapper.InitializrMetadataVersion;
import io.spring.initializr.web.support.DefaultInitializrMetadataProvider;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Expand;
import org.apache.tools.ant.taskdefs.Untar;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.skyscreamer.jsonassert.JSONAssert;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import static org.junit.Assert.assertTrue;
/**
* @author Stephane Nicoll
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Config.class)
public abstract class AbstractInitializrIntegrationTests {
protected static final MediaType CURRENT_METADATA_MEDIA_TYPE =
InitializrMetadataVersion.V2_1.getMediaType();
@Rule
public final TemporaryFolder folder = new TemporaryFolder();
@Autowired
private RestTemplateBuilder restTemplateBuilder;
private RestTemplate restTemplate;
@Before
public void before() {
restTemplate = restTemplateBuilder.build();
}
protected abstract String createUrl(String context);
protected String htmlHome() {
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
return restTemplate.exchange(createUrl("/"), HttpMethod.GET,
new HttpEntity<Void>(headers), String.class).getBody();
}
/**
* Validate the "Content-Type" header of the specified response.
*/
protected void validateContentType(ResponseEntity<String> response,
MediaType expected) {
MediaType actual = response.getHeaders().getContentType();
assertTrue("Non compatible media-type, expected " + expected + ", got " + actual,
actual.isCompatibleWith(expected));
}
protected void validateMetadata(ResponseEntity<String> response, MediaType mediaType,
String version, JSONCompareMode compareMode) {
validateContentType(response, mediaType);
JSONObject json = new JSONObject(response.getBody());
JSONObject expected = readMetadataJson(version);
JSONAssert.assertEquals(expected, json, compareMode);
}
protected void validateCurrentMetadata(ResponseEntity<String> response) {
validateContentType(response, CURRENT_METADATA_MEDIA_TYPE);
validateCurrentMetadata(new JSONObject(response.getBody()));
}
protected void validateCurrentMetadata(JSONObject json) {
JSONObject expected = readMetadataJson("2.1.0");
JSONAssert.assertEquals(expected, json, JSONCompareMode.STRICT);
}
private JSONObject readMetadataJson(String version) {
return readJsonFrom("metadata/test-default-" + version + ".json");
}
/**
* Return a {@link ProjectAssert} for the following archive content.
*/
protected ProjectAssert zipProjectAssert(byte[] content) {
return projectAssert(content, ArchiveType.ZIP);
}
/**
* Return a {@link ProjectAssert} for the following TGZ archive.
*/
protected ProjectAssert tgzProjectAssert(byte[] content) {
return projectAssert(content, ArchiveType.TGZ);
}
protected ProjectAssert downloadZip(String context) {
byte[] body = downloadArchive(context);
return zipProjectAssert(body);
}
protected ProjectAssert downloadTgz(String context) {
byte[] body = downloadArchive(context);
return tgzProjectAssert(body);
}
protected byte[] downloadArchive(String context) {
return restTemplate.getForObject(createUrl(context), byte[].class);
}
protected ResponseEntity<String> invokeHome(String userAgentHeader,
String... acceptHeaders) {
return execute("/", String.class, userAgentHeader, acceptHeaders);
}
protected <T> ResponseEntity<T> execute(String contextPath, Class<T> responseType,
String userAgentHeader, String... acceptHeaders) {
HttpHeaders headers = new HttpHeaders();
if (userAgentHeader != null) {
headers.set("User-Agent", userAgentHeader);
}
if (acceptHeaders != null) {
List<MediaType> mediaTypes = new ArrayList<>();
for (String acceptHeader : acceptHeaders) {
mediaTypes.add(MediaType.parseMediaType(acceptHeader));
}
headers.setAccept(mediaTypes);
}
else {
headers.setAccept(Collections.emptyList());
}
return restTemplate.exchange(createUrl(contextPath), HttpMethod.GET,
new HttpEntity<Void>(headers), responseType);
}
protected ProjectAssert projectAssert(byte[] content, ArchiveType archiveType) {
try {
File archiveFile = writeArchive(content);
File project = folder.newFolder();
switch (archiveType) {
case ZIP:
unzip(archiveFile, project);
break;
case TGZ:
untar(archiveFile, project);
break;
}
return new ProjectAssert(project);
}
catch (Exception e) {
throw new IllegalStateException("Cannot unpack archive", e);
}
}
private void untar(File archiveFile, File project) {
Untar expand = new Untar();
expand.setProject(new Project());
expand.setDest(project);
expand.setSrc(archiveFile);
Untar.UntarCompressionMethod method = new Untar.UntarCompressionMethod();
method.setValue("gzip");
expand.setCompression(method );
expand.execute();
}
private void unzip(File archiveFile, File project) {
Expand expand = new Expand();
expand.setProject(new Project());
expand.setDest(project);
expand.setSrc(archiveFile);
expand.execute();
}
protected File writeArchive(byte[] body) throws IOException {
File archiveFile = folder.newFile();
try (FileOutputStream stream = new FileOutputStream(archiveFile)) {
stream.write(body);
}
return archiveFile;
}
protected JSONObject readJsonFrom(String path) {
try {
ClassPathResource resource = new ClassPathResource(path);
try (InputStream stream = resource.getInputStream()) {
String json = StreamUtils.copyToString(stream, Charset.forName("UTF-8"));
String placeholder = "";
if (this instanceof AbstractInitializrControllerIntegrationTests) {
placeholder = ((AbstractInitializrControllerIntegrationTests) this).host;
}
if (this instanceof AbstractFullStackInitializrIntegrationTests) {
AbstractFullStackInitializrIntegrationTests test =
(AbstractFullStackInitializrIntegrationTests) this;
placeholder = test.host + ":" + test.port;
}
// Let's parse the port as it is random
// TODO: put the port back somehow so it appears in stubs
String content = json.replaceAll("@host@", placeholder);
return new JSONObject(content);
}
}
catch (Exception e) {
throw new IllegalStateException("Cannot read JSON from path=" + path);
}
}
public RestTemplate getRestTemplate() {
return restTemplate;
}
private enum ArchiveType {
ZIP,
TGZ
}
@EnableAutoConfiguration
public static class Config {
@Bean
public InitializrMetadataProvider initializrMetadataProvider(
InitializrProperties properties) {
return new DefaultInitializrMetadataProvider(InitializrMetadataBuilder
.fromInitializrProperties(properties).build(), new RestTemplate()) {
@Override
protected void updateInitializrMetadata(InitializrMetadata metadata) {
// Disable metadata fetching from spring.io
}
};
}
}
}