Add some unit test for sentinel-transport-netty-http module (#321)
This commit is contained in:
parent
f046194d6b
commit
4fbccd879b
|
|
@ -41,5 +41,15 @@
|
|||
<artifactId>httpcore</artifactId>
|
||||
<version>4.4.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -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<FlowRule> rules = new ArrayList<FlowRule>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, CommandHandler> 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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue