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

Added unit test cases for rate limiting #124

Merged
merged 8 commits into from
Feb 24, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions contrib/endpoints/src/api_manager/service_control/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,16 @@ cc_test(
"//external:googletest_main",
],
)

cc_test(
name = "allocate_quota_response_test",
size = "small",
srcs = [
"allocate_quota_response_test.cc",
],
linkstatic = 1,
deps = [
":service_control",
"//external:googletest_main",
],
)
154 changes: 154 additions & 0 deletions contrib/endpoints/src/api_manager/service_control/aggregated_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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";
Expand Down Expand Up @@ -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:
Copy link
Contributor

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.

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 to know. Thanks.

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_;
Copy link
Contributor

Choose a reason for hiding this comment

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

can we shorten the name as request and response without tailing _?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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;
Copy link
Contributor

Choose a reason for hiding this comment

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

can we do:
convert them into
std::set<std::pair<string, int>>

then we can do

ASSERT_EQ(set, { {metric1, cost1}, {metric2, cost2}});

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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();

Copy link
Contributor

Choose a reason for hiding this comment

The 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;
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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.

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

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));
Copy link
Contributor

Choose a reason for hiding this comment

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

can we use the class member metric_cost_vector_?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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
Expand Down
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
Loading