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.enhanced.dynamodb.internal.client; 17 18 import static org.hamcrest.MatcherAssert.assertThat; 19 import static org.hamcrest.Matchers.containsInAnyOrder; 20 import static org.hamcrest.Matchers.is; 21 import static org.hamcrest.Matchers.sameInstance; 22 import static org.mockito.Mockito.verify; 23 import static software.amazon.awssdk.enhanced.dynamodb.internal.AttributeValues.stringValue; 24 25 import java.util.Iterator; 26 import java.util.List; 27 import java.util.Optional; 28 import java.util.stream.Collectors; 29 import org.junit.Test; 30 import org.junit.runner.RunWith; 31 import org.mockito.ArgumentCaptor; 32 import org.mockito.Mock; 33 import org.mockito.Mockito; 34 import org.mockito.junit.MockitoJUnitRunner; 35 import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClientExtension; 36 import software.amazon.awssdk.enhanced.dynamodb.Key; 37 import software.amazon.awssdk.enhanced.dynamodb.TableSchema; 38 import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.FakeItem; 39 import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.FakeItemWithIndices; 40 import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.FakeItemWithSort; 41 import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.SecondaryIndexBean; 42 import software.amazon.awssdk.enhanced.dynamodb.mapper.testbeans.SecondaryIndexMatchingTableKeyBean; 43 import software.amazon.awssdk.enhanced.dynamodb.model.CreateTableEnhancedRequest; 44 import software.amazon.awssdk.enhanced.dynamodb.model.EnhancedGlobalSecondaryIndex; 45 import software.amazon.awssdk.enhanced.dynamodb.model.EnhancedLocalSecondaryIndex; 46 import software.amazon.awssdk.services.dynamodb.DynamoDbClient; 47 48 @RunWith(MockitoJUnitRunner.class) 49 public class DefaultDynamoDbTableTest { 50 private static final String TABLE_NAME = "table-name"; 51 52 @Mock 53 private DynamoDbClient mockDynamoDbClient; 54 55 @Mock 56 private DynamoDbEnhancedClientExtension mockDynamoDbEnhancedClientExtension; 57 58 @Test index_constructsCorrectMappedIndex()59 public void index_constructsCorrectMappedIndex() { 60 DefaultDynamoDbTable<FakeItemWithIndices> dynamoDbMappedTable = 61 new DefaultDynamoDbTable<>(mockDynamoDbClient, 62 mockDynamoDbEnhancedClientExtension, 63 FakeItemWithIndices.getTableSchema(), 64 TABLE_NAME); 65 66 DefaultDynamoDbIndex<FakeItemWithIndices> dynamoDbMappedIndex = dynamoDbMappedTable.index("gsi_1"); 67 68 assertThat(dynamoDbMappedIndex.dynamoDbClient(), is(sameInstance(mockDynamoDbClient))); 69 assertThat(dynamoDbMappedIndex.mapperExtension(), is(sameInstance(mockDynamoDbEnhancedClientExtension))); 70 assertThat(dynamoDbMappedIndex.tableSchema(), is(sameInstance(FakeItemWithIndices.getTableSchema()))); 71 assertThat(dynamoDbMappedIndex.indexName(), is("gsi_1")); 72 } 73 74 @Test(expected = IllegalArgumentException.class) index_invalidIndex_throwsIllegalArgumentException()75 public void index_invalidIndex_throwsIllegalArgumentException() { 76 DefaultDynamoDbTable<FakeItemWithIndices> dynamoDbMappedTable = 77 new DefaultDynamoDbTable<>(mockDynamoDbClient, 78 mockDynamoDbEnhancedClientExtension, 79 FakeItemWithIndices.getTableSchema(), 80 TABLE_NAME); 81 82 dynamoDbMappedTable.index("invalid"); 83 } 84 85 @Test keyFrom_primaryIndex_partitionAndSort()86 public void keyFrom_primaryIndex_partitionAndSort() { 87 FakeItemWithSort item = FakeItemWithSort.createUniqueFakeItemWithSort(); 88 DefaultDynamoDbTable<FakeItemWithSort> dynamoDbMappedIndex = 89 new DefaultDynamoDbTable<>(mockDynamoDbClient, 90 mockDynamoDbEnhancedClientExtension, 91 FakeItemWithSort.getTableSchema(), 92 "test_table"); 93 94 Key key = dynamoDbMappedIndex.keyFrom(item); 95 96 assertThat(key.partitionKeyValue(), is(stringValue(item.getId()))); 97 assertThat(key.sortKeyValue(), is(Optional.of(stringValue(item.getSort())))); 98 } 99 100 @Test keyFrom_primaryIndex_partitionOnly()101 public void keyFrom_primaryIndex_partitionOnly() { 102 FakeItem item = FakeItem.createUniqueFakeItem(); 103 DefaultDynamoDbTable<FakeItem> dynamoDbMappedIndex = 104 new DefaultDynamoDbTable<>(mockDynamoDbClient, 105 mockDynamoDbEnhancedClientExtension, 106 FakeItem.getTableSchema(), 107 "test_table"); 108 109 Key key = dynamoDbMappedIndex.keyFrom(item); 110 111 assertThat(key.partitionKeyValue(), is(stringValue(item.getId()))); 112 assertThat(key.sortKeyValue(), is(Optional.empty())); 113 } 114 115 @Test keyFrom_primaryIndex_partitionAndNullSort()116 public void keyFrom_primaryIndex_partitionAndNullSort() { 117 FakeItemWithSort item = FakeItemWithSort.createUniqueFakeItemWithoutSort(); 118 DefaultDynamoDbTable<FakeItemWithSort> dynamoDbMappedIndex = 119 new DefaultDynamoDbTable<>(mockDynamoDbClient, 120 mockDynamoDbEnhancedClientExtension, 121 FakeItemWithSort.getTableSchema(), 122 "test_table"); 123 124 Key key = dynamoDbMappedIndex.keyFrom(item); 125 126 assertThat(key.partitionKeyValue(), is(stringValue(item.getId()))); 127 assertThat(key.sortKeyValue(), is(Optional.empty())); 128 } 129 130 @Test createTable_doesNotTreatPrimaryIndexAsAnyOfSecondaryIndexes()131 public void createTable_doesNotTreatPrimaryIndexAsAnyOfSecondaryIndexes() { 132 DefaultDynamoDbTable<FakeItem> dynamoDbMappedIndex = 133 Mockito.spy(new DefaultDynamoDbTable<>(mockDynamoDbClient, 134 mockDynamoDbEnhancedClientExtension, 135 FakeItem.getTableSchema(), 136 "test_table")); 137 138 dynamoDbMappedIndex.createTable(); 139 140 CreateTableEnhancedRequest request = captureCreateTableRequest(dynamoDbMappedIndex); 141 142 assertThat(request.localSecondaryIndices().size(), is(0)); 143 assertThat(request.globalSecondaryIndices().size(), is(0)); 144 } 145 146 @Test createTable_groupsSecondaryIndexesExistingInTableSchema()147 public void createTable_groupsSecondaryIndexesExistingInTableSchema() { 148 DefaultDynamoDbTable<FakeItemWithIndices> dynamoDbMappedIndex = 149 Mockito.spy(new DefaultDynamoDbTable<>(mockDynamoDbClient, 150 mockDynamoDbEnhancedClientExtension, 151 FakeItemWithIndices.getTableSchema(), 152 "test_table")); 153 154 dynamoDbMappedIndex.createTable(); 155 156 CreateTableEnhancedRequest request = captureCreateTableRequest(dynamoDbMappedIndex); 157 158 assertThat(request.localSecondaryIndices().size(), is(1)); 159 Iterator<EnhancedLocalSecondaryIndex> lsiIterator = request.localSecondaryIndices().iterator(); 160 assertThat(lsiIterator.next().indexName(), is("lsi_1")); 161 162 assertThat(request.globalSecondaryIndices().size(), is(2)); 163 List<String> globalIndicesNames = request.globalSecondaryIndices().stream() 164 .map(EnhancedGlobalSecondaryIndex::indexName) 165 .collect(Collectors.toList()); 166 assertThat(globalIndicesNames, containsInAnyOrder("gsi_1", "gsi_2")); 167 } 168 169 @Test createTable_groupsSecondaryIndexesExistingInTableSchema_fromBeanTableSchema()170 public void createTable_groupsSecondaryIndexesExistingInTableSchema_fromBeanTableSchema() { 171 DefaultDynamoDbTable<SecondaryIndexBean> dynamoDbMappedIndex = 172 Mockito.spy(new DefaultDynamoDbTable<>(mockDynamoDbClient, 173 mockDynamoDbEnhancedClientExtension, 174 TableSchema.fromBean(SecondaryIndexBean.class), 175 "test_table")); 176 177 dynamoDbMappedIndex.createTable(); 178 179 CreateTableEnhancedRequest request = captureCreateTableRequest(dynamoDbMappedIndex); 180 181 assertThat(request.localSecondaryIndices().size(), is(1)); 182 assertThat(request.localSecondaryIndices().iterator().next().indexName(), is("lsi")); 183 184 assertThat(request.globalSecondaryIndices().size(), is(1)); 185 assertThat(request.globalSecondaryIndices().iterator().next().indexName(), is("gsi")); 186 } 187 188 @Test createTable_allowsGsiWithSamePartitionKeyAsDefaultPartitionKey_fromBeanTableSchema()189 public void createTable_allowsGsiWithSamePartitionKeyAsDefaultPartitionKey_fromBeanTableSchema() { 190 DefaultDynamoDbTable<SecondaryIndexMatchingTableKeyBean> dynamoDbMappedIndex = 191 Mockito.spy(new DefaultDynamoDbTable<>(mockDynamoDbClient, 192 mockDynamoDbEnhancedClientExtension, 193 TableSchema.fromBean(SecondaryIndexMatchingTableKeyBean.class), 194 "test_table")); 195 196 dynamoDbMappedIndex.createTable(); 197 198 CreateTableEnhancedRequest request = captureCreateTableRequest(dynamoDbMappedIndex); 199 200 assertThat(request.localSecondaryIndices().size(), is(0)); 201 assertThat(request.globalSecondaryIndices().size(), is(1)); 202 assertThat(request.globalSecondaryIndices().iterator().next().indexName(), is("gsi")); 203 } 204 captureCreateTableRequest(DefaultDynamoDbTable<T> index)205 private static <T> CreateTableEnhancedRequest captureCreateTableRequest(DefaultDynamoDbTable<T> index) { 206 ArgumentCaptor<CreateTableEnhancedRequest> createTableOperationCaptor = 207 ArgumentCaptor.forClass(CreateTableEnhancedRequest.class); 208 verify(index).createTable(createTableOperationCaptor.capture()); 209 return createTableOperationCaptor.getValue(); 210 } 211 } 212