package org.jooby.internal.jetty;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.servlet.AsyncContext;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpOutput;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.jooby.test.MockUnit;
import org.jooby.test.MockUnit.Block;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
@PrepareForTest({JettySse.class, Executors.class })
public class JettySseTest {
private Block httpOutput = unit -> {
Response rsp = unit.get(Response.class);
expect(rsp.getHttpOutput()).andReturn(unit.get(HttpOutput.class));
};
@Test
public void defaults() throws Exception {
new MockUnit(Request.class, Response.class, HttpOutput.class)
.expect(httpOutput)
.run(unit -> {
new JettySse(unit.get(Request.class), unit.get(Response.class));
});
}
@Test
public void send() throws Exception {
byte[] bytes = {0 };
CountDownLatch latch = new CountDownLatch(1);
new MockUnit(Request.class, Response.class, HttpOutput.class)
.expect(httpOutput)
.expect(write(bytes))
.run(unit -> {
new JettySse(unit.get(Request.class),
unit.get(Response.class))
.send(Optional.of("1"), bytes).future().onSuccess(id -> {
assertEquals("1", id.get());
latch.countDown();
});
latch.await();
});
}
@Test
public void sendFailure() throws Exception {
byte[] bytes = {0 };
IOException cause = new IOException("intentional error");
CountDownLatch latch = new CountDownLatch(1);
new MockUnit(Request.class, Response.class, HttpOutput.class)
.expect(httpOutput)
.expect(unit -> {
HttpOutput output = unit.get(HttpOutput.class);
output.write(bytes);
expectLastCall().andThrow(cause);
})
.run(unit -> {
new JettySse(unit.get(Request.class),
unit.get(Response.class))
.send(Optional.of("1"), bytes).future().onFailure(ex -> {
assertEquals(cause, ex);
latch.countDown();
});
latch.await();
});
}
@Test
public void handshake() throws Exception {
new MockUnit(Request.class, Response.class, HttpOutput.class, Runnable.class,
AsyncContext.class, HttpChannel.class, Connector.class, Executor.class)
.expect(httpOutput)
.expect(unit -> {
AsyncContext async = unit.get(AsyncContext.class);
async.setTimeout(0L);
Request req = unit.get(Request.class);
expect(req.getAsyncContext()).andReturn(async);
})
.expect(unit -> {
Response rsp = unit.get(Response.class);
rsp.setStatus(200);
rsp.setHeader("Connection", "Close");
rsp.setContentType("text/event-stream; charset=utf-8");
rsp.flushBuffer();
HttpChannel channel = unit.get(HttpChannel.class);
expect(rsp.getHttpChannel()).andReturn(channel);
Connector connector = unit.get(Connector.class);
expect(channel.getConnector()).andReturn(connector);
Executor executor = unit.get(Executor.class);
expect(connector.getExecutor()).andReturn(executor);
executor.execute(unit.get(Runnable.class));
})
.run(unit -> {
new JettySse(unit.get(Request.class), unit.get(Response.class))
.handshake(unit.get(Runnable.class));
});
}
@SuppressWarnings("resource")
@Test
public void shouldCloseEof() throws Exception {
new MockUnit(Request.class, Response.class, HttpOutput.class)
.expect(httpOutput)
.run(unit -> {
JettySse sse = new JettySse(unit.get(Request.class), unit.get(Response.class));
assertEquals(true, sse.shouldClose(new EofException()));
});
}
@SuppressWarnings("resource")
@Test
public void shouldCloseBrokenPipe() throws Exception {
new MockUnit(Request.class, Response.class, HttpOutput.class)
.expect(httpOutput)
.run(unit -> {
JettySse sse = new JettySse(unit.get(Request.class), unit.get(Response.class));
assertEquals(true, sse.shouldClose(new IOException("broken pipe")));
});
}
@Test
public void close() throws Exception {
new MockUnit(Request.class, Response.class, HttpOutput.class)
.expect(httpOutput)
.expect(unit -> {
Response rsp = unit.get(Response.class);
rsp.closeOutput();
})
.run(unit -> {
JettySse sse = new JettySse(unit.get(Request.class), unit.get(Response.class));
sse.close();
});
}
@Test
public void ignoreClosedStream() throws Exception {
new MockUnit(Request.class, Response.class, HttpOutput.class)
.expect(httpOutput)
.expect(unit -> {
Response rsp = unit.get(Response.class);
rsp.closeOutput();
})
.run(unit -> {
JettySse sse = new JettySse(unit.get(Request.class), unit.get(Response.class));
sse.close();
sse.close();
});
}
@Test
public void closeFailure() throws Exception {
new MockUnit(Request.class, Response.class, HttpOutput.class)
.expect(httpOutput)
.expect(unit -> {
Response rsp = unit.get(Response.class);
rsp.closeOutput();
expectLastCall().andThrow(new EofException("intentional err"));
})
.run(unit -> {
JettySse sse = new JettySse(unit.get(Request.class), unit.get(Response.class));
sse.close();
});
}
private Block write(final byte[] bytes) {
return unit -> {
HttpOutput output = unit.get(HttpOutput.class);
output.write(bytes);
output.flush();
};
}
}