package org.jooby.internal.netty;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.isA;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import org.jooby.test.MockUnit;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.concurrent.EventExecutor;
@RunWith(PowerMockRunner.class)
@PrepareForTest({NettySse.class, DefaultHttpHeaders.class, DefaultHttpResponse.class })
public class NettySseTest {
@Test
public void defaults() throws Exception {
new MockUnit(ChannelHandlerContext.class)
.run(unit -> {
new NettySse(unit.get(ChannelHandlerContext.class));
});
}
@Test
public void close() throws Exception {
new MockUnit(ChannelHandlerContext.class)
.expect(unit -> {
expect(unit.get(ChannelHandlerContext.class).close()).andReturn(null);
})
.run(unit -> {
new NettySse(unit.get(ChannelHandlerContext.class))
.close();
});
}
@Test
public void handshake() throws Exception {
new MockUnit(ChannelHandlerContext.class, EventExecutor.class, Runnable.class)
.expect(unit -> {
DefaultHttpHeaders headers = unit.constructor(DefaultHttpHeaders.class)
.build();
expect(headers.set(HttpHeaderNames.CONNECTION, "Close")).andReturn(headers);
expect(headers.set(HttpHeaderNames.CONTENT_TYPE, "text/event-stream; charset=utf-8"))
.andReturn(headers);
DefaultHttpResponse rsp = unit.constructor(DefaultHttpResponse.class)
.args(HttpVersion.class, HttpResponseStatus.class, HttpHeaders.class)
.build(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, headers);
EventExecutor executor = unit.get(EventExecutor.class);
executor.execute(unit.get(Runnable.class));
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.writeAndFlush(rsp)).andReturn(null);
expect(ctx.executor()).andReturn(executor);
})
.run(unit -> {
new NettySse(unit.get(ChannelHandlerContext.class))
.handshake(unit.get(Runnable.class));
});
}
@Test
public void send() throws Exception {
byte[] bytes = {0 };
CountDownLatch latch = new CountDownLatch(1);
new MockUnit(ChannelHandlerContext.class, ChannelFuture.class)
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
expect(future.isSuccess()).andReturn(true);
expect(future.addListener(unit.capture(ChannelFutureListener.class))).andReturn(future);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.writeAndFlush(isA(ByteBuf.class))).andReturn(future);
})
.run(unit -> {
new NettySse(unit.get(ChannelHandlerContext.class))
.send(Optional.of("id"), bytes).future().onSuccess(id -> {
assertEquals("id", id.get());
latch.countDown();
});
}, unit -> {
ChannelFutureListener listener = unit.captured(ChannelFutureListener.class).iterator()
.next();
listener.operationComplete(unit.get(ChannelFuture.class));
latch.await();
});
}
@Test(expected = IllegalStateException.class)
public void sendErr() throws Exception {
byte[] bytes = {0 };
new MockUnit(ChannelHandlerContext.class)
.expect(unit -> {
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.writeAndFlush(isA(ByteBuf.class)))
.andThrow(new IllegalStateException("intentional error"));
})
.run(unit -> {
new NettySse(unit.get(ChannelHandlerContext.class))
.send(Optional.of("id"), bytes);
});
}
@Test
public void sendFailure() throws Exception {
byte[] bytes = {0 };
CountDownLatch latch = new CountDownLatch(1);
IOException ex = new IOException("intentional err");
new MockUnit(ChannelHandlerContext.class, ChannelFuture.class)
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
expect(future.isSuccess()).andReturn(false);
expect(future.cause()).andReturn(ex);
expect(future.addListener(unit.capture(ChannelFutureListener.class))).andReturn(future);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.writeAndFlush(isA(ByteBuf.class))).andReturn(future);
})
.run(unit -> {
new NettySse(unit.get(ChannelHandlerContext.class))
.send(Optional.of("id"), bytes).future().onFailure(cause -> {
assertEquals(ex, cause);
latch.countDown();
});
}, unit -> {
ChannelFutureListener listener = unit.captured(ChannelFutureListener.class).iterator()
.next();
listener.operationComplete(unit.get(ChannelFuture.class));
latch.await();
});
}
}