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(); }); } }