Skip to content

Commit de95b1e

Browse files
committed
unit test for mock contentrange response
1 parent a1f9c72 commit de95b1e

File tree

3 files changed

+106
-111
lines changed

3 files changed

+106
-111
lines changed

src/aws-cpp-sdk-transfer/source/transfer/TransferManager.cpp

Lines changed: 45 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,60 +1134,57 @@ namespace Aws
11341134
handle->SetError(outcome.GetError());
11351135
TriggerErrorCallback(handle, outcome.GetError());
11361136
}
1137-
else
1137+
else if (request.RangeHasBeenSet())
11381138
{
1139-
if (request.RangeHasBeenSet())
1140-
{
1141-
const auto& requestedRange = request.GetRange();
1142-
const auto& responseContentRange = outcome.GetResult().GetContentRange();
1143-
1144-
if (responseContentRange.empty() or !VerifyContentRange(requestedRange, responseContentRange)) {
1145-
Aws::Client::AWSError<Aws::S3::S3Errors> error(Aws::S3::S3Errors::INTERNAL_FAILURE,
1146-
"ContentRangeMismatch",
1147-
"ContentRange in response does not match requested range",
1148-
false);
1149-
AWS_LOGSTREAM_ERROR(CLASS_TAG, "Transfer handle [" << handle->GetId()
1150-
<< "] ContentRange mismatch. Requested: [" << requestedRange
1151-
<< "] Received: [" << responseContentRange << "]");
1152-
handle->ChangePartToFailed(partState);
1153-
handle->SetError(error);
1154-
TriggerErrorCallback(handle, error);
1155-
handle->Cancel();
1156-
1157-
if(partState->GetDownloadBuffer())
1158-
{
1159-
m_bufferManager.Release(partState->GetDownloadBuffer());
1160-
partState->SetDownloadBuffer(nullptr);
1161-
}
1162-
return;
1163-
}
1164-
}
1139+
const auto& requestedRange = request.GetRange();
1140+
const auto& responseContentRange = outcome.GetResult().GetContentRange();
1141+
1142+
if (responseContentRange.empty() or !VerifyContentRange(requestedRange, responseContentRange)) {
1143+
Aws::Client::AWSError<Aws::S3::S3Errors> error(Aws::S3::S3Errors::INTERNAL_FAILURE,
1144+
"ContentRangeMismatch",
1145+
"ContentRange in response does not match requested range",
1146+
false);
1147+
AWS_LOGSTREAM_ERROR(CLASS_TAG, "Transfer handle [" << handle->GetId()
1148+
<< "] ContentRange mismatch. Requested: [" << requestedRange
1149+
<< "] Received: [" << responseContentRange << "]");
1150+
handle->ChangePartToFailed(partState);
1151+
handle->SetError(error);
1152+
TriggerErrorCallback(handle, error);
1153+
handle->Cancel();
11651154

1166-
if(handle->ShouldContinue())
1167-
{
1168-
Aws::IOStream* bufferStream = partState->GetDownloadPartStream();
1169-
assert(bufferStream);
1170-
1171-
Aws::String errMsg{handle->WritePartToDownloadStream(bufferStream, partState->GetRangeBegin())};
1172-
if (errMsg.empty()) {
1173-
handle->ChangePartToCompleted(partState, outcome.GetResult().GetETag());
1174-
} else {
1175-
Aws::Client::AWSError<Aws::S3::S3Errors> error(Aws::S3::S3Errors::INTERNAL_FAILURE,
1176-
"InternalFailure", errMsg, false);
1177-
AWS_LOGSTREAM_ERROR(CLASS_TAG, "Transfer handle [" << handle->GetId()
1178-
<< "] Failed to download object in Bucket: ["
1179-
<< handle->GetBucketName() << "] with Key: [" << handle->GetKey()
1180-
<< "] " << errMsg);
1181-
handle->ChangePartToFailed(partState);
1182-
handle->SetError(error);
1183-
TriggerErrorCallback(handle, error);
1155+
if(partState->GetDownloadBuffer())
1156+
{
1157+
m_bufferManager.Release(partState->GetDownloadBuffer());
1158+
partState->SetDownloadBuffer(nullptr);
11841159
}
1185-
}
1186-
else
1187-
{
1160+
return;
1161+
}
1162+
1163+
if(handle->ShouldContinue())
1164+
{
1165+
Aws::IOStream* bufferStream = partState->GetDownloadPartStream();
1166+
assert(bufferStream);
1167+
1168+
Aws::String errMsg{handle->WritePartToDownloadStream(bufferStream, partState->GetRangeBegin())};
1169+
if (errMsg.empty()) {
1170+
handle->ChangePartToCompleted(partState, outcome.GetResult().GetETag());
1171+
} else {
1172+
Aws::Client::AWSError<Aws::S3::S3Errors> error(Aws::S3::S3Errors::INTERNAL_FAILURE,
1173+
"InternalFailure", errMsg, false);
1174+
AWS_LOGSTREAM_ERROR(CLASS_TAG, "Transfer handle [" << handle->GetId()
1175+
<< "] Failed to download object in Bucket: ["
1176+
<< handle->GetBucketName() << "] with Key: [" << handle->GetKey()
1177+
<< "] " << errMsg);
11881178
handle->ChangePartToFailed(partState);
1179+
handle->SetError(error);
1180+
TriggerErrorCallback(handle, error);
11891181
}
11901182
}
1183+
else
1184+
{
1185+
handle->ChangePartToFailed(partState);
1186+
}
1187+
}
11911188

11921189
// buffer cleanup
11931190
if(partState->GetDownloadBuffer())

tests/aws-cpp-sdk-transfer-unit-tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
add_project(aws-cpp-sdk-transfer-unit-tests
22
"Unit Tests for the Transfer Manager"
33
aws-cpp-sdk-transfer
4+
aws-cpp-sdk-s3
45
testing-resources
56
aws_test_main
67
aws-cpp-sdk-core)
Lines changed: 60 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,66 @@
11
#include <gtest/gtest.h>
22
#include <aws/core/Aws.h>
3+
#include <aws/core/utils/threading/PooledThreadExecutor.h>
4+
#include <aws/s3/S3Client.h>
5+
#include <aws/s3/model/GetObjectRequest.h>
6+
#include <aws/s3/model/GetObjectResult.h>
7+
#include <aws/transfer/TransferManager.h>
38
#include <aws/testing/AwsTestHelpers.h>
49
#include <aws/testing/MemoryTesting.h>
10+
#include <sstream>
511

612
using namespace Aws;
13+
using namespace Aws::S3;
14+
using namespace Aws::S3::Model;
15+
using namespace Aws::Transfer;
16+
using namespace Aws::Utils::Threading;
717

818
const char* ALLOCATION_TAG = "TransferUnitTest";
919

10-
// Copy the VerifyContentRange function for testing
11-
// In production, this would be exposed in a header or made testable
12-
static bool VerifyContentRange(const Aws::String& requestedRange, const Aws::String& responseContentRange)
13-
{
14-
if (requestedRange.empty() || responseContentRange.empty())
15-
{
16-
return false;
17-
}
18-
19-
if (requestedRange.find("bytes=") != 0)
20-
{
21-
return false;
20+
class MockS3Client : public S3Client {
21+
public:
22+
MockS3Client() : S3Client(), shouldReturnWrongRange(false) {}
23+
24+
void SetReturnWrongRange(bool wrongRange) {
25+
shouldReturnWrongRange = wrongRange;
2226
}
23-
Aws::String requestRange = requestedRange.substr(6);
2427

25-
if (responseContentRange.find("bytes ") != 0)
26-
{
27-
return false;
28-
}
29-
Aws::String responseRange = responseContentRange.substr(6);
30-
size_t slashPos = responseRange.find('/');
31-
if (slashPos != Aws::String::npos)
32-
{
33-
responseRange = responseRange.substr(0, slashPos);
28+
GetObjectOutcome GetObject(const GetObjectRequest& request) const override {
29+
GetObjectResult result;
30+
31+
if (request.RangeHasBeenSet()) {
32+
if (shouldReturnWrongRange) {
33+
// Return a different range than requested
34+
result.SetContentRange("bytes 1024-2047/2048");
35+
} else {
36+
// Parse the requested range and return matching ContentRange
37+
Aws::String requestRange = request.GetRange();
38+
if (requestRange.find("bytes=") == 0) {
39+
Aws::String range = requestRange.substr(6); // Remove "bytes="
40+
result.SetContentRange("bytes " + range + "/2048");
41+
}
42+
}
43+
}
44+
45+
// Create a raw pointer stream
46+
auto stream = Aws::New<std::stringstream>(ALLOCATION_TAG);
47+
*stream << "mock data";
48+
result.ReplaceBody(stream);
49+
50+
return GetObjectOutcome(std::move(result));
3451
}
3552

36-
return requestRange == responseRange;
37-
}
53+
private:
54+
bool shouldReturnWrongRange;
55+
};
3856

3957
class TransferUnitTest : public testing::Test {
4058
protected:
59+
void SetUp() override {
60+
executor = Aws::MakeShared<PooledThreadExecutor>(ALLOCATION_TAG, 1);
61+
mockS3Client = Aws::MakeShared<MockS3Client>(ALLOCATION_TAG);
62+
}
63+
4164
static void SetUpTestSuite() {
4265
#ifdef USE_AWS_MEMORY_MANAGEMENT
4366
_testMemorySystem.reset(new ExactTestMemorySystem(1024, 128));
@@ -52,16 +75,12 @@ class TransferUnitTest : public testing::Test {
5275
EXPECT_EQ(_testMemorySystem->GetCurrentOutstandingAllocations(), 0ULL);
5376
EXPECT_EQ(_testMemorySystem->GetCurrentBytesAllocated(), 0ULL);
5477
EXPECT_TRUE(_testMemorySystem->IsClean());
55-
if (_testMemorySystem->GetCurrentOutstandingAllocations() != 0ULL)
56-
FAIL();
57-
if (_testMemorySystem->GetCurrentBytesAllocated() != 0ULL)
58-
FAIL();
59-
if (!_testMemorySystem->IsClean())
60-
FAIL();
6178
_testMemorySystem.reset();
6279
#endif
6380
}
6481

82+
std::shared_ptr<PooledThreadExecutor> executor;
83+
std::shared_ptr<MockS3Client> mockS3Client;
6584
static SDKOptions _options;
6685
#ifdef USE_AWS_MEMORY_MANAGEMENT
6786
static std::unique_ptr<ExactTestMemorySystem> _testMemorySystem;
@@ -73,41 +92,19 @@ SDKOptions TransferUnitTest::_options;
7392
std::unique_ptr<ExactTestMemorySystem> TransferUnitTest::_testMemorySystem = nullptr;
7493
#endif
7594

76-
TEST_F(TransferUnitTest, VerifyContentRange_ValidRanges) {
77-
// Test matching ranges
78-
EXPECT_TRUE(VerifyContentRange("bytes=0-1023", "bytes 0-1023/2048"));
79-
EXPECT_TRUE(VerifyContentRange("bytes=1024-2047", "bytes 1024-2047/2048"));
80-
EXPECT_TRUE(VerifyContentRange("bytes=0-499", "bytes 0-499/500"));
81-
82-
// Test without total size in response
83-
EXPECT_TRUE(VerifyContentRange("bytes=0-1023", "bytes 0-1023"));
84-
}
85-
86-
TEST_F(TransferUnitTest, VerifyContentRange_InvalidRanges) {
87-
// Test mismatched ranges - this is what @kai-ion wanted to test!
88-
EXPECT_FALSE(VerifyContentRange("bytes=0-1023", "bytes 0-1022/2048"));
89-
EXPECT_FALSE(VerifyContentRange("bytes=0-1023", "bytes 1024-2047/2048"));
90-
EXPECT_FALSE(VerifyContentRange("bytes=1024-2047", "bytes 0-1023/2048"));
95+
TEST_F(TransferUnitTest, ContentRangeVerification_Failure) {
96+
mockS3Client->SetReturnWrongRange(true);
9197

92-
// Test empty inputs
93-
EXPECT_FALSE(VerifyContentRange("", "bytes 0-1023/2048"));
94-
EXPECT_FALSE(VerifyContentRange("bytes=0-1023", ""));
95-
EXPECT_FALSE(VerifyContentRange("", ""));
98+
TransferManagerConfiguration config(executor.get());
99+
config.s3Client = mockS3Client;
100+
auto transferManager = TransferManager::Create(config);
96101

97-
// Test invalid format
98-
EXPECT_FALSE(VerifyContentRange("0-1023", "bytes 0-1023/2048"));
99-
EXPECT_FALSE(VerifyContentRange("bytes=0-1023", "0-1023/2048"));
100-
EXPECT_FALSE(VerifyContentRange("range=0-1023", "bytes 0-1023/2048"));
101-
}
102-
103-
TEST_F(TransferUnitTest, VerifyContentRange_EdgeCases) {
104-
// Test single byte range
105-
EXPECT_TRUE(VerifyContentRange("bytes=0-0", "bytes 0-0/1"));
102+
auto createStreamFn = []() {
103+
return Aws::New<std::stringstream>(ALLOCATION_TAG);
104+
};
106105

107-
// Test large ranges
108-
EXPECT_TRUE(VerifyContentRange("bytes=0-1073741823", "bytes 0-1073741823/1073741824"));
106+
auto handle = transferManager->DownloadFile("test-bucket", "test-key", 0, 1024, createStreamFn);
107+
handle->WaitUntilFinished();
109108

110-
// Test ranges with different total sizes (should still match the range part)
111-
EXPECT_TRUE(VerifyContentRange("bytes=0-1023", "bytes 0-1023/5000"));
112-
EXPECT_TRUE(VerifyContentRange("bytes=0-1023", "bytes 0-1023/1024"));
109+
EXPECT_EQ(TransferStatus::FAILED, handle->GetStatus());
113110
}

0 commit comments

Comments
 (0)