diff --git a/CHANGELOG.md b/CHANGELOG.md index 8510d6c0410..087c2d3d68b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,13 @@ All notable changes to this project will be documented in this file. - Stopping creation of temporary directory prior to creating temporary file (#443) ### Changed +- Added `forResponsePredicate` method to HttpWaitStrategy to test response body (#441) - Changed `DockerClientProviderStrategy` to be loaded via Service Loader (#434, #435) - Made it possible to specify docker compose container in configuration (#422, #425) - Clarified wording of pre-flight check messages (#457, #436) - Added caching of failure to find a docker daemon, so that subsequent tests fail fast. This is likely to be a significant improvement in situations where there is no docker daemon available, dramatically reducing run time and log output when further attempts to find the docker daemon cannot succeed. - Allowing JDBC containers' username, password and DB name to be customized (#400, #354) - ## [1.4.2] - 2017-07-25 ### Fixed - Worked around incompatibility between Netty's Unix socket support and OS X 10.11. Reinstated use of TCP-Unix Socket proxy when running on OS X prior to v10.12. (Fixes #402) diff --git a/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java b/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java index abe3e110676..bc0aacb00ce 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java @@ -3,15 +3,17 @@ import com.google.common.base.Strings; import com.google.common.io.BaseEncoding; import org.rnorth.ducttape.TimeoutException; -import org.testcontainers.DockerClientFactory; import org.testcontainers.containers.ContainerLaunchException; import org.testcontainers.containers.GenericContainer; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; import static org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess; @@ -36,6 +38,7 @@ public class HttpWaitStrategy extends GenericContainer.AbstractWaitStrategy { private boolean tlsEnabled; private String username; private String password; + private Predicate responsePredicate; /** * Waits for the given status code. @@ -82,6 +85,16 @@ public HttpWaitStrategy withBasicCredentials(String username, String password) { return this; } + /** + * Waits for the response to pass the given predicate + * @param responsePredicate The predicate to test the response against + * @return this + */ + public HttpWaitStrategy forResponsePredicate(Predicate responsePredicate) { + this.responsePredicate = responsePredicate; + return this; + } + @Override protected void waitUntilReady() { final Integer livenessCheckPort = getLivenessCheckPort(); @@ -114,6 +127,14 @@ protected void waitUntilReady() { connection.getResponseCode())); } + if(responsePredicate != null) { + String responseBody = getResponseBody(connection); + if(!responsePredicate.test(responseBody)) { + throw new RuntimeException(String.format("Response: %s did not match predicate", + responseBody)); + } + } + } catch (IOException e) { throw new RuntimeException(e); } @@ -155,4 +176,20 @@ private URI buildLivenessUri(int livenessCheckPort) { private String buildAuthString(String username, String password) { return AUTH_BASIC + BaseEncoding.base64().encode((username + ":" + password).getBytes()); } + + private String getResponseBody(HttpURLConnection connection) throws IOException { + BufferedReader reader; + if (200 <= connection.getResponseCode() && connection.getResponseCode() <= 299) { + reader = new BufferedReader(new InputStreamReader((connection.getInputStream()))); + } else { + reader = new BufferedReader(new InputStreamReader((connection.getErrorStream()))); + } + + StringBuilder builder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line); + } + return builder.toString(); + } } diff --git a/core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java b/core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java index 2c345ae6ca4..e434ff90466 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java @@ -14,16 +14,18 @@ */ public class HttpWaitStrategyTest extends AbstractWaitStrategyTest { /** - * Doubly-escaped newline sequence indicating end of the HTTP header. + * newline sequence indicating end of the HTTP header. */ - private static final String DOUBLE_NEWLINE = "\\\r\\\n\\\r\\\n"; + private static final String NEWLINE = "\r\n"; + + private static final String GOOD_RESPONSE_BODY = "Good Response Body"; /** * Expects that the WaitStrategy returns successfully after receiving an HTTP 200 response from the container. */ @Test public void testWaitUntilReady_Success() { - waitUntilReadyAndSucceed("while true; do echo -e \"HTTP/1.1 200 OK" + DOUBLE_NEWLINE + "\" | nc -lp 8080; done"); + waitUntilReadyAndSucceed(createShellCommand("200 OK", GOOD_RESPONSE_BODY)); } /** @@ -32,7 +34,16 @@ public void testWaitUntilReady_Success() { */ @Test public void testWaitUntilReady_Timeout() { - waitUntilReadyAndTimeout("while true; do echo -e \"HTTP/1.1 400 Bad Request" + DOUBLE_NEWLINE + "\" | nc -lp 8080; done"); + waitUntilReadyAndTimeout(createShellCommand("400 Bad Request", GOOD_RESPONSE_BODY)); + } + + /** + * Expects that the WaitStrategy throws a {@link RetryCountExceededException} after not the expected response body + * from the container within the timeout period. + */ + @Test + public void testWaitUntilReady_Timeout_BadResponseBody() { + waitUntilReadyAndTimeout(createShellCommand("200 OK", "Bad Response")); } /** @@ -48,6 +59,14 @@ protected void waitUntilReady() { super.waitUntilReady(); ready.set(true); } - }; + }.forResponsePredicate(s -> s.equals(GOOD_RESPONSE_BODY)); + } + + private String createShellCommand(String header, String responseBody) { + int length = responseBody.getBytes().length; + return "while true; do { echo -e \"HTTP/1.1 "+header+NEWLINE+ + "Content-Type: text/html"+NEWLINE+ + "Content-Length: "+length +NEWLINE+ "\";" + +" echo \""+responseBody+"\";} | nc -lp 8080; done"; } }