/* (c) 2016 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geogig.geoserver.functional;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.Random;
import java.util.Set;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.geogig.geoserver.GeoGigTestData;
import org.geogig.geoserver.GeoGigTestData.CatalogBuilder;
import org.geogig.geoserver.config.RepositoryInfo;
import org.geogig.geoserver.config.RepositoryManager;
import org.geogig.geoserver.rest.GeoServerRepositoryProvider;
import org.geogig.web.functional.FunctionalTestContext;
import org.geoserver.catalog.Catalog;
import org.geoserver.catalog.DataStoreInfo;
import org.geoserver.data.test.SystemTestData;
import org.geoserver.test.GeoServerSystemTestSupport;
import org.geoserver.test.TestSetupFrequency;
import org.geotools.data.DataAccess;
import org.locationtech.geogig.geotools.data.GeoGigDataStore;
import org.locationtech.geogig.geotools.data.GeoGigDataStoreFactory;
import org.locationtech.geogig.repository.Repository;
import org.locationtech.geogig.repository.RepositoryResolver;
import org.locationtech.geogig.repository.impl.GeoGIG;
import org.locationtech.geogig.web.api.TestData;
import org.opengis.feature.Feature;
import org.opengis.feature.type.FeatureType;
import org.restlet.data.Method;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.w3c.dom.Document;
import com.google.common.base.Throwables;
import com.google.common.collect.Sets;
/**
* Context for running GeoGIG web API functional tests from the plugin endpoints.
*/
public class GeoServerFunctionalTestContext extends FunctionalTestContext {
private static final Random rnd = new Random();
private MockHttpServletResponse lastResponse = null;
private GeoServerRepositoryProvider repoProvider = null;
private GeoGigTestData testData;
/**
* Helper class for running mock http requests.
*/
private class TestHelper extends GeoServerSystemTestSupport {
public TestHelper() {
super();
testSetupFrequency = TestSetupFrequency.REPEAT;
}
/**
* Override to avoid creating default geoserver test data
*/
@Override
protected void setUpTestData(SystemTestData testData) throws Exception {
}
/**
* @return the catalog used by the test helper
*/
public Catalog getCatalog() {
return super.getCatalog();
}
/**
* Issue a POST request to the provided URL with the given file passed as form data.
*
* @param resourceUri the url to issue the request to
* @param formFieldName the form field name for the file to be posted
* @param file the file to post
*
* @return the response to the request
*/
public MockHttpServletResponse postFile(String resourceUri, String formFieldName, File file)
throws Exception {
Part[] parts = new Part[1];
parts[0] = new FilePart(formFieldName, file);
MultipartRequestEntity multipart = new MultipartRequestEntity(parts,
new PostMethod().getParams());
ByteArrayOutputStream bout = new ByteArrayOutputStream();
multipart.writeRequest(bout);
MockHttpServletRequest req = createRequest(resourceUri);
req.setContentType(multipart.getContentType());
req.addHeader("Content-Type", multipart.getContentType());
req.setMethod("POST");
req.setContent(bout.toByteArray());
return dispatch(req);
}
/**
* Issue a POST request to the provided URL with the given content.
*
* @param contentType the content type of the data
* @param resourceUri the url to issue the request to
* @param postContent the content to be posted
*
* @return the response to the request
*/
public MockHttpServletResponse postContent(String contentType, String resourceUri, String postContent)
throws Exception {
MockHttpServletRequest req = createRequest(resourceUri);
req.setContentType(contentType);
req.addHeader("Content-Type", contentType);
req.setMethod("POST");
req.setContent(postContent == null ? null : postContent.getBytes());
return dispatch(req);
}
/**
* Issue a request with the given {@link Method} to the provided resource URI.
*
* @param method the http method to use
* @param resourceUri the uri to issue the request to
*
* @return the response to the request
*/
public MockHttpServletResponse callInternal(Method method, String resourceUri)
throws Exception {
MockHttpServletRequest request = super.createRequest(resourceUri);
request.setMethod(method.getName());
return dispatch(request, null);
}
public MockHttpServletResponse callWithContentTypeInternal(Method method, String resourceUri,
String payload, String contentType) throws Exception {
MockHttpServletRequest request = super.createRequest(resourceUri);
request.setMethod(method.getName());
// set the JSON payload
request.setContent(payload.getBytes());
request.setContentType(contentType);
return dispatch(request, null);
}
/**
* Provide access to the helper function that turns the response into a {@link Document}.
*
* @param stream the stream to read as a document
*
* @return the {@link Document}
*
* @throws Exception
*/
public Document getDom(InputStream stream) throws Exception {
return dom(stream);
}
}
private TestHelper helper = null;
/**
* Set up the context for a scenario.
*/
@Override
protected void setUp() throws Exception {
testData = new GeoGigTestData(this.tempFolder);
if (helper == null) {
helper = new TestHelper();
helper.doSetup();
repoProvider = new GeoServerRepositoryProvider();
RepositoryManager.get().setCatalog(helper.getCatalog());
}
setVariable("@systemTempPath", tempFolder.getRoot().getCanonicalPath().replace("\\", "/"));
}
/**
* Clean up resources used in the scenario.
*/
@Override
protected void tearDown() throws Exception {
try {
if (helper != null) {
RepositoryManager.close();
helper.doTearDown();
}
if (testData != null) {
testData.tearDown();
}
} finally {
helper = null;
}
System.runFinalization();
}
/**
* Return the {@link GeoGIG} that corresponds to the given repository name.
*
* @param name the repository to get
*
* @return the repository
*/
@Override
public Repository getRepo(String name) {
return repoProvider.getGeogig(name).orNull();
}
/**
* Create a repository with the given name for testing.
*
* @param name the repository name
*
* @return a newly created {@link TestData} for the repository.
*
* @throws Exception
*/
@Override
protected TestData createRepo(String name) throws Exception {
testData.setUp(name);
testData.init().config("user.name", "John").config("user.email", "John.Doe@example.com");
GeoGIG geogig = testData.getGeogig();
Catalog catalog = helper.getCatalog();
CatalogBuilder catalogBuilder = testData.newCatalogBuilder(catalog);
int i = rnd.nextInt();
catalogBuilder.namespace("geogig.org/" + i).workspace("geogigws" + i)
.store("geogigstore" + i);
catalogBuilder.addAllRepoLayers().build();
String workspaceName = catalogBuilder.workspaceName();
String storeName = catalogBuilder.storeName();
DataStoreInfo dsInfo = catalog.getDataStoreByName(workspaceName, storeName);
assertNotNull(dsInfo);
assertEquals(GeoGigDataStoreFactory.DISPLAY_NAME, dsInfo.getType());
DataAccess<? extends FeatureType, ? extends Feature> dataStore = dsInfo.getDataStore(null);
assertNotNull(dataStore);
assertTrue(dataStore instanceof GeoGigDataStore);
String repoStr = (String) dsInfo.getConnectionParameters()
.get(GeoGigDataStoreFactory.REPOSITORY.key);
// resolve the repo
URI repoURI = new URI(repoStr);
RepositoryResolver resolver = RepositoryResolver.lookup(repoURI);
String repoName = resolver.getName(repoURI);
RepositoryInfo repositoryInfo = RepositoryManager.get().getByRepoName(repoName);
assertNotNull(repositoryInfo);
catalog.dispose();
return new TestData(geogig);
}
/**
* Helper function that asserts that there is a last response and returns it.
*
* @return the last response
*/
private MockHttpServletResponse getLastResponse() {
assertNotNull(lastResponse);
return lastResponse;
}
/**
* Issue a POST request to the provided URL with the given file passed as form data.
*
* @param resourceUri the url to issue the request to
* @param formFieldName the form field name for the file to be posted
* @param file the file to post
*/
@Override
protected void postFileInternal(String resourceUri, String formFieldName, File file) {
resourceUri = replaceVariables(resourceUri);
try {
lastResponse = helper.postFile("/geogig" + resourceUri, formFieldName, file);
} catch (Exception e) {
Throwables.propagate(e);
}
}
/**
* Issue a request with the given {@link Method} to the provided resource URI.
*
* @param method the http method to use
* @param resourceUri the uri to issue the request to
*/
@Override
protected void callInternal(Method method, String resourceUri) {
try {
//resourceUri = replaceVariables(resourceUri);
this.lastResponse = helper.callInternal(method, "/geogig" + resourceUri);
} catch (Exception e) {
Throwables.propagate(e);
}
}
/**
* @return the content of the last response as text
*/
@Override
public String getLastResponseText() {
try {
return getLastResponse().getContentAsString();
} catch (UnsupportedEncodingException ex) {
Throwables.propagate(ex);
}
return null;
}
/**
* @return the content type of the last response
*/
@Override
public String getLastResponseContentType() {
return getLastResponse().getContentType();
}
/**
* @return the content of the last response as a {@link Document}
*/
@Override
public Document getLastResponseAsDom() {
Document result = null;
try {
result = helper.getDom(getLastResponseInputStream());
} catch (Exception e) {
Throwables.propagate(e);
}
return result;
}
/**
* @return the status code of the last response
*/
@Override
public int getLastResponseStatus() {
MockHttpServletResponse response = getLastResponse();
// int code = response.getStatusCode();
// if (response.getStatusCode() == 200) {
// code = response.getErrorCode();
// }
// return code;
return response.getStatus();
}
/**
* @return the content of the last response as an {@link InputStream}
*
* @throws Exception
*/
@Override
public InputStream getLastResponseInputStream() throws Exception {
return new ByteArrayInputStream(getBinary(getLastResponse()));
}
/**
* @return the allowed http methods of the last response
*/
@Override
public Set<String> getLastResponseAllowedMethods() {
return Sets.newHashSet(getLastResponse().getHeader("ALLOW").replace(" ", "").split(","));
}
protected byte[] getBinary(MockHttpServletResponse response) {
// try {
return response.getContentAsByteArray();
// } catch (Exception e) {
// throw new RuntimeException("Whoops, did you change the MockRunner version? " +
// "If so, you might want to change this method too", e);
// }
}
/**
* Invokes URI request with specified Content-Type.
* @param method HTTP Method to invoke
* @param resourceUri URI address to which to send the request
* @param payload payload to encode into the request
* @param contentType Specific Content-Type header value to send
*/
@Override
public void callInternal(Method method, String resourceUri, String payload,
String contentType) {
try {
resourceUri = replaceVariables(resourceUri);
this.lastResponse = helper.callWithContentTypeInternal(method, "/geogig" + resourceUri,
payload, contentType);
} catch (Exception e) {
Throwables.propagate(e);
}
}
@Override
public String getHttpLocation(String repoName) {
return String.format("http://localhost:%d/geoserver/geogig/repos/%s", 8080, repoName);
}
@Override
protected void postContentInternal(String contentType, String resourceUri, String postContent) {
resourceUri = replaceVariables(resourceUri);
postContent = replaceVariables(postContent);
try {
lastResponse = helper.postContent(contentType, "/geogig" + resourceUri, postContent);
} catch (Exception e) {
Throwables.propagate(e);
}
}
@Override
protected void serveHttpRepos() throws Exception {
// Do Nothing
}
}