blob: 659a54c97d9acd31cbcbae31a63532f5882ec41a [file] [log] [blame]
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.olingo.odata2.core.debug;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.olingo.odata2.api.commons.HttpContentType;
import org.apache.olingo.odata2.api.commons.HttpHeaders;
import org.apache.olingo.odata2.api.commons.HttpStatusCodes;
import org.apache.olingo.odata2.api.commons.ODataHttpMethod;
import org.apache.olingo.odata2.api.edm.EdmNavigationProperty;
import org.apache.olingo.odata2.api.edm.EdmProperty;
import org.apache.olingo.odata2.api.exception.ODataException;
import org.apache.olingo.odata2.api.exception.ODataMessageException;
import org.apache.olingo.odata2.api.processor.ODataContext;
import org.apache.olingo.odata2.api.processor.ODataContext.RuntimeMeasurement;
import org.apache.olingo.odata2.api.processor.ODataResponse;
import org.apache.olingo.odata2.api.uri.NavigationPropertySegment;
import org.apache.olingo.odata2.api.uri.PathInfo;
import org.apache.olingo.odata2.api.uri.SelectItem;
import org.apache.olingo.odata2.api.uri.UriInfo;
import org.apache.olingo.odata2.api.uri.UriParser;
import org.apache.olingo.odata2.api.uri.expression.CommonExpression;
import org.apache.olingo.odata2.api.uri.expression.ExpressionParserException;
import org.apache.olingo.odata2.api.uri.expression.FilterExpression;
import org.apache.olingo.odata2.api.uri.expression.OrderByExpression;
import org.apache.olingo.odata2.testutil.fit.BaseTest;
import org.apache.olingo.odata2.testutil.helper.StringHelper;
import org.junit.Test;
/**
* Tests for the debug information output.
*/
public class ODataDebugResponseWrapperTest extends BaseTest {
private static final String EXPECTED = "{"
+ "\"request\":{\"method\":\"GET\",\"uri\":\"http://test/entity\",\"protocol\":null},"
+ "\"response\":{\"status\":{\"code\":200,\"info\":\"OK\"}},"
+ "\"server\":{\"version\":null}}";
private ODataContext mockContext(final ODataHttpMethod method) throws ODataException {
ODataContext context = mock(ODataContext.class);
when(context.getHttpMethod()).thenReturn(method.name());
PathInfo pathInfo = mock(PathInfo.class);
when(pathInfo.getRequestUri()).thenReturn(URI.create("http://test/entity"));
when(pathInfo.getServiceRoot()).thenReturn(URI.create("http://test/"));
when(context.getPathInfo()).thenReturn(pathInfo);
when(context.getRuntimeMeasurements()).thenReturn(null);
return context;
}
private ODataResponse mockResponse(final HttpStatusCodes status, final String body, final String contentType) {
ODataResponse response = mock(ODataResponse.class);
when(response.getStatus()).thenReturn(status);
when(response.getEntity()).thenReturn(body);
if (contentType != null) {
when(response.getHeaderNames()).thenReturn(new HashSet<String>(Arrays.asList(HttpHeaders.CONTENT_TYPE)));
when(response.getHeader(HttpHeaders.CONTENT_TYPE)).thenReturn(contentType);
when(response.getContentHeader()).thenReturn(contentType);
}
return response;
}
private RuntimeMeasurement mockRuntimeMeasurement(final String method, final long startTime, final long stopTime) {
RuntimeMeasurement measurement = mock(RuntimeMeasurement.class);
when(measurement.getClassName()).thenReturn("class");
when(measurement.getMethodName()).thenReturn(method);
when(measurement.getTimeStarted()).thenReturn(startTime);
when(measurement.getTimeStopped()).thenReturn(stopTime);
return measurement;
}
@Test
public void minimal() throws Exception {
final ODataContext context = mockContext(ODataHttpMethod.PUT);
final ODataResponse wrappedResponse = mockResponse(HttpStatusCodes.NO_CONTENT, null, null);
ODataResponse response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), null,
ODataDebugResponseWrapper.ODATA_DEBUG_JSON).wrapResponse();
final String actualJson = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertEquals(EXPECTED.replace(ODataHttpMethod.GET.name(), ODataHttpMethod.PUT.name())
.replace(Integer.toString(HttpStatusCodes.OK.getStatusCode()),
Integer.toString(HttpStatusCodes.NO_CONTENT.getStatusCode()))
.replace(HttpStatusCodes.OK.getInfo(), HttpStatusCodes.NO_CONTENT.getInfo()),
actualJson);
response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), null,
ODataDebugResponseWrapper.ODATA_DEBUG_HTML).wrapResponse();
final String html = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertTrue(html.contains(HttpStatusCodes.NO_CONTENT.getInfo()));
}
@Test
public void body() throws Exception {
final ODataContext context = mockContext(ODataHttpMethod.GET);
ODataResponse wrappedResponse = mockResponse(HttpStatusCodes.OK, "\"test\"", HttpContentType.APPLICATION_JSON);
ODataResponse response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), null,
ODataDebugResponseWrapper.ODATA_DEBUG_JSON).wrapResponse();
String entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertEquals(EXPECTED.replace("}},\"server",
"},\"headers\":{\"" + HttpHeaders.CONTENT_TYPE + "\":\"" + HttpContentType.APPLICATION_JSON + "\"},"
+ "\"body\":\"test\"},\"server"),
entity);
wrappedResponse = mockResponse(HttpStatusCodes.OK, "test", HttpContentType.TEXT_PLAIN);
response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), null,
ODataDebugResponseWrapper.ODATA_DEBUG_JSON).wrapResponse();
entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertEquals(EXPECTED.replace("}},\"server",
"},\"headers\":{\"" + HttpHeaders.CONTENT_TYPE + "\":\"" + HttpContentType.TEXT_PLAIN + "\"},"
+ "\"body\":\"test\"},\"server"),
entity);
wrappedResponse = mockResponse(HttpStatusCodes.OK, null, "image/png");
when(wrappedResponse.getEntity()).thenReturn(new ByteArrayInputStream("test".getBytes()));
response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), null,
ODataDebugResponseWrapper.ODATA_DEBUG_JSON).wrapResponse();
entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertEquals(EXPECTED.replace("}},\"server",
"},\"headers\":{\"" + HttpHeaders.CONTENT_TYPE + "\":\"image/png\"},"
+ "\"body\":\"dGVzdA==\"},\"server"),
entity);
when(wrappedResponse.getEntity()).thenReturn(new ByteArrayInputStream("test".getBytes()));
response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), null,
ODataDebugResponseWrapper.ODATA_DEBUG_HTML).wrapResponse();
entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertTrue(entity.contains("<img src=\"data:image/png;base64,dGVzdA==\" />"));
}
@Test
public void headers() throws Exception {
ODataContext context = mockContext(ODataHttpMethod.GET);
Map<String, List<String>> headers = new HashMap<String, List<String>>();
headers.put(HttpHeaders.CONTENT_TYPE, Arrays.asList(HttpContentType.APPLICATION_JSON));
when(context.getRequestHeaders()).thenReturn(headers);
final ODataResponse wrappedResponse = mockResponse(HttpStatusCodes.OK, null, HttpContentType.APPLICATION_JSON);
ODataResponse response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), null,
ODataDebugResponseWrapper.ODATA_DEBUG_JSON).wrapResponse();
String entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertEquals(EXPECTED.replace("},\"response",
",\"headers\":{\"" + HttpHeaders.CONTENT_TYPE + "\":\"" + HttpContentType.APPLICATION_JSON + "\"}},\"response")
.replace("}},\"server",
"},\"headers\":{\"" + HttpHeaders.CONTENT_TYPE + "\":\"" + HttpContentType.APPLICATION_JSON
+ "\"}},\"server"),
entity);
response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), null,
ODataDebugResponseWrapper.ODATA_DEBUG_HTML).wrapResponse();
entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertTrue(entity.contains("<td class=\"name\">Content-Type</td><td class=\"value\">application/json</td>"));
}
@Test
public void server() throws Exception {
ODataContext context = mockContext(ODataHttpMethod.GET);
HttpServletRequest servletRequest = mock(HttpServletRequest.class);
when(servletRequest.getServerPort()).thenReturn(12345);
when(context.getParameter(ODataContext.HTTP_SERVLET_REQUEST_OBJECT)).thenReturn(servletRequest);
final ODataResponse wrappedResponse = mockResponse(HttpStatusCodes.OK, null, null);
ODataResponse response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), null,
ODataDebugResponseWrapper.ODATA_DEBUG_JSON).wrapResponse();
String entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertEquals(EXPECTED.replace("null}}", "null,\"environment\":{\"serverPort\":\"12345\"}}}"),
entity);
response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), null,
ODataDebugResponseWrapper.ODATA_DEBUG_HTML).wrapResponse();
entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertTrue(entity.contains("<td class=\"name\">serverPort</td><td class=\"value\">12345</td>"));
}
@Test
public void uri() throws Exception {
final ODataContext context = mockContext(ODataHttpMethod.GET);
final ODataResponse wrappedResponse = mockResponse(HttpStatusCodes.OK, null, null);
UriInfo uriInfo = mock(UriInfo.class);
final FilterExpression filter = UriParser.parseFilter(null, null, "true");
when(uriInfo.getFilter()).thenReturn(filter);
final OrderByExpression orderBy = UriParser.parseOrderBy(null, null, "true");
when(uriInfo.getOrderBy()).thenReturn(orderBy);
List<ArrayList<NavigationPropertySegment>> expand = new ArrayList<ArrayList<NavigationPropertySegment>>();
NavigationPropertySegment segment = mock(NavigationPropertySegment.class);
EdmNavigationProperty navigationProperty = mock(EdmNavigationProperty.class);
when(navigationProperty.getName()).thenReturn("nav");
when(segment.getNavigationProperty()).thenReturn(navigationProperty);
ArrayList<NavigationPropertySegment> segments = new ArrayList<NavigationPropertySegment>();
segments.add(segment);
expand.add(segments);
when(uriInfo.getExpand()).thenReturn(expand);
SelectItem select1 = mock(SelectItem.class);
SelectItem select2 = mock(SelectItem.class);
EdmProperty property = mock(EdmProperty.class);
when(property.getName()).thenReturn("property");
when(select1.getProperty()).thenReturn(property);
when(select2.getProperty()).thenReturn(property);
when(select2.getNavigationPropertySegments()).thenReturn(segments);
when(uriInfo.getSelect()).thenReturn(Arrays.asList(select1, select2));
ODataResponse response = new ODataDebugResponseWrapper(context, wrappedResponse, uriInfo, null,
ODataDebugResponseWrapper.ODATA_DEBUG_JSON).wrapResponse();
String entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertEquals(EXPECTED.replace("null}}", "null,"
+ "\"uri\":{\"filter\":{\"nodeType\":\"LITERAL\",\"type\":\"Edm.Boolean\",\"value\":\"true\"},"
+ "\"orderby\":{\"nodeType\":\"order collection\","
+ "\"orders\":[{\"nodeType\":\"ORDER\",\"sortorder\":\"asc\","
+ "\"expression\":{\"nodeType\":\"LITERAL\",\"type\":\"Edm.Boolean\",\"value\":\"true\"}}]},"
+ "\"expandSelect\":{\"all\":false,\"properties\":[\"property\"],"
+ "\"links\":[{\"nav\":{\"all\":false,\"properties\":[\"property\"],\"links\":[]}}]}}}}"),
entity);
response = new ODataDebugResponseWrapper(context, wrappedResponse, uriInfo, null,
ODataDebugResponseWrapper.ODATA_DEBUG_HTML).wrapResponse();
entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertTrue(entity.contains("Edm.Boolean"));
assertTrue(entity.contains("asc"));
}
@Test
public void uriWithException() throws Exception {
final ODataContext context = mockContext(ODataHttpMethod.GET);
final ODataResponse wrappedResponse = mockResponse(HttpStatusCodes.OK, null, null);
ExpressionParserException exception = mock(ExpressionParserException.class);
when(exception.getMessageReference()).thenReturn(ExpressionParserException.COMMON_ERROR);
when(exception.getStackTrace()).thenReturn(new StackTraceElement[] {
new StackTraceElement("class", "method", "file", 42) });
CommonExpression filterTree = mock(CommonExpression.class);
when(filterTree.getUriLiteral()).thenReturn("wrong");
when(exception.getFilterTree()).thenReturn(filterTree);
ODataResponse response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), exception,
ODataDebugResponseWrapper.ODATA_DEBUG_JSON).wrapResponse();
String entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertEquals(EXPECTED.replace("null}}", "null,"
+ "\"uri\":{\"error\":{\"expression\":\"wrong\"}},"
+ "\"stacktrace\":{\"exceptions\":[{\"class\":\"" + exception.getClass().getName() + "\","
+ "\"message\":\"Error while parsing a ODATA expression.\","
+ "\"invocation\":{\"class\":\"class\",\"method\":\"method\",\"line\":42}}],"
+ "\"stacktrace\":[{\"class\":\"class\",\"method\":\"method\",\"line\":42}]}}}"),
entity);
response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), exception,
ODataDebugResponseWrapper.ODATA_DEBUG_HTML).wrapResponse();
entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertTrue(entity.contains("wrong"));
assertTrue(entity.contains(exception.getClass().getName()));
assertTrue(entity.contains("42"));
}
@Test
public void runtime() throws Exception {
ODataContext context = mockContext(ODataHttpMethod.GET);
List<RuntimeMeasurement> runtimeMeasurements = new ArrayList<RuntimeMeasurement>();
runtimeMeasurements.add(mockRuntimeMeasurement("method", 1000, 42000));
runtimeMeasurements.add(mockRuntimeMeasurement("inner", 2000, 5000));
runtimeMeasurements.add(mockRuntimeMeasurement("inner", 7000, 12000));
runtimeMeasurements.add(mockRuntimeMeasurement("inner", 13000, 16000));
runtimeMeasurements.add(mockRuntimeMeasurement("inner2", 14000, 15000));
runtimeMeasurements.add(mockRuntimeMeasurement("child", 17000, 21000));
runtimeMeasurements.add(mockRuntimeMeasurement("second", 45000, 99000));
when(context.getRuntimeMeasurements()).thenReturn(runtimeMeasurements);
final ODataResponse wrappedResponse = mockResponse(HttpStatusCodes.OK, null, null);
ODataResponse response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), null,
ODataDebugResponseWrapper.ODATA_DEBUG_JSON).wrapResponse();
String entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertEquals(EXPECTED.replace("null}}", "null,"
+ "\"runtime\":[{\"class\":\"class\",\"method\":\"method\",\"duration\":{\"value\":41,\"unit\":\"µs\"},"
+ "\"children\":[{\"class\":\"class\",\"method\":\"inner\",\"duration\":{\"value\":8,\"unit\":\"µs\"}},"
+ "{\"class\":\"class\",\"method\":\"inner\",\"duration\":{\"value\":3,\"unit\":\"µs\"},\"children\":["
+ "{\"class\":\"class\",\"method\":\"inner2\",\"duration\":{\"value\":1,\"unit\":\"µs\"}}]},"
+ "{\"class\":\"class\",\"method\":\"child\",\"duration\":{\"value\":4,\"unit\":\"µs\"}}]},"
+ "{\"class\":\"class\",\"method\":\"second\",\"duration\":{\"value\":54,\"unit\":\"µs\"}}]}}"),
entity);
response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), null,
ODataDebugResponseWrapper.ODATA_DEBUG_HTML).wrapResponse();
entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertTrue(entity.contains("54&nbsp;&micro;s"));
}
@Test
public void exception() throws Exception {
final ODataContext context = mockContext(ODataHttpMethod.GET);
final ODataResponse wrappedResponse = mockResponse(HttpStatusCodes.BAD_REQUEST, null, null);
ODataMessageException exception = mock(ODataMessageException.class);
when(exception.getMessageReference()).thenReturn(ODataMessageException.COMMON);
RuntimeException innerException = mock(RuntimeException.class);
when(innerException.getLocalizedMessage()).thenReturn("error");
final StackTraceElement[] stackTrace = new StackTraceElement[] {
new StackTraceElement("innerClass", "innerMethod", "innerFile", 99),
new StackTraceElement("class", "method", "file", 42),
new StackTraceElement("outerClass", "outerMethod", "outerFile", 11) };
when(innerException.getStackTrace()).thenReturn(stackTrace);
when(exception.getCause()).thenReturn(innerException);
when(exception.getStackTrace()).thenReturn(Arrays.copyOfRange(stackTrace, 1, 3));
ODataResponse response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), exception,
ODataDebugResponseWrapper.ODATA_DEBUG_JSON).wrapResponse();
String entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertEquals(EXPECTED
.replace(Integer.toString(HttpStatusCodes.OK.getStatusCode()),
Integer.toString(HttpStatusCodes.BAD_REQUEST.getStatusCode()))
.replace(HttpStatusCodes.OK.getInfo(), HttpStatusCodes.BAD_REQUEST.getInfo())
.replace("null}}", "null,"
+ "\"stacktrace\":{\"exceptions\":[{\"class\":\"" + exception.getClass().getName() + "\","
+ "\"message\":\"Common exception\","
+ "\"invocation\":{\"class\":\"class\",\"method\":\"method\",\"line\":42}},"
+ "{\"class\":\"" + innerException.getClass().getName() + "\",\"message\":\"error\","
+ "\"invocation\":{\"class\":\"innerClass\",\"method\":\"innerMethod\",\"line\":99}}],"
+ "\"stacktrace\":[{\"class\":\"class\",\"method\":\"method\",\"line\":42},"
+ "{\"class\":\"outerClass\",\"method\":\"outerMethod\",\"line\":11}]}}}"),
entity);
response = new ODataDebugResponseWrapper(context, wrappedResponse, mock(UriInfo.class), exception,
ODataDebugResponseWrapper.ODATA_DEBUG_HTML).wrapResponse();
entity = StringHelper.inputStreamToString((InputStream) response.getEntity());
assertTrue(entity.contains("Common exception"));
assertTrue(entity.contains("42"));
assertTrue(entity.contains("innerMethod"));
assertTrue(entity.contains("outerMethod"));
}
}