-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add framework to allow test-specific assertions on HTTP response.
Motivation: Individual tests may have certain expectations on how IDS responds to HTTP requests. Some interactions supported by `TestingClient` exercise IDS requests that return content; for example, `getData`. For these interactions, `TestingClient` returns the content from IDS and not the HTTP response. This makes it impossible for a test to assert that, for these interactions, the HTTP response from IDS matches the test's expectations. Modification: Update `TestingClient` to allow the test to include one or more assertions on the HTTP response. The support following the builder pattern. The assertions are provided by the test as Consumer objects that accept an HttpResponse object, with lambdas providing an easy to build such objects. Some static methods are included that provide common assertions, to keep code readable and DRY. The Apache client is customised to include a response interceptor, through which the assertions are made. The existing code that checks the combined `Content-Length` (if present) and `Transfer-Encoding` headers (if present) is valid is refactored to take advantage of this framework. This check now applies uniformly to all HTTP responses from IDS. Some other, existing tests are updated to take advantage of the new framework. Further refactoring is possible; for example, to make the check on the response's status code more explicit. Such a refactoring would reduce the complexity of the `TestingClient` interaction methods by reducing the parameter count. Result: It is now possible to write tests that make test-specific assertions on the HTTP response from IDS for interactions where IDS returns content. Signed-off-by: Paul Millar <paul.millar@desy.de>
- Loading branch information
1 parent
1e32fb4
commit cfce753
Showing
6 changed files
with
266 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
src/test/java/org/icatproject/ids/integration/util/HeaderMatcher.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package org.icatproject.ids.integration.util; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
import org.apache.http.Header; | ||
import org.apache.http.HttpResponse; | ||
import org.hamcrest.BaseMatcher; | ||
import org.hamcrest.Description; | ||
import org.hamcrest.Matcher; | ||
import static org.hamcrest.Matchers.equalToIgnoringCase; | ||
|
||
/** | ||
* A custom header that matches HTTP headers in some HttpResponse object. It | ||
* supports two kind of matches: that the header is missing or that the header | ||
* is present and the value matches some other Matcher. | ||
*/ | ||
public class HeaderMatcher extends BaseMatcher<HttpResponse> { | ||
|
||
private final String headerName; | ||
private final Matcher<String> matcher; | ||
|
||
public HeaderMatcher(String headerName, Matcher<String> valueMatcher) { | ||
this.headerName = requireNonNull(headerName); | ||
this.matcher = valueMatcher; | ||
} | ||
|
||
private boolean isHeaderExpected() { | ||
return matcher != null; | ||
} | ||
|
||
@Override | ||
public boolean matches(Object item) { | ||
HttpResponse response = (HttpResponse) item; // It is a bug if item is not an HttpResponse. | ||
|
||
Header header = response.getFirstHeader(headerName); | ||
|
||
if (!isHeaderExpected()) { | ||
return header == null; | ||
} | ||
|
||
if (header == null) { | ||
return false; | ||
} | ||
|
||
String headerValue = header.getValue(); | ||
|
||
return matcher.matches(headerValue); | ||
} | ||
|
||
@Override | ||
public void describeTo(Description description) { | ||
if (isHeaderExpected()) { | ||
description.appendText("a '") | ||
.appendText(headerName) | ||
.appendText("' header that ") | ||
.appendDescriptionOf(matcher); | ||
} else { | ||
description.appendText("no '").appendText(headerName).appendText("' header"); | ||
} | ||
} | ||
|
||
/** | ||
* Match an HttpResponse that does not contain the named header. | ||
* @param header The name of the HTTP response header. | ||
* @return a Matcher that verifies expected response. | ||
*/ | ||
public static Matcher<HttpResponse> hasNoHeader(String header) { | ||
return new HeaderMatcher(header, null); | ||
} | ||
|
||
/** | ||
* Match an HttpResponse that contains the named header with a value that | ||
* matches the supplied matcher. | ||
* @param header The name of the HTTP response header. | ||
* @param matcher The matcher for the header value. | ||
* @return a Matcher that verifies expected response. | ||
*/ | ||
public static Matcher hasHeaderMatching(String header, Matcher<String> matcher) { | ||
return new HeaderMatcher(header, matcher); | ||
} | ||
|
||
/** | ||
* Match an HttpResponse that contains the named header with the specific | ||
* value (ignoring case). | ||
* @param header The name of the HTTP response header. | ||
* @param value The expected value. | ||
* @return a Matcher that verifies expected response. | ||
*/ | ||
public static Matcher hasHeader(String header, Object value) { | ||
Matcher<String> m = equalToIgnoringCase(String.valueOf(value)); | ||
return hasHeaderMatching(header, m); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/test/java/org/icatproject/ids/integration/util/LongValue.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package org.icatproject.ids.integration.util; | ||
|
||
import org.hamcrest.Description; | ||
import org.hamcrest.Matcher; | ||
import org.hamcrest.TypeSafeMatcher; | ||
|
||
/** | ||
* A Matcher that checks whether a String argument is a Long value. The code is | ||
* heavily based on an answer from Ezequiel to | ||
* <a href="https://stackoverflow.com/questions/58099695/is-there-a-way-in-hamcrest-to-test-for-a-value-to-be-a-number"> | ||
* a stack overview question</a>. | ||
*/ | ||
public class LongValue extends TypeSafeMatcher<String> { | ||
|
||
@Override | ||
protected boolean matchesSafely(String s) { | ||
try { | ||
Long.valueOf(s); | ||
} catch (NumberFormatException nfe) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
@Override | ||
public void describeTo(Description description) { | ||
description.appendText("is long"); | ||
} | ||
|
||
public static Matcher<String> longValue() { | ||
return new LongValue(); | ||
} | ||
} |
Oops, something went wrong.