From 4fbccd879bba98529f123ddebf431348b974f209 Mon Sep 17 00:00:00 2001 From: cdfive <31885791+cdfive@users.noreply.github.com> Date: Wed, 26 Dec 2018 16:08:04 +0800 Subject: [PATCH] Add some unit test for sentinel-transport-netty-http module (#321) --- .../sentinel-transport-netty-http/pom.xml | 10 + .../command/netty/HttpServerHandlerTest.java | 212 ++++++++++++++++++ .../netty/HttpServerInitializerTest.java | 64 ++++++ .../command/netty/HttpServerTest.java | 117 ++++++++++ 4 files changed, 403 insertions(+) create mode 100644 sentinel-transport/sentinel-transport-netty-http/src/test/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerHandlerTest.java create mode 100644 sentinel-transport/sentinel-transport-netty-http/src/test/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerInitializerTest.java create mode 100644 sentinel-transport/sentinel-transport-netty-http/src/test/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerTest.java diff --git a/sentinel-transport/sentinel-transport-netty-http/pom.xml b/sentinel-transport/sentinel-transport-netty-http/pom.xml index ae403039..ad978a6e 100755 --- a/sentinel-transport/sentinel-transport-netty-http/pom.xml +++ b/sentinel-transport/sentinel-transport-netty-http/pom.xml @@ -41,5 +41,15 @@ httpcore 4.4.5 + + junit + junit + test + + + org.mockito + mockito-core + test + \ No newline at end of file diff --git a/sentinel-transport/sentinel-transport-netty-http/src/test/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerHandlerTest.java b/sentinel-transport/sentinel-transport-netty-http/src/test/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerHandlerTest.java new file mode 100644 index 00000000..3102c217 --- /dev/null +++ b/sentinel-transport/sentinel-transport-netty-http/src/test/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerHandlerTest.java @@ -0,0 +1,212 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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.alibaba.csp.sentinel.transport.command.netty; + +import com.alibaba.csp.sentinel.Constants; +import com.alibaba.csp.sentinel.config.SentinelConfig; +import com.alibaba.csp.sentinel.init.InitExecutor; +import com.alibaba.csp.sentinel.slots.block.RuleConstant; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; +import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; +import com.alibaba.csp.sentinel.transport.CommandCenter; +import com.alibaba.csp.sentinel.transport.command.NettyHttpCommandCenter; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.http.*; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; +import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static org.junit.Assert.assertEquals; + +/** + * Test cases for {@link HttpServerHandler}. + * + * @author cdfive + * @date 2018-12-17 + */ +public class HttpServerHandlerTest { + + private static String CRLF = "\r\n"; + + private static String SENTINEL_CHARSET_NAME = SentinelConfig.charset(); + + private static Charset SENTINEL_CHARSET = Charset.forName(SENTINEL_CHARSET_NAME); + + private static EmbeddedChannel embeddedChannel; + + @BeforeClass + public static void beforeClass() throws Exception { + // don't execute InitExecutor.doInit, to avoid CommandCenter SPI loaded + Field[] declaredFields = InitExecutor.class.getDeclaredFields(); + for (Field declaredField : declaredFields) { + if (declaredField.getName().equals("initialized")) { + declaredField.setAccessible(true); + ((AtomicBoolean)declaredField.get(InitExecutor.class)).set(true); + } + } + + // create NettyHttpCommandCenter to create HttpServer + CommandCenter commandCenter = new NettyHttpCommandCenter(); + // call beforeStart to register handlers + commandCenter.beforeStart(); + } + + @Before + public void before() { + // the same Handlers in order as the ChannelPipeline in HttpServerInitializer + HttpRequestDecoder httpRequestDecoder = new HttpRequestDecoder(); + HttpObjectAggregator httpObjectAggregator = new HttpObjectAggregator(1024 * 1024); + HttpResponseEncoder httpResponseEncoder = new HttpResponseEncoder(); + + HttpServerHandler httpServerHandler = new HttpServerHandler(); + + // create new EmbeddedChannel every method call + embeddedChannel = new EmbeddedChannel(httpRequestDecoder, httpObjectAggregator, httpResponseEncoder, httpServerHandler); + } + + @Test + public void testInvalidCommand() { + String httpRequestStr = "GET / HTTP/1.1" + CRLF + + "Host: localhost:8719" + CRLF + + CRLF; + String body = "Invalid command"; + + processError(httpRequestStr, body); + } + + @Test + public void testUnknownCommand() { + String httpRequestStr = "GET /aaa HTTP/1.1" + CRLF + + "Host: localhost:8719" + CRLF + + CRLF; + String body = String.format("Unknown command \"%s\"", "aaa"); + + processError(httpRequestStr, body); + } + + @Test + public void testVersionCommand() { + String httpRequestStr = "GET /version HTTP/1.1" + CRLF + + "Host: localhost:8719" + CRLF + + CRLF; + String body = Constants.SENTINEL_VERSION; + + processSuccess(httpRequestStr, body); + } + + @Test + public void testGetRuleCommandInvalidType() { + String httpRequestStr = "GET /getRules HTTP/1.1" + CRLF + + "Host: localhost:8719" + CRLF + + CRLF; + String body = "invalid type"; + + processFailed(httpRequestStr, body); + } + + @Test + public void testGetRuleCommandFlowEmptyRule() { + String httpRequestStr = "GET /getRules?type=flow HTTP/1.1" + CRLF + + "Host: localhost:8719" + CRLF + + CRLF; + String body = "[]"; + + processSuccess(httpRequestStr, body); + } + +// FIXME byteBuf.toString can't get body response now, need to find another way +// @Test + public void testGetRuleCommandFlowSomeRules() { + List rules = new ArrayList(); + FlowRule rule1 = new FlowRule(); + rule1.setResource("key"); + rule1.setCount(20); + rule1.setGrade(RuleConstant.FLOW_GRADE_QPS); + rule1.setLimitApp("default"); + rules.add(rule1); + FlowRuleManager.loadRules(rules); + + String httpRequestStr = "GET /getRules?type=flow HTTP/1.1" + CRLF + + "Host: localhost:8719" + CRLF + + CRLF; + String body = ""; + + processSuccess(httpRequestStr, body); + } + + private void processError(String httpRequestStr, String body) { + processError(httpRequestStr, BAD_REQUEST, body); + } + + private void processError(String httpRequestStr, HttpResponseStatus status, String body) { + String httpResponseStr = processResponse(httpRequestStr); + assertErrorStatusAndBody(status, body, httpResponseStr); + } + + private void processSuccess(String httpRequestStr, String body) { + process(httpRequestStr, OK, body); + } + + private void processFailed(String httpRequestStr, String body) { + process(httpRequestStr, BAD_REQUEST, body); + } + + private void process(String httpRequestStr, HttpResponseStatus status, String body) { + String responseStr = processResponse(httpRequestStr); + assertStatusAndBody(status, body, responseStr); + } + + private String processResponse(String httpRequestStr) { + embeddedChannel.writeInbound(Unpooled.wrappedBuffer(httpRequestStr.getBytes(SENTINEL_CHARSET))); + + ByteBuf byteBuf = embeddedChannel.readOutbound(); + + String responseStr = byteBuf.toString(SENTINEL_CHARSET); + return responseStr; + } + + private void assertErrorStatusAndBody(HttpResponseStatus status, String body, String httpResponseStr) { + StringBuilder text = new StringBuilder(); + text.append(HttpVersion.HTTP_1_1.toString()).append(' ').append(status.toString()).append(CRLF); + text.append("Content-Type: text/plain; charset=").append(SENTINEL_CHARSET_NAME).append(CRLF); + text.append(CRLF); + text.append(body); + + assertEquals(text.toString(), httpResponseStr); + } + + private void assertStatusAndBody(HttpResponseStatus status, String body, String httpResponseStr) { + StringBuilder text = new StringBuilder(); + text.append(HttpVersion.HTTP_1_1.toString()).append(' ').append(status.toString()).append(CRLF); + text.append("Content-Type: text/plain; charset=").append(SENTINEL_CHARSET_NAME).append(CRLF); + text.append("content-length: " + body.length()).append(CRLF); + text.append("connection: close").append(CRLF); + text.append(CRLF); + text.append(body); + + assertEquals(text.toString(), httpResponseStr); + } +} \ No newline at end of file diff --git a/sentinel-transport/sentinel-transport-netty-http/src/test/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerInitializerTest.java b/sentinel-transport/sentinel-transport-netty-http/src/test/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerInitializerTest.java new file mode 100644 index 00000000..ba43448c --- /dev/null +++ b/sentinel-transport/sentinel-transport-netty-http/src/test/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerInitializerTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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.alibaba.csp.sentinel.transport.command.netty; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpResponseEncoder; +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.*; + +/** + * Test cases for {@link HttpServerInitializer}. + * + * @author cdfive + * @date 2018-12-19 + */ +public class HttpServerInitializerTest { + + @Test + public void testInitChannel() throws Exception { + // mock Objects + HttpServerInitializer httpServerInitializer = mock(HttpServerInitializer.class); + SocketChannel socketChannel = mock(SocketChannel.class); + ChannelPipeline channelPipeline = mock(ChannelPipeline.class); + + // mock SocketChannel#pipeline() method + when(socketChannel.pipeline()).thenReturn(channelPipeline); + + // HttpServerInitializer#initChannel(SocketChannel) call real method + doCallRealMethod().when(httpServerInitializer).initChannel(socketChannel); + + // start test for HttpServerInitializer#initChannel(SocketChannel) + httpServerInitializer.initChannel(socketChannel); + + // verify 4 times calling ChannelPipeline#addLast() method + verify(channelPipeline, times(4)).addLast(any(ChannelHandler.class)); + + // verify the order of calling ChannelPipeline#addLast() method + InOrder inOrder = inOrder(channelPipeline); + inOrder.verify(channelPipeline).addLast(any(HttpRequestDecoder.class)); + inOrder.verify(channelPipeline).addLast(any(HttpObjectAggregator.class)); + inOrder.verify(channelPipeline).addLast(any(HttpResponseEncoder.class)); + + inOrder.verify(channelPipeline).addLast(any(HttpServerHandler.class)); + } +} diff --git a/sentinel-transport/sentinel-transport-netty-http/src/test/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerTest.java b/sentinel-transport/sentinel-transport-netty-http/src/test/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerTest.java new file mode 100644 index 00000000..dbb213c1 --- /dev/null +++ b/sentinel-transport/sentinel-transport-netty-http/src/test/java/com/alibaba/csp/sentinel/transport/command/netty/HttpServerTest.java @@ -0,0 +1,117 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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.alibaba.csp.sentinel.transport.command.netty; + +import com.alibaba.csp.sentinel.command.CommandHandler; +import com.alibaba.csp.sentinel.command.CommandHandlerProvider; +import com.alibaba.csp.sentinel.command.handler.BasicInfoCommandHandler; +import com.alibaba.csp.sentinel.command.handler.VersionCommandHandler; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Map; + +import static org.junit.Assert.*; + +/** + * Test cases for {@link HttpServer}. + * + * @author cdfive + * @date 2018-12-19 + */ +public class HttpServerTest { + + private static HttpServer httpServer; + + @BeforeClass + public static void beforeClass() { + // note: clear handlerMap first, as other test case may init HttpServer.handlerMap + // if not, run mvn test, the next assertEquals(0, HttpServer.handlerMap.size()) may fail + HttpServer.handlerMap.clear(); + + // create new HttpServer + httpServer = new HttpServer(); + + // no handler in handlerMap at first + assertEquals(0, HttpServer.handlerMap.size()); + } + + @Before + public void before() { + // clear handlerMap every method call + HttpServer.handlerMap.clear(); + } + + @Test + public void testRegisterCommand() { + String commandName; + CommandHandler handler; + + // if commandName is null, no handler added in handlerMap + commandName = null; + handler = new VersionCommandHandler(); + httpServer.registerCommand(commandName, handler); + assertEquals(0, HttpServer.handlerMap.size()); + + // if commandName is "", no handler added in handlerMap + commandName = ""; + handler = new VersionCommandHandler(); + httpServer.registerCommand(commandName, handler); + assertEquals(0, HttpServer.handlerMap.size()); + + // if handler is null, no handler added in handlerMap + commandName = "version"; + handler = null; + httpServer.registerCommand(commandName, handler); + assertEquals(0, HttpServer.handlerMap.size()); + + // add one handler, commandName:version, handler:VersionCommandHandler + commandName = "version"; + handler = new VersionCommandHandler(); + httpServer.registerCommand(commandName, handler); + assertEquals(1, HttpServer.handlerMap.size()); + + // add the same name Handler, no handler added in handlerMap + commandName = "version"; + handler = new VersionCommandHandler(); + httpServer.registerCommand(commandName, handler); + assertEquals(1, HttpServer.handlerMap.size()); + + // add another handler, commandName:basicInfo, handler:BasicInfoCommandHandler + commandName = "basicInfo"; + handler = new BasicInfoCommandHandler(); + httpServer.registerCommand(commandName, handler); + assertEquals(2, HttpServer.handlerMap.size()); + } + + @Test + public void testRegisterCommands() { + Map handlerMap = null; + + // if handlerMap is null, no handler added in handlerMap + httpServer.registerCommands(handlerMap); + assertEquals(0, HttpServer.handlerMap.size()); + + // add handler from CommandHandlerProvider + handlerMap = CommandHandlerProvider.getInstance().namedHandlers(); + httpServer.registerCommands(handlerMap); + // check same size + assertEquals(handlerMap.size(), HttpServer.handlerMap.size()); + // check not same reference + assertTrue(handlerMap != HttpServer.handlerMap); + } +}