Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support multiple HTTP status codes for HttpWaitStrategy #630

Merged
merged 17 commits into from Apr 13, 2018

Conversation

dadoonet
Copy link
Contributor

@dadoonet dadoonet commented Apr 1, 2018

In the context of elasticsearch test containers module, I'd like to
add an ElasticsearchWaitStrategy class which extends the HttpWaitStrategy
with some default settings so it will be even easier for a end user to
start an ElasticsearchTestContainer.

Anyway, in this context, I found helpful that the HttpWaitStrategy expects
more than on status code.

For example, you can imagine running elasticsearch in secure mode or
without any security. In which cases the elasticsearch service might answer 200 or 401.

This commit proposes this change.

In the context of elasticsearch test containers module, I'd like to
add an ElasticsearchWaitStrategy class which extends the HttpWaitStrategy
with some default settings so it will be even easier for a end user to
start an ElasticsearchTestContainer.

Anyway, in this context, I found helpful that the HttpWaitStrategy expects
more than on status code.

For example, you can imagine running elasticsearch in secure mode or
without any security. In which cases the elasticsearch service might answer 200 or 401.

This commit proposes this change.
*/
@Deprecated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you really think we should deprecate the old API? From a technical standpoint you are of course right, I'm just thinking about the developer experience, with a lot of people using this API right now I assume.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would keep this method and add one more with ranges. ES' case (two values, 200 and 401) is not as popular as e.g. ranges like 200-299 where any 2xx code is considered valid.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, to keep this PR small, we can allow calling forStatusCode multiple times:

 Wait.forHttp("/all").forStatusCode(200).forStatusCode(401)`

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it works for the ES module, I'd be fine with calling the method multiple times.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bsideup

ES' case (two values, 200 and 401) is not as popular as e.g. ranges like 200-299 where any 2xx code is considered valid.

Yeah. I thought about that as well as I saw how fabric8 docker maven plugin offers that as well. But my plan was to come with this later may be with something like forStatusCodes(String...).

Wait.forHttp("/all").forStatusCode(200).forStatusCode(401)

Yeah. I agree with this proposal. And I can then support as well:

Wait.forHttp("/all").forStatusCode(200, 299).forStatusCode(401)

WDYT?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forStatusCode(200, 299) is too unambiguous :(

I would actually go for

Wait.forHttp("/all").forStatusCodeMatching(it -> it >= 200 && it < 300 || it == 401)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And also keep forStatusCode, plus maybe make it chain-able ( Wait.forHttp("/all").forStatusCode(200).forStatusCode(401) )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome. Sounds like great ideas. I'll do that

@dadoonet
Copy link
Contributor Author

dadoonet commented Apr 1, 2018

I don't know TBH. I could also have only change the existing signature.

Up to you. I can update the PR if you wish but having 2 methods doing the same does not look appealing to me.

@barrycommins
Copy link
Contributor

Sorry to butt in here, but this class has never been released, so maybe there's no need to deprecate the method at all?

This class (and package) was created as part of #600, and the previous implementation just wraps it.

@kiview
Copy link
Member

kiview commented Apr 2, 2018

Thx for the comment @barrycommins. If this isn't released yet, WDYT about changing this internal API for now to allow for multiargs by default?

@barrycommins
Copy link
Contributor

barrycommins commented Apr 2, 2018

@kiview I see no harm in it, but I think if it were up to me, I'd keep the original method name forStatusCode and just update it to support varargs.

I think that allows the easiest upgrade path for current users of HttpWaitStrategy, if they want to move to the new class. They would just have to change the package name and the method would still work.

For users changing from a custom subclass, they would need to change the method signature to support varargs, but since they were changing the superclass anyway I don't think it's too big a problem.

Of course, this is contingent on this PR being merged before the next release, because otherwise it would become a breaking change :-)

@dadoonet
Copy link
Contributor Author

dadoonet commented Apr 2, 2018

Makes sense to me. I wanted initially to add the plural form to make obvious that you can many status codes but I'm fine keeping the singular form.

I'll update the PR that way.

@barrycommins
Copy link
Contributor

@dadoonet Just a suggestion, I'm not a maintainer, so hopefully @kiview has an opinion on this.

@kiview
Copy link
Member

kiview commented Apr 2, 2018

The plural is of course much nicer regarding API readability and discoverability. I think I even would be fine with having 2 public methods, with the singular one delegating to the varargs variant.

I'm sure @bsideup or @rnorth have a stronger opinion on the API design 😅

`forStatusCodeMatching()` comes with a default Predicate which checks
status codes that have been provided with `forStatusCode()`.

Also copied the default tests which were using the deprecated package to
the new one to make sure we test the new methods.
@@ -0,0 +1,93 @@
package org.testcontainers.junit.wait.strategy;
Copy link
Member

@bsideup bsideup Apr 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... I'm not sure these changes are related to the PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's somewhat needed to test the new methods I added. See strategy/HttpWaitStrategyTest.java class.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should just start using new implementation (under org.testcontainers.containers.wait.strategy package) in HttpWaitStrategyTest & AbstractWaitStrategyTest since the old classes are just wrappers now

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dadoonet ping :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hum. I'm missing something. I probably misunderstood what you are expecting.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I added a call to my method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I added a call to my method

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should just fix the imports in the old test and start using new wait strategies, so that you can also add your new method to that test

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Works for me. I'll do that. In which case, I'll also move the tests to the new package, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, thanks :)

* @param statusCodePredicate The predicate to test the response against
* @return this
*/
public HttpWaitStrategy forStatusCodeMatching(Predicate<Integer> statusCodePredicate) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking about the use case where somebody will call Wait.forHttp().forStatusCode(200).forStatusCodeMatching(it -> it > 400). Maybe we should throw an error, or handle both statuses?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do really like the Predicate approach here - it opens the door to having some useful ready-to-use predicates in the future (e.g. anySuccessfulStatusCode() or anyServerErrorStatusCode() etc...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the latest change below I'm now hopefully supporting both.

@bsideup bsideup added this to the next milestone Apr 7, 2018
Also use a Set instead of a List to avoid duplicates.
* @return this
*/
public HttpWaitStrategy forStatusCodeMatching(Predicate<Integer> statusCodePredicate) {
this.statusCodePredicate.and(statusCodePredicate);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be:

this.statusCodePredicate = this.statusCodePredicate.and(statusCodePredicate);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was unsure and thought that we can directly modify the existing predicate and chain it to a new one without changing its reference. Let me update that.

@@ -3,7 +3,7 @@
import org.junit.ClassRule;
import org.junit.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.Wait;
import org.testcontainers.containers.wait.strategy.Wait;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated to this PR

@dadoonet
Copy link
Contributor Author

dadoonet commented Apr 8, 2018

The travis failure seems unrelated to my changes.

And stop testing deprecated methods
@dadoonet
Copy link
Contributor Author

I believe it's ok now.
Please LMK if I should squash it and rebase.

@@ -59,7 +59,9 @@ protected void waitUntilReady() {
super.waitUntilReady();
ready.set(true);
}
}.forResponsePredicate(s -> s.equals(GOOD_RESPONSE_BODY));
}
.forResponsePredicate(s -> s.equals(GOOD_RESPONSE_BODY))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add a test where both forStatusCodeMatching and withStatusCode are used (to check that it's not being overwritten)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. My code is actually failing! :p
I've been wondering how to add a test and by your question I actually understood how to write. (Was so easy that I feel dumb not having found that before...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But we're still missing a test where we call

forHttp()
    .forStatusCode(200) // <- 
    .forStatusCode(205) // <- 
   .forStatusCodeMatching(it -> it > 400)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So should I clone the class or just change the buildWaitStrategy method?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just add a test method which is not based on buildWaitStrategy's result

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did it. A bit differently though so I can reuse most of the existing code and that could help in the future.
I hope it's ok for you

@bsideup
Copy link
Member

bsideup commented Apr 13, 2018

@dadoonet ok, looks good now! Just the last thing before we can merge - CHANGELOG.md :)
Sorry for not pointing at it before, I keep forgetting it :(

P.S. we squash automatically when we merge, no need to squash it yourself 👍

@dadoonet
Copy link
Contributor Author

No problem.

@bsideup bsideup merged commit 1d85a38 into testcontainers:master Apr 13, 2018
@bsideup
Copy link
Member

bsideup commented Apr 13, 2018

@dadoonet merged, thanks a lot! Will be released this weeks :)

@dadoonet
Copy link
Contributor Author

Awesome! Thanks for guiding me for my 1st code PR :)

@dadoonet dadoonet deleted the pr/http-multiple-ports branch April 13, 2018 12:05
dadoonet referenced this pull request in dadoonet/testcontainers-java Sep 24, 2018
….forStatusCode

In #630 we introduced predicates but with a default one which is always present whatever what is passed in the `forStatusCodeMatching()` method.

This commit adds a test that demonstrates the issue:

* We have a service returning `200 OK`
* The predicate expects anything which is a code >= to `300`
* The test should throw a Timeout as this condition is never reached but without the current fix, the test never throws the Timeout as 200 matches the default builtin predicate.

This commit fixes the problem by checking at startup time what is/are the predicates that needs to be applied.

Note that in most cases, an HTTP service is expected to throw a `200 OK` status so that fix might not fix actually any real problem and might be a theory only. But I'd prefer to have code that actually implements what is supposed to work.

Closes #880.
rnorth pushed a commit that referenced this pull request Sep 25, 2018
….forStatusCode (#881)

In #630 we introduced predicates but with a default one which is always present whatever is passed in the `forStatusCodeMatching()` method.

This commit adds a test that demonstrates the issue:

* We have a service returning `200 OK`
* The predicate expects anything which is a code >= to `300`
* The test should throw a Timeout as this condition is never reached but without the current fix, the test never throws the Timeout as 200 matches the default builtin predicate.

This commit fixes the problem by checking at startup time what is/are the predicates that needs to be applied.

Note that in most cases, an HTTP service is expected to throw a `200 OK` status so that fix might not fix actually any real problem and might be a theory only. But I'd prefer to have code that actually implements what is supposed to work.

Closes #880.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants