BAEL-1372: Adding jUnits for Testing Netty with EmbeddedChannel article (#3973)
This commit is contained in:
parent
c2034ca7cc
commit
1977fe28f0
@ -0,0 +1,49 @@
|
|||||||
|
package com.baeldung.netty;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
|
import io.netty.handler.codec.http.*;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
|
public class CalculatorOperationHandler extends SimpleChannelInboundHandler<Operation> {
|
||||||
|
|
||||||
|
protected void channelRead0(ChannelHandlerContext ctx, Operation msg) throws Exception {
|
||||||
|
Long result = calculateEndpoint(msg);
|
||||||
|
sendHttpResponse(ctx, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CREATED), result.toString());
|
||||||
|
ctx.fireChannelRead(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long calculateEndpoint(Operation operation) {
|
||||||
|
|
||||||
|
String operator = operation.getOperator().toLowerCase().trim();
|
||||||
|
switch (operator) {
|
||||||
|
case "add":
|
||||||
|
return operation.getNumber1() + operation.getNumber2();
|
||||||
|
case "multiply":
|
||||||
|
return operation.getNumber1() * operation.getNumber2();
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Operation not defined");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpResponse res, String content) {
|
||||||
|
|
||||||
|
// Generate an error page if response getStatus code is not OK (200).
|
||||||
|
ByteBuf buf = Unpooled.copiedBuffer(content, CharsetUtil.UTF_8);
|
||||||
|
res.content().writeBytes(buf);
|
||||||
|
|
||||||
|
HttpUtil.setContentLength(res, res.content().readableBytes());
|
||||||
|
|
||||||
|
ctx.channel().writeAndFlush(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
sendHttpResponse(ctx, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR), "Operation not defined");
|
||||||
|
ctx.fireExceptionCaught(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
package com.baeldung.netty;
|
||||||
|
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
|
|
||||||
|
public class HttpMessageHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
|
||||||
|
|
||||||
|
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
|
||||||
|
|
||||||
|
String uri = msg.uri();
|
||||||
|
HttpMethod httpMethod = msg.method();
|
||||||
|
HttpHeaders headers = msg.headers();
|
||||||
|
|
||||||
|
if (HttpMethod.GET == httpMethod) {
|
||||||
|
|
||||||
|
String[] uriComponents = uri.split("[?]");
|
||||||
|
String endpoint = uriComponents[0];
|
||||||
|
String[] queryParams = uriComponents[1].split("&");
|
||||||
|
|
||||||
|
if ("/calculate".equalsIgnoreCase(endpoint)) {
|
||||||
|
|
||||||
|
String[] firstQueryParam = queryParams[0].split("=");
|
||||||
|
String[] secondQueryParam = queryParams[1].split("=");
|
||||||
|
|
||||||
|
Integer a = Integer.valueOf(firstQueryParam[1]);
|
||||||
|
Integer b = Integer.valueOf(secondQueryParam[1]);
|
||||||
|
String operator = headers.get("operator");
|
||||||
|
|
||||||
|
Operation operation = new Operation(a, b, operator);
|
||||||
|
ctx.fireChannelRead(operation);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("HTTP method not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
49
libraries/src/main/java/com/baeldung/netty/Operation.java
Normal file
49
libraries/src/main/java/com/baeldung/netty/Operation.java
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package com.baeldung.netty;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class Operation implements Serializable {
|
||||||
|
|
||||||
|
private Integer number1;
|
||||||
|
private Integer number2;
|
||||||
|
private String operator;
|
||||||
|
|
||||||
|
public Operation(Integer number1, Integer number2, String operator) {
|
||||||
|
this.number1 = number1;
|
||||||
|
this.number2 = number2;
|
||||||
|
this.operator = operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getNumber1() {
|
||||||
|
return number1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumber1(Integer number1) {
|
||||||
|
this.number1 = number1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getNumber2() {
|
||||||
|
return number2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumber2(Integer number2) {
|
||||||
|
this.number2 = number2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOperator() {
|
||||||
|
return operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOperator(String operator) {
|
||||||
|
this.operator = operator;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Operation{" +
|
||||||
|
"number1=" + number1 +
|
||||||
|
", number2=" + number2 +
|
||||||
|
", operator='" + operator + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
package com.baeldung.netty;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
|
||||||
|
public class EmbeddedChannelUnitTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenTwoChannelHandlers_testPipeline() {
|
||||||
|
|
||||||
|
final FullHttpRequest httpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/calculate?a=10&b=5");
|
||||||
|
httpRequest.headers().add("Operator", "Add");
|
||||||
|
|
||||||
|
EmbeddedChannel channel = new EmbeddedChannel(
|
||||||
|
new HttpMessageHandler(), new CalculatorOperationHandler());
|
||||||
|
|
||||||
|
channel.pipeline()
|
||||||
|
.addFirst(new HttpMessageHandler())
|
||||||
|
.addLast(new CalculatorOperationHandler());
|
||||||
|
|
||||||
|
|
||||||
|
// send HTTP request to server and check that the message is on the inbound pipeline
|
||||||
|
assertTrue(channel.writeInbound(httpRequest));
|
||||||
|
|
||||||
|
long inboundChannelResponse = channel.readInbound();
|
||||||
|
assertEquals(15, inboundChannelResponse);
|
||||||
|
|
||||||
|
// we should have an outbound message in the form of a HTTP response
|
||||||
|
assertEquals(1, channel.outboundMessages().size());
|
||||||
|
// Object response = channel.readOutbound();
|
||||||
|
|
||||||
|
FullHttpResponse httpResponse = channel.readOutbound();
|
||||||
|
String httpResponseContent = httpResponse.content().toString(Charset.defaultCharset());
|
||||||
|
assertTrue("15".equalsIgnoreCase(httpResponseContent));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenTwoChannelHandlers_testExceptionHandlingInHttpMessageHandler() {
|
||||||
|
|
||||||
|
EmbeddedChannel channel = new EmbeddedChannel(
|
||||||
|
new HttpMessageHandler(), new CalculatorOperationHandler());
|
||||||
|
|
||||||
|
final FullHttpRequest wrongHttpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/calculate?a=10&b=5");
|
||||||
|
wrongHttpRequest.headers().add("Operator", "Add");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// send invalid HTTP request to server and expect and error
|
||||||
|
channel.pipeline().fireChannelRead(wrongHttpRequest);
|
||||||
|
channel.checkException();
|
||||||
|
// channel.writeInbound(wrongHttpRequest);
|
||||||
|
Assert.fail();
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
|
||||||
|
// the HttpMessageHandler does not handle the exception and throws it down the pipeline
|
||||||
|
assertTrue(ex instanceof UnsupportedOperationException);
|
||||||
|
assertTrue(ex.getMessage().equalsIgnoreCase("HTTP method not supported"));
|
||||||
|
|
||||||
|
FullHttpResponse errorHttpResponse = channel.readOutbound();
|
||||||
|
String errorHttpResponseContent = errorHttpResponse.content().toString(Charset.defaultCharset());
|
||||||
|
assertTrue("Operation not defined".equalsIgnoreCase(errorHttpResponseContent));
|
||||||
|
assertEquals(HttpResponseStatus.INTERNAL_SERVER_ERROR, errorHttpResponse.status());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void givenTwoChannelHandlers_testExceptionHandlingInCalculatorOperationHandler() {
|
||||||
|
EmbeddedChannel channel = new EmbeddedChannel(
|
||||||
|
new HttpMessageHandler(), new CalculatorOperationHandler());
|
||||||
|
|
||||||
|
final FullHttpRequest wrongHttpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/calculate?a=10&b=5");
|
||||||
|
wrongHttpRequest.headers().add("Operator", "Invalid_operation");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// send invalid HTTP request to server and expect and error
|
||||||
|
channel.writeInbound(wrongHttpRequest);
|
||||||
|
Assert.fail();
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
|
||||||
|
// the HttpMessageHandler does not handle the exception and throws it down the pipeline
|
||||||
|
assertTrue(ex instanceof IllegalArgumentException);
|
||||||
|
assertTrue(ex.getMessage().equalsIgnoreCase("Operation not defined"));
|
||||||
|
|
||||||
|
// the outbound message is a HTTP response with the status code 500
|
||||||
|
FullHttpResponse errorHttpResponse = channel.readOutbound();
|
||||||
|
String errorHttpResponseContent = errorHttpResponse.content().toString(Charset.defaultCharset());
|
||||||
|
assertTrue("Operation not defined".equalsIgnoreCase(errorHttpResponseContent));
|
||||||
|
assertEquals(HttpResponseStatus.INTERNAL_SERVER_ERROR, errorHttpResponse.status());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user