package org.jooby.internal.jetty; import static org.easymock.EasyMock.eq; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.isA; import static org.junit.Assert.assertArrayEquals; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.Channels; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import javax.servlet.AsyncContext; import javax.servlet.http.HttpServletRequest; import org.eclipse.jetty.server.HttpOutput; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Response; import org.jooby.servlet.ServletServletRequest; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; @RunWith(PowerMockRunner.class) @PrepareForTest({JettyResponse.class, Channels.class, LoggerFactory.class }) public class JettyResponseTest { private MockUnit.Block servletRequest = unit -> { Request req = unit.get(Request.class); ServletServletRequest request = unit.get(ServletServletRequest.class); expect(request.servletRequest()).andReturn(req); }; private MockUnit.Block startAsync = unit -> { ServletServletRequest request = unit.get(ServletServletRequest.class); HttpServletRequest req = unit.mock(HttpServletRequest.class); expect(req.isAsyncStarted()).andReturn(false); expect(req.startAsync()).andReturn(unit.get(AsyncContext.class)); expect(request.servletRequest()).andReturn(req); }; private MockUnit.Block asyncStarted = unit -> { Request request = unit.get(Request.class); expect(request.isAsyncStarted()).andReturn(true); }; private MockUnit.Block noAsyncStarted = unit -> { Request request = unit.get(Request.class); expect(request.isAsyncStarted()).andReturn(false); }; @Test public void defaults() throws Exception { new MockUnit(ServletServletRequest.class, Request.class, Response.class) .expect(servletRequest) .run(unit -> { new JettyResponse(unit.get(ServletServletRequest.class), unit.get(Response.class)); }); } @Test public void sendBytes() throws Exception { byte[] bytes = "bytes".getBytes(); new MockUnit(ServletServletRequest.class, Request.class, Response.class, HttpOutput.class) .expect(servletRequest) .expect(unit -> { HttpOutput output = unit.get(HttpOutput.class); output.sendContent(unit.capture(ByteBuffer.class)); Response rsp = unit.get(Response.class); expect(rsp.getHttpOutput()).andReturn(output); }) .run(unit -> { new JettyResponse(unit.get(ServletServletRequest.class), unit.get(Response.class)) .send(bytes); }, unit -> { assertArrayEquals(bytes, unit.captured(ByteBuffer.class).iterator().next().array()); }); } @Test public void sendBuffer() throws Exception { byte[] bytes = "bytes".getBytes(); ByteBuffer buffer = ByteBuffer.wrap(bytes); new MockUnit(ServletServletRequest.class, Request.class, Response.class, HttpOutput.class) .expect(servletRequest) .expect(unit -> { HttpOutput output = unit.get(HttpOutput.class); output.sendContent(eq(buffer)); Response rsp = unit.get(Response.class); expect(rsp.getHttpOutput()).andReturn(output); }) .run(unit -> { new JettyResponse(unit.get(ServletServletRequest.class), unit.get(Response.class)) .send(buffer); }); } @Test public void sendInputStream() throws Exception { new MockUnit(ServletServletRequest.class, Request.class, Response.class, HttpOutput.class, InputStream.class, AsyncContext.class) .expect(servletRequest) .expect(unit -> { unit.mockStatic(Channels.class); ReadableByteChannel channel = unit.mock(ReadableByteChannel.class); expect(Channels.newChannel(unit.get(InputStream.class))).andReturn(channel); HttpOutput output = unit.get(HttpOutput.class); output.sendContent(eq(channel), isA(JettyResponse.class)); Response rsp = unit.get(Response.class); expect(rsp.getHttpOutput()).andReturn(output); }) .expect(startAsync) .run(unit -> { new JettyResponse(unit.get(ServletServletRequest.class), unit.get(Response.class)) .send(unit.get(InputStream.class)); }); } @Test public void sendInputStreamAsyncStarted() throws Exception { new MockUnit(ServletServletRequest.class, Request.class, Response.class, HttpOutput.class, InputStream.class, AsyncContext.class) .expect(servletRequest) .expect(unit -> { unit.mockStatic(Channels.class); ReadableByteChannel channel = unit.mock(ReadableByteChannel.class); expect(Channels.newChannel(unit.get(InputStream.class))).andReturn(channel); HttpOutput output = unit.get(HttpOutput.class); output.sendContent(eq(channel), isA(JettyResponse.class)); Response rsp = unit.get(Response.class); expect(rsp.getHttpOutput()).andReturn(output); }) .expect(unit -> { ServletServletRequest request = unit.get(ServletServletRequest.class); HttpServletRequest req = unit.mock(HttpServletRequest.class); expect(req.isAsyncStarted()).andReturn(true); expect(request.servletRequest()).andReturn(req); }) .run(unit -> { JettyResponse rsp = new JettyResponse(unit.get(ServletServletRequest.class), unit.get(Response.class)); rsp.send(unit.get(InputStream.class)); rsp.end(); }); } @Test public void sendSmallFileChannel() throws Exception { FileChannel channel = newFileChannel(1); new MockUnit(ServletServletRequest.class, Request.class, Response.class, HttpOutput.class, AsyncContext.class) .expect(servletRequest) .expect(unit -> { HttpOutput output = unit.get(HttpOutput.class); output.sendContent(eq(channel)); Response rsp = unit.get(Response.class); expect(rsp.getBufferSize()).andReturn(2); expect(rsp.getHttpOutput()).andReturn(output); }) .run(unit -> { new JettyResponse(unit.get(ServletServletRequest.class), unit.get(Response.class)) .send(channel); }); } @Test public void sendLargeFileChannel() throws Exception { FileChannel channel = newFileChannel(10); new MockUnit(ServletServletRequest.class, Request.class, Response.class, HttpOutput.class, AsyncContext.class) .expect(servletRequest) .expect(unit -> { HttpOutput output = unit.get(HttpOutput.class); output.sendContent(eq(channel), isA(JettyResponse.class)); Response rsp = unit.get(Response.class); expect(rsp.getBufferSize()).andReturn(5); expect(rsp.getHttpOutput()).andReturn(output); }) .expect(startAsync) .run(unit -> { new JettyResponse(unit.get(ServletServletRequest.class), unit.get(Response.class)) .send(channel); }); } @Test public void succeeded() throws Exception { byte[] bytes = "bytes".getBytes(); new MockUnit(ServletServletRequest.class, Request.class, Response.class, HttpOutput.class, AsyncContext.class) .expect(servletRequest) .expect(unit -> { HttpOutput output = unit.get(HttpOutput.class); output.sendContent(unit.capture(ByteBuffer.class)); output.close(); Response rsp = unit.get(Response.class); expect(rsp.getHttpOutput()).andReturn(output).times(2); }) .expect(noAsyncStarted) .run(unit -> { JettyResponse rsp = new JettyResponse(unit.get(ServletServletRequest.class), unit.get(Response.class)); rsp.send(bytes); rsp.succeeded(); }); } @Test public void succeededAsync() throws Exception { FileChannel channel = newFileChannel(10); new MockUnit(ServletServletRequest.class, Request.class, Response.class, HttpOutput.class, AsyncContext.class) .expect(servletRequest) .expect(unit -> { HttpOutput output = unit.get(HttpOutput.class); output.sendContent(eq(channel), isA(JettyResponse.class)); Response rsp = unit.get(Response.class); expect(rsp.getBufferSize()).andReturn(5); expect(rsp.getHttpOutput()).andReturn(output); }) .expect(startAsync) .expect(asyncStarted) .expect(unit -> { Request req = unit.get(Request.class); AsyncContext ctx = unit.get(AsyncContext.class); ctx.complete(); expect(req.getAsyncContext()).andReturn(ctx); }) .run(unit -> { JettyResponse rsp = new JettyResponse(unit.get(ServletServletRequest.class), unit.get(Response.class)); rsp.send(channel); rsp.succeeded(); }); } @Test public void end() throws Exception { new MockUnit(ServletServletRequest.class, Request.class, Response.class, HttpOutput.class) .expect(servletRequest) .expect(unit -> { HttpOutput output = unit.get(HttpOutput.class); output.close(); Response rsp = unit.get(Response.class); expect(rsp.getHttpOutput()).andReturn(output); }) .expect(noAsyncStarted) .run(unit -> { new JettyResponse(unit.get(ServletServletRequest.class), unit.get(Response.class)) .end(); }); } @Test public void failed() throws Exception { IOException cause = new IOException(); new MockUnit(ServletServletRequest.class, Request.class, Response.class, HttpOutput.class) .expect(servletRequest) .expect(unit -> { Logger log = unit.mock(Logger.class); log.error("execution of /path resulted in exception", cause); unit.mockStatic(LoggerFactory.class); expect(LoggerFactory.getLogger(org.jooby.Response.class)).andReturn(log); }) .expect(unit -> { HttpOutput output = unit.get(HttpOutput.class); output.close(); Response rsp = unit.get(Response.class); expect(rsp.getHttpOutput()).andReturn(output); }) .expect(noAsyncStarted) .expect(unit -> { ServletServletRequest req = unit.get(ServletServletRequest.class); expect(req.path()).andReturn("/path"); }) .run(unit -> { new JettyResponse(unit.get(ServletServletRequest.class), unit.get(Response.class)) .failed(cause); }); } private FileChannel newFileChannel(final int size) { return new FileChannel() { @Override public int read(final ByteBuffer dst) throws IOException { return 0; } @Override public long read(final ByteBuffer[] dsts, final int offset, final int length) throws IOException { return 0; } @Override public int write(final ByteBuffer src) throws IOException { return 0; } @Override public long write(final ByteBuffer[] srcs, final int offset, final int length) throws IOException { return 0; } @Override public long position() throws IOException { return 0; } @Override public FileChannel position(final long newPosition) throws IOException { return null; } @Override public long size() throws IOException { return size; } @Override public FileChannel truncate(final long size) throws IOException { return null; } @Override public void force(final boolean metaData) throws IOException { } @Override public long transferTo(final long position, final long count, final WritableByteChannel target) throws IOException { return 0; } @Override public long transferFrom(final ReadableByteChannel src, final long position, final long count) throws IOException { return 0; } @Override public int read(final ByteBuffer dst, final long position) throws IOException { return 0; } @Override public int write(final ByteBuffer src, final long position) throws IOException { return 0; } @Override public MappedByteBuffer map(final MapMode mode, final long position, final long size) throws IOException { return null; } @Override public FileLock lock(final long position, final long size, final boolean shared) throws IOException { return null; } @Override public FileLock tryLock(final long position, final long size, final boolean shared) throws IOException { return null; } @Override protected void implCloseChannel() throws IOException { } }; } }