diff --git a/cmake/sdksCommon.cmake b/cmake/sdksCommon.cmake index 4cc041d3ccf..cbb796f87fc 100644 --- a/cmake/sdksCommon.cmake +++ b/cmake/sdksCommon.cmake @@ -156,6 +156,7 @@ list(APPEND SDK_TEST_PROJECT_LIST "cloudfront:tests/aws-cpp-sdk-cloudfront-integ list(APPEND SDK_TEST_PROJECT_LIST "cognito-identity:tests/aws-cpp-sdk-cognitoidentity-integration-tests") list(APPEND SDK_TEST_PROJECT_LIST "core:tests/aws-cpp-sdk-core-tests") list(APPEND SDK_TEST_PROJECT_LIST "dynamodb:tests/aws-cpp-sdk-dynamodb-integration-tests") +list(APPEND SDK_TEST_PROJECT_LIST "dynamodb:tests/aws-cpp-sdk-dynamodb-unit-tests") list(APPEND SDK_TEST_PROJECT_LIST "ec2:tests/aws-cpp-sdk-ec2-integration-tests") list(APPEND SDK_TEST_PROJECT_LIST "elasticfilesystem:tests/aws-cpp-sdk-elasticfilesystem-integration-tests") list(APPEND SDK_TEST_PROJECT_LIST "identity-management:tests/aws-cpp-sdk-identity-management-tests") diff --git a/tests/aws-cpp-sdk-dynamodb-unit-tests/CMakeLists.txt b/tests/aws-cpp-sdk-dynamodb-unit-tests/CMakeLists.txt new file mode 100644 index 00000000000..5ffab84f9fa --- /dev/null +++ b/tests/aws-cpp-sdk-dynamodb-unit-tests/CMakeLists.txt @@ -0,0 +1,33 @@ +add_project(aws-cpp-sdk-dynamodb-unit-tests + "Unit Tests for the Dynamo DB SDK Client" + aws-cpp-sdk-dynamodb + testing-resources + aws_test_main + aws-cpp-sdk-core) + +add_definitions(-DRESOURCES_DIR="${CMAKE_CURRENT_SOURCE_DIR}/resources") + +if(MSVC AND BUILD_SHARED_LIBS) + add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY=1) +endif() + +enable_testing() + +if(PLATFORM_ANDROID AND BUILD_SHARED_LIBS) + add_library(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/DynamoDBUnitTests.cpp) +else() + add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/DynamoDBUnitTests.cpp) +endif() + +set_compiler_flags(${PROJECT_NAME}) +set_compiler_warnings(${PROJECT_NAME}) + +target_link_libraries(${PROJECT_NAME} ${PROJECT_LIBS}) + +if(MSVC AND BUILD_SHARED_LIBS) + set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "/DELAYLOAD:aws-cpp-sdk-dynamodb.dll /DELAYLOAD:aws-cpp-sdk-core.dll") + target_link_libraries(${PROJECT_NAME} delayimp.lib) +endif() + +include(GoogleTest) +gtest_add_tests(TARGET ${PROJECT_NAME}) diff --git a/tests/aws-cpp-sdk-dynamodb-unit-tests/DynamoDBUnitTests.cpp b/tests/aws-cpp-sdk-dynamodb-unit-tests/DynamoDBUnitTests.cpp new file mode 100644 index 00000000000..aa0ebee9361 --- /dev/null +++ b/tests/aws-cpp-sdk-dynamodb-unit-tests/DynamoDBUnitTests.cpp @@ -0,0 +1,140 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace Aws; +using namespace Aws::Client; +using namespace Aws::Auth; +using namespace Aws::DynamoDB; +using namespace Aws::Http; +using namespace Aws::Http::Standard; + +const char* LOG_TAG = "DynamoDBUnitTest"; +const int MAX_RETRIES = 2; + +class DynamoDBUnitTest : public testing::Test { +protected: + + static void SetUpTestSuite() { +#ifdef USE_AWS_MEMORY_MANAGEMENT + test_memory_system.reset(new ExactTestMemorySystem(1024, 128)); + options_.memoryManagementOptions.memoryManager = test_memory_system.get(); +#endif + InitAPI(options_); + mock_client_factory_ = Aws::MakeShared(LOG_TAG); + mock_http_client_ = Aws::MakeShared(LOG_TAG); + mock_client_factory_->SetClient(mock_http_client_); + SetHttpClientFactory(mock_client_factory_); + AWSCredentials credentials{"mock", "credentials"}; + const auto epProvider = Aws::MakeShared(LOG_TAG); + DynamoDBClientConfiguration configuration; + configuration.retryStrategy = Aws::MakeShared(LOG_TAG, MAX_RETRIES); + configuration.region = "us-east-1"; + client_ = Aws::MakeShared("ALLOCATION_TAG", credentials, epProvider, configuration); + } + + static void TearDownTestSuite() { + mock_client_factory_.reset(); + mock_http_client_.reset(); + client_.reset(); + ShutdownAPI(options_); +#ifdef USE_AWS_MEMORY_MANAGEMENT + EXPECT_EQ(test_memory_system->GetCurrentOutstandingAllocations(), 0ULL); + EXPECT_EQ(test_memory_system->GetCurrentBytesAllocated(), 0ULL); + EXPECT_TRUE(test_memory_system->IsClean()); + if (test_memory_system->GetCurrentOutstandingAllocations() != 0ULL) + FAIL(); + if (test_memory_system->GetCurrentBytesAllocated() != 0ULL) + FAIL(); + if (!test_memory_system->IsClean()) + FAIL(); + test_memory_system.reset(); +#endif + } + + static SDKOptions options_; + static std::shared_ptr mock_http_client_; + static std::shared_ptr mock_client_factory_; + static std::shared_ptr client_; +#ifdef USE_AWS_MEMORY_MANAGEMENT + static std::unique_ptr test_memory_system; +#endif +}; + +SDKOptions DynamoDBUnitTest::options_; +std::shared_ptr DynamoDBUnitTest::mock_http_client_ = nullptr; +std::shared_ptr DynamoDBUnitTest::mock_client_factory_ = nullptr; +std::shared_ptr DynamoDBUnitTest::client_ = nullptr; +#ifdef USE_AWS_MEMORY_MANAGEMENT + std::unique_ptr DynamoDBUnitTest::test_memory_system = nullptr; +#endif + +TEST_F(DynamoDBUnitTest, RetryShouldWork) +{ + //Mock bad request + auto failed_stream_ = Aws::MakeShared(LOG_TAG, "mockuri", HttpMethod::HTTP_GET); + failed_stream_->SetResponseStreamFactory([]() -> IOStream* { + return Aws::New(LOG_TAG, "response-string", std::ios_base::in | std::ios_base::binary); + }); + auto failed_response = Aws::MakeShared(LOG_TAG, failed_stream_); + failed_response->SetResponseCode(HttpResponseCode::INTERNAL_SERVER_ERROR); + + //Mock good request + auto success_stream_ = Aws::MakeShared(LOG_TAG, "mockuri", HttpMethod::HTTP_GET); + failed_stream_->SetResponseStreamFactory([]() -> IOStream* { + auto list_tables_string = R"({"LastEvaluatedTableName": "Thread","TableNames": ["Forum","Reply","Thread"]}))"; + return Aws::New(LOG_TAG, list_tables_string, std::ios_base::in | std::ios_base::binary); + }); + auto success_response = Aws::MakeShared(LOG_TAG, failed_stream_); + success_response->SetResponseCode(HttpResponseCode::OK); + + //succeed on second attempt + mock_http_client_->Reset(); + mock_http_client_->AddResponseToReturn(failed_response); + mock_http_client_->AddResponseToReturn(success_response); + + const auto list_tables_outcome = client_->ListTables(); + EXPECT_TRUE(list_tables_outcome.IsSuccess()); +} + +TEST_F(DynamoDBUnitTest, DefaultRetryStrategyShouldFailWhenRetriesFail) +{ + //Mock bad request + auto failed_stream_ = Aws::MakeShared(LOG_TAG, "mockuri", HttpMethod::HTTP_GET); + failed_stream_->SetResponseStreamFactory([]() -> IOStream* { + return Aws::New(LOG_TAG, "response-string", std::ios_base::in | std::ios_base::binary); + }); + auto failed_response = Aws::MakeShared(LOG_TAG, failed_stream_); + failed_response->SetResponseCode(HttpResponseCode::INTERNAL_SERVER_ERROR); + + //Mock good request + auto success_stream_ = Aws::MakeShared(LOG_TAG, "mockuri", HttpMethod::HTTP_GET); + failed_stream_->SetResponseStreamFactory([]() -> IOStream* { + auto list_tables_string = R"({"LastEvaluatedTableName": "Thread","TableNames": ["Forum","Reply","Thread"]}))"; + return Aws::New(LOG_TAG, list_tables_string, std::ios_base::in | std::ios_base::binary); + }); + auto success_response = Aws::MakeShared(LOG_TAG, failed_stream_); + success_response->SetResponseCode(HttpResponseCode::OK); + + // We will only retry twice + mock_http_client_->Reset(); + mock_http_client_->AddResponseToReturn(failed_response); + mock_http_client_->AddResponseToReturn(failed_response); + mock_http_client_->AddResponseToReturn(success_response); + + const auto list_tables_outcome = client_->ListTables(); + EXPECT_FALSE(list_tables_outcome.IsSuccess()); +} diff --git a/tools/scripts/run_integration_tests.py b/tools/scripts/run_integration_tests.py index 48d0dd9c47c..32783ffff01 100644 --- a/tools/scripts/run_integration_tests.py +++ b/tools/scripts/run_integration_tests.py @@ -37,6 +37,7 @@ def main(): test_list = [ "aws-cpp-sdk-transcribestreaming-integ-tests", + "aws-cpp-sdk-dynamodb-unit-tests", "aws-cpp-sdk-dynamodb-integration-tests", "aws-cpp-sdk-sqs-integration-tests", "aws-cpp-sdk-s3-integration-tests",