-
Notifications
You must be signed in to change notification settings - Fork 94
Add generic c++ integration test for uniform extension behavior. #533
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
Changes from 17 commits
805f950
0c6f182
8d30820
4827909
5f18691
7eeb7e9
467e8a7
e4a50bd
ca17f41
9a2f69e
97322d8
139cfff
2aff84a
fa2ff48
f0c8eba
3440fd9
0f3110c
ab3585b
5ff6d56
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| #include "server/http_dynamic_delay_filter.h" | ||
| #include "server/http_test_server_filter.h" | ||
| #include "server/http_time_tracking_filter.h" | ||
|
|
||
| #include "test/server/http_filter_integration_test_base.h" | ||
|
|
||
| #include "gtest/gtest.h" | ||
|
|
||
| namespace Nighthawk { | ||
|
|
||
| using namespace testing; | ||
|
|
||
| class HttpFilterBaseIntegrationTest | ||
| : public HttpFilterIntegrationTestBase, | ||
| public testing::TestWithParam< | ||
| std::tuple<Envoy::Network::Address::IpVersion, absl::string_view, bool>> { | ||
| public: | ||
| HttpFilterBaseIntegrationTest() : HttpFilterIntegrationTestBase(std::get<0>(GetParam())){}; | ||
| }; | ||
|
|
||
| INSTANTIATE_TEST_SUITE_P( | ||
| IpVersions, HttpFilterBaseIntegrationTest, | ||
| ::testing::Combine(testing::ValuesIn(Envoy::TestEnvironment::getIpVersionsForTest()), | ||
| testing::ValuesIn({absl::string_view(R"EOF( | ||
| name: time-tracking | ||
| typed_config: | ||
| "@type": type.googleapis.com/nighthawk.server.ResponseOptions | ||
| emit_previous_request_delta_in_response_header: "foo" | ||
| )EOF"), | ||
| absl::string_view(R"EOF( | ||
| name: dynamic-delay | ||
| typed_config: | ||
| "@type": type.googleapis.com/nighthawk.server.ResponseOptions | ||
| static_delay: 0.1s | ||
| )EOF"), | ||
| absl::string_view("name: test-server")}), | ||
| testing::ValuesIn({true, false}))); | ||
|
|
||
| // Verify extensions are well-behaved when it comes to: | ||
| // - GET requests. | ||
| // - POST requests with an entity body (takes a slightly different code path). | ||
| // - Valid but empty configuration. | ||
| // - Bad request-level json configuration. | ||
| // We will be low on functional expectations for the extensions, but this will catch hangs and | ||
| // ensure that bad configuration handling is in-place. | ||
| TEST_P(HttpFilterBaseIntegrationTest, BasicExtensionFlows) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we split these into separate tests? I'm not sure what the preference in the code base is, but it seems like each chunk is testing a different behaviour and might deserve a separate test?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can see your point, and in general I agree that the clearer test names that arise out of more finely grained separate tests are better. So personally I lean towards the compact version as is is now. With the context above, are you comfortable with leaving this as-is?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, makes sense to me.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding to what @wjuan-AFK commented on - it would really help if we can separate these into individual test cases - one per behavior. Such approach simplifies future code changes, because it ensures that the tests won't be affected when a new behavior is added or that less tests are affected when a behavior is changed. Additionally when a behavior breaks, we get a clear signal which behavior broke, rather than a generic failure saying "the BasicExtensionFlows no longer works". I see your point about duplicating the parameter unpacking. I wonder if we could place that into a separate helper function. If we figure out we can't, I think we should prefer more instances of smaller independent tests for individual behavior to shorter code that uses parametrised tests. In other words, if we need to drop the parametrised test approach and write few more test cases, I think that is a worthwhile price to pay for shorter and easier to maintain test cases. Can we burn another cycle on this and see if we can find a win-win solution?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will take a look. Hopefully a convenient helper will suffice to support this, and then I can split these out into different tests. I'd like to avoid the need to drop parameterized testing here if we can: we would not only have to duplicate the test(s) for our three current extensions, but also require that for future extensions that would want to piggy back on this.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done in 5ff6d56. |
||
| absl::string_view config = std::get<1>(GetParam()); | ||
| initializeFilterConfiguration(std::string(config)); | ||
| bool is_post = std::get<2>(GetParam()); | ||
| if (is_post) { | ||
| switchToPostWithEntityBody(); | ||
| } | ||
|
|
||
| // Modulo the test-server, extensions are expected to need an upstream to synthesize a reply | ||
| // when effective configuration is valid. | ||
| ResponseOrigin happy_flow_response_origin = config.find_first_of("name: test-server") == 0 | ||
| ? ResponseOrigin::EXTENSION | ||
| : ResponseOrigin::UPSTREAM; | ||
| // Post without any request-level configuration. Should succeed. | ||
| Envoy::IntegrationStreamDecoderPtr response = getResponse(happy_flow_response_origin); | ||
| ASSERT_TRUE(response->complete()); | ||
| EXPECT_EQ("200", response->headers().Status()->value().getStringView()); | ||
| EXPECT_TRUE(response->body().empty()); | ||
|
|
||
| // Test with a valid but empty request-level configuration. | ||
| setRequestLevelConfiguration("{}"); | ||
| response = getResponse(happy_flow_response_origin); | ||
| ASSERT_TRUE(response->complete()); | ||
| EXPECT_EQ("200", response->headers().Status()->value().getStringView()); | ||
| EXPECT_TRUE(response->body().empty()); | ||
|
|
||
| const std::string kBadConfigErrorSentinel = | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not use status instead of doing this substring check?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We do check the http response code, but then also look for this substring in the response body because we know it should look like that if the extension under test is using the shared configuration handling code. Does that sound good?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. That also makes sense. |
||
| "didn't understand the request: Error merging json config: Unable to parse " | ||
| "JSON as proto (INVALID_ARGUMENT:Unexpected"; | ||
|
|
||
| // When sending bad request-level configuration, the extension ought to reply directly. | ||
| setRequestLevelConfiguration("bad_json"); | ||
| response = getResponse(ResponseOrigin::EXTENSION); | ||
| EXPECT_EQ(Envoy::Http::Utility::getResponseStatus(response->headers()), 500); | ||
| EXPECT_THAT(response->body(), HasSubstr(kBadConfigErrorSentinel)); | ||
|
|
||
| // When sending empty request-level configuration, the extension ought to reply directly. | ||
| setRequestLevelConfiguration(""); | ||
| response = getResponse(ResponseOrigin::EXTENSION); | ||
| EXPECT_EQ(Envoy::Http::Utility::getResponseStatus(response->headers()), 500); | ||
| EXPECT_THAT(response->body(), HasSubstr(kBadConfigErrorSentinel)); | ||
| } | ||
|
|
||
| } // namespace Nighthawk | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this, since most symbols from the namespace are qualified? E.g. ::testing::Combine.
If you do prefer to shorten them, can we list them explicitly instead of subscribing to the entire testing namespace which could cause a conflict and confuse someone in the future?
E.g. for symbols that repeat a lot we can:
using ::testing::Combine;
using ::testing::ValuesIn;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done; added an anonymous namespace as well based on that it can only benefit us.