-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Added unit test cases for rate limiting #124
Changes from 4 commits
9d96d7e
a7098ad
99af110
9dc18d7
267d0bb
021c942
f84d7d8
4c9dbd3
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 |
---|---|---|
|
@@ -19,10 +19,13 @@ | |
#include "contrib/endpoints/src/api_manager/mock_api_manager_environment.h" | ||
#include "contrib/endpoints/src/api_manager/service_control/proto.h" | ||
#include "gmock/gmock.h" | ||
#include "google/protobuf/text_format.h" | ||
#include "gtest/gtest.h" | ||
|
||
using ::google::api::servicecontrol::v1::CheckRequest; | ||
using ::google::api::servicecontrol::v1::CheckResponse; | ||
using ::google::api::servicecontrol::v1::AllocateQuotaRequest; | ||
using ::google::api::servicecontrol::v1::AllocateQuotaResponse; | ||
using ::google::api::servicecontrol::v1::ReportRequest; | ||
using ::google::api::servicecontrol::v1::ReportResponse; | ||
using ::google::api_manager::utils::Status; | ||
|
@@ -39,6 +42,39 @@ namespace api_manager { | |
namespace service_control { | ||
|
||
namespace { | ||
|
||
const char kAllocateQuotaResponse[] = R"( | ||
operation_id: "test_service" | ||
quota_metrics { | ||
metric_name: "serviceruntime.googleapis.com/api/consumer/quota_used_count" | ||
metric_values { | ||
labels { | ||
key: "/quota_name" | ||
value: "metric_first" | ||
} | ||
int64_value: 2 | ||
} | ||
metric_values { | ||
labels { | ||
key: "/quota_name" | ||
value: "metric" | ||
} | ||
int64_value: 1 | ||
} | ||
}service_config_id: "2017-02-08r9" | ||
|
||
)"; | ||
|
||
const char kAllocateQuotaResponseErrorExhausted[] = R"( | ||
operation_id: "test_service" | ||
allocate_errors { | ||
code: RESOURCE_EXHAUSTED | ||
description: "Insufficient tokens for quota group and limit \'apiWriteQpsPerProject_LOW\' of service \'jaebonginternal.sandbox.google.com\', using the limit by ID \'container:1002409420961\'." | ||
} | ||
service_config_id: "2017-02-08r9" | ||
|
||
)"; | ||
|
||
void FillOperationInfo(OperationInfo* op) { | ||
op->operation_id = "operation_id"; | ||
op->operation_name = "operation_name"; | ||
|
@@ -195,6 +231,124 @@ TEST_F(AggregatedTestWithRealClient, CheckOKTest) { | |
EXPECT_EQ(stat.send_report_operations, 0); | ||
} | ||
|
||
class QuotaAllocationFailedTestWithRealClient : public ::testing::Test { | ||
public: | ||
void SetUp() { | ||
service_.set_name("test_service"); | ||
service_.mutable_control()->set_environment( | ||
"servicecontrol.googleapis.com"); | ||
env_.reset(new ::testing::NiceMock<MockApiManagerEnvironment>); | ||
sc_lib_.reset(Aggregated::Create(service_, nullptr, env_.get(), nullptr)); | ||
ASSERT_TRUE((bool)(sc_lib_)); | ||
// This is the call actually creating the client. | ||
sc_lib_->Init(); | ||
} | ||
|
||
void DoRunHTTPRequest(HTTPRequest* request) { | ||
std::map<std::string, std::string> headers; | ||
AllocateQuotaResponse allocate_quota_response_; | ||
|
||
ASSERT_TRUE(::google::protobuf::TextFormat::ParseFromString( | ||
kAllocateQuotaResponseErrorExhausted, &allocate_quota_response_)); | ||
|
||
std::string body = allocate_quota_response_.SerializeAsString(); | ||
|
||
request->OnComplete(Status::OK, std::move(headers), std::move(body)); | ||
} | ||
|
||
::google::api::Service service_; | ||
std::unique_ptr<MockApiManagerEnvironment> env_; | ||
std::unique_ptr<Interface> sc_lib_; | ||
}; | ||
|
||
TEST_F(QuotaAllocationFailedTestWithRealClient, AllocateQuotaTest) { | ||
EXPECT_CALL(*env_, DoRunHTTPRequest(_)) | ||
.WillOnce(Invoke( | ||
this, &QuotaAllocationFailedTestWithRealClient::DoRunHTTPRequest)); | ||
|
||
std::vector<std::pair<std::string, int>> metric_cost_vector; | ||
metric_cost_vector.push_back( | ||
std::make_pair<std::string, int>("metric_first", 1)); | ||
metric_cost_vector.push_back( | ||
std::make_pair<std::string, int>("metric_second", 2)); | ||
|
||
QuotaRequestInfo info; | ||
|
||
info.metric_cost_vector = &metric_cost_vector; | ||
|
||
FillOperationInfo(&info); | ||
sc_lib_->Quota(info, nullptr, [](Status status) { | ||
ASSERT_TRUE(status.code() == Code::RESOURCE_EXHAUSTED); | ||
}); | ||
} | ||
|
||
class QuotaAllocationTestWithRealClient : public ::testing::Test { | ||
public: | ||
void SetUp() { | ||
service_.set_name("test_service"); | ||
service_.mutable_control()->set_environment( | ||
"servicecontrol.googleapis.com"); | ||
env_.reset(new ::testing::NiceMock<MockApiManagerEnvironment>); | ||
sc_lib_.reset(Aggregated::Create(service_, nullptr, env_.get(), nullptr)); | ||
ASSERT_TRUE((bool)(sc_lib_)); | ||
// This is the call actually creating the client. | ||
sc_lib_->Init(); | ||
} | ||
|
||
void DoRunHTTPRequest(HTTPRequest* request) { | ||
std::map<std::string, std::string> headers; | ||
AllocateQuotaRequest allocate_quota_request_; | ||
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. can we shorten the name as request and response without tailing _? 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. Changed to quota_request and quota_response |
||
AllocateQuotaResponse allocate_quota_response_; | ||
|
||
ASSERT_TRUE(allocate_quota_request_.ParseFromString(request->body())); | ||
ASSERT_EQ(allocate_quota_request_.allocate_operation().quota_metrics_size(), | ||
2); | ||
|
||
std::unordered_map<std::string, int> metric_rules; | ||
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. can we do: then we can do ASSERT_EQ(set, { {metric1, cost1}, {metric2, cost2}}); 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. Applied |
||
for (auto rule : | ||
allocate_quota_request_.allocate_operation().quota_metrics()) { | ||
metric_rules[rule.metric_name()] = rule.metric_values(0).int64_value(); | ||
} | ||
ASSERT_EQ(metric_rules.size(), 2); | ||
|
||
ASSERT_NE(metric_rules.find("metric_first"), metric_rules.end()); | ||
ASSERT_NE(metric_rules.find("metric_second"), metric_rules.end()); | ||
ASSERT_EQ(metric_rules["metric_first"], 1); | ||
ASSERT_EQ(metric_rules["metric_second"], 2); | ||
|
||
ASSERT_TRUE(::google::protobuf::TextFormat::ParseFromString( | ||
kAllocateQuotaResponse, &allocate_quota_response_)); | ||
|
||
std::string body = allocate_quota_response_.SerializeAsString(); | ||
|
||
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. it seems that line 279-284 can be a small function so it can be called by line 269 too. |
||
request->OnComplete(Status::OK, std::move(headers), std::move(body)); | ||
} | ||
|
||
::google::api::Service service_; | ||
std::unique_ptr<MockApiManagerEnvironment> env_; | ||
std::unique_ptr<Interface> sc_lib_; | ||
}; | ||
|
||
TEST_F(QuotaAllocationTestWithRealClient, AllocateQuotaTest) { | ||
EXPECT_CALL(*env_, DoRunHTTPRequest(_)) | ||
.WillOnce( | ||
Invoke(this, &QuotaAllocationTestWithRealClient::DoRunHTTPRequest)); | ||
|
||
std::vector<std::pair<std::string, int>> metric_cost_vector; | ||
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. can we try this: metric_cost_ve = { {metric1, cost1}, {metric2, cost2} } to use cxx11 initialization feature. 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. Beside, this can be in test class variable and setup in SetUp() fucntion. 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 |
||
metric_cost_vector.push_back( | ||
std::make_pair<std::string, int>("metric_first", 1)); | ||
metric_cost_vector.push_back( | ||
std::make_pair<std::string, int>("metric_second", 2)); | ||
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. can we use the class member metric_cost_vector_? 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. Removed |
||
|
||
QuotaRequestInfo info; | ||
|
||
info.metric_cost_vector = &metric_cost_vector; | ||
|
||
FillOperationInfo(&info); | ||
sc_lib_->Quota(info, nullptr, | ||
[](Status status) { ASSERT_TRUE(status.ok()); }); | ||
} | ||
|
||
TEST(AggregatedServiceControlTest, Create) { | ||
// Verify that invalid service config yields nullptr. | ||
::google::api::Service | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
// Copyright 2016 Google Inc. All Rights Reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
//////////////////////////////////////////////////////////////////////////////// | ||
// | ||
#include "contrib/endpoints/include/api_manager/utils/status.h" | ||
#include "contrib/endpoints/src/api_manager/service_control/proto.h" | ||
#include "gtest/gtest.h" | ||
|
||
namespace gasv1 = ::google::api::servicecontrol::v1; | ||
|
||
using ::google::api::servicecontrol::v1::QuotaError; | ||
using ::google::api_manager::utils::Status; | ||
using ::google::protobuf::util::error::Code; | ||
|
||
namespace google { | ||
namespace api_manager { | ||
namespace service_control { | ||
|
||
namespace { | ||
|
||
Status ConvertAllocateQuotaErrorToStatus(gasv1::QuotaError::Code code, | ||
const char* error_detail, | ||
const char* service_name) { | ||
gasv1::AllocateQuotaResponse response; | ||
gasv1::QuotaError* quota_error = response.add_allocate_errors(); | ||
QuotaRequestInfo info; | ||
quota_error->set_code(code); | ||
quota_error->set_description(error_detail); | ||
return Proto::ConvertAllocateQuotaResponse(response, service_name); | ||
} | ||
|
||
Status ConvertAllocateQuotaErrorToStatus(gasv1::QuotaError::Code code) { | ||
gasv1::AllocateQuotaResponse response; | ||
std::string service_name; | ||
response.add_allocate_errors()->set_code(code); | ||
return Proto::ConvertAllocateQuotaResponse(response, service_name); | ||
} | ||
|
||
} // namespace | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AbortedWithInvalidArgumentWhenRespIsKeyInvalid) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::API_KEY_INVALID); | ||
EXPECT_EQ(Code::INVALID_ARGUMENT, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AbortedWithInvalidArgumentWhenRespIsKeyExpired) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::API_KEY_EXPIRED); | ||
EXPECT_EQ(Code::INVALID_ARGUMENT, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AbortedWithInvalidArgumentWhenRespIsBlockedWithResourceExausted) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::RESOURCE_EXHAUSTED); | ||
EXPECT_EQ(Code::RESOURCE_EXHAUSTED, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AbortedWithInvalidArgumentWhenRespIsBlockedWithProjectSuspended) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::PROJECT_SUSPENDED); | ||
EXPECT_EQ(Code::PERMISSION_DENIED, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AbortedWithPermissionDeniedWhenRespIsBlockedWithServiceNotEnabled) { | ||
Status result = ConvertAllocateQuotaErrorToStatus( | ||
QuotaError::SERVICE_NOT_ENABLED, | ||
"API api_xxxx is not enabled for the project.", "api_xxxx"); | ||
EXPECT_EQ(Code::PERMISSION_DENIED, result.code()); | ||
EXPECT_EQ(result.message(), "API api_xxxx is not enabled for the project."); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AbortedWithPermissionDeniedWhenRespIsBlockedWithBillingNotActivated) { | ||
Status result = ConvertAllocateQuotaErrorToStatus( | ||
QuotaError::BILLING_NOT_ACTIVE, | ||
"API api_xxxx has billing disabled. Please enable it..", "api_xxxx"); | ||
EXPECT_EQ(Code::PERMISSION_DENIED, result.code()); | ||
EXPECT_EQ(result.message(), | ||
"API api_xxxx has billing disabled. Please enable it."); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AbortedWithPermissionDeniedWhenRespIsBlockedWithIpAddressBlocked) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::IP_ADDRESS_BLOCKED); | ||
EXPECT_EQ(Code::PERMISSION_DENIED, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AbortedWithPermissionDeniedWhenRespIsBlockedWithRefererBlocked) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::REFERER_BLOCKED); | ||
EXPECT_EQ(Code::PERMISSION_DENIED, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AbortedWithPermissionDeniedWhenRespIsBlockedWithClientAppBlocked) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::CLIENT_APP_BLOCKED); | ||
EXPECT_EQ(Code::PERMISSION_DENIED, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AbortedWithPermissionDeniedWhenResponseIsBlockedWithProjectInvalid) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::PROJECT_INVALID); | ||
EXPECT_EQ(Code::INVALID_ARGUMENT, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AbortedWithPermissionDeniedWhenRespIsBlockedWithProjectDeleted) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::PROJECT_DELETED); | ||
EXPECT_EQ(Code::INVALID_ARGUMENT, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AbortedWithPermissionDeniedWhenRespIsBlockedWithApiKeyInvalid) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::API_KEY_INVALID); | ||
EXPECT_EQ(Code::INVALID_ARGUMENT, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AbortedWithPermissionDeniedWhenRespIsBlockedWithApiKeyExpiread) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::API_KEY_EXPIRED); | ||
EXPECT_EQ(Code::INVALID_ARGUMENT, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AcceptOKWhenRespIsBlockedWithProjectStatusUnavailable) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::PROJECT_STATUS_UNVAILABLE); | ||
EXPECT_EQ(Code::OK, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AcceptOKWhenRespIsBlockedWithServiceStatusUnavailable) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::SERVICE_STATUS_UNAVAILABLE); | ||
EXPECT_EQ(Code::OK, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, | ||
AcceptOKWhenRespIsBlockedWithBillingStatusUnavailable) { | ||
Status result = | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::BILLING_STATUS_UNAVAILABLE); | ||
EXPECT_EQ(Code::OK, result.code()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, FailOpenWhenResponseIsUnknownBillingStatus) { | ||
EXPECT_TRUE( | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::BILLING_STATUS_UNAVAILABLE) | ||
.ok()); | ||
} | ||
|
||
TEST(AllocateQuotaResponseTest, FailOpenWhenResponseIsUnknownServiceStatus) { | ||
EXPECT_TRUE( | ||
ConvertAllocateQuotaErrorToStatus(QuotaError::SERVICE_STATUS_UNAVAILABLE) | ||
.ok()); | ||
} | ||
|
||
} // namespace service_control | ||
} // namespace api_manager | ||
} // namespace google |
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.
It seem your two test classes are almost the same except DoRunHTTPRequest() function. You can combine them into one test class with two different DoRun functions. EXPECT_CALL() can invoke different one.
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.
Good to know. Thanks.