From cf0aa84e81152172933dc21d913ace1399c13be6 Mon Sep 17 00:00:00 2001 From: sbiscigl Date: Tue, 28 Nov 2023 16:34:34 -0500 Subject: [PATCH] add integ tests for s3express --- .../S3ExpressTest.cpp | 385 ++++++++++++++++++ .../S3ExpressTest.cpp | 385 ++++++++++++++++++ 2 files changed, 770 insertions(+) create mode 100644 tests/aws-cpp-sdk-s3-crt-integration-tests/S3ExpressTest.cpp create mode 100644 tests/aws-cpp-sdk-s3-integration-tests/S3ExpressTest.cpp diff --git a/tests/aws-cpp-sdk-s3-crt-integration-tests/S3ExpressTest.cpp b/tests/aws-cpp-sdk-s3-crt-integration-tests/S3ExpressTest.cpp new file mode 100644 index 00000000000..d25a4090179 --- /dev/null +++ b/tests/aws-cpp-sdk-s3-crt-integration-tests/S3ExpressTest.cpp @@ -0,0 +1,385 @@ +/** +* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#pragma warning(disable: 4127) +#ifdef GetObject +#undef GetObject +#endif +#endif + +using namespace Aws; +using namespace Aws::Client; +using namespace Aws::S3Crt; +using namespace Aws::S3Crt::Model; +using namespace Aws::Utils; + + +namespace { + static const char* ALLOCATION_TAG = "S3CrtClientS3ExpressTest"; + static const char* S3_EXPRESS_SUFFIX = "--use1-az6--x-s3"; + + class S3ExpressTest : public ::testing::Test { + public: + CreateBucketOutcome CreateBucket(const Aws::String&bucketName = randomString(8) + S3_EXPRESS_SUFFIX) { + bucketsToCleanup.push_back(bucketName); + return client->CreateBucket(CreateBucketRequest() + .WithBucket(bucketName) + .WithCreateBucketConfiguration(CreateBucketConfiguration() + .WithLocation(LocationInfo() + .WithType(LocationType::AvailabilityZone) + .WithName("use1-az6")) + .WithBucket(BucketInfo() + .WithType(BucketType::Directory) + .WithDataRedundancy(DataRedundancy::SingleAvailabilityZone)))); + } + + DeleteBucketOutcome DeleteBucket(const Aws::String&bucketName) { + return client->DeleteBucket(DeleteBucketRequest().WithBucket(bucketName)); + } + + HeadBucketOutcome HeadBucket(const Aws::String&bucketName) { + return client->HeadBucket(HeadBucketRequest().WithBucket(bucketName)); + } + + ListDirectoryBucketsOutcome ListDirectoryBuckets() { + return client->ListDirectoryBuckets(ListDirectoryBucketsRequest()); + } + + CreateSessionOutcome CreateSession(const Aws::String&bucketName) { + return client->CreateSession(CreateSessionRequest().WithBucket(bucketName)); + } + + PutBucketPolicyOutcome PutBucketPolicy(const Aws::String&bucketName) { + auto request = PutBucketPolicyRequest().WithBucket(bucketName); + auto accountId = Aws::Environment::GetEnv("CATAPULT_TEST_ACCOUNT"); + auto policy = + R"({"Version":"2012-10-17","Statement":[{"Sid":"Whatever","Effect":"Allow","Resource":"arn:aws:s3express:us-east-1:)" + + accountId + R"(:bucket/)" + + bucketName + R"(","Principal":{"AWS":[")" + + accountId + R"("]},"Action":["*"]}]})"; + std::shared_ptr inputData = Aws::MakeShared(ALLOCATION_TAG, policy, + std::ios_base::in | std::ios_base::binary); + request.SetBody(inputData); + return client->PutBucketPolicy(request); + } + + GetBucketPolicyOutcome GetBucketPolicy(const Aws::String&bucketName) { + return client->GetBucketPolicy(GetBucketPolicyRequest().WithBucket(bucketName)); + } + + DeleteBucketPolicyOutcome DeleteBucketPolicy(const Aws::String&bucketName) { + return client->DeleteBucketPolicy(DeleteBucketPolicyRequest().WithBucket(bucketName)); + } + + PutObjectOutcome PutObject(const Aws::String&bucketName, const Aws::String&keyName) { + auto request = PutObjectRequest().WithBucket(bucketName).WithKey(keyName); + std::shared_ptr inputData = Aws::MakeShared(ALLOCATION_TAG, + "I'll take a quiet life a handshake of carbon monoxide", + std::ios_base::in | std::ios_base::binary); + request.SetBody(inputData); + return client->PutObject(request); + } + + ListObjectsV2Outcome ListObjectsV2(const Aws::String&bucketName) { + return client->ListObjectsV2(ListObjectsV2Request().WithBucket(bucketName)); + } + + GetObjectOutcome GetObject(const Aws::String&bucketName, const Aws::String&keyName) { + return client->GetObject(GetObjectRequest().WithBucket(bucketName).WithKey(keyName)); + } + + HeadObjectOutcome HeadObject(const Aws::String&bucketName, const Aws::String&keyName) { + return client->HeadObject(HeadObjectRequest().WithBucket(bucketName).WithKey(keyName)); + } + + DeleteObjectOutcome DeleteObject(const Aws::String&bucketName, const Aws::String&keyName) { + return client->DeleteObject(DeleteObjectRequest().WithBucket(bucketName).WithKey(keyName)); + } + + DeleteObjectsOutcome DeleteObjects(const Aws::String&bucketName) { + auto verseOne = randomString(8); + auto requestOne = PutObjectRequest().WithBucket(bucketName).WithKey(verseOne); + std::shared_ptr inputDataOne = Aws::MakeShared(ALLOCATION_TAG, + "A heart that's full up like a landfill", + std::ios_base::in | std::ios_base::binary); + requestOne.SetBody(inputDataOne); + auto outcomeOne = client->PutObject(requestOne); + AWS_EXPECT_SUCCESS(outcomeOne); + + auto verseTwo = randomString(8); + auto requestTwo = PutObjectRequest().WithBucket(bucketName).WithKey(verseOne); + std::shared_ptr inputDataTwo = Aws::MakeShared(ALLOCATION_TAG, + "A job that slowly kills you", + std::ios_base::in | std::ios_base::binary); + requestTwo.SetBody(inputDataTwo); + auto outcomeTwo = client->PutObject(requestTwo); + AWS_EXPECT_SUCCESS(outcomeTwo); + + return client->DeleteObjects(DeleteObjectsRequest().WithBucket(bucketName).WithDelete( + Model::Delete().WithObjects({ + ObjectIdentifier().WithKey(verseOne), + ObjectIdentifier().WithKey(verseTwo) + } + ))); + } + + CompleteMultipartUploadOutcome CreateMultipartUpload(const Aws::String&bucketName, const Aws::String&keyName) { + auto createOutcome = client->CreateMultipartUpload(CreateMultipartUploadRequest() + .WithBucket(bucketName) + .WithKey(keyName) + .WithChecksumAlgorithm(ChecksumAlgorithm::CRC32)); + AWS_EXPECT_SUCCESS(createOutcome); + + auto uploadPartRequest = UploadPartRequest() + .WithBucket(bucketName) + .WithKey(keyName) + .WithPartNumber(1) + .WithUploadId(createOutcome.GetResult().GetUploadId()) + .WithChecksumAlgorithm(ChecksumAlgorithm::CRC32); + std::shared_ptr data = Aws::MakeShared(ALLOCATION_TAG, + "This is my final fit my final bellyache with No alarms and no surprises", + std::ios_base::in | std::ios_base::binary); + uploadPartRequest.SetBody(data); + auto uploadPart = client->UploadPart(uploadPartRequest); + AWS_EXPECT_SUCCESS(uploadPart); + + auto parts = client->ListParts(ListPartsRequest() + .WithUploadId(createOutcome.GetResult().GetUploadId()) + .WithBucket(bucketName) + .WithKey(keyName)); + AWS_EXPECT_SUCCESS(parts); + + Model::CompletedMultipartUpload completedUpload; + completedUpload.AddParts(CompletedPart() + .WithPartNumber(1) + .WithETag(uploadPart.GetResult().GetETag()) + .WithChecksumCRC32(uploadPart.GetResult().GetChecksumCRC32())); + + return client->CompleteMultipartUpload(CompleteMultipartUploadRequest() + .WithBucket(bucketName) + .WithKey(keyName) + .WithUploadId(createOutcome.GetResult().GetUploadId()) + .WithMultipartUpload(completedUpload)); + } + + UploadPartCopyOutcome UploadPartCopy(const Aws::String&bucketName, const Aws::String&keyName) { + auto createOutcome = client->CreateMultipartUpload(CreateMultipartUploadRequest() + .WithBucket(bucketName) + .WithKey(keyName) + .WithChecksumAlgorithm(ChecksumAlgorithm::CRC32)); + AWS_EXPECT_SUCCESS(createOutcome); + + auto copyPartKey = randomString(8); + auto putObjectRequest = PutObjectRequest().WithBucket(bucketName).WithKey(copyPartKey); + std::shared_ptr inputData = Aws::MakeShared(ALLOCATION_TAG, + "I'll take a quiet life a handshake of carbon monoxide", + std::ios_base::in | std::ios_base::binary); + putObjectRequest.SetBody(inputData); + auto outcome = client->PutObject(putObjectRequest); + AWS_EXPECT_SUCCESS(outcome); + + return client->UploadPartCopy(UploadPartCopyRequest() + .WithUploadId(createOutcome.GetResult().GetUploadId()) + .WithBucket(bucketName) + .WithPartNumber(1) + .WithKey(keyName) + .WithCopySource(bucketName + "/" + copyPartKey)); + } + + AbortMultipartUploadOutcome AbortMultipartUpload(const Aws::String&bucketName, const Aws::String&keyName) { + auto createOutcome = client->CreateMultipartUpload(CreateMultipartUploadRequest() + .WithBucket(bucketName) + .WithKey(keyName) + .WithChecksumAlgorithm(ChecksumAlgorithm::CRC32)); + AWS_EXPECT_SUCCESS(createOutcome); + + auto uploadPartRequest = UploadPartRequest() + .WithBucket(bucketName) + .WithKey(keyName) + .WithPartNumber(1) + .WithUploadId(createOutcome.GetResult().GetUploadId()) + .WithChecksumAlgorithm(ChecksumAlgorithm::CRC32); + std::shared_ptr data = Aws::MakeShared(ALLOCATION_TAG, + "This is my final fit my final bellyache with No alarms and no surprises", + std::ios_base::in | std::ios_base::binary); + uploadPartRequest.SetBody(data); + auto uploadPart = client->UploadPart(uploadPartRequest); + AWS_EXPECT_SUCCESS(uploadPart); + + auto parts = client->ListParts(ListPartsRequest() + .WithUploadId(createOutcome.GetResult().GetUploadId()) + .WithBucket(bucketName) + .WithKey(keyName)); + AWS_EXPECT_SUCCESS(parts); + + return client->AbortMultipartUpload(AbortMultipartUploadRequest() + .WithBucket(bucketName).WithKey(keyName) + .WithUploadId(createOutcome.GetResult().GetUploadId())); + } + + void EmptyBucketUtil(Aws::Vector bucketsToCleanup) { + for (const auto&bucket: bucketsToCleanup) { + auto bucketExists = HeadBucket(bucket); + if (!bucketExists.IsSuccess()) { + continue; + } + auto objects = client->ListObjectsV2(ListObjectsV2Request().WithBucket(bucket)); + AWS_EXPECT_SUCCESS(objects); + while (objects.GetResult().GetIsTruncated() || !objects.GetResult().GetContents().empty()) { + for (const auto&object: objects.GetResult().GetContents()) { + DeleteObject(bucket, object.GetKey()); + } + objects = client->ListObjectsV2(ListObjectsV2Request() + .WithBucket(bucket) + .WithContinuationToken(objects.GetResult().GetContinuationToken())); + } + auto uploads = client->ListMultipartUploads(ListMultipartUploadsRequest(). + WithBucket(bucket)); + AWS_EXPECT_SUCCESS(uploads); + while (uploads.GetResult().GetIsTruncated() || !uploads.GetResult().GetUploads().empty()) { + for (const auto&upload: uploads.GetResult().GetUploads()) { + auto abortMPU = client->AbortMultipartUpload(AbortMultipartUploadRequest() + .WithBucket(bucket) + .WithKey(upload.GetKey()) + .WithUploadId(upload.GetUploadId())); + AWS_EXPECT_SUCCESS(abortMPU); + } + uploads = client->ListMultipartUploads(ListMultipartUploadsRequest().WithBucket(bucket)); + } + auto outcome = client->DeleteBucket(DeleteBucketRequest().WithBucket(bucket)); + if (!outcome.IsSuccess()) { + std::cout << "Failed to delete bucket: " << outcome.GetError().GetMessage() << "\n"; + } + } + } + + protected: + void SetUp() override { + client = Aws::MakeShared(ALLOCATION_TAG); + } + + void TearDown() override { + EmptyBucketUtil(bucketsToCleanup); + } + + static Aws::String randomString(std::size_t length) { + const Aws::String CHARACTERS = "abcdefghijklmnopqrstuvwxyz"; + + std::random_device random_device; + std::mt19937 generator(random_device()); + std::uniform_int_distribution<> distribution(0, CHARACTERS.size() - 1); + + Aws::String random_string; + + for (std::size_t i = 0; i < length; ++i) { + random_string += CHARACTERS[distribution(generator)]; + } + + return random_string; + } + + private: + std::shared_ptr client; + Aws::Vector bucketsToCleanup; + }; + + TEST_F(S3ExpressTest, ShouldCreateS3ExpressBucket) { + auto bucketName = Testing::GetAwsResourcePrefix() + randomString(8) + S3_EXPRESS_SUFFIX; + auto createOutcome = CreateBucket(bucketName); + AWS_EXPECT_SUCCESS(createOutcome); + auto headOutcome = HeadBucket(bucketName); + AWS_EXPECT_SUCCESS(headOutcome); + auto buckets = ListDirectoryBuckets(); + AWS_EXPECT_SUCCESS(buckets); + EXPECT_TRUE(std::any_of(buckets.GetResult().GetBuckets().begin(), + buckets.GetResult().GetBuckets().end(), + [&bucketName](const Bucket&bucket) -> bool { return bucket.GetName() == bucketName; })); + auto sessionOutcome = CreateSession(bucketName); + AWS_EXPECT_SUCCESS(sessionOutcome); + auto deleteOutcome = DeleteBucket(bucketName); + AWS_EXPECT_SUCCESS(deleteOutcome); + } + + TEST_F(S3ExpressTest, BucketPolicyTest) { + auto bucketName = Testing::GetAwsResourcePrefix() + randomString(8) + S3_EXPRESS_SUFFIX; + auto createOutcome = CreateBucket(bucketName); + AWS_EXPECT_SUCCESS(createOutcome); + auto putBucketPolicyOutcome = PutBucketPolicy(bucketName); + AWS_EXPECT_SUCCESS(putBucketPolicyOutcome); + auto getBucketPolicyOutcome = GetBucketPolicy(bucketName); + AWS_EXPECT_SUCCESS(getBucketPolicyOutcome); + auto deleteBucketPolicyOutcome = DeleteBucketPolicy(bucketName); + AWS_EXPECT_SUCCESS(deleteBucketPolicyOutcome); + auto deleteOutcome = DeleteBucket(bucketName); + AWS_EXPECT_SUCCESS(deleteOutcome); + } + + TEST_F(S3ExpressTest, ObjectTest) { + auto bucketName = Testing::GetAwsResourcePrefix() + randomString(8) + S3_EXPRESS_SUFFIX; + auto keyName = randomString(8); + auto createOutcome = CreateBucket(bucketName); + AWS_EXPECT_SUCCESS(createOutcome); + auto putObjectOutcome = PutObject(bucketName, keyName); + AWS_EXPECT_SUCCESS(putObjectOutcome); + auto getObjectOutcome = GetObject(bucketName, keyName); + AWS_EXPECT_SUCCESS(getObjectOutcome); + auto objects = ListObjectsV2(bucketName); + EXPECT_TRUE(std::any_of(objects.GetResult().GetContents().begin(), + objects.GetResult().GetContents().end(), + [&keyName](const Object&object) -> bool { return object.GetKey() == keyName; })); + auto headObjecOutcome = HeadObject(bucketName, keyName); + AWS_EXPECT_SUCCESS(headObjecOutcome); + auto deleteObjecOutcome = DeleteObject(bucketName, keyName); + AWS_EXPECT_SUCCESS(deleteObjecOutcome); + auto deleteObjectsOutcome = DeleteObjects(bucketName); + AWS_EXPECT_SUCCESS(deleteObjectsOutcome); + auto deleteOutcome = DeleteBucket(bucketName); + AWS_EXPECT_SUCCESS(deleteOutcome); + } + + TEST_F(S3ExpressTest, MultipartUploadTest) { + auto bucketName = Testing::GetAwsResourcePrefix() + randomString(8) + S3_EXPRESS_SUFFIX; + auto createOutcome = CreateBucket(bucketName); + AWS_EXPECT_SUCCESS(createOutcome); + auto createMPUOutcome = CreateMultipartUpload(bucketName, randomString(8) + S3_EXPRESS_SUFFIX); + AWS_EXPECT_SUCCESS(createMPUOutcome); + auto uploadPartCopyOutcome = UploadPartCopy(bucketName, randomString(8) + S3_EXPRESS_SUFFIX); + AWS_EXPECT_SUCCESS(uploadPartCopyOutcome); + auto abortMPUOutcome = AbortMultipartUpload(bucketName, randomString(8) + S3_EXPRESS_SUFFIX); + AWS_EXPECT_SUCCESS(abortMPUOutcome); + EmptyBucketUtil({bucketName}); + } +} diff --git a/tests/aws-cpp-sdk-s3-integration-tests/S3ExpressTest.cpp b/tests/aws-cpp-sdk-s3-integration-tests/S3ExpressTest.cpp new file mode 100644 index 00000000000..77032081234 --- /dev/null +++ b/tests/aws-cpp-sdk-s3-integration-tests/S3ExpressTest.cpp @@ -0,0 +1,385 @@ +/** +* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#pragma warning(disable: 4127) +#ifdef GetObject +#undef GetObject +#endif +#endif + +using namespace Aws; +using namespace Aws::Client; +using namespace Aws::S3; +using namespace Aws::S3::Model; +using namespace Aws::Utils; + + +namespace { + static const char* ALLOCATION_TAG = "S3ClientS3ExpressTest"; + static const char* S3_EXPRESS_SUFFIX = "--use1-az6--x-s3"; + + class S3ExpressTest : public ::testing::Test { + public: + CreateBucketOutcome CreateBucket(const Aws::String&bucketName = randomString(8) + S3_EXPRESS_SUFFIX) { + bucketsToCleanup.push_back(bucketName); + return client->CreateBucket(CreateBucketRequest() + .WithBucket(bucketName) + .WithCreateBucketConfiguration(CreateBucketConfiguration() + .WithLocation(LocationInfo() + .WithType(LocationType::AvailabilityZone) + .WithName("use1-az6")) + .WithBucket(BucketInfo() + .WithType(BucketType::Directory) + .WithDataRedundancy(DataRedundancy::SingleAvailabilityZone)))); + } + + DeleteBucketOutcome DeleteBucket(const Aws::String&bucketName) { + return client->DeleteBucket(DeleteBucketRequest().WithBucket(bucketName)); + } + + HeadBucketOutcome HeadBucket(const Aws::String&bucketName) { + return client->HeadBucket(HeadBucketRequest().WithBucket(bucketName)); + } + + ListDirectoryBucketsOutcome ListDirectoryBuckets() { + return client->ListDirectoryBuckets(ListDirectoryBucketsRequest()); + } + + CreateSessionOutcome CreateSession(const Aws::String&bucketName) { + return client->CreateSession(CreateSessionRequest().WithBucket(bucketName)); + } + + PutBucketPolicyOutcome PutBucketPolicy(const Aws::String&bucketName) { + auto request = PutBucketPolicyRequest().WithBucket(bucketName); + auto accountId = Aws::Environment::GetEnv("CATAPULT_TEST_ACCOUNT"); + auto policy = + R"({"Version":"2012-10-17","Statement":[{"Sid":"Whatever","Effect":"Allow","Resource":"arn:aws:s3express:us-east-1:)" + + accountId + R"(:bucket/)" + + bucketName + R"(","Principal":{"AWS":[")" + + accountId + R"("]},"Action":["*"]}]})"; + std::shared_ptr inputData = Aws::MakeShared(ALLOCATION_TAG, policy, + std::ios_base::in | std::ios_base::binary); + request.SetBody(inputData); + return client->PutBucketPolicy(request); + } + + GetBucketPolicyOutcome GetBucketPolicy(const Aws::String&bucketName) { + return client->GetBucketPolicy(GetBucketPolicyRequest().WithBucket(bucketName)); + } + + DeleteBucketPolicyOutcome DeleteBucketPolicy(const Aws::String&bucketName) { + return client->DeleteBucketPolicy(DeleteBucketPolicyRequest().WithBucket(bucketName)); + } + + PutObjectOutcome PutObject(const Aws::String&bucketName, const Aws::String&keyName) { + auto request = PutObjectRequest().WithBucket(bucketName).WithKey(keyName); + std::shared_ptr inputData = Aws::MakeShared(ALLOCATION_TAG, + "I'll take a quiet life a handshake of carbon monoxide", + std::ios_base::in | std::ios_base::binary); + request.SetBody(inputData); + return client->PutObject(request); + } + + ListObjectsV2Outcome ListObjectsV2(const Aws::String&bucketName) { + return client->ListObjectsV2(ListObjectsV2Request().WithBucket(bucketName)); + } + + GetObjectOutcome GetObject(const Aws::String&bucketName, const Aws::String&keyName) { + return client->GetObject(GetObjectRequest().WithBucket(bucketName).WithKey(keyName)); + } + + HeadObjectOutcome HeadObject(const Aws::String&bucketName, const Aws::String&keyName) { + return client->HeadObject(HeadObjectRequest().WithBucket(bucketName).WithKey(keyName)); + } + + DeleteObjectOutcome DeleteObject(const Aws::String&bucketName, const Aws::String&keyName) { + return client->DeleteObject(DeleteObjectRequest().WithBucket(bucketName).WithKey(keyName)); + } + + DeleteObjectsOutcome DeleteObjects(const Aws::String&bucketName) { + auto verseOne = randomString(8); + auto requestOne = PutObjectRequest().WithBucket(bucketName).WithKey(verseOne); + std::shared_ptr inputDataOne = Aws::MakeShared(ALLOCATION_TAG, + "A heart that's full up like a landfill", + std::ios_base::in | std::ios_base::binary); + requestOne.SetBody(inputDataOne); + auto outcomeOne = client->PutObject(requestOne); + AWS_EXPECT_SUCCESS(outcomeOne); + + auto verseTwo = randomString(8); + auto requestTwo = PutObjectRequest().WithBucket(bucketName).WithKey(verseOne); + std::shared_ptr inputDataTwo = Aws::MakeShared(ALLOCATION_TAG, + "A job that slowly kills you", + std::ios_base::in | std::ios_base::binary); + requestTwo.SetBody(inputDataTwo); + auto outcomeTwo = client->PutObject(requestTwo); + AWS_EXPECT_SUCCESS(outcomeTwo); + + return client->DeleteObjects(DeleteObjectsRequest().WithBucket(bucketName).WithDelete( + Model::Delete().WithObjects({ + ObjectIdentifier().WithKey(verseOne), + ObjectIdentifier().WithKey(verseTwo) + } + ))); + } + + CompleteMultipartUploadOutcome CreateMultipartUpload(const Aws::String&bucketName, const Aws::String&keyName) { + auto createOutcome = client->CreateMultipartUpload(CreateMultipartUploadRequest() + .WithBucket(bucketName) + .WithKey(keyName) + .WithChecksumAlgorithm(ChecksumAlgorithm::CRC32)); + AWS_EXPECT_SUCCESS(createOutcome); + + auto uploadPartRequest = UploadPartRequest() + .WithBucket(bucketName) + .WithKey(keyName) + .WithPartNumber(1) + .WithUploadId(createOutcome.GetResult().GetUploadId()) + .WithChecksumAlgorithm(ChecksumAlgorithm::CRC32); + std::shared_ptr data = Aws::MakeShared(ALLOCATION_TAG, + "This is my final fit my final bellyache with No alarms and no surprises", + std::ios_base::in | std::ios_base::binary); + uploadPartRequest.SetBody(data); + auto uploadPart = client->UploadPart(uploadPartRequest); + AWS_EXPECT_SUCCESS(uploadPart); + + auto parts = client->ListParts(ListPartsRequest() + .WithUploadId(createOutcome.GetResult().GetUploadId()) + .WithBucket(bucketName) + .WithKey(keyName)); + AWS_EXPECT_SUCCESS(parts); + + Model::CompletedMultipartUpload completedUpload; + completedUpload.AddParts(CompletedPart() + .WithPartNumber(1) + .WithETag(uploadPart.GetResult().GetETag()) + .WithChecksumCRC32(uploadPart.GetResult().GetChecksumCRC32())); + + return client->CompleteMultipartUpload(CompleteMultipartUploadRequest() + .WithBucket(bucketName) + .WithKey(keyName) + .WithUploadId(createOutcome.GetResult().GetUploadId()) + .WithMultipartUpload(completedUpload)); + } + + UploadPartCopyOutcome UploadPartCopy(const Aws::String&bucketName, const Aws::String&keyName) { + auto createOutcome = client->CreateMultipartUpload(CreateMultipartUploadRequest() + .WithBucket(bucketName) + .WithKey(keyName) + .WithChecksumAlgorithm(ChecksumAlgorithm::CRC32)); + AWS_EXPECT_SUCCESS(createOutcome); + + auto copyPartKey = randomString(8); + auto putObjectRequest = PutObjectRequest().WithBucket(bucketName).WithKey(copyPartKey); + std::shared_ptr inputData = Aws::MakeShared(ALLOCATION_TAG, + "I'll take a quiet life a handshake of carbon monoxide", + std::ios_base::in | std::ios_base::binary); + putObjectRequest.SetBody(inputData); + auto outcome = client->PutObject(putObjectRequest); + AWS_EXPECT_SUCCESS(outcome); + + return client->UploadPartCopy(UploadPartCopyRequest() + .WithUploadId(createOutcome.GetResult().GetUploadId()) + .WithBucket(bucketName) + .WithPartNumber(1) + .WithKey(keyName) + .WithCopySource(bucketName + "/" + copyPartKey)); + } + + AbortMultipartUploadOutcome AbortMultipartUpload(const Aws::String&bucketName, const Aws::String&keyName) { + auto createOutcome = client->CreateMultipartUpload(CreateMultipartUploadRequest() + .WithBucket(bucketName) + .WithKey(keyName) + .WithChecksumAlgorithm(ChecksumAlgorithm::CRC32)); + AWS_EXPECT_SUCCESS(createOutcome); + + auto uploadPartRequest = UploadPartRequest() + .WithBucket(bucketName) + .WithKey(keyName) + .WithPartNumber(1) + .WithUploadId(createOutcome.GetResult().GetUploadId()) + .WithChecksumAlgorithm(ChecksumAlgorithm::CRC32); + std::shared_ptr data = Aws::MakeShared(ALLOCATION_TAG, + "This is my final fit my final bellyache with No alarms and no surprises", + std::ios_base::in | std::ios_base::binary); + uploadPartRequest.SetBody(data); + auto uploadPart = client->UploadPart(uploadPartRequest); + AWS_EXPECT_SUCCESS(uploadPart); + + auto parts = client->ListParts(ListPartsRequest() + .WithUploadId(createOutcome.GetResult().GetUploadId()) + .WithBucket(bucketName) + .WithKey(keyName)); + AWS_EXPECT_SUCCESS(parts); + + return client->AbortMultipartUpload(AbortMultipartUploadRequest() + .WithBucket(bucketName).WithKey(keyName) + .WithUploadId(createOutcome.GetResult().GetUploadId())); + } + + void EmptyBucketUtil(Aws::Vector bucketsToCleanup) { + for (const auto&bucket: bucketsToCleanup) { + auto bucketExists = HeadBucket(bucket); + if (!bucketExists.IsSuccess()) { + continue; + } + auto objects = client->ListObjectsV2(ListObjectsV2Request().WithBucket(bucket)); + AWS_EXPECT_SUCCESS(objects); + while (objects.GetResult().GetIsTruncated() || !objects.GetResult().GetContents().empty()) { + for (const auto&object: objects.GetResult().GetContents()) { + DeleteObject(bucket, object.GetKey()); + } + objects = client->ListObjectsV2(ListObjectsV2Request() + .WithBucket(bucket) + .WithContinuationToken(objects.GetResult().GetContinuationToken())); + } + auto uploads = client->ListMultipartUploads(ListMultipartUploadsRequest(). + WithBucket(bucket)); + AWS_EXPECT_SUCCESS(uploads); + while (uploads.GetResult().GetIsTruncated() || !uploads.GetResult().GetUploads().empty()) { + for (const auto&upload: uploads.GetResult().GetUploads()) { + auto abortMPU = client->AbortMultipartUpload(AbortMultipartUploadRequest() + .WithBucket(bucket) + .WithKey(upload.GetKey()) + .WithUploadId(upload.GetUploadId())); + AWS_EXPECT_SUCCESS(abortMPU); + } + uploads = client->ListMultipartUploads(ListMultipartUploadsRequest().WithBucket(bucket)); + } + auto outcome = client->DeleteBucket(DeleteBucketRequest().WithBucket(bucket)); + if (!outcome.IsSuccess()) { + std::cout << "Failed to delete bucket: " << outcome.GetError().GetMessage() << "\n"; + } + } + } + + protected: + void SetUp() override { + client = Aws::MakeShared(ALLOCATION_TAG); + } + + void TearDown() override { + EmptyBucketUtil(bucketsToCleanup); + } + + static Aws::String randomString(std::size_t length) { + const Aws::String CHARACTERS = "abcdefghijklmnopqrstuvwxyz"; + + std::random_device random_device; + std::mt19937 generator(random_device()); + std::uniform_int_distribution<> distribution(0, CHARACTERS.size() - 1); + + Aws::String random_string; + + for (std::size_t i = 0; i < length; ++i) { + random_string += CHARACTERS[distribution(generator)]; + } + + return random_string; + } + + private: + std::shared_ptr client; + Aws::Vector bucketsToCleanup; + }; + + TEST_F(S3ExpressTest, ShouldCreateS3ExpressBucket) { + auto bucketName = Testing::GetAwsResourcePrefix() + randomString(8) + S3_EXPRESS_SUFFIX; + auto createOutcome = CreateBucket(bucketName); + AWS_EXPECT_SUCCESS(createOutcome); + auto headOutcome = HeadBucket(bucketName); + AWS_EXPECT_SUCCESS(headOutcome); + auto buckets = ListDirectoryBuckets(); + AWS_EXPECT_SUCCESS(buckets); + EXPECT_TRUE(std::any_of(buckets.GetResult().GetBuckets().begin(), + buckets.GetResult().GetBuckets().end(), + [&bucketName](const Bucket&bucket) -> bool { return bucket.GetName() == bucketName; })); + auto sessionOutcome = CreateSession(bucketName); + AWS_EXPECT_SUCCESS(sessionOutcome); + auto deleteOutcome = DeleteBucket(bucketName); + AWS_EXPECT_SUCCESS(deleteOutcome); + } + + TEST_F(S3ExpressTest, BucketPolicyTest) { + auto bucketName = Testing::GetAwsResourcePrefix() + randomString(8) + S3_EXPRESS_SUFFIX; + auto createOutcome = CreateBucket(bucketName); + AWS_EXPECT_SUCCESS(createOutcome); + auto putBucketPolicyOutcome = PutBucketPolicy(bucketName); + AWS_EXPECT_SUCCESS(putBucketPolicyOutcome); + auto getBucketPolicyOutcome = GetBucketPolicy(bucketName); + AWS_EXPECT_SUCCESS(getBucketPolicyOutcome); + auto deleteBucketPolicyOutcome = DeleteBucketPolicy(bucketName); + AWS_EXPECT_SUCCESS(deleteBucketPolicyOutcome); + auto deleteOutcome = DeleteBucket(bucketName); + AWS_EXPECT_SUCCESS(deleteOutcome); + } + + TEST_F(S3ExpressTest, ObjectTest) { + auto bucketName = Testing::GetAwsResourcePrefix() + randomString(8) + S3_EXPRESS_SUFFIX; + auto keyName = randomString(8); + auto createOutcome = CreateBucket(bucketName); + AWS_EXPECT_SUCCESS(createOutcome); + auto putObjectOutcome = PutObject(bucketName, keyName); + AWS_EXPECT_SUCCESS(putObjectOutcome); + auto getObjectOutcome = GetObject(bucketName, keyName); + AWS_EXPECT_SUCCESS(getObjectOutcome); + auto objects = ListObjectsV2(bucketName); + EXPECT_TRUE(std::any_of(objects.GetResult().GetContents().begin(), + objects.GetResult().GetContents().end(), + [&keyName](const Object&object) -> bool { return object.GetKey() == keyName; })); + auto headObjecOutcome = HeadObject(bucketName, keyName); + AWS_EXPECT_SUCCESS(headObjecOutcome); + auto deleteObjecOutcome = DeleteObject(bucketName, keyName); + AWS_EXPECT_SUCCESS(deleteObjecOutcome); + auto deleteObjectsOutcome = DeleteObjects(bucketName); + AWS_EXPECT_SUCCESS(deleteObjectsOutcome); + auto deleteOutcome = DeleteBucket(bucketName); + AWS_EXPECT_SUCCESS(deleteOutcome); + } + + TEST_F(S3ExpressTest, MultipartUploadTest) { + auto bucketName = Testing::GetAwsResourcePrefix() + randomString(8) + S3_EXPRESS_SUFFIX; + auto createOutcome = CreateBucket(bucketName); + AWS_EXPECT_SUCCESS(createOutcome); + auto createMPUOutcome = CreateMultipartUpload(bucketName, randomString(8) + S3_EXPRESS_SUFFIX); + AWS_EXPECT_SUCCESS(createMPUOutcome); + auto uploadPartCopyOutcome = UploadPartCopy(bucketName, randomString(8) + S3_EXPRESS_SUFFIX); + AWS_EXPECT_SUCCESS(uploadPartCopyOutcome); + auto abortMPUOutcome = AbortMultipartUpload(bucketName, randomString(8) + S3_EXPRESS_SUFFIX); + AWS_EXPECT_SUCCESS(abortMPUOutcome); + EmptyBucketUtil({bucketName}); + } +}