| /* |
| * 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.servicecomb.transport.rest.vertx; |
| |
| import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException; |
| import io.vertx.core.Context; |
| import io.vertx.core.Vertx; |
| import io.vertx.core.http.HttpServerRequest; |
| import io.vertx.core.json.JsonObject; |
| import io.vertx.core.net.SocketAddress; |
| import io.vertx.ext.web.Router; |
| import io.vertx.ext.web.RoutingContext; |
| import mockit.Expectations; |
| import mockit.Mock; |
| import mockit.MockUp; |
| import mockit.Mocked; |
| import org.apache.http.HttpHeaders; |
| import org.apache.servicecomb.common.rest.RestConst; |
| import org.apache.servicecomb.common.rest.RestProducerInvocation; |
| import org.apache.servicecomb.common.rest.VertxRestInvocation; |
| import org.apache.servicecomb.common.rest.filter.HttpServerFilter; |
| import org.apache.servicecomb.config.ConfigUtil; |
| import org.apache.servicecomb.core.SCBEngine; |
| import org.apache.servicecomb.core.Transport; |
| import org.apache.servicecomb.core.bootstrap.SCBBootstrap; |
| import org.apache.servicecomb.core.transport.TransportManager; |
| import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils; |
| import org.apache.servicecomb.foundation.vertx.http.HttpServletRequestEx; |
| import org.apache.servicecomb.foundation.vertx.http.HttpServletResponseEx; |
| import org.apache.servicecomb.swagger.invocation.exception.InvocationException; |
| import org.hamcrest.MatcherAssert; |
| import org.hamcrest.Matchers; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.jupiter.api.Assertions; |
| import org.mockito.Mockito; |
| |
| import javax.ws.rs.core.MediaType; |
| import javax.ws.rs.core.Response.Status; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| public class TestVertxRestDispatcher { |
| @Mocked |
| Router mainRouter; |
| |
| @Mocked |
| TransportManager transportManager; |
| |
| VertxRestDispatcher dispatcher; |
| |
| Throwable throwable; |
| |
| boolean invoked; |
| |
| @Before |
| public void setUp() { |
| ConfigUtil.installDynamicConfig(); |
| dispatcher = new VertxRestDispatcher(); |
| dispatcher.init(mainRouter); |
| |
| new MockUp<RestProducerInvocation>() { |
| @Mock |
| void sendFailResponse(Throwable throwable) { |
| TestVertxRestDispatcher.this.throwable = throwable; |
| } |
| |
| @Mock |
| void invoke(Transport transport, HttpServletRequestEx requestEx, HttpServletResponseEx responseEx, |
| List<HttpServerFilter> httpServerFilters) { |
| invoked = true; |
| } |
| }; |
| |
| SCBBootstrap.createSCBEngineForTest().setTransportManager(transportManager); |
| } |
| |
| @After |
| public void teardown() { |
| SCBEngine.getInstance().destroy(); |
| ArchaiusUtils.resetConfig(); |
| } |
| |
| @Test |
| public void getOrder() { |
| Assertions.assertEquals(Integer.MAX_VALUE, dispatcher.getOrder()); |
| } |
| |
| @Test |
| public void failureHandlerNormal() { |
| RoutingContext context = Mockito.mock(RoutingContext.class); |
| RestProducerInvocation restProducerInvocation = new RestProducerInvocation(); |
| Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(restProducerInvocation); |
| Exception e = new Exception(); |
| Mockito.when(context.failure()).thenReturn(e); |
| MockHttpServerResponse response = new MockHttpServerResponse(); |
| Mockito.when(context.response()).thenReturn(response); |
| |
| dispatcher.failureHandler(context); |
| |
| Assertions.assertSame(e, this.throwable); |
| Assertions.assertTrue(response.responseClosed); |
| } |
| |
| @Test |
| public void failureHandlerErrorDataWithInvocation() { |
| RestProducerInvocation restProducerInvocation = new RestProducerInvocation(); |
| RoutingContext context = Mockito.mock(RoutingContext.class); |
| Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(restProducerInvocation); |
| InvocationException e = Mockito.mock(InvocationException.class); |
| ErrorDataDecoderException edde = new ErrorDataDecoderException(e); |
| Mockito.when(context.failure()).thenReturn(edde); |
| MockHttpServerResponse response = new MockHttpServerResponse(); |
| Mockito.when(context.response()).thenReturn(response); |
| |
| dispatcher.failureHandler(context); |
| |
| Assertions.assertSame(e, this.throwable); |
| Assertions.assertTrue(response.responseClosed); |
| } |
| |
| @Test |
| public void failureHandlerErrorDataWithNormal() { |
| RestProducerInvocation restProducerInvocation = new RestProducerInvocation(); |
| RoutingContext context = Mockito.mock(RoutingContext.class); |
| Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(restProducerInvocation); |
| Exception e = new Exception(); |
| ErrorDataDecoderException edde = new ErrorDataDecoderException(e); |
| Mockito.when(context.failure()).thenReturn(edde); |
| MockHttpServerResponse response = new MockHttpServerResponse(); |
| Mockito.when(context.response()).thenReturn(response); |
| |
| dispatcher.failureHandler(context); |
| |
| Assertions.assertSame(edde, this.throwable); |
| Assertions.assertTrue(response.responseClosed); |
| } |
| |
| @Test |
| public void failureHandlerWithNoRestProducerInvocationAndInvocationException() { |
| RoutingContext context = Mockito.mock(RoutingContext.class); |
| Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(null); |
| InvocationException e = new InvocationException(Status.REQUEST_ENTITY_TOO_LARGE, "testMsg"); |
| ErrorDataDecoderException edde = new ErrorDataDecoderException(e); |
| Mockito.when(context.failure()).thenReturn(edde); |
| MockHttpServerResponse response = new MockHttpServerResponse(); |
| Mockito.when(context.response()).thenReturn(response); |
| |
| dispatcher.failureHandler(context); |
| |
| MatcherAssert.assertThat(response.responseHeader, Matchers.hasEntry(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD)); |
| MatcherAssert.assertThat(response.responseStatusCode, Matchers.is(Status.REQUEST_ENTITY_TOO_LARGE.getStatusCode())); |
| MatcherAssert.assertThat(response.responseStatusMessage, Matchers.is(Status.REQUEST_ENTITY_TOO_LARGE.getReasonPhrase())); |
| MatcherAssert.assertThat(response.responseChunk, |
| Matchers.is("{\"message\":\"" + Status.REQUEST_ENTITY_TOO_LARGE.getReasonPhrase() + "\"}")); |
| Assertions.assertTrue(response.responseEnded); |
| } |
| |
| @Test |
| public void failureHandlerWithNoRestProducerInvocationAndOtherException() { |
| RoutingContext context = Mockito.mock(RoutingContext.class); |
| Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(null); |
| String exceptionMessage = "Internal Server Error"; |
| Exception exception = new Exception(exceptionMessage); |
| Mockito.when(context.failure()).thenReturn(exception); |
| MockHttpServerResponse response = new MockHttpServerResponse(); |
| Mockito.when(context.response()).thenReturn(response); |
| |
| dispatcher.failureHandler(context); |
| |
| MatcherAssert.assertThat(response.responseHeader, Matchers.hasEntry(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD)); |
| MatcherAssert.assertThat(response.responseStatusCode, Matchers.is(Status.INTERNAL_SERVER_ERROR.getStatusCode())); |
| MatcherAssert.assertThat(response.responseChunk, |
| Matchers.is("{\"message\":\"" + exceptionMessage + "\"}")); |
| Assertions.assertTrue(response.responseEnded); |
| } |
| |
| @Test |
| public void failureHandlerWithNoExceptionAndStatusCodeIsSet() { |
| RoutingContext context = Mockito.mock(RoutingContext.class); |
| Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(null); |
| Mockito.when(context.failure()).thenReturn(null); |
| MockHttpServerResponse response = new MockHttpServerResponse(); |
| Mockito.when(context.response()).thenReturn(response); |
| Mockito.when(context.statusCode()).thenReturn(Status.REQUEST_ENTITY_TOO_LARGE.getStatusCode()); |
| |
| dispatcher.failureHandler(context); |
| |
| MatcherAssert.assertThat(response.responseHeader, Matchers.hasEntry(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD)); |
| MatcherAssert.assertThat(response.responseStatusCode, Matchers.is(Status.REQUEST_ENTITY_TOO_LARGE.getStatusCode())); |
| Assertions.assertTrue(response.responseEnded); |
| } |
| |
| @Test |
| public void failureHandlerWithNoExceptionAndStatusCodeIsNotSet() { |
| RoutingContext context = Mockito.mock(RoutingContext.class); |
| Mockito.when(context.get(RestConst.REST_PRODUCER_INVOCATION)).thenReturn(null); |
| Mockito.when(context.failure()).thenReturn(null); |
| MockHttpServerResponse response = new MockHttpServerResponse(); |
| Mockito.when(context.response()).thenReturn(response); |
| Mockito.when(context.statusCode()).thenReturn(Status.OK.getStatusCode()); |
| |
| dispatcher.failureHandler(context); |
| |
| MatcherAssert.assertThat(response.responseHeader, Matchers.hasEntry(HttpHeaders.CONTENT_TYPE, MediaType.WILDCARD)); |
| MatcherAssert.assertThat(response.responseStatusCode, Matchers.is(Status.INTERNAL_SERVER_ERROR.getStatusCode())); |
| MatcherAssert.assertThat(response.responseStatusMessage, Matchers.is(Status.INTERNAL_SERVER_ERROR.getReasonPhrase())); |
| MatcherAssert.assertThat(response.responseChunk, |
| Matchers.is("{\"message\":\"" + Status.INTERNAL_SERVER_ERROR.getReasonPhrase() + "\"}")); |
| Assertions.assertTrue(response.responseEnded); |
| } |
| |
| @Test |
| public void onRequest(@Mocked Vertx vertx, @Mocked Context context, @Mocked HttpServerRequest request, |
| @Mocked SocketAddress socketAddress) { |
| |
| Map<String, Object> map = new HashMap<>(); |
| RoutingContext routingContext = new MockUp<RoutingContext>() { |
| @Mock |
| RoutingContext put(String key, Object obj) { |
| map.put(key, obj); |
| return null; |
| } |
| |
| @Mock |
| HttpServerRequest request() { |
| return request; |
| } |
| }.getMockInstance(); |
| |
| new Expectations() { |
| { |
| Vertx.currentContext(); |
| result = context; |
| } |
| }; |
| |
| dispatcher.onRequest(routingContext); |
| |
| Assertions.assertEquals(VertxRestInvocation.class, map.get(RestConst.REST_PRODUCER_INVOCATION).getClass()); |
| Assertions.assertTrue(invoked); |
| } |
| |
| @Test |
| public void testWrapResponseBody() { |
| VertxRestDispatcher vertxRestDispatcher = new VertxRestDispatcher(); |
| String message = "abcd"; |
| String bodyString = vertxRestDispatcher.wrapResponseBody(message); |
| Assertions.assertNotNull(bodyString); |
| Assertions.assertEquals("{\"message\":\"abcd\"}", bodyString); |
| |
| message = "\"abcd\""; |
| bodyString = vertxRestDispatcher.wrapResponseBody(message); |
| Assertions.assertNotNull(bodyString); |
| Assertions.assertEquals("{\"message\":\"\\\"abcd\\\"\"}", bodyString); |
| |
| message = ".01ab\"!@#$%^&*()'\\cd"; |
| bodyString = vertxRestDispatcher.wrapResponseBody(message); |
| Assertions.assertNotNull(bodyString); |
| Assertions.assertEquals("{\"message\":\".01ab\\\"!@#$%^&*()'\\\\cd\"}", bodyString); |
| |
| message = new JsonObject().put("key", new JsonObject().put("k2", "value")).toString(); |
| bodyString = vertxRestDispatcher.wrapResponseBody(message); |
| Assertions.assertNotNull(bodyString); |
| Assertions.assertEquals("{\"key\":{\"k2\":\"value\"}}", bodyString); |
| |
| message = "ab\"23\n@!#cd"; |
| bodyString = vertxRestDispatcher.wrapResponseBody(message); |
| Assertions.assertNotNull(bodyString); |
| Assertions.assertEquals("{\"message\":\"ab\\\"23\\n@!#cd\"}", bodyString); |
| |
| message = "ab\"23\r\n@!#cd"; |
| bodyString = vertxRestDispatcher.wrapResponseBody(message); |
| Assertions.assertNotNull(bodyString); |
| Assertions.assertEquals("{\"message\":\"ab\\\"23\\r\\n@!#cd\"}", bodyString); |
| } |
| } |