View Javadoc
1   /*
2    * SPDX-FileCopyrightText: Copyright (c) 2012-2026 Yegor Bugayenko
3    * SPDX-License-Identifier: MIT
4    */
5   package com.jcabi.dynamo;
6   
7   import java.io.IOException;
8   import java.util.ArrayList;
9   import java.util.Collections;
10  import java.util.Map;
11  import java.util.Random;
12  import org.hamcrest.MatcherAssert;
13  import org.hamcrest.Matchers;
14  import org.junit.jupiter.api.Assertions;
15  import org.junit.jupiter.api.Test;
16  import org.mockito.Mockito;
17  import software.amazon.awssdk.core.exception.SdkClientException;
18  import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
19  import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
20  import software.amazon.awssdk.services.dynamodb.model.ConsumedCapacity;
21  import software.amazon.awssdk.services.dynamodb.model.ScanRequest;
22  import software.amazon.awssdk.services.dynamodb.model.ScanResponse;
23  
24  /**
25   * Test case for {@link ScanValve}.
26   * @since 0.1
27   */
28  final class ScanValveTest {
29  
30      @Test
31      @SuppressWarnings("unchecked")
32      void fetchesDataWithNoNext() throws Exception {
33          final Credentials credentials = Mockito.mock(Credentials.class);
34          final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
35          Mockito.doReturn(aws).when(credentials).aws();
36          Mockito.doReturn(
37              ScanResponse.builder()
38                  .items(
39                      Collections.singletonList(Collections.emptyMap())
40                  )
41                  .consumedCapacity(
42                      ConsumedCapacity.builder().capacityUnits(1d).build()
43                  )
44                  .build()
45          ).when(aws).scan(Mockito.any(ScanRequest.class));
46          MatcherAssert.assertThat(
47              "should not has next",
48              new ScanValve().fetch(
49                  credentials, "table",
50                  new Conditions(), new ArrayList<>(0)
51              ).hasNext(),
52              Matchers.is(false)
53          );
54      }
55  
56      @Test
57      @SuppressWarnings("unchecked")
58      void fetchesDataWithItems() throws Exception {
59          final Credentials credentials = Mockito.mock(Credentials.class);
60          final Map<String, AttributeValue> item = Collections.emptyMap();
61          final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
62          Mockito.doReturn(aws).when(credentials).aws();
63          Mockito.doReturn(
64              ScanResponse.builder()
65                  .items(
66                      Collections.singletonList(item)
67                  )
68                  .consumedCapacity(
69                      ConsumedCapacity.builder().capacityUnits(1d).build()
70                  )
71                  .build()
72          ).when(aws).scan(Mockito.any(ScanRequest.class));
73          MatcherAssert.assertThat(
74              "should has items",
75              new ScanValve().fetch(
76                  credentials, "table",
77                  new Conditions(), new ArrayList<>(0)
78              ).items(),
79              Matchers.hasItem(item)
80          );
81      }
82  
83      @Test
84      void countsItemsViaScan() {
85          final Credentials creds = Mockito.mock(Credentials.class);
86          final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
87          Mockito.doReturn(aws).when(creds).aws();
88          final int expected = new Random().nextInt(100) + 1;
89          Mockito.doReturn(
90              ScanResponse.builder()
91                  .count(expected)
92                  .consumedCapacity(
93                      ConsumedCapacity.builder()
94                          .capacityUnits(1d).build()
95                  )
96                  .build()
97          ).when(aws).scan(Mockito.any(ScanRequest.class));
98          MatcherAssert.assertThat(
99              "did not return correct count from scan",
100             new ScanValve().count(
101                 creds, "c\u00f6unt-tbl", new Conditions()
102             ),
103             Matchers.equalTo(expected)
104         );
105     }
106 
107     @Test
108     void wrapsExceptionOnFetch() {
109         Assertions.assertThrows(
110             IOException.class,
111             () -> {
112                 final Credentials creds =
113                     Mockito.mock(Credentials.class);
114                 final DynamoDbClient aws =
115                     Mockito.mock(DynamoDbClient.class);
116                 Mockito.doReturn(aws).when(creds).aws();
117                 Mockito.doThrow(
118                     SdkClientException.create("f\u00e4iled")
119                 ).when(aws).scan(Mockito.any(ScanRequest.class));
120                 new ScanValve().fetch(
121                     creds, "f\u00e4il-tbl",
122                     new Conditions(), new ArrayList<>(0)
123                 );
124             }
125         );
126     }
127 
128     @Test
129     @SuppressWarnings("unchecked")
130     void reportsNextWhenMorePages() throws Exception {
131         final Credentials creds = Mockito.mock(Credentials.class);
132         final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
133         Mockito.doReturn(aws).when(creds).aws();
134         Mockito.doReturn(
135             ScanResponse.builder()
136                 .items(
137                     Collections.singletonList(
138                         Collections.singletonMap(
139                             "h\u00e4sh",
140                             AttributeValue.builder()
141                                 .s("v\u00e4l").build()
142                         )
143                     )
144                 )
145                 .lastEvaluatedKey(
146                     Collections.singletonMap(
147                         "h\u00e4sh",
148                         AttributeValue.builder()
149                             .s("l\u00e4st").build()
150                     )
151                 )
152                 .consumedCapacity(
153                     ConsumedCapacity.builder()
154                         .capacityUnits(1d).build()
155                 )
156                 .build()
157         ).when(aws).scan(Mockito.any(ScanRequest.class));
158         MatcherAssert.assertThat(
159             "did not report next when more pages exist",
160             new ScanValve().fetch(
161                 creds, "p\u00e4ge-tbl",
162                 new Conditions(), new ArrayList<>(0)
163             ).hasNext(),
164             Matchers.is(true)
165         );
166     }
167 
168     @Test
169     @SuppressWarnings("unchecked")
170     void fetchesNextPage() throws Exception {
171         final Credentials creds = Mockito.mock(Credentials.class);
172         final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
173         Mockito.doReturn(aws).when(creds).aws();
174         final Map<String, AttributeValue> item =
175             Collections.singletonMap(
176                 "s\u00f6rt",
177                 AttributeValue.builder().s("v\u00e4l2").build()
178             );
179         Mockito.doReturn(
180             ScanResponse.builder()
181                 .items(
182                     Collections.singletonList(
183                         Collections.singletonMap(
184                             "s\u00f6rt",
185                             AttributeValue.builder()
186                                 .s("v\u00e4l1").build()
187                         )
188                     )
189                 )
190                 .lastEvaluatedKey(
191                     Collections.singletonMap(
192                         "s\u00f6rt",
193                         AttributeValue.builder()
194                             .s("l\u00e4st").build()
195                     )
196                 )
197                 .consumedCapacity(
198                     ConsumedCapacity.builder()
199                         .capacityUnits(1d).build()
200                 )
201                 .build()
202         ).doReturn(
203             ScanResponse.builder()
204                 .items(Collections.singletonList(item))
205                 .consumedCapacity(
206                     ConsumedCapacity.builder()
207                         .capacityUnits(1d).build()
208                 )
209                 .build()
210         ).when(aws).scan(Mockito.any(ScanRequest.class));
211         MatcherAssert.assertThat(
212             "did not return correct items on next page",
213             new ScanValve().fetch(
214                 creds, "n\u00e9xt-tbl",
215                 new Conditions(), new ArrayList<>(0)
216             ).next().items(),
217             Matchers.hasItem(item)
218         );
219     }
220 
221     @Test
222     @SuppressWarnings("unchecked")
223     void throwsOnNextWithoutMorePages() {
224         Assertions.assertThrows(
225             IllegalStateException.class,
226             () -> {
227                 final Credentials creds =
228                     Mockito.mock(Credentials.class);
229                 final DynamoDbClient aws =
230                     Mockito.mock(DynamoDbClient.class);
231                 Mockito.doReturn(aws).when(creds).aws();
232                 Mockito.doReturn(
233                     ScanResponse.builder()
234                         .items(
235                             Collections.singletonList(
236                                 Collections.emptyMap()
237                             )
238                         )
239                         .consumedCapacity(
240                             ConsumedCapacity.builder()
241                                 .capacityUnits(1d).build()
242                         )
243                         .build()
244                 ).when(aws).scan(Mockito.any(ScanRequest.class));
245                 new ScanValve().fetch(
246                     creds, "n\u00f6-next-tbl",
247                     new Conditions(), new ArrayList<>(0)
248                 ).next();
249             }
250         );
251     }
252 
253     @Test
254     @SuppressWarnings("unchecked")
255     void fetchesWithCustomLimit() throws Exception {
256         final Credentials creds = Mockito.mock(Credentials.class);
257         final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
258         Mockito.doReturn(aws).when(creds).aws();
259         Mockito.doReturn(
260             ScanResponse.builder()
261                 .items(
262                     Collections.singletonList(Collections.emptyMap())
263                 )
264                 .consumedCapacity(
265                     ConsumedCapacity.builder()
266                         .capacityUnits(1d).build()
267                 )
268                 .build()
269         ).when(aws).scan(Mockito.any(ScanRequest.class));
270         MatcherAssert.assertThat(
271             "did not fetch with custom limit",
272             new ScanValve()
273                 .withLimit(5)
274                 .fetch(
275                     creds, "l\u00efmit-tbl",
276                     new Conditions(), new ArrayList<>(0)
277                 ).items(),
278             Matchers.hasSize(1)
279         );
280     }
281 
282     @Test
283     @SuppressWarnings("unchecked")
284     void fetchesWithAttributeToGet() throws Exception {
285         final Credentials creds = Mockito.mock(Credentials.class);
286         final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
287         Mockito.doReturn(aws).when(creds).aws();
288         Mockito.doReturn(
289             ScanResponse.builder()
290                 .items(
291                     Collections.singletonList(Collections.emptyMap())
292                 )
293                 .consumedCapacity(
294                     ConsumedCapacity.builder()
295                         .capacityUnits(1d).build()
296                 )
297                 .build()
298         ).when(aws).scan(Mockito.any(ScanRequest.class));
299         MatcherAssert.assertThat(
300             "did not fetch with attribute to get",
301             new ScanValve()
302                 .withAttributeToGet("\u00e4ttr")
303                 .fetch(
304                     creds, "\u00e4ttr-tbl",
305                     new Conditions(), new ArrayList<>(0)
306                 ).items(),
307             Matchers.hasSize(1)
308         );
309     }
310 
311 }