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 org.hamcrest.MatcherAssert;
12  import org.hamcrest.Matchers;
13  import org.junit.jupiter.api.Assertions;
14  import org.junit.jupiter.api.Test;
15  import org.mockito.Mockito;
16  import software.amazon.awssdk.core.exception.SdkClientException;
17  import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
18  import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
19  import software.amazon.awssdk.services.dynamodb.model.ConsumedCapacity;
20  import software.amazon.awssdk.services.dynamodb.model.QueryRequest;
21  import software.amazon.awssdk.services.dynamodb.model.QueryResponse;
22  
23  /**
24   * Test case for {@link QueryValve}.
25   * @since 0.1
26   */
27  @SuppressWarnings("PMD.TooManyMethods")
28  final class QueryValveTest {
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              QueryResponse.builder()
38                  .items(
39                      Collections.singletonList(Collections.emptyMap())
40                  )
41                  .consumedCapacity(
42                      ConsumedCapacity.builder().capacityUnits(1.0d).build()
43                  )
44                  .build()
45          ).when(aws).query(Mockito.any(QueryRequest.class));
46          MatcherAssert.assertThat(
47              "should be false",
48              new QueryValve().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              QueryResponse.builder()
65                  .items(
66                      Collections.singletonList(item)
67                  )
68                  .consumedCapacity(
69                      ConsumedCapacity.builder().capacityUnits(1.0d).build()
70                  )
71                  .build()
72          ).when(aws).query(Mockito.any(QueryRequest.class));
73          MatcherAssert.assertThat(
74              "should has item",
75              new QueryValve().fetch(
76                  credentials, "table",
77                  new Conditions(), new ArrayList<>(0)
78              ).items(),
79              Matchers.hasItem(item)
80          );
81      }
82  
83      @Test
84      void countsItemsViaQuery() throws Exception {
85          final Credentials credentials = Mockito.mock(Credentials.class);
86          final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
87          Mockito.doReturn(aws).when(credentials).aws();
88          final int expected = 7;
89          Mockito.doReturn(
90              QueryResponse.builder()
91                  .count(expected)
92                  .consumedCapacity(
93                      ConsumedCapacity.builder().capacityUnits(1.0d).build()
94                  )
95                  .build()
96          ).when(aws).query(Mockito.any(QueryRequest.class));
97          MatcherAssert.assertThat(
98              "should not return wrong count",
99              new QueryValve().count(
100                 credentials, "c\u00f6unt-tbl", new Conditions()
101             ),
102             Matchers.equalTo(expected)
103         );
104     }
105 
106     @Test
107     void wrapsExceptionOnFetch() {
108         Assertions.assertThrows(
109             IOException.class,
110             () -> {
111                 final Credentials creds =
112                     Mockito.mock(Credentials.class);
113                 final DynamoDbClient aws =
114                     Mockito.mock(DynamoDbClient.class);
115                 Mockito.doReturn(aws).when(creds).aws();
116                 Mockito.doThrow(SdkClientException.create("f\u00e4iled"))
117                     .when(aws).query(Mockito.any(QueryRequest.class));
118                 new QueryValve().fetch(
119                     creds, "f\u00e4il-tbl",
120                     new Conditions(), new ArrayList<>(0)
121                 );
122             }
123         );
124     }
125 
126     @Test
127     void wrapsExceptionOnCount() {
128         Assertions.assertThrows(
129             IOException.class,
130             () -> {
131                 final Credentials creds =
132                     Mockito.mock(Credentials.class);
133                 final DynamoDbClient aws =
134                     Mockito.mock(DynamoDbClient.class);
135                 Mockito.doReturn(aws).when(creds).aws();
136                 Mockito.doThrow(SdkClientException.create("f\u00e4iled"))
137                     .when(aws).query(Mockito.any(QueryRequest.class));
138                 new QueryValve().count(
139                     creds, "c\u00f6unt-err", new Conditions()
140                 );
141             }
142         );
143     }
144 
145     @Test
146     @SuppressWarnings("unchecked")
147     void reportsNextWhenMorePages() throws Exception {
148         final Credentials credentials = Mockito.mock(Credentials.class);
149         final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
150         Mockito.doReturn(aws).when(credentials).aws();
151         Mockito.doReturn(
152             QueryResponse.builder()
153                 .items(
154                     Collections.singletonList(
155                         Collections.singletonMap(
156                             "h\u00e4sh",
157                             AttributeValue.builder()
158                                 .s("v\u00e4l").build()
159                         )
160                     )
161                 )
162                 .lastEvaluatedKey(
163                     Collections.singletonMap(
164                         "h\u00e4sh",
165                         AttributeValue.builder()
166                             .s("l\u00e4st").build()
167                     )
168                 )
169                 .consumedCapacity(
170                     ConsumedCapacity.builder().capacityUnits(1.0d).build()
171                 )
172                 .build()
173         ).when(aws).query(Mockito.any(QueryRequest.class));
174         MatcherAssert.assertThat(
175             "should not report no next when there is next",
176             new QueryValve().fetch(
177                 credentials, "p\u00e4ge-tbl",
178                 new Conditions(), new ArrayList<>(0)
179             ).hasNext(),
180             Matchers.is(true)
181         );
182     }
183 
184     @Test
185     @SuppressWarnings("unchecked")
186     void fetchesNextPage() throws Exception {
187         final Credentials credentials = Mockito.mock(Credentials.class);
188         final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
189         Mockito.doReturn(aws).when(credentials).aws();
190         final Map<String, AttributeValue> item = Collections.singletonMap(
191             "s\u00f6rt",
192             AttributeValue.builder().s("v\u00e4l2").build()
193         );
194         Mockito.doReturn(
195             QueryResponse.builder()
196                 .items(
197                     Collections.singletonList(
198                         Collections.singletonMap(
199                             "s\u00f6rt",
200                             AttributeValue.builder()
201                                 .s("v\u00e4l1").build()
202                         )
203                     )
204                 )
205                 .lastEvaluatedKey(
206                     Collections.singletonMap(
207                         "s\u00f6rt",
208                         AttributeValue.builder()
209                             .s("l\u00e4st").build()
210                     )
211                 )
212                 .consumedCapacity(
213                     ConsumedCapacity.builder().capacityUnits(1.0d).build()
214                 )
215                 .build()
216         ).doReturn(
217             QueryResponse.builder()
218                 .items(Collections.singletonList(item))
219                 .consumedCapacity(
220                     ConsumedCapacity.builder().capacityUnits(1.0d).build()
221                 )
222                 .build()
223         ).when(aws).query(Mockito.any(QueryRequest.class));
224         MatcherAssert.assertThat(
225             "should not return wrong items on next page",
226             new QueryValve().fetch(
227                 credentials, "n\u00e9xt-tbl",
228                 new Conditions(), new ArrayList<>(0)
229             ).next().items(),
230             Matchers.hasItem(item)
231         );
232     }
233 
234     @Test
235     @SuppressWarnings("unchecked")
236     void throwsOnNextWithoutMorePages() {
237         Assertions.assertThrows(
238             IllegalStateException.class,
239             () -> {
240                 final Credentials creds =
241                     Mockito.mock(Credentials.class);
242                 final DynamoDbClient aws =
243                     Mockito.mock(DynamoDbClient.class);
244                 Mockito.doReturn(aws).when(creds).aws();
245                 Mockito.doReturn(
246                     QueryResponse.builder()
247                         .items(
248                             Collections.singletonList(
249                                 Collections.emptyMap()
250                             )
251                         )
252                         .consumedCapacity(
253                             ConsumedCapacity.builder()
254                                 .capacityUnits(1.0d).build()
255                         )
256                         .build()
257                 ).when(aws).query(Mockito.any(QueryRequest.class));
258                 new QueryValve().fetch(
259                     creds, "n\u00f6-next-tbl",
260                     new Conditions(), new ArrayList<>(0)
261                 ).next();
262             }
263         );
264     }
265 
266     @Test
267     @SuppressWarnings("unchecked")
268     void fetchesWithIndexName() throws Exception {
269         final Credentials credentials = Mockito.mock(Credentials.class);
270         final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
271         Mockito.doReturn(aws).when(credentials).aws();
272         Mockito.doReturn(
273             QueryResponse.builder()
274                 .items(
275                     Collections.singletonList(Collections.emptyMap())
276                 )
277                 .consumedCapacity(
278                     ConsumedCapacity.builder().capacityUnits(1.0d).build()
279                 )
280                 .build()
281         ).when(aws).query(Mockito.any(QueryRequest.class));
282         MatcherAssert.assertThat(
283             "should not fail fetching with index name",
284             new QueryValve()
285                 .withIndexName("\u00efndex")
286                 .fetch(
287                     credentials, "t\u00e4ble",
288                     new Conditions(), new ArrayList<>(0)
289                 ).items(),
290             Matchers.hasSize(1)
291         );
292     }
293 
294     @Test
295     @SuppressWarnings("unchecked")
296     void fetchesWithConsistentRead() throws Exception {
297         final Credentials creds = Mockito.mock(Credentials.class);
298         final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
299         Mockito.doReturn(aws).when(creds).aws();
300         Mockito.doReturn(
301             QueryResponse.builder()
302                 .items(
303                     Collections.singletonList(Collections.emptyMap())
304                 )
305                 .consumedCapacity(
306                     ConsumedCapacity.builder()
307                         .capacityUnits(1.0d).build()
308                 )
309                 .build()
310         ).when(aws).query(Mockito.any(QueryRequest.class));
311         MatcherAssert.assertThat(
312             "did not fetch with consistent read disabled",
313             new QueryValve()
314                 .withConsistentRead(false)
315                 .fetch(
316                     creds, "c\u00f6nsist-tbl",
317                     new Conditions(), new ArrayList<>(0)
318                 ).items(),
319             Matchers.hasSize(1)
320         );
321     }
322 
323     @Test
324     @SuppressWarnings("unchecked")
325     void fetchesWithCustomLimit() throws Exception {
326         final Credentials creds = Mockito.mock(Credentials.class);
327         final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
328         Mockito.doReturn(aws).when(creds).aws();
329         Mockito.doReturn(
330             QueryResponse.builder()
331                 .items(
332                     Collections.singletonList(Collections.emptyMap())
333                 )
334                 .consumedCapacity(
335                     ConsumedCapacity.builder()
336                         .capacityUnits(1.0d).build()
337                 )
338                 .build()
339         ).when(aws).query(Mockito.any(QueryRequest.class));
340         MatcherAssert.assertThat(
341             "did not fetch with custom limit",
342             new QueryValve()
343                 .withLimit(5)
344                 .fetch(
345                     creds, "l\u00efmit-tbl",
346                     new Conditions(), new ArrayList<>(0)
347                 ).items(),
348             Matchers.hasSize(1)
349         );
350     }
351 
352     @Test
353     @SuppressWarnings("unchecked")
354     void fetchesWithAttributeToGet() throws Exception {
355         final Credentials creds = Mockito.mock(Credentials.class);
356         final DynamoDbClient aws = Mockito.mock(DynamoDbClient.class);
357         Mockito.doReturn(aws).when(creds).aws();
358         Mockito.doReturn(
359             QueryResponse.builder()
360                 .items(
361                     Collections.singletonList(Collections.emptyMap())
362                 )
363                 .consumedCapacity(
364                     ConsumedCapacity.builder()
365                         .capacityUnits(1.0d).build()
366                 )
367                 .build()
368         ).when(aws).query(Mockito.any(QueryRequest.class));
369         MatcherAssert.assertThat(
370             "did not fetch with attribute to get",
371             new QueryValve()
372                 .withAttributeToGet("\u00e4ttr")
373                 .fetch(
374                     creds, "\u00e4ttr-tbl",
375                     new Conditions(), new ArrayList<>(0)
376                 ).items(),
377             Matchers.hasSize(1)
378         );
379     }
380 
381 }