Skip to content

Commit 2bdcd73

Browse files
committed
adding unit test for checksum of doenload
1 parent 52768a5 commit 2bdcd73

File tree

1 file changed

+156
-0
lines changed

1 file changed

+156
-0
lines changed

tests/aws-cpp-sdk-transfer-unit-tests/TransferUnitTests.cpp

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
#include <aws/s3/S3Client.h>
55
#include <aws/s3/model/GetObjectRequest.h>
66
#include <aws/s3/model/GetObjectResult.h>
7+
#include <aws/s3/model/HeadObjectRequest.h>
8+
#include <aws/s3/model/HeadObjectResult.h>
79
#include <aws/transfer/TransferManager.h>
810
#include <aws/testing/AwsTestHelpers.h>
911
#include <aws/testing/MemoryTesting.h>
1012
#include <sstream>
13+
#include <fstream>
1114

1215
using namespace Aws;
1316
using namespace Aws::S3;
@@ -36,11 +39,87 @@ class MockS3Client : public S3Client {
3639
}
3740
};
3841

42+
class MockMultipartS3Client : public S3Client {
43+
public:
44+
Aws::String FULL_OBJECT_CHECKSUM; //"SBi/K+1ooBg="
45+
MockMultipartS3Client(Aws::String expected_checksum) : S3Client() {
46+
FULL_OBJECT_CHECKSUM = expected_checksum;
47+
};
48+
49+
HeadObjectOutcome HeadObject(const HeadObjectRequest&) const override {
50+
HeadObjectResult result;
51+
result.SetContentLength(78643200);
52+
result.SetChecksumCRC64NVME(FULL_OBJECT_CHECKSUM);
53+
result.SetChecksumType(Aws::S3::Model::ChecksumType::FULL_OBJECT); // This is key!
54+
result.SetETag("\"test-etag-12345\""); // Add ETag
55+
return HeadObjectOutcome(std::move(result));
56+
}
57+
58+
GetObjectOutcome GetObject(const GetObjectRequest& request) const override {
59+
GetObjectResult result;
60+
61+
const uint64_t totalSize = 78643200;
62+
const uint64_t partSize = 5242880;
63+
const std::vector<Aws::String> checksums = {
64+
"wAQOkgd/LJk=", "zfmsUj6AZfs=", "oyENjcGDHcY=", "wAQOkgd/LJk=", "zfmsUj6AZfs=",
65+
"oyENjcGDHcY=", "wAQOkgd/LJk=", "zfmsUj6AZfs=", "oyENjcGDHcY=", "wAQOkgd/LJk=",
66+
"zfmsUj6AZfs=", "oyENjcGDHcY=", "wAQOkgd/LJk=", "zfmsUj6AZfs=", "oyENjcGDHcY="
67+
};
68+
69+
if (request.RangeHasBeenSet()) {
70+
auto range = request.GetRange();
71+
size_t dashPos = range.find('-');
72+
uint64_t start = std::stoull(range.substr(6, dashPos - 6).c_str());
73+
uint64_t end = std::stoull(range.substr(dashPos + 1).c_str());
74+
uint64_t size = end - start + 1;
75+
76+
int partNum = static_cast<int>(start) / partSize;
77+
if (partNum < 15) {
78+
result.SetContentRange(Aws::String("bytes ") + Aws::String(std::to_string(start).c_str()) + "-" + Aws::String(std::to_string(end).c_str()) + "/" + Aws::String(std::to_string(totalSize).c_str()));
79+
result.SetChecksumCRC64NVME(checksums[partNum]);
80+
result.SetContentLength(size);
81+
result.SetETag(Aws::String("\"part-etag-") + Aws::String(std::to_string(partNum).c_str()) + "\"");
82+
83+
// Call the response stream factory if provided
84+
if (request.GetResponseStreamFactory()) {
85+
auto responseStream = request.GetResponseStreamFactory()();
86+
87+
// Write part-specific data to the response stream
88+
char partChar = 'A' + (partNum % 3);
89+
for (uint64_t i = 0; i < size; ++i) {
90+
responseStream->put(partChar);
91+
}
92+
responseStream->flush();
93+
94+
// Simulate data received callback to track bytes transferred
95+
if (request.GetDataReceivedEventHandler()) {
96+
request.GetDataReceivedEventHandler()(nullptr, nullptr, size);
97+
}
98+
99+
result.ReplaceBody(responseStream);
100+
} else {
101+
// Fallback for non-factory requests
102+
auto stream = Aws::New<std::stringstream>(ALLOCATION_TAG);
103+
char partChar = 'A' + (partNum % 3);
104+
for (uint64_t i = 0; i < size; ++i) {
105+
stream->put(partChar);
106+
}
107+
stream->seekg(0, std::ios::beg);
108+
result.ReplaceBody(stream);
109+
}
110+
}
111+
}
112+
113+
return GetObjectOutcome(std::move(result));
114+
}
115+
};
116+
39117
class TransferUnitTest : public testing::Test {
40118
protected:
41119
void SetUp() override {
42120
executor = Aws::MakeShared<PooledThreadExecutor>(ALLOCATION_TAG, 1);
43121
mockS3Client = Aws::MakeShared<MockS3Client>(ALLOCATION_TAG);
122+
mockMultipartS3Client = Aws::MakeShared<MockMultipartS3Client>(ALLOCATION_TAG, "SBi/K+1ooBg=");
44123
}
45124

46125
static void SetUpTestSuite() {
@@ -53,6 +132,7 @@ class TransferUnitTest : public testing::Test {
53132

54133
std::shared_ptr<PooledThreadExecutor> executor;
55134
std::shared_ptr<MockS3Client> mockS3Client;
135+
std::shared_ptr<MockMultipartS3Client> mockMultipartS3Client;
56136
static SDKOptions _options;
57137
};
58138

@@ -72,4 +152,80 @@ TEST_F(TransferUnitTest, ContentValidationShouldFail) {
72152
handle->WaitUntilFinished();
73153

74154
EXPECT_EQ(TransferStatus::FAILED, handle->GetStatus());
155+
}
156+
157+
TEST_F(TransferUnitTest, MultipartDownloadTest) {
158+
TransferManagerConfiguration config(executor.get());
159+
config.s3Client = mockMultipartS3Client;
160+
config.bufferSize = 5242880; // 5MB to ensure multipart
161+
auto transferManager = TransferManager::Create(config);
162+
163+
// Create a temporary file for download since multipart needs seekable stream
164+
std::string tempFile;
165+
#ifdef _WIN32
166+
char tempPath[MAX_PATH];
167+
GetTempPathA(MAX_PATH, tempPath);
168+
tempFile = std::string(tempPath) + "test_download_" + std::to_string(rand());
169+
#else
170+
tempFile = "/tmp/test_download_" + std::to_string(rand());
171+
#endif
172+
auto createStreamFn = [tempFile]() -> Aws::IOStream* {
173+
return Aws::New<Aws::FStream>(ALLOCATION_TAG, tempFile.c_str(),
174+
std::ios_base::out | std::ios_base::in |
175+
std::ios_base::binary | std::ios_base::trunc);
176+
};
177+
178+
// Download the full 78MB file
179+
auto handle = transferManager->DownloadFile("test-bucket", "test-key", createStreamFn);
180+
handle->WaitUntilFinished();
181+
182+
// Test multipart download functionality - should PASS with correct checksum
183+
EXPECT_TRUE(handle->IsMultipart());
184+
EXPECT_EQ(78643200u, handle->GetBytesTotalSize());
185+
EXPECT_EQ(15u, handle->GetCompletedParts().size());
186+
EXPECT_EQ(0u, handle->GetFailedParts().size());
187+
EXPECT_EQ(0u, handle->GetPendingParts().size());
188+
EXPECT_EQ(TransferStatus::COMPLETED, handle->GetStatus()); // Should PASS
189+
190+
// Clean up
191+
std::remove(tempFile.c_str());
192+
}
193+
194+
TEST_F(TransferUnitTest, MultipartDownloadTest_Fail) {
195+
TransferManagerConfiguration config(executor.get());
196+
auto mockFailClient = Aws::MakeShared<MockMultipartS3Client>(ALLOCATION_TAG, "WRONG_CHECKSUM=");
197+
config.s3Client = mockFailClient;
198+
config.bufferSize = 5242880; // 5MB to ensure multipart
199+
auto transferManager = TransferManager::Create(config);
200+
201+
// Create a temporary file for download since multipart needs seekable stream
202+
std::string tempFile;
203+
#ifdef _WIN32
204+
char tempPath[MAX_PATH];
205+
GetTempPathA(MAX_PATH, tempPath);
206+
tempFile = std::string(tempPath) + "test_download_" + std::to_string(rand());
207+
#else
208+
tempFile = "/tmp/test_download_" + std::to_string(rand());
209+
#endif
210+
auto createStreamFn = [tempFile]() -> Aws::IOStream* {
211+
return Aws::New<Aws::FStream>(ALLOCATION_TAG, tempFile.c_str(),
212+
std::ios_base::out | std::ios_base::in |
213+
std::ios_base::binary | std::ios_base::trunc);
214+
};
215+
216+
// Download the full 78MB file
217+
auto handle = transferManager->DownloadFile("test-bucket", "test-key", createStreamFn);
218+
handle->WaitUntilFinished();
219+
220+
// Test multipart download functionality - should FAIL with wrong checksum
221+
EXPECT_TRUE(handle->IsMultipart());
222+
EXPECT_EQ(78643200u, handle->GetBytesTotalSize());
223+
EXPECT_EQ(15u, handle->GetCompletedParts().size());
224+
EXPECT_EQ(0u, handle->GetFailedParts().size());
225+
EXPECT_EQ(0u, handle->GetPendingParts().size());
226+
EXPECT_EQ(TransferStatus::FAILED, handle->GetStatus()); // Should FAIL due to wrong checksum
227+
EXPECT_EQ("Full-object checksum validation failed", handle->GetLastError().GetMessage());
228+
229+
// Clean up
230+
std::remove(tempFile.c_str());
75231
}

0 commit comments

Comments
 (0)