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 all 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",
],
)
121 changes: 121 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,91 @@ TEST_F(AggregatedTestWithRealClient, CheckOKTest) {
EXPECT_EQ(stat.send_report_operations, 0);
}

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

metric_cost_vector_ = {{"metric_first", 1}, {"metric_second", 2}};
}

std::string getResponseBody(const char* response) {
AllocateQuotaResponse quota_response;
::google::protobuf::TextFormat::ParseFromString(response, &quota_response);
return quota_response.SerializeAsString();
}

void DoRunHTTPRequest(HTTPRequest* request) {
std::map<std::string, std::string> headers;

AllocateQuotaRequest quota_request;

ASSERT_TRUE(quota_request.ParseFromString(request->body()));
ASSERT_EQ(quota_request.allocate_operation().quota_metrics_size(), 2);

std::set<std::pair<std::string, int>> expected_costs = {
{"metric_first", 1}, {"metric_second", 2}};
std::set<std::pair<std::string, int>> actual_costs;

for (auto rule : quota_request.allocate_operation().quota_metrics()) {
actual_costs.insert(std::make_pair(rule.metric_name(),
rule.metric_values(0).int64_value()));
}

ASSERT_EQ(actual_costs, expected_costs);

request->OnComplete(Status::OK, std::move(headers),
std::move(getResponseBody(kAllocateQuotaResponse)));
}

void DoRunHTTPRequestAllocationFailed(HTTPRequest* request) {
std::map<std::string, std::string> headers;

request->OnComplete(
Status::OK, std::move(headers),
std::move(getResponseBody(kAllocateQuotaResponseErrorExhausted)));
}

::google::api::Service service_;
std::unique_ptr<MockApiManagerEnvironment> env_;
std::unique_ptr<Interface> sc_lib_;
std::vector<std::pair<std::string, int>> metric_cost_vector_;
};

TEST_F(QuotaAllocationTestWithRealClient, AllocateQuotaTest) {
EXPECT_CALL(*env_, DoRunHTTPRequest(_))
.WillOnce(
Invoke(this, &QuotaAllocationTestWithRealClient::DoRunHTTPRequest));

QuotaRequestInfo info;
info.metric_cost_vector = &metric_cost_vector_;

FillOperationInfo(&info);
sc_lib_->Quota(info, nullptr,
[](Status status) { ASSERT_TRUE(status.ok()); });
}

TEST_F(QuotaAllocationTestWithRealClient, AllocateQuotaFailedTest) {
EXPECT_CALL(*env_, DoRunHTTPRequest(_))
.WillOnce(Invoke(this, &QuotaAllocationTestWithRealClient::
DoRunHTTPRequestAllocationFailed));

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);
});
}

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