package WEBPIECESxPACKAGE; import java.util.List; import java.util.concurrent.CompletableFuture; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.webpieces.ddl.api.JdbcApi; import org.webpieces.ddl.api.JdbcConstants; import org.webpieces.ddl.api.JdbcFactory; import org.webpieces.httpcommon.api.RequestId; import org.webpieces.httpcommon.api.RequestListener; import org.webpieces.httpparser.api.common.Header; import org.webpieces.httpparser.api.common.KnownHeaderName; import org.webpieces.httpparser.api.dto.HttpRequest; import org.webpieces.httpparser.api.dto.HttpRequestLine; import org.webpieces.httpparser.api.dto.HttpUri; import org.webpieces.httpparser.api.dto.KnownHttpMethod; import org.webpieces.httpparser.api.dto.KnownStatusCode; import org.webpieces.plugins.hibernate.HibernatePlugin; import org.webpieces.util.logging.Logger; import org.webpieces.util.logging.LoggerFactory; import org.webpieces.webserver.test.Asserts; import org.webpieces.webserver.test.FullResponse; import org.webpieces.webserver.test.MockResponseSender; import org.webpieces.webserver.test.PlatformOverridesForTest; import com.google.inject.Binder; import com.google.inject.Module; import WEBPIECESxPACKAGE.base.libs.RemoteService; import WEBPIECESxPACKAGE.mock.MockRemoteSystem; /** * These are working examples of tests that sometimes are better done with the BasicSeleniumTest example but are here for completeness * so you can test the way you would like to test * * @author dhiller * */ public class TestLesson1BasicRequestResponse { private final static Logger log = LoggerFactory.getLogger(TestLesson1BasicRequestResponse.class); private RequestListener server; //In the future, we may develop a FrontendSimulator that can be used instead of MockResponseSender that would follow //any redirects in the application properly.. private MockResponseSender mockResponseSender = new MockResponseSender(); //see below comments in AppOverrideModule private MockRemoteSystem mockRemote = new MockRemoteSystem(); //our your favorite mock library private JdbcApi jdbc = JdbcFactory.create(JdbcConstants.jdbcUrl, JdbcConstants.jdbcUser, JdbcConstants.jdbcPassword); private static String pUnit = HibernatePlugin.PERSISTENCE_TEST_UNIT; @Before public void setUp() throws InterruptedException, ClassNotFoundException { log.info("Setting up test"); Asserts.assertWasCompiledWithParamNames("test"); //clear in-memory database jdbc.dropAllTablesFromDatabase(); //you may want to create this server ONCE in a static method BUT if you do, also remember to clear out all your //mocks after every test AND you can no longer run single threaded(tradeoffs, tradeoffs) //This is however pretty fast to do in many systems... Server webserver = new Server( new PlatformOverridesForTest(), new AppOverridesModule(), new ServerConfig(pUnit)); server = webserver.start(); } /** * Testing a synchronous controller may be easier especially if there is no remote communication. */ @Test public void testSynchronousController() { HttpRequest req = createRequest("/"); server.incomingRequest(req, new RequestId(0), true, mockResponseSender); List<FullResponse> responses = mockResponseSender.getResponses(); Assert.assertEquals(1, responses.size()); FullResponse httpPayload = responses.get(0); httpPayload.assertStatusCode(KnownStatusCode.HTTP_200_OK); } /** * It is highly suggested you step through this test in debug mode to understand the description below... * * This is a single threaded test that actually allows the webserver thread to return back to the test before * the response comes. (in production the thread would process other requests while waiting for remote system response). * Then the test simulates the response coming in from remote system and makes sure we send a response back * to the ResponseSender. In implementations like this with a remote system, one can avoid holding threads up * and allow them to keep working while waiting for a response from the remote system. */ @Test public void testAsyncControllerAndRemoteSystem() { CompletableFuture<Integer> future = new CompletableFuture<Integer>(); mockRemote.addValueToReturn(future); HttpRequest req = createRequest("/async"); server.incomingRequest(req, new RequestId(0), true, mockResponseSender); List<FullResponse> responses = mockResponseSender.getResponses(); Assert.assertEquals(0, responses.size()); //notice that the thread returned but there is no response back to browser yet such that thread can do more work. //next, simulate remote system returning a value.. int value = 85; future.complete(value); List<FullResponse> responses2 = mockResponseSender.getResponses(); Assert.assertEquals(1, responses2.size()); FullResponse httpPayload = responses2.get(0); httpPayload.assertStatusCode(KnownStatusCode.HTTP_200_OK); httpPayload.assertContains("This is a page with value="+value); } /** * For the heck of it, test out chunked compressed response... */ @Test public void testChunkedCompression() { HttpRequest req = createRequest("/"); req.addHeader(new Header(KnownHeaderName.ACCEPT_ENCODING, "gzip, deflate")); server.incomingRequest(req, new RequestId(0), true, mockResponseSender); List<FullResponse> responses = mockResponseSender.getResponses(); Assert.assertEquals(1, responses.size()); FullResponse httpPayload = responses.get(0); httpPayload.assertStatusCode(KnownStatusCode.HTTP_200_OK); Assert.assertTrue(httpPayload.getChunks().size() > 0); httpPayload.uncompressBodyAndAssertContainsString("Webpieces"); } static HttpRequest createRequest(String uri) { HttpRequestLine requestLine = new HttpRequestLine(); requestLine.setMethod(KnownHttpMethod.GET); requestLine.setUri(new HttpUri(uri)); HttpRequest req = new HttpRequest(); req.setRequestLine(requestLine ); req.addHeader(new Header(KnownHeaderName.HOST, "yourdomain.com")); return req; } private class AppOverridesModule implements Module { @Override public void configure(Binder binder) { //Add overrides here generally using mocks from fields in the test class binder.bind(RemoteService.class).toInstance(mockRemote); //see above comment on the field mockRemote } } }