/**
* Copyright 2016-2017 Sixt GmbH & Co. Autovermietung KG
* 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 com.sixt.service.framework.jetty;
import com.codahale.metrics.Counter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.sixt.service.framework.FeatureFlags;
import com.sixt.service.framework.MethodHandlerDictionary;
import com.sixt.service.framework.OrangeContext;
import com.sixt.service.framework.ServiceProperties;
import com.sixt.service.framework.json.JsonRpcRequest;
import com.sixt.service.framework.metrics.GoTimer;
import com.sixt.service.framework.protobuf.RpcEnvelope;
import com.sixt.service.framework.rpc.RpcCallException;
import com.sixt.service.framework.servicetest.mockservice.MockHttpServletResponse;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.regex.Pattern;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
public class JsonHandlerTest {
private static final Logger LOGGER = LoggerFactory.getLogger(JsonHandlerTest.class);
private JsonHandler servlet;
private MethodHandlerDictionary handlerDictionary;
private MetricRegistry metricRegistry;
private RpcHandlerMetrics handlerMetrics;
@Before
public void setup() throws RpcCallException {
handlerDictionary = new MethodHandlerDictionary();
handlerDictionary.put("a", null);
ServiceMethodHandlerUnderTest mockHandlerThrowsRpcCallEx = new ServiceMethodHandlerUnderTest();
handlerDictionary.put("jsonRpcWithException", mockHandlerThrowsRpcCallEx);
metricRegistry = mock(MetricRegistry.class);
when(metricRegistry.counter(anyString())).thenReturn(mock(Counter.class));
when(metricRegistry.timer(anyString())).thenReturn(mock(Timer.class));
handlerMetrics = mock(RpcHandlerMetrics.class);
when(handlerMetrics.getMethodTimer(anyString(), anyString(), anyString())).thenReturn(mock(GoTimer.class));
servlet = new JsonHandler(handlerDictionary, metricRegistry, handlerMetrics, new ServiceProperties(), null);
}
@Test
public void testParseRpcRequestNoId() {
String input = "{\"service\":\"x\",\"method\":\"a\",\"params\":[{\"data\":\"\"}]}";
JsonRpcRequest request = servlet.parseRpcRequest(input);
assertThat(request.getId()).isNull();
}
@Test
public void testParseRpcRequestNullId() {
String input = "{\"service\":\"x\",\"method\":\"a\",\"params\":[{\"data\":\"\"}],\"id\":null}";
JsonRpcRequest request = servlet.parseRpcRequest(input);
assertThat(request.getId()).isInstanceOf(JsonNull.class);
}
@Test
public void testParseRpcRequestNumericId() {
String input = "{\"service\":\"x\",\"method\":\"a\",\"params\":[{\"data\":\"\"}],\"id\":123456789}";
JsonRpcRequest request = servlet.parseRpcRequest(input);
JsonElement idElement = request.getId();
assertThat(idElement.toString()).isEqualTo("123456789");
}
@Test
public void testParseRpcRequestStringNumericId() {
String input = "{\"service\":\"x\",\"method\":\"a\",\"params\":[{\"data\":\"\"}],\"id\":\"234234\"}";
JsonRpcRequest request = servlet.parseRpcRequest(input);
JsonElement idElement = request.getId();
assertThat(idElement.toString()).isEqualTo("\"234234\"");
}
@Test
public void testParseRpcRequestStringId() {
String input = "{\"service\":\"x\",\"method\":\"a\",\"params\":[{\"data\":\"\"}],\"id\":\"dead-beef\"}";
JsonRpcRequest request = servlet.parseRpcRequest(input);
JsonElement idElement = request.getId();
assertThat(idElement.toString()).isEqualTo("\"dead-beef\"");
}
@Test
public void testRpcCallExceptionJsonRpc() throws IOException {
String input = "{\"service\":\"x\",\"method\":\"jsonRpcWithException\",\"params\":[{}],\"id\":\"dead-beef\"}";
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(MockHttpServletResponse.class);
BufferedReader readerFromRequest = new BufferedReader(new StringReader(input));
when(request.getReader()).thenReturn(readerFromRequest);
CharArrayWriter charArryWriter = new CharArrayWriter(512);
PrintWriter writer = new PrintWriter(charArryWriter);
when(response.getWriter()).thenReturn(writer);
doCallRealMethod().when(response).setStatus(anyInt());
doCallRealMethod().when(response).getStatus();
servlet.doPost(request, response);
String responseAsString = charArryWriter.toString();
LOGGER.debug(responseAsString);
assertThat(responseAsString).contains("no p4s5!");
assertThat(responseAsString).matches(Pattern.compile(".*retriable.+true.*"));
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@Test
public void testRpcCallExceptionJsonRpcWithHttpExposed() throws IOException {
String input = "{\"service\":\"x\",\"method\":\"jsonRpcWithException\",\"params\":[{}],\"id\":\"dead-beef\"}";
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(MockHttpServletResponse.class);
BufferedReader readerFromRequest = new BufferedReader(new StringReader(input));
when(request.getReader()).thenReturn(readerFromRequest);
CharArrayWriter charArryWriter = new CharArrayWriter(512);
PrintWriter writer = new PrintWriter(charArryWriter);
when(response.getWriter()).thenReturn(writer);
doCallRealMethod().when(response).setStatus(anyInt());
doCallRealMethod().when(response).getStatus();
ServiceProperties props = new ServiceProperties();
props.addProperty(FeatureFlags.FLAG_EXPOSE_ERRORS_HTTP, "true");
servlet = new JsonHandler(handlerDictionary, metricRegistry, handlerMetrics, props, null);
servlet.doPost(request, response);
String responseAsString = charArryWriter.toString();
LOGGER.debug(responseAsString);
assertThat(responseAsString).contains("no p4s5!");
assertThat(responseAsString).matches(Pattern.compile(".*retriable.+true.*"));
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
@Test
public void doPost_NotValidJson_ErrorMessageBadRequest() throws IOException {
// given
final String json = "xxxx";
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(MockHttpServletResponse.class);
BufferedReader readerFromRequest = new BufferedReader(new StringReader(json));
when(request.getReader()).thenReturn(readerFromRequest);
CharArrayWriter charArryWriter = new CharArrayWriter(512);
PrintWriter writer = new PrintWriter(charArryWriter);
when(response.getWriter()).thenReturn(writer);
doCallRealMethod().when(response).setStatus(anyInt());
doCallRealMethod().when(response).getStatus();
// when
servlet.doPost(request, response);
// then
final String responseAsString = charArryWriter.toString();
LOGGER.debug(responseAsString);
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
}
@Test
public void doPost_NotValidJson_ErrorMessageBadRequestWithHttpExposed() throws IOException {
// given
final String json = "xxxx";
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(MockHttpServletResponse.class);
BufferedReader readerFromRequest = new BufferedReader(new StringReader(json));
when(request.getReader()).thenReturn(readerFromRequest);
CharArrayWriter charArryWriter = new CharArrayWriter(512);
PrintWriter writer = new PrintWriter(charArryWriter);
when(response.getWriter()).thenReturn(writer);
doCallRealMethod().when(response).setStatus(anyInt());
doCallRealMethod().when(response).getStatus();
// when
ServiceProperties props = new ServiceProperties();
props.addProperty(FeatureFlags.FLAG_EXPOSE_ERRORS_HTTP, "true");
servlet = new JsonHandler(handlerDictionary, metricRegistry, handlerMetrics, props, null);
servlet.doPost(request, response);
// then
final String responseAsString = charArryWriter.toString();
LOGGER.debug(responseAsString);
assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_BAD_REQUEST);
}
@Test
public void parseRpcRequest_NotJson_ThrowIllegalArgumentException() {
// given
final String json = "xxxx";
// when
Throwable thrown = catchThrowable(() -> {
servlet.parseRpcRequest(json);
});
LOGGER.debug(thrown.getMessage());
// then
assertThat(thrown).isInstanceOf(IllegalArgumentException.class);
assertThat(thrown).hasMessageContaining("Not a JSON");
}
@Test
public void parseRpcRequest_NotValidJson_ThrowIllegalArgumentException() {
// given
final String json = "xxxx}";
// when
Throwable thrown = catchThrowable(() -> {
servlet.parseRpcRequest(json);
});
LOGGER.debug(thrown.getMessage());
// then
assertThat(thrown).isInstanceOf(IllegalArgumentException.class);
assertThat(thrown).hasMessageContaining("Malformed");
}
@Test
public void parseRpcRequest_NoMethodNameGiven_ThrowIllegalArgumentExecption() {
// given
final String json = "{\"service\":\"x\",\"params\":[{\"data\":\"\"}],\"id\":\"dead-beef\"}";
// when
Throwable thrown = catchThrowable(() -> {
servlet.parseRpcRequest(json);
});
// then
assertThat(thrown).isInstanceOf(IllegalArgumentException.class);
assertThat(thrown).hasMessage("Missing method name");
}
class ServiceMethodHandlerUnderTest implements com.sixt.service.framework.ServiceMethodHandler<RpcEnvelope.Request, RpcEnvelope.Response>{
@Override
public RpcEnvelope.Response handleRequest(RpcEnvelope.Request request, OrangeContext ctx) throws RpcCallException {
throw new RpcCallException(RpcCallException.Category.InternalServerError, "no p4s5!");
}
};
}