blob: cfe6ed570d2d9f06deacadcbc548ffb1088a9cf8 [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.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);
}
}