Improve Test coverage of DFC
[nonrtric/plt/ranpm.git] / datafilecollector / src / test / java / org / oran / datafile / datastore / S3ObjectStoreTest.java
1 /*-
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
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.oran.datafile.datastore;
22
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;
29
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;
69
70
71 @ExtendWith(MockitoExtension.class)
72 class S3ObjectStoreTest {
73
74     @Mock
75     private AppConfig appConfig;
76
77     @Mock
78     private S3AsyncClient s3AsynchClient;
79
80     private S3ObjectStore s3ObjectStore;
81
82     @BeforeEach
83     void setup() {
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");
87
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);
91
92         s3ObjectStore = new S3ObjectStore(appConfig, s3AsynchClient);
93     }
94
95     @Test
96     void createS3Bucket() {
97         CreateBucketRequest request = CreateBucketRequest.builder()
98             .bucket("test-bucket")
99             .build();
100
101         when(s3AsynchClient.createBucket(any(CreateBucketRequest.class)))
102             .thenReturn(CompletableFuture.completedFuture(CreateBucketResponse.builder().build()));
103
104         Mono<String> result = s3ObjectStore.create(DataStore.Bucket.FILES);
105
106         verify(s3AsynchClient, atLeast(1)).createBucket(any(CreateBucketRequest.class));
107
108         StepVerifier.create(result).expectNext("test-bucket").verifyComplete();
109
110         assertThat(result.block()).isEqualTo("test-bucket");
111     }
112
113     @Test
114     void listObjects() {
115         String prefix = "prefix/";
116
117         ListObjectsResponse response1 = ListObjectsResponse.builder()
118             .contents(createS3Object("object1"))
119             .isTruncated(true)
120             .nextMarker("marker1")
121             .build();
122
123         ListObjectsResponse response2 = ListObjectsResponse.builder()
124             .contents(createS3Object("object2"))
125             .isTruncated(false)
126             .build();
127
128         when(s3AsynchClient.listObjects(any(ListObjectsRequest.class)))
129             .thenReturn(CompletableFuture.completedFuture(response1),
130                 CompletableFuture.completedFuture(response2));
131
132         Flux<String> result = s3ObjectStore.listObjects(DataStore.Bucket.FILES, prefix);
133
134         verify(s3AsynchClient, atLeast(1)).listObjects(any(ListObjectsRequest.class));
135
136         StepVerifier.create(result)
137             .expectNext("object1")
138             .expectNext("object2")
139             .verifyComplete();
140
141         // Collect the results into a list
142         List<String> resultList = result.collectList().block();
143
144         assertEquals(Arrays.asList("object1", "object2"), resultList);
145     }
146
147     @Test
148     void testCreateLockWithExistingHead() {
149         HeadObjectResponse headObjectResponse = HeadObjectResponse.builder().build();
150
151         when(s3AsynchClient.headObject(any(HeadObjectRequest.class)))
152             .thenReturn(CompletableFuture.completedFuture(headObjectResponse));
153
154         Mono<Boolean> result = s3ObjectStore.createLock("lockName");
155
156         StepVerifier.create(result)
157             .expectNext(false)
158             .verifyComplete();
159
160         assertThat(result.block()).isFalse();
161     }
162
163     @Test
164     void testCreateLockWithoutExistingHead() {
165         HeadObjectResponse headObjectResponse = null;
166         Mockito.doReturn(CompletableFuture.completedFuture(headObjectResponse))
167             .when(s3AsynchClient)
168             .headObject(any(HeadObjectRequest.class));
169
170         Mono<Boolean> result = s3ObjectStore.createLock("lockName");
171
172         StepVerifier.create(result)
173             .expectComplete()
174             .verify();
175
176         Boolean resultVal = result.block();
177
178         assertThat(resultVal).isNull();
179     }
180
181
182     @Test
183     void deleteLock() {
184         when(s3AsynchClient.deleteObject(any(DeleteObjectRequest.class)))
185             .thenReturn(CompletableFuture.completedFuture(DeleteObjectResponse.builder().build()));
186
187         Mono<Boolean> result = s3ObjectStore.deleteLock("lock-name");
188
189         StepVerifier.create(result)
190             .expectNext(true)
191             .verifyComplete();
192
193         assertThat(result.block()).isTrue();
194     }
195
196     @Test
197     void testDeleteObject() {
198         when(s3AsynchClient.deleteObject(any(DeleteObjectRequest.class)))
199             .thenReturn(CompletableFuture.completedFuture(DeleteObjectResponse.builder().build()));
200
201         Mono<Boolean> result = s3ObjectStore.deleteObject(DataStore.Bucket.LOCKS, "objectName");
202
203         StepVerifier.create(result)
204             .expectNext(true)
205             .verifyComplete();
206
207         assertThat(result.block()).isTrue();
208     }
209
210     @Test
211     void testDeleteBucket_Success() {
212         DeleteBucketRequest request = DeleteBucketRequest.builder() //
213             .bucket("test-bucket")
214             .build();
215
216         when(s3AsynchClient.deleteBucket(any(DeleteBucketRequest.class)))
217             .thenReturn(CompletableFuture.completedFuture(DeleteBucketResponse.builder().build()));
218
219         DeleteObjectsRequest objectRequest = DeleteObjectsRequest.builder() //
220             .bucket("test-bucket")
221             .build();
222
223         when(s3AsynchClient.deleteObjects(any(DeleteObjectsRequest.class)))
224             .thenReturn(CompletableFuture.completedFuture(DeleteObjectsResponse.builder().build()));
225
226         String prefix = "prefix/";
227
228         ListObjectsResponse response1 = ListObjectsResponse.builder()
229             .contents(createS3Object("object1"))
230             .isTruncated(true)
231             .nextMarker("marker1")
232             .build();
233
234         ListObjectsResponse response2 = ListObjectsResponse.builder()
235             .contents(createS3Object("object2"))
236             .isTruncated(false)
237             .build();
238
239         when(s3AsynchClient.listObjects(any(ListObjectsRequest.class)))
240             .thenReturn(CompletableFuture.completedFuture(response1),
241                 CompletableFuture.completedFuture(response2));
242
243         Mono<String> result = s3ObjectStore.deleteBucket(DataStore.Bucket.FILES);
244
245         StepVerifier.create(result)
246             .expectNext("OK")
247             .verifyComplete();
248     }
249
250     @Test
251     void testCopyFileTo_Success() throws URISyntaxException {
252         PutObjectRequest request = PutObjectRequest.builder() //
253             .bucket("test-bucket") //
254             .key("test-access-key-id") //
255             .build();
256
257         when(s3AsynchClient.putObject(any(PutObjectRequest.class), any(AsyncRequestBody.class)))
258             .thenAnswer(invocation -> {
259                 CompletableFuture<PutObjectResponse> future = CompletableFuture.completedFuture(
260                     PutObjectResponse.builder().build()
261                 );
262                 return future;
263             });
264
265         Path testFile = Paths.get(getClass().getResource("/org/oran/datafile/datastore/file.txt").toURI());
266
267         Mono<String> result = s3ObjectStore.copyFileTo(testFile, "test-key");
268
269         StepVerifier.create(result)
270             .expectNext("test-key")
271             .verifyComplete();
272     }
273
274     @Test
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)
282                 );
283                 CompletableFuture<ResponseBytes<GetObjectResponse>> future = CompletableFuture.completedFuture(
284                     responseBytes
285                 );
286                 return future;
287             });
288
289         // Call the method under test
290         Mono<byte[]> result = s3ObjectStore.readObject(DataStore.Bucket.FILES, "test-key");
291
292         byte[] expectedBytes = "Hello, World!".getBytes(StandardCharsets.UTF_8);
293         StepVerifier.create(result)
294             .consumeNextWith(actualBytes -> Assertions.assertArrayEquals(expectedBytes, actualBytes))
295             .verifyComplete();
296     }
297
298     @Test
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()
305                 );
306                 return future;
307             });
308
309         // Call the method under test
310         Mono<String> result = s3ObjectStore.putObject(DataStore.Bucket.FILES, "test-key", "Hello, World!");
311
312         // Verify the Mono's behavior using StepVerifier
313         StepVerifier.create(result)
314             .expectNext("test-key")
315             .verifyComplete();
316     }
317
318     private S3Object createS3Object(String key) {
319         return S3Object.builder().key(key).build();
320     }
321 }