From 3dc7a8ad6522ff0eea6ae5a5959ef5d0f267f320 Mon Sep 17 00:00:00 2001 From: Barry Commins Date: Tue, 15 Aug 2017 01:20:19 +0100 Subject: [PATCH 1/5] Adding ability to test response body in HttpWaitStrategy --- .../containers/wait/HttpWaitStrategy.java | 71 ++++++++++++++++++- .../junit/wait/HttpWaitStrategyTest.java | 31 ++++++-- 2 files changed, 96 insertions(+), 6 deletions(-) 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..e8d45453943 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; @@ -37,6 +39,10 @@ public class HttpWaitStrategy extends GenericContainer.AbstractWaitStrategy { private String username; private String password; + private String responseBody; + private String responseContaining; + private Predicate responsePredicate; + /** * Waits for the given status code. * @@ -82,6 +88,36 @@ public HttpWaitStrategy withBasicCredentials(String username, String password) { return this; } + /** + * Waits for the given response body + * @param responseBody The response body to expect + * @return this + */ + public HttpWaitStrategy forResponseBody(String responseBody) { + this.responseBody = responseBody; + return this; + } + + /** + * Waits for the response to contain the expected String + * @param responseContaining the String the response is expected to contain + * @return this + */ + public HttpWaitStrategy forResponseContaining(String responseContaining) { + this.responseContaining = responseContaining; + 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 +150,23 @@ protected void waitUntilReady() { connection.getResponseCode())); } + String actualResponseBody = getResponseBody(connection); + + if(responseBody != null && !actualResponseBody.equals(responseBody)) { + throw new RuntimeException(String.format("Response was: %s", + connection.getContent())); + } + + if(responseContaining != null && !actualResponseBody.contains(responseContaining)) { + throw new RuntimeException(String.format("Response was: %s", + connection.getContent())); + } + + if(responsePredicate != null && !responsePredicate.test(actualResponseBody)) { + throw new RuntimeException(String.format("Response: %s did not match predicate", + connection.getContent())); + } + } catch (IOException e) { throw new RuntimeException(e); } @@ -155,4 +208,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..d7ccce833f4 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,16 @@ protected void waitUntilReady() { super.waitUntilReady(); ready.set(true); } - }; + }.forResponseBody(GOOD_RESPONSE_BODY) + .forResponseContaining("Body") + .forResponsePredicate(s -> s.length() == GOOD_RESPONSE_BODY.length()); + } + + 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"; } } From 461e58261c974950f8b84052539a18c8a01daa77 Mon Sep 17 00:00:00 2001 From: Barry Commins Date: Tue, 15 Aug 2017 01:24:04 +0100 Subject: [PATCH 2/5] Added HttpWaitStrategy changes to CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0a7a5ea703..55334c5acdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file. - Fixed local Docker Compose executable name resolution on Windows (#416) ### Changed +- Added methods to HttpWaitStrategy to test response body ## [1.4.2] - 2017-07-25 ### Fixed From 1f0f8d9e2a76af4c39e8e8ee89b06bc139d2874d Mon Sep 17 00:00:00 2001 From: barrycommins Date: Tue, 15 Aug 2017 10:13:07 +0100 Subject: [PATCH 3/5] Simplified HttpWaitStrategy response checking --- CHANGELOG.md | 2 +- .../containers/wait/HttpWaitStrategy.java | 37 +------------------ .../junit/wait/HttpWaitStrategyTest.java | 4 +- 3 files changed, 3 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55334c5acdd..f68e3ca3f47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ All notable changes to this project will be documented in this file. - Fixed local Docker Compose executable name resolution on Windows (#416) ### Changed -- Added methods to HttpWaitStrategy to test response body +- Added `forResponsePredicate` method to HttpWaitStrategy to test response body ## [1.4.2] - 2017-07-25 ### Fixed 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 e8d45453943..dcb73c57ff7 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java @@ -38,9 +38,6 @@ public class HttpWaitStrategy extends GenericContainer.AbstractWaitStrategy { private boolean tlsEnabled; private String username; private String password; - - private String responseBody; - private String responseContaining; private Predicate responsePredicate; /** @@ -88,26 +85,6 @@ public HttpWaitStrategy withBasicCredentials(String username, String password) { return this; } - /** - * Waits for the given response body - * @param responseBody The response body to expect - * @return this - */ - public HttpWaitStrategy forResponseBody(String responseBody) { - this.responseBody = responseBody; - return this; - } - - /** - * Waits for the response to contain the expected String - * @param responseContaining the String the response is expected to contain - * @return this - */ - public HttpWaitStrategy forResponseContaining(String responseContaining) { - this.responseContaining = responseContaining; - return this; - } - /** * Waits for the response to pass the given predicate * @param responsePredicate The predicate to test the response against @@ -150,19 +127,7 @@ protected void waitUntilReady() { connection.getResponseCode())); } - String actualResponseBody = getResponseBody(connection); - - if(responseBody != null && !actualResponseBody.equals(responseBody)) { - throw new RuntimeException(String.format("Response was: %s", - connection.getContent())); - } - - if(responseContaining != null && !actualResponseBody.contains(responseContaining)) { - throw new RuntimeException(String.format("Response was: %s", - connection.getContent())); - } - - if(responsePredicate != null && !responsePredicate.test(actualResponseBody)) { + if(responsePredicate != null && !responsePredicate.test(getResponseBody(connection))) { throw new RuntimeException(String.format("Response: %s did not match predicate", connection.getContent())); } 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 d7ccce833f4..e434ff90466 100644 --- a/core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java +++ b/core/src/test/java/org/testcontainers/junit/wait/HttpWaitStrategyTest.java @@ -59,9 +59,7 @@ protected void waitUntilReady() { super.waitUntilReady(); ready.set(true); } - }.forResponseBody(GOOD_RESPONSE_BODY) - .forResponseContaining("Body") - .forResponsePredicate(s -> s.length() == GOOD_RESPONSE_BODY.length()); + }.forResponsePredicate(s -> s.equals(GOOD_RESPONSE_BODY)); } private String createShellCommand(String header, String responseBody) { From fe06d0a597f915ee4af016767562eab3ed0a1917 Mon Sep 17 00:00:00 2001 From: barrycommins Date: Tue, 15 Aug 2017 10:18:03 +0100 Subject: [PATCH 4/5] Fixed exception message for HttpStrategy response predicate --- .../org/testcontainers/containers/wait/HttpWaitStrategy.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 dcb73c57ff7..03f0b98b4e8 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java @@ -127,9 +127,10 @@ protected void waitUntilReady() { connection.getResponseCode())); } - if(responsePredicate != null && !responsePredicate.test(getResponseBody(connection))) { + String responseBody = getResponseBody(connection); + if(responsePredicate != null && !responsePredicate.test(responseBody)) { throw new RuntimeException(String.format("Response: %s did not match predicate", - connection.getContent())); + responseBody)); } } catch (IOException e) { From 8442127ea2386f53e60d0c7348645c04da03d864 Mon Sep 17 00:00:00 2001 From: Barry Commins Date: Mon, 18 Sep 2017 21:58:02 +0100 Subject: [PATCH 5/5] #441 only read http response body if responsePredicate is not null --- .../containers/wait/HttpWaitStrategy.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 03f0b98b4e8..bc0aacb00ce 100644 --- a/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java +++ b/core/src/main/java/org/testcontainers/containers/wait/HttpWaitStrategy.java @@ -127,10 +127,12 @@ protected void waitUntilReady() { connection.getResponseCode())); } - String responseBody = getResponseBody(connection); - if(responsePredicate != null && !responsePredicate.test(responseBody)) { - throw new RuntimeException(String.format("Response: %s did not match predicate", - responseBody)); + 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) {