/* (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.geoserver.rest.resources;
import net.sf.json.JSON;
import net.sf.json.JSONObject;
import net.sf.json.test.JSONAssert;
import org.apache.commons.lang.SystemUtils;
import org.custommonkey.xmlunit.NamespaceContext;
import org.custommonkey.xmlunit.SimpleNamespaceContext;
import org.custommonkey.xmlunit.XMLAssert;
import org.custommonkey.xmlunit.XMLUnit;
import org.geoserver.platform.resource.Resource;
import org.geoserver.platform.resource.Resource.Type;
import org.geoserver.platform.resource.Resources;
import org.geoserver.rest.RestBaseController;
import org.geoserver.rest.util.IOUtils;
import org.geoserver.test.GeoServerSystemTestSupport;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.w3c.dom.Document;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
*
* @author Niels Charlier
*
*/
public class ResourceControllerTest extends GeoServerSystemTestSupport {
private final String STR_MY_TEST;
private final String STR_MY_NEW_TEST;
private final NamespaceContext NS_XML, NS_HTML;
private final DateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.S z");
private final DateFormat FORMAT_HEADER = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
private Resource myRes;
public ResourceControllerTest() {
CharsetEncoder encoder = Charset.defaultCharset().newEncoder();
if (encoder.canEncode("éö")) {
STR_MY_TEST = "This is my test. é ö";
} else {
STR_MY_TEST = "This is my test.";
}
if (encoder.canEncode("ۏ")) {
STR_MY_NEW_TEST = "This is my new test. € è";
} else {
STR_MY_NEW_TEST = "This is my new test.";
}
FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
FORMAT_HEADER.setTimeZone(TimeZone.getTimeZone("GMT"));
Map<String, String> mapXML = new HashMap<>();
mapXML.put("atom", "http://www.w3.org/2005/Atom");
NS_XML = new SimpleNamespaceContext(mapXML);
Map<String, String> mapHTML = new HashMap<>();
mapHTML.put("x", "http://www.w3.org/1999/xhtml");
NS_HTML = new SimpleNamespaceContext(mapHTML);
}
@Before
public void initialise() throws IOException {
myRes = getDataDirectory().get("/mydir/myres");
try (OutputStreamWriter os = new OutputStreamWriter(myRes.out())) {
os.append(STR_MY_TEST);
}
try (OutputStreamWriter os = new OutputStreamWriter(getDataDirectory().get("/mydir2/fake.png").out())) {
os.append("This is not a real png file.");
}
try (OutputStreamWriter os = new OutputStreamWriter(getDataDirectory().get("/poëzie/café").out())) {
os.append("The content of this file is irrelevant.");
}
IOUtils.copyStream(getClass().getResourceAsStream("testimage.png"),
getDataDirectory().get("/mydir2/imagewithoutextension").out(), true, true);
}
@Test
public void testResource() throws Exception {
String str = getAsString(RestBaseController.ROOT_PATH+"/resource/mydir/myres").trim();
Assert.assertEquals(STR_MY_TEST, str);
}
@Test
public void testResourceMetadataXML() throws Exception {
XMLUnit.setXpathNamespaceContext(NS_XML);
Document doc = getAsDOM(RestBaseController.ROOT_PATH+"/resource/mydir/myres?operation=mEtAdATa&format=xml");
//print(doc);
XMLAssert.assertXpathEvaluatesTo("myres", "/ResourceMetadata/name", doc);
XMLAssert.assertXpathEvaluatesTo("/mydir", "/ResourceMetadata/parent/path", doc);
XMLAssert.assertXpathEvaluatesTo("http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/mydir",
"/ResourceMetadata/parent/atom:link/@href", doc);
XMLAssert.assertXpathEvaluatesTo(FORMAT.format(myRes.lastmodified()),
"/ResourceMetadata/lastModified", doc);
}
@Test
public void testResourceMetadataJSON() throws Exception {
JSON json = getAsJSON(RestBaseController.ROOT_PATH+"/resource/mydir/myres?operation=metadata&format=json");
//print(json);
String expected = "{\"ResourceMetadata\": {"
+ " \"name\": \"myres\","
+ " \"parent\": {"
+ " \"path\": \"/mydir\","
+ " \"link\": {"
+ " \"href\": \"http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/mydir\","
+ " \"rel\": \"alternate\", "
+ " \"type\": \"application/json\""
+ " }"
+ " },"
+ " \"lastModified\": \"" + FORMAT.format(myRes.lastmodified()) + "\","
+ " \"type\": \"resource\""
+ "}}";
JSONAssert.assertEquals(expected, (JSONObject) json);
}
@Test
public void testResourceMetadataHTML() throws Exception {
XMLUnit.setXpathNamespaceContext(NS_HTML);
Document doc = getAsDOM(RestBaseController.ROOT_PATH+"/resource/mydir/myres?operation=metadata&format=html");
//print(doc);
XMLAssert.assertXpathEvaluatesTo("Name: 'myres'", "/x:html/x:body/x:ul/x:li[1]", doc);
XMLAssert.assertXpathEvaluatesTo("http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/mydir",
"/x:html/x:body/x:ul/x:li[2]/x:a/@href", doc);
XMLAssert.assertXpathEvaluatesTo("Type: resource", "/x:html/x:body/x:ul/x:li[3]", doc);
XMLAssert.assertXpathEvaluatesTo("Last modified: " + new Date(myRes.lastmodified()).toString(),
"/x:html/x:body/x:ul/x:li[4]", doc);
}
@Test
public void testResourceHeaders() throws Exception {
MockHttpServletResponse response = getAsServletResponse(RestBaseController.ROOT_PATH+"/resource/mydir2/fake.png");
Assert.assertEquals(
FORMAT_HEADER.format(getDataDirectory().get("/mydir2/fake.png").lastmodified()),
response.getHeader("Last-Modified"));
Assert.assertEquals("http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/mydir2",
response.getHeader("Resource-Parent"));
Assert.assertEquals("resource", response.getHeader("Resource-Type"));
assertContentType("image/png", response);
}
@Test
public void testResourceHead() throws Exception {
MockHttpServletResponse response = headAsServletResponse(RestBaseController.ROOT_PATH+"/resource/mydir2/fake.png");
Assert.assertEquals(
FORMAT_HEADER.format(getDataDirectory().get("/mydir2/fake.png").lastmodified()),
response.getHeader("Last-Modified"));
Assert.assertEquals("http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/mydir2",
response.getHeader("Resource-Parent"));
Assert.assertEquals("resource", response.getHeader("Resource-Type"));
assertContentType("image/png", response);
}
@Test
public void testSpecialCharacterNames() throws Exception {
// if the file system encoded the file with a ? we need to skip this test
Assume.assumeTrue(SystemUtils.IS_OS_WINDOWS || getDataDirectory().get("po?zie").getType() == Type.UNDEFINED);
Assert.assertEquals(Type.DIRECTORY, getDataDirectory().get("poëzie").getType());
XMLUnit.setXpathNamespaceContext(NS_XML);
Document doc = getAsDOM(RestBaseController.ROOT_PATH+"/resource/po%c3%abzie?format=xml");
XMLAssert.assertXpathEvaluatesTo("http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/po%C3%ABzie/caf%C3%A9",
"/ResourceDirectory/children/child/atom:link/@href", doc);
MockHttpServletResponse response = getAsServletResponse(RestBaseController.ROOT_PATH+"/resource/po%c3%abzie/caf%c3%a9?format=xml");
Assert.assertEquals(200, response.getStatus());
Assert.assertEquals("resource", response.getHeader("Resource-Type"));
Assert.assertEquals("http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/po%C3%ABzie",
response.getHeader("Resource-Parent"));
}
@Test
public void testDirectoryXML() throws Exception {
XMLUnit.setXpathNamespaceContext(NS_XML);
Document doc = getAsDOM(RestBaseController.ROOT_PATH+"/resource/mydir?format=xml");
print(doc);
XMLAssert.assertXpathEvaluatesTo("mydir", "/ResourceDirectory/name", doc);
XMLAssert.assertXpathEvaluatesTo("/", "/ResourceDirectory/parent/path", doc);
XMLAssert.assertXpathEvaluatesTo("http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/",
"/ResourceDirectory/parent/atom:link/@href", doc);
XMLAssert.assertXpathEvaluatesTo(FORMAT.format(myRes.parent().lastmodified()),
"/ResourceDirectory/lastModified", doc);
XMLAssert.assertXpathEvaluatesTo("myres", "/ResourceDirectory/children/child/name", doc);
XMLAssert.assertXpathEvaluatesTo("http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/mydir/myres",
"/ResourceDirectory/children/child/atom:link/@href", doc);
}
@Test
public void testDirectoryJSON() throws Exception {
JSON json = getAsJSON(RestBaseController.ROOT_PATH+"/resource/mydir?format=json");
print(json);
String expected = "{\"ResourceDirectory\": {"
+ "\"name\": \"mydir\","
+ "\"parent\": {"
+ " \"path\": \"/\","
+ " \"link\": {"
+ " \"href\": \"http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/\","
+ " \"rel\": \"alternate\","
+ " \"type\": \"application/json\""
+ " }"
+ "},"
+ "\"lastModified\": \"" + FORMAT.format(myRes.parent().lastmodified()) + "\","
+ " \"children\": {\"child\": [ {"
+ " \"name\": \"myres\","
+ " \"link\": {"
+ " \"href\": \"http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/mydir/myres\","
+ " \"rel\": \"alternate\","
+ " \"type\": \"application/octet-stream\""
+ " }"
+ " }]}"
+ "}}";
JSONAssert.assertEquals(expected, (JSONObject) json);
}
@Test
public void testDirectoryHTML() throws Exception {
XMLUnit.setXpathNamespaceContext(NS_HTML);
Document doc = getAsDOM(RestBaseController.ROOT_PATH+"/resource/mydir?format=html");
//print(doc);
XMLAssert.assertXpathEvaluatesTo("Name: 'mydir'", "/x:html/x:body/x:ul/x:li[1]", doc);
XMLAssert.assertXpathEvaluatesTo("http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/",
"/x:html/x:body/x:ul/x:li[2]/x:a/@href", doc);
XMLAssert.assertXpathEvaluatesTo(
"Last modified: " + new Date(myRes.parent().lastmodified()).toString(),
"/x:html/x:body/x:ul/x:li[3]", doc);
XMLAssert.assertXpathEvaluatesTo("http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/mydir/myres",
"/x:html/x:body/x:ul/x:li[4]/x:ul/x:li/x:a/@href", doc);
}
@Test
public void testDirectoryHeaders() throws Exception {
MockHttpServletResponse response = getAsServletResponse(RestBaseController.ROOT_PATH+"/resource/mydir?format=xml");
Assert.assertEquals(FORMAT_HEADER.format(myRes.parent().lastmodified()),
response.getHeader("Last-Modified"));
Assert.assertEquals("http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/",
response.getHeader("Resource-Parent"));
Assert.assertEquals("directory", response.getHeader("Resource-Type"));
assertContentType("application/xml", response);
}
@Test
public void testDirectoryHead() throws Exception {
MockHttpServletResponse response = headAsServletResponse(RestBaseController.ROOT_PATH+"/resource/mydir?format=xml");
Assert.assertEquals(FORMAT_HEADER.format(myRes.parent().lastmodified()),
response.getHeader("Last-Modified"));
Assert.assertEquals("http://localhost:8080/geoserver"+RestBaseController.ROOT_PATH+"/resource/",
response.getHeader("Resource-Parent"));
Assert.assertEquals("directory", response.getHeader("Resource-Type"));
assertContentType("application/xml", response);
}
@Test
public void testDirectoryMimeTypes() throws Exception {
XMLUnit.setXpathNamespaceContext(NS_XML);
Document doc = getAsDOM(RestBaseController.ROOT_PATH+"/resource/mydir2?format=xml");
//print(doc);
XMLAssert.assertXpathEvaluatesTo("image/png", "/ResourceDirectory/children/child[name='imagewithoutextension']/atom:link/@type", doc);
XMLAssert.assertXpathEvaluatesTo("image/png", "/ResourceDirectory/children/child[name='fake.png']/atom:link/@type", doc);
}
@Test
public void testUpload() throws Exception {
put(RestBaseController.ROOT_PATH+"/resource/mydir/mynewres", STR_MY_NEW_TEST);
Resource newRes = getDataDirectory().get("/mydir/mynewres");
try (InputStream is = newRes.in()) {
Assert.assertEquals(STR_MY_NEW_TEST, IOUtils.toString(is));
}
newRes.delete();
}
@Test
public void testCopy() throws Exception {
put(RestBaseController.ROOT_PATH+"/resource/mydir/mynewres?operation=cOpY", "/mydir/myres");
Resource newRes = getDataDirectory().get("/mydir/mynewres");
Assert.assertTrue(Resources.exists(myRes));
Assert.assertTrue(Resources.exists(newRes));
try (InputStream is = newRes.in()) {
Assert.assertEquals(STR_MY_TEST, IOUtils.toString(is));
}
newRes.delete();
}
@Test
public void testMove() throws Exception {
put(RestBaseController.ROOT_PATH+"/resource/mydir/mynewres?operation=move", "/mydir/myres");
Resource newRes = getDataDirectory().get("/mydir/mynewres");
Assert.assertFalse(Resources.exists(myRes));
Assert.assertTrue(Resources.exists(newRes));
try (InputStream is = newRes.in()) {
Assert.assertEquals(STR_MY_TEST, IOUtils.toString(is));
}
newRes.renameTo(myRes);
}
@Test
public void testMoveDirectory() throws Exception {
put(RestBaseController.ROOT_PATH+"/resource/mydir/mynewdir?operation=move", "/mydir");
put(RestBaseController.ROOT_PATH+"/resource/mynewdir?operation=move", "/mydir");
Resource newDir = getDataDirectory().get("/mynewdir");
Assert.assertTrue(Resources.exists(newDir));
Assert.assertTrue(newDir.getType() == Type.DIRECTORY);
Assert.assertFalse(Resources.exists(myRes));
Assert.assertTrue(Resources.exists(getDataDirectory().get("/mynewdir/myres")));
newDir.renameTo(getDataDirectory().get("/mydir"));
}
@Test
public void testDelete() throws Exception {
Resource newRes = getDataDirectory().get("/mydir/mynewres");
Resources.copy(myRes, newRes);
Assert.assertTrue(Resources.exists(newRes));
deleteAsServletResponse(RestBaseController.ROOT_PATH+"/resource/mydir/mynewres");
Assert.assertFalse(Resources.exists(newRes));
}
@Test
public void testErrorResponseCodes() throws Exception {
MockHttpServletResponse response;
//get resource that doesn't exist
response = getAsServletResponse(RestBaseController.ROOT_PATH+"/resource/doesntexist");
Assert.assertEquals(404, response.getStatus());
//delete resource that doesn't exist
response = deleteAsServletResponse(RestBaseController.ROOT_PATH+"/resource/doesntexist");
Assert.assertEquals(404, response.getStatus());
//upload to dir
response = putAsServletResponse(RestBaseController.ROOT_PATH+"/resource/mydir");
Assert.assertEquals(405, response.getStatus());
//copy dir
response = putAsServletResponse(RestBaseController.ROOT_PATH+"/resource/mynewdir?operation=copy", "/mydir", "text/plain");
Assert.assertEquals(405, response.getStatus());
//copy resource that doesn't exist
response = putAsServletResponse(RestBaseController.ROOT_PATH+"/resource/mynewres?operation=copy", "/doesntexist", "text/plain");
Assert.assertEquals(404, response.getStatus());
//move resource that doesn't exist
response = putAsServletResponse(RestBaseController.ROOT_PATH+"/resource/mynewres?operation=move", "/doesntexist", "text/plain");
Assert.assertEquals(404, response.getStatus());
//post
response = postAsServletResponse(RestBaseController.ROOT_PATH+"/resource/mydir", "blabla");
Assert.assertEquals(405, response.getStatus());
}
//TODO: Migrate this (properly) to GeoServerSystemTestSupport)
public MockHttpServletResponse headAsServletResponse(String path) throws Exception {
MockHttpServletRequest request = createRequest( path );
request.setMethod( "HEAD" );
request.setContent(new byte[]{});
return dispatch( request, null );
}
}