| /******************************************************************************* |
| * 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 µ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")); |
| } |
| } |