Skip to content

Commit cfc2eb6

Browse files
committed
Integration test cases for Cross region Sync and Sync Clients
1 parent 5d57416 commit cfc2eb6

File tree

4 files changed

+468
-0
lines changed

4 files changed

+468
-0
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.services.s3.crossregion;
17+
18+
import java.util.ArrayList;
19+
import java.util.Collections;
20+
import java.util.List;
21+
import java.util.concurrent.atomic.AtomicInteger;
22+
import software.amazon.awssdk.core.interceptor.Context;
23+
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
24+
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
25+
import software.amazon.awssdk.http.SdkHttpMethod;
26+
27+
public final class CaptureInterceptor implements ExecutionInterceptor {
28+
private final List<SdkHttpMethod> httpMethods = new ArrayList<>();
29+
private List<String> hosts = new ArrayList<>();
30+
private AtomicInteger serviceCalls = new AtomicInteger(0);
31+
32+
@Override
33+
public void beforeTransmission(Context.BeforeTransmission context, ExecutionAttributes executionAttributes) {
34+
hosts.add(context.httpRequest().host());
35+
httpMethods.add(context.httpRequest().method());
36+
serviceCalls.incrementAndGet();
37+
}
38+
39+
public int getServiceCalls() {
40+
return serviceCalls.get();
41+
}
42+
43+
public List<String> hosts() {
44+
return Collections.unmodifiableList(this.hosts);
45+
}
46+
47+
public List<SdkHttpMethod> httpMethods() {
48+
return Collections.unmodifiableList(this.httpMethods);
49+
}
50+
51+
public void reset() {
52+
this.hosts = new ArrayList<>();
53+
}
54+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.services.s3.crossregion;
17+
18+
import static software.amazon.awssdk.testutils.service.S3BucketUtils.temporaryBucketName;
19+
20+
import java.util.ArrayList;
21+
import java.util.List;
22+
import java.util.concurrent.CompletableFuture;
23+
import org.junit.jupiter.api.AfterAll;
24+
import org.junit.jupiter.api.AfterEach;
25+
import org.junit.jupiter.api.BeforeAll;
26+
import org.junit.jupiter.api.BeforeEach;
27+
import software.amazon.awssdk.core.ResponseBytes;
28+
import software.amazon.awssdk.core.async.AsyncRequestBody;
29+
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
30+
import software.amazon.awssdk.services.s3.S3AsyncClient;
31+
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
32+
import software.amazon.awssdk.services.s3.model.DeleteObjectResponse;
33+
import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest;
34+
import software.amazon.awssdk.services.s3.model.DeleteObjectsResponse;
35+
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
36+
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
37+
import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
38+
import software.amazon.awssdk.services.s3.model.HeadBucketResponse;
39+
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
40+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
41+
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
42+
import software.amazon.awssdk.services.s3.model.S3Object;
43+
import software.amazon.awssdk.services.s3.paginators.ListObjectsV2Publisher;
44+
45+
public class S3AsyncCrossRegionTest extends S3CrossRegionTestBase {
46+
private S3AsyncClient crossRegionS3Client ;
47+
48+
@BeforeEach
49+
public void initialize() {
50+
captureInterceptor = new CaptureInterceptor();
51+
crossRegionS3Client = S3AsyncClient.builder()
52+
.region(CROSS_REGION)
53+
.serviceConfiguration(s -> s.crossRegionAccessEnabled(true))
54+
.overrideConfiguration(o -> o.addExecutionInterceptor(captureInterceptor))
55+
.build();
56+
}
57+
58+
@AfterEach
59+
void reset(){
60+
captureInterceptor.reset();
61+
}
62+
@BeforeAll
63+
static void setUpClass(){
64+
// Bucket Created in CROSS Region
65+
s3 = s3ClientBuilder().build();
66+
createBucket(BUCKET);
67+
}
68+
69+
@AfterAll
70+
static void clearClass(){
71+
deleteBucketAndAllContents(BUCKET);
72+
}
73+
74+
private static final String BUCKET = temporaryBucketName(S3AsyncCrossRegionTest.class);
75+
76+
@Override
77+
protected List<S3Object> paginatedAPICall(ListObjectsV2Request listObjectsV2Request) {
78+
List<S3Object> resultObjects = new ArrayList<>();
79+
ListObjectsV2Publisher publisher = crossRegionS3Client.listObjectsV2Paginator(listObjectsV2Request);
80+
CompletableFuture<Void> subscribe = publisher.subscribe(response -> {
81+
response.contents().forEach(a -> resultObjects.add(a));
82+
});
83+
subscribe.join();
84+
return resultObjects;
85+
}
86+
87+
@Override
88+
protected DeleteObjectsResponse postObjectAPICall(DeleteObjectsRequest deleteObjectsRequest) {
89+
return crossRegionS3Client.deleteObjects(deleteObjectsRequest).join();
90+
}
91+
92+
@Override
93+
protected HeadBucketResponse headAPICall(HeadBucketRequest headBucketRequest) {
94+
return crossRegionS3Client.headBucket(headBucketRequest).join();
95+
}
96+
97+
@Override
98+
protected DeleteObjectResponse deleteObjectAPICall(DeleteObjectRequest deleteObjectRequest) {
99+
return crossRegionS3Client.deleteObject(deleteObjectRequest).join();
100+
}
101+
102+
@Override
103+
protected PutObjectResponse putAPICall(PutObjectRequest putObjectRequest, String testString) {
104+
return crossRegionS3Client.putObject(putObjectRequest, AsyncRequestBody.fromString(testString)).join();
105+
}
106+
107+
@Override
108+
protected ResponseBytes<GetObjectResponse> getAPICall(GetObjectRequest getObjectRequest) {
109+
return crossRegionS3Client.getObject(getObjectRequest, AsyncResponseTransformer.toBytes()).join();
110+
}
111+
112+
@Override
113+
protected String bucketName() {
114+
return BUCKET;
115+
}
116+
117+
}
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.services.s3.crossregion;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import java.util.Arrays;
21+
import java.util.List;
22+
import java.util.stream.Collectors;
23+
import java.util.stream.IntStream;
24+
import org.junit.jupiter.api.Test;
25+
import software.amazon.awssdk.core.ResponseBytes;
26+
import software.amazon.awssdk.core.sync.RequestBody;
27+
import software.amazon.awssdk.http.SdkHttpMethod;
28+
import software.amazon.awssdk.regions.Region;
29+
import software.amazon.awssdk.services.s3.S3IntegrationTestBase;
30+
import software.amazon.awssdk.services.s3.model.ChecksumAlgorithm;
31+
import software.amazon.awssdk.services.s3.model.ChecksumMode;
32+
import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
33+
import software.amazon.awssdk.services.s3.model.DeleteObjectResponse;
34+
import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest;
35+
import software.amazon.awssdk.services.s3.model.DeleteObjectsResponse;
36+
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
37+
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
38+
import software.amazon.awssdk.services.s3.model.HeadBucketRequest;
39+
import software.amazon.awssdk.services.s3.model.HeadBucketResponse;
40+
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
41+
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
42+
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
43+
import software.amazon.awssdk.services.s3.model.S3Object;
44+
45+
public abstract class S3CrossRegionTestBase extends S3IntegrationTestBase {
46+
47+
public static final String X_AMZ_BUCKET_REGION = "x-amz-bucket-region";
48+
49+
protected static final Region CROSS_REGION = Region.of("eu-central-1");
50+
51+
private static final String KEY = "key";
52+
53+
protected CaptureInterceptor captureInterceptor;
54+
55+
@Test
56+
void getApi_CrossRegionCall() {
57+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY), RequestBody.fromString(
58+
"TEST_STRING"));
59+
GetObjectRequest getObjectRequest =
60+
GetObjectRequest.builder().bucket(bucketName()).checksumMode(ChecksumMode.ENABLED).key(KEY).build();
61+
ResponseBytes<GetObjectResponse> response = getAPICall(getObjectRequest);
62+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
63+
DEFAULT_REGION.id()));
64+
assertThat(captureInterceptor.httpMethods()).isEqualTo(Arrays.asList(SdkHttpMethod.GET, SdkHttpMethod.GET));
65+
assertThat(new String(response.asByteArray())).isEqualTo("TEST_STRING");
66+
assertThat(captureInterceptor.getServiceCalls()).isEqualTo(2);
67+
}
68+
69+
@Test
70+
void putApi_CrossRegionCall() {
71+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY), RequestBody.fromString(
72+
"TEST_STRING"));
73+
PutObjectRequest putObjectRequest =
74+
PutObjectRequest.builder().bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY).build();
75+
PutObjectResponse response = putAPICall(putObjectRequest, "TEST_STRING");
76+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
77+
DEFAULT_REGION.id()));
78+
assertThat(captureInterceptor.httpMethods()).isEqualTo(Arrays.asList(SdkHttpMethod.PUT, SdkHttpMethod.PUT));
79+
assertThat(response.checksumCRC32()).isEqualTo("S9ke8w==");
80+
assertThat(captureInterceptor.getServiceCalls()).isEqualTo(2);
81+
}
82+
83+
@Test
84+
void deleteApi_CrossRegionCall() {
85+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY), RequestBody.fromString(
86+
"TEST_STRING"));
87+
DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder().bucket(bucketName()).key(KEY).build();
88+
DeleteObjectResponse response = deleteObjectAPICall(deleteObjectRequest);
89+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
90+
DEFAULT_REGION.id()));
91+
assertThat(captureInterceptor.httpMethods()).isEqualTo(Arrays.asList(SdkHttpMethod.DELETE, SdkHttpMethod.DELETE));
92+
assertThat(response).isNotNull();
93+
assertThat(captureInterceptor.getServiceCalls()).isEqualTo(2);
94+
}
95+
96+
@Test
97+
void postApi_CrossRegionCall() {
98+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY), RequestBody.fromString(
99+
"TEST_STRING"));
100+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY + "_1"),
101+
RequestBody.fromString("TEST_STRING"));
102+
DeleteObjectsRequest deleteObjectsRequest =
103+
DeleteObjectsRequest.builder().bucket(bucketName()).delete(d -> d.objects(o -> o.key(KEY), o -> o.key(KEY + "_1"))).build();
104+
DeleteObjectsResponse response = postObjectAPICall(deleteObjectsRequest);
105+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
106+
DEFAULT_REGION.id()));
107+
assertThat(captureInterceptor.httpMethods()).isEqualTo(Arrays.asList(SdkHttpMethod.POST, SdkHttpMethod.POST));
108+
assertThat(response).isNotNull();
109+
assertThat(captureInterceptor.getServiceCalls()).isEqualTo(2);
110+
}
111+
112+
@Test
113+
void cachedRegionGetsUsed_when_CrossRegionCall() {
114+
putAPICall(PutObjectRequest.builder().bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY).build(),
115+
"TEST_STRING");
116+
getAPICall(GetObjectRequest.builder().bucket(bucketName()).checksumMode(ChecksumMode.ENABLED).key(KEY).build());
117+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
118+
DEFAULT_REGION.id(),
119+
DEFAULT_REGION.id()));
120+
assertThat(captureInterceptor.httpMethods()).isEqualTo(Arrays.asList(SdkHttpMethod.PUT,
121+
SdkHttpMethod.PUT,
122+
SdkHttpMethod.GET));
123+
assertThat(captureInterceptor.getServiceCalls()).isEqualTo(3);
124+
}
125+
126+
@Test
127+
void paginatedApi_CrossRegionCall() {
128+
s3.deleteObject(p -> p.bucket(bucketName()).key(KEY));
129+
int maxKeys = 3;
130+
int totalKeys = maxKeys * 2 ;
131+
IntStream.range(0, totalKeys )
132+
.forEach(
133+
i ->
134+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY + "_" + i),
135+
RequestBody.fromString("TEST_STRING"))
136+
);
137+
ListObjectsV2Request listObjectsV2Request = ListObjectsV2Request.builder().bucket(bucketName()).maxKeys(maxKeys).build();
138+
List<S3Object> s3ObjectList = paginatedAPICall(listObjectsV2Request);
139+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
140+
DEFAULT_REGION.id(),
141+
DEFAULT_REGION.id()));
142+
143+
IntStream.range(0, totalKeys ).forEach(i -> s3.deleteObject(p -> p.bucket(bucketName()).key(KEY + "_" + i)));
144+
}
145+
146+
@Test
147+
void headApi_CrossRegionCall() {
148+
s3.putObject(p -> p.bucket(bucketName()).checksumAlgorithm(ChecksumAlgorithm.CRC32).key(KEY), RequestBody.fromString(
149+
"TEST_STRING"));
150+
HeadBucketRequest headBucketRequest = HeadBucketRequest.builder().bucket(bucketName()).build();
151+
HeadBucketResponse response = headAPICall(headBucketRequest);
152+
assertThat(regionsIntercepted(captureInterceptor.hosts())).isEqualTo(Arrays.asList(CROSS_REGION.id(),
153+
DEFAULT_REGION.id()));
154+
assertThat(captureInterceptor.httpMethods()).isEqualTo(Arrays.asList(SdkHttpMethod.HEAD, SdkHttpMethod.HEAD));
155+
assertThat(response).isNotNull();
156+
assertThat(captureInterceptor.getServiceCalls()).isEqualTo(2);
157+
}
158+
159+
protected abstract List<S3Object> paginatedAPICall(ListObjectsV2Request listObjectsV2Request);
160+
161+
protected abstract DeleteObjectsResponse postObjectAPICall(DeleteObjectsRequest deleteObjectsRequest);
162+
163+
protected abstract HeadBucketResponse headAPICall(HeadBucketRequest headBucketRequest);
164+
165+
protected abstract DeleteObjectResponse deleteObjectAPICall(DeleteObjectRequest deleteObjectRequest);
166+
167+
protected abstract PutObjectResponse putAPICall(PutObjectRequest putObjectRequest, String testString);
168+
169+
protected abstract ResponseBytes<GetObjectResponse> getAPICall(GetObjectRequest getObjectRequest);
170+
171+
protected abstract String bucketName();
172+
173+
private List<String> regionsIntercepted(List<String> hosts) {
174+
return hosts.stream()
175+
.map(req -> req.substring(bucketName().length() + 4, req.length() - 14))
176+
.collect(Collectors.toList());
177+
}
178+
179+
}

0 commit comments

Comments
 (0)