/*******************************************************************************
* Copyright (c) 2013 GigaSpaces Technologies Ltd. All rights reserved
*
* 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 org.cloudifysource.rest;
import java.io.File;
import java.io.IOException;
import java.security.InvalidParameterException;
import java.util.Map;
import junit.framework.Assert;
import org.apache.commons.lang.StringUtils;
import org.cloudifysource.dsl.internal.CloudifyConstants;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.type.TypeFactory;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockMultipartHttpServletRequest;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
/**
* A class for testing the controllers.
* @author yael
*
*/
public abstract class ControllerTest {
private static final ObjectMapper PROJECT_MAPPER = new ObjectMapper();
protected RequestMappingHandlerAdapter handlerAdapter;
@Autowired
protected ApplicationContext applicationContext;
/**
* Given a uri and a request method, gets the expected handler method.
* @param requestUri - the uri.
* @param requestMethod - {@link RequestMethod#GET}, {@link RequestMethod#POST} or {@link RequestMethod#DELETE}
* @return the expected handler method.
*/
public abstract HandlerMethod getExpectedMethod(final String requestUri, final RequestMethod requestMethod);
@Before
public void initControllerTest() throws NoSuchMethodException {
handlerAdapter = applicationContext.getBean(RequestMappingHandlerAdapter.class);
}
public void testGet(final String requestUri, final String expectedResponseContent) throws Exception {
final MockHttpServletRequest reqeust = createMockGetRequest(requestUri);
testRequest(reqeust, getExpectedMethod(requestUri, RequestMethod.GET), expectedResponseContent);
}
public void testDelete(final String requestUri, final String expectedResponseContent) throws Exception {
final MockHttpServletRequest reqeust = createMockDeleteRequest(requestUri);
testRequest(reqeust, getExpectedMethod(requestUri, RequestMethod.DELETE), expectedResponseContent);
}
public void testPost(final String requestUri, final String postContentAsJson, final String expectedResponseContent)
throws Exception {
final MockHttpServletRequest reqeust = createMockPostRequest(requestUri, postContentAsJson);
testRequest(reqeust, getExpectedMethod(requestUri, RequestMethod.POST), expectedResponseContent);
}
public MockHttpServletResponse testPostFile(final String requestUri, final File file)
throws Exception {
final MockHttpServletRequest reqeust = createMockPostFileRequest(requestUri, file);
return testRequest(reqeust, getExpectedMethod(requestUri, RequestMethod.POST));
}
private MockHttpServletResponse testRequest(final MockHttpServletRequest request,
final HandlerMethod expectedHandlerMethod) throws Exception {
final MockHttpServletResponse response = new MockHttpServletResponse();
final HandlerExecutionChain handlerExecutionChain = getHandlerToRequest(request);
Object handler = handlerExecutionChain.getHandler();
Assert.assertEquals("Wrong handler selected for request uri: "
+ request.getRequestURI(), expectedHandlerMethod.toString(),
handler.toString());
HandlerInterceptor[] interceptors = handlerExecutionChain.getInterceptors();
// pre handle
for (HandlerInterceptor handlerInterceptor : interceptors) {
handlerInterceptor.preHandle(request, response, handler);
}
// handle the request
ModelAndView modelAndView = handlerAdapter.handle(request, response, handler);
// post handle
for (HandlerInterceptor handlerInterceptor : interceptors) {
handlerInterceptor.postHandle(request, response, handler, modelAndView);
}
// validate the response
Assert.assertTrue("Wrong response status: " + response.getStatus(),
response.getStatus() == HttpStatus.OK.value());
Assert.assertTrue(response.getContentType().contains(CloudifyConstants.MIME_TYPE_APPLICATION_JSON));
return response;
}
private void testRequest(final MockHttpServletRequest reqeust,
final HandlerMethod expectedHandlerMethod,
final String expectedResponseContent) throws Exception {
final MockHttpServletResponse response = testRequest(reqeust, expectedHandlerMethod);
// validate the response's content
Assert.assertEquals(
"Wrong response content: " + response.getContentAsString(),
expectedResponseContent, response.getContentAsString());
}
/**
* This method finds the handler for a given request URI. It will also ensure that the URI Parameters i.e.
* /context/test/{name} are added to the request
*
* @param request
* The request object to be used
* @return The correct handler for the request
* @throws Exception
* Indicates a matching handler could not be found
*/
private HandlerExecutionChain getHandlerToRequest(final MockHttpServletRequest request)
throws Exception {
HandlerExecutionChain chain = null;
final Map<String, HandlerMapping> map = applicationContext
.getBeansOfType(HandlerMapping.class);
for (final HandlerMapping mapping : map.values()) {
chain = mapping.getHandler(request);
if (chain != null) {
break;
}
}
if (chain == null) {
throw new InvalidParameterException(
"Unable to find handler for request URI: "
+ request.getRequestURI());
}
return chain;
}
private MockHttpServletRequest createMockGetRequest(final String requestUri) {
final MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI(requestUri);
request.setMethod("GET");
request.setContentType(CloudifyConstants.MIME_TYPE_APPLICATION_JSON);
return request;
}
private MockHttpServletRequest createMockPostRequest(
final String requestUri, final String contentAsJson) {
final MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI(requestUri);
request.setMethod("POST");
request.setContentType(CloudifyConstants.MIME_TYPE_APPLICATION_JSON);
if (StringUtils.isNotBlank(contentAsJson)) {
request.setContent(contentAsJson.getBytes());
}
return request;
}
private MockHttpServletRequest createMockPostFileRequest(
final String requestUri, final File file) throws IOException {
MultipartFile multiFile = UploadRepoTest.createNewMultiFile(file);
MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest();
request.addFile(multiFile);
request.setMethod("POST");
request.setRequestURI(requestUri);
return request;
}
private MockHttpServletRequest createMockDeleteRequest(
final String requestUri) {
final MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI(requestUri);
request.setMethod("DELETE");
request.setContentType(CloudifyConstants.MIME_TYPE_APPLICATION_JSON);
return request;
}
/**
* Converts a json String to a Map<String, Object>.
*
* @param jsonStr
* a json-format String to convert to a map
* @return a Map<String, Object> based on the given json-format String
* @throws IOException
* Reporting failure to read or convert the json-format string to a map
*/
public static Map<String, Object> jsonToMap(final String jsonStr)
throws IOException {
return PROJECT_MAPPER.readValue(jsonStr, TypeFactory.type(Map.class));
}
/**
* Converts an object to a json-format String.
*
* @param value
* an object to convert to json-format String
* @return a json-format String based on the given object
* @throws IOException
* Reporting failure to convert the object
*/
public static String convertToJson(final Object value) throws IOException {
return PROJECT_MAPPER.writeValueAsString(value);
}
}