2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2023 Nordix Foundation.
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.oran.datafile.datastore;
23 import static org.assertj.core.api.Assertions.assertThat;
24 import static org.junit.jupiter.api.Assertions.assertEquals;
25 import static org.mockito.ArgumentMatchers.any;
26 import static org.mockito.Mockito.atLeast;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.when;
30 import java.net.URISyntaxException;
31 import java.nio.charset.StandardCharsets;
32 import java.nio.file.Path;
33 import java.nio.file.Paths;
34 import java.util.Arrays;
35 import java.util.List;
36 import java.util.concurrent.CompletableFuture;
37 import org.junit.jupiter.api.Assertions;
38 import org.junit.jupiter.api.BeforeEach;
39 import org.junit.jupiter.api.Test;
40 import org.junit.jupiter.api.extension.ExtendWith;
41 import org.mockito.Mock;
42 import org.mockito.Mockito;
43 import org.mockito.junit.jupiter.MockitoExtension;
44 import org.oran.datafile.configuration.AppConfig;
45 import reactor.core.publisher.Flux;
46 import reactor.core.publisher.Mono;
47 import reactor.test.StepVerifier;
48 import software.amazon.awssdk.core.ResponseBytes;
49 import software.amazon.awssdk.core.async.AsyncRequestBody;
50 import software.amazon.awssdk.core.async.AsyncResponseTransformer;
51 import software.amazon.awssdk.services.s3.S3AsyncClient;
52 import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
53 import software.amazon.awssdk.services.s3.model.CreateBucketResponse;
54 import software.amazon.awssdk.services.s3.model.DeleteBucketRequest;
55 import software.amazon.awssdk.services.s3.model.DeleteBucketResponse;
56 import software.amazon.awssdk.services.s3.model.DeleteObjectRequest;
57 import software.amazon.awssdk.services.s3.model.DeleteObjectResponse;
58 import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest;
59 import software.amazon.awssdk.services.s3.model.DeleteObjectsResponse;
60 import software.amazon.awssdk.services.s3.model.GetObjectRequest;
61 import software.amazon.awssdk.services.s3.model.GetObjectResponse;
62 import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
63 import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
64 import software.amazon.awssdk.services.s3.model.ListObjectsRequest;
65 import software.amazon.awssdk.services.s3.model.ListObjectsResponse;
66 import software.amazon.awssdk.services.s3.model.PutObjectRequest;
67 import software.amazon.awssdk.services.s3.model.PutObjectResponse;
68 import software.amazon.awssdk.services.s3.model.S3Object;
71 @ExtendWith(MockitoExtension.class)
72 class S3ObjectStoreTest {
75 private AppConfig appConfig;
78 private S3AsyncClient s3AsynchClient;
80 private S3ObjectStore s3ObjectStore;
84 Mockito.lenient().when(appConfig.getS3EndpointOverride()).thenReturn("https://dummy-s3-bucket.s3.amazonaws.com");
85 Mockito.lenient().when(appConfig.getS3AccessKeyId()).thenReturn("test-access-key-id");
86 Mockito.lenient().when(appConfig.getS3SecretAccessKey()).thenReturn("test-access-key-secret");
88 Mockito.lenient().when(appConfig.getS3Bucket()).thenReturn("test-bucket");
89 Mockito.lenient().when(appConfig.getS3LocksBucket()).thenReturn("test-lock-bucket");
90 Mockito.lenient().when(appConfig.isS3Enabled()).thenReturn(true);
92 s3ObjectStore = new S3ObjectStore(appConfig, s3AsynchClient);
96 void createS3Bucket() {
97 CreateBucketRequest request = CreateBucketRequest.builder()
98 .bucket("test-bucket")
101 when(s3AsynchClient.createBucket(any(CreateBucketRequest.class)))
102 .thenReturn(CompletableFuture.completedFuture(CreateBucketResponse.builder().build()));
104 Mono<String> result = s3ObjectStore.create(DataStore.Bucket.FILES);
106 verify(s3AsynchClient, atLeast(1)).createBucket(any(CreateBucketRequest.class));
108 StepVerifier.create(result).expectNext("test-bucket").verifyComplete();
110 assertThat(result.block()).isEqualTo("test-bucket");
115 String prefix = "prefix/";
117 ListObjectsResponse response1 = ListObjectsResponse.builder()
118 .contents(createS3Object("object1"))
120 .nextMarker("marker1")
123 ListObjectsResponse response2 = ListObjectsResponse.builder()
124 .contents(createS3Object("object2"))
128 when(s3AsynchClient.listObjects(any(ListObjectsRequest.class)))
129 .thenReturn(CompletableFuture.completedFuture(response1),
130 CompletableFuture.completedFuture(response2));
132 Flux<String> result = s3ObjectStore.listObjects(DataStore.Bucket.FILES, prefix);
134 verify(s3AsynchClient, atLeast(1)).listObjects(any(ListObjectsRequest.class));
136 StepVerifier.create(result)
137 .expectNext("object1")
138 .expectNext("object2")
141 // Collect the results into a list
142 List<String> resultList = result.collectList().block();
144 assertEquals(Arrays.asList("object1", "object2"), resultList);
148 void testCreateLockWithExistingHead() {
149 HeadObjectResponse headObjectResponse = HeadObjectResponse.builder().build();
151 when(s3AsynchClient.headObject(any(HeadObjectRequest.class)))
152 .thenReturn(CompletableFuture.completedFuture(headObjectResponse));
154 Mono<Boolean> result = s3ObjectStore.createLock("lockName");
156 StepVerifier.create(result)
160 assertThat(result.block()).isFalse();
164 void testCreateLockWithoutExistingHead() {
165 HeadObjectResponse headObjectResponse = null;
166 Mockito.doReturn(CompletableFuture.completedFuture(headObjectResponse))
167 .when(s3AsynchClient)
168 .headObject(any(HeadObjectRequest.class));
170 Mono<Boolean> result = s3ObjectStore.createLock("lockName");
172 StepVerifier.create(result)
176 Boolean resultVal = result.block();
178 assertThat(resultVal).isNull();
184 when(s3AsynchClient.deleteObject(any(DeleteObjectRequest.class)))
185 .thenReturn(CompletableFuture.completedFuture(DeleteObjectResponse.builder().build()));
187 Mono<Boolean> result = s3ObjectStore.deleteLock("lock-name");
189 StepVerifier.create(result)
193 assertThat(result.block()).isTrue();
197 void testDeleteObject() {
198 when(s3AsynchClient.deleteObject(any(DeleteObjectRequest.class)))
199 .thenReturn(CompletableFuture.completedFuture(DeleteObjectResponse.builder().build()));
201 Mono<Boolean> result = s3ObjectStore.deleteObject(DataStore.Bucket.LOCKS, "objectName");
203 StepVerifier.create(result)
207 assertThat(result.block()).isTrue();
211 void testDeleteBucket_Success() {
212 DeleteBucketRequest request = DeleteBucketRequest.builder() //
213 .bucket("test-bucket")
216 when(s3AsynchClient.deleteBucket(any(DeleteBucketRequest.class)))
217 .thenReturn(CompletableFuture.completedFuture(DeleteBucketResponse.builder().build()));
219 DeleteObjectsRequest objectRequest = DeleteObjectsRequest.builder() //
220 .bucket("test-bucket")
223 when(s3AsynchClient.deleteObjects(any(DeleteObjectsRequest.class)))
224 .thenReturn(CompletableFuture.completedFuture(DeleteObjectsResponse.builder().build()));
226 String prefix = "prefix/";
228 ListObjectsResponse response1 = ListObjectsResponse.builder()
229 .contents(createS3Object("object1"))
231 .nextMarker("marker1")
234 ListObjectsResponse response2 = ListObjectsResponse.builder()
235 .contents(createS3Object("object2"))
239 when(s3AsynchClient.listObjects(any(ListObjectsRequest.class)))
240 .thenReturn(CompletableFuture.completedFuture(response1),
241 CompletableFuture.completedFuture(response2));
243 Mono<String> result = s3ObjectStore.deleteBucket(DataStore.Bucket.FILES);
245 StepVerifier.create(result)
251 void testCopyFileTo_Success() throws URISyntaxException {
252 PutObjectRequest request = PutObjectRequest.builder() //
253 .bucket("test-bucket") //
254 .key("test-access-key-id") //
257 when(s3AsynchClient.putObject(any(PutObjectRequest.class), any(AsyncRequestBody.class)))
258 .thenAnswer(invocation -> {
259 CompletableFuture<PutObjectResponse> future = CompletableFuture.completedFuture(
260 PutObjectResponse.builder().build()
265 Path testFile = Paths.get(getClass().getResource("/org/oran/datafile/datastore/file.txt").toURI());
267 Mono<String> result = s3ObjectStore.copyFileTo(testFile, "test-key");
269 StepVerifier.create(result)
270 .expectNext("test-key")
275 void testReadObject() {
276 // Mock the getObject method to return a CompletableFuture with ResponseBytes
277 when(s3AsynchClient.getObject(any(GetObjectRequest.class), any(AsyncResponseTransformer.class)))
278 .thenAnswer(invocation -> {
279 ResponseBytes<GetObjectResponse> responseBytes = ResponseBytes.fromByteArray(
280 GetObjectResponse.builder().build(),
281 "Hello, World!".getBytes(StandardCharsets.UTF_8)
283 CompletableFuture<ResponseBytes<GetObjectResponse>> future = CompletableFuture.completedFuture(
289 // Call the method under test
290 Mono<byte[]> result = s3ObjectStore.readObject(DataStore.Bucket.FILES, "test-key");
292 byte[] expectedBytes = "Hello, World!".getBytes(StandardCharsets.UTF_8);
293 StepVerifier.create(result)
294 .consumeNextWith(actualBytes -> Assertions.assertArrayEquals(expectedBytes, actualBytes))
299 void testPutObject() {
300 // Mock the putObject method to return a CompletableFuture with PutObjectResponse
301 when(s3AsynchClient.putObject(any(PutObjectRequest.class), any(AsyncRequestBody.class)))
302 .thenAnswer(invocation -> {
303 CompletableFuture<PutObjectResponse> future = CompletableFuture.completedFuture(
304 PutObjectResponse.builder().build()
309 // Call the method under test
310 Mono<String> result = s3ObjectStore.putObject(DataStore.Bucket.FILES, "test-key", "Hello, World!");
312 // Verify the Mono's behavior using StepVerifier
313 StepVerifier.create(result)
314 .expectNext("test-key")
318 private S3Object createS3Object(String key) {
319 return S3Object.builder().key(key).build();