Skip to content

Commit ee5fb8a

Browse files
SalusaSeconduspraus
authored andcommitted
New tests to cover more data types and optimistic locking. Also increments SDK dependency.
1 parent 1ecc6dd commit ee5fb8a

File tree

4 files changed

+135
-5
lines changed

4 files changed

+135
-5
lines changed

pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
<dependency>
4444
<groupId>com.amazonaws</groupId>
4545
<artifactId>aws-java-sdk-bom</artifactId>
46-
<version>1.11.380</version>
46+
<version>1.11.434</version>
4747
<type>pom</type>
4848
<scope>import</scope>
4949
</dependency>

src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/TransformerHolisticTests.java

+74
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
package com.amazonaws.services.dynamodbv2.datamodeling;
1616

1717
import static org.junit.Assert.assertEquals;
18+
import static org.junit.Assert.assertFalse;
1819
import static org.junit.Assert.assertNull;
20+
import static org.junit.Assert.assertTrue;
21+
import static org.junit.Assert.fail;
1922

2023
import java.nio.ByteBuffer;
2124
import java.security.GeneralSecurityException;
@@ -34,6 +37,8 @@
3437
import javax.crypto.SecretKey;
3538
import javax.crypto.spec.SecretKeySpec;
3639

40+
import com.amazonaws.ClientConfiguration;
41+
import com.amazonaws.services.dynamodbv2.model.ConditionalCheckFailedException;
3742
import org.junit.Before;
3843
import org.junit.Test;
3944

@@ -323,6 +328,75 @@ public void simpleSaveLoad() {
323328
assertNull(mapper.load(Mixed.class, 0, 15));
324329
}
325330

331+
/**
332+
* This test ensures that optimistic locking can be successfully done through the {@link DynamoDBMapper} when
333+
* combined with the @{link AttributeEncryptor}. Specifically it checks that {@link SaveBehavior#PUT} properly
334+
* enforces versioning and will result in a {@link ConditionalCheckFailedException} when optimistic locking should
335+
* prevent a write. Finally, it checks that {@link SaveBehavior#CLOBBER} properly ignores optimistic locking and
336+
* overwrites the old value.
337+
*/
338+
@Test
339+
public void optimisticLockingTest() {
340+
DynamoDBMapper mapper = new DynamoDBMapper(client,
341+
DynamoDBMapperConfig.builder()
342+
.withSaveBehavior(SaveBehavior.PUT).build(),
343+
new AttributeEncryptor(symProv));
344+
DynamoDBMapper clobberMapper = new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor(symProv));
345+
346+
/*
347+
* Lineage of objects
348+
* expected -> v1 -> v2 -> v3
349+
* |
350+
* -> v2_2 -> clobbered
351+
* Splitting the lineage after v1 is what should
352+
* cause the ConditionalCheckFailedException.
353+
*/
354+
final int hashKey = 0;
355+
final int rangeKey = 15;
356+
final Mixed expected = new Mixed();
357+
expected.setHashKey(hashKey);
358+
expected.setRangeKey(rangeKey);
359+
expected.setIntSet(new HashSet<Integer>());
360+
expected.getIntSet().add(3);
361+
expected.getIntSet().add(5);
362+
expected.getIntSet().add(7);
363+
expected.setDoubleValue(15);
364+
expected.setStringValue("Blargh!");
365+
expected.setDoubleSet(
366+
new HashSet<Double>(Arrays.asList(15.0D, 7.6D, -3D, -34.2D, 0.0D)));
367+
368+
mapper.save(expected);
369+
Mixed v1 = mapper.load(Mixed.class, hashKey, rangeKey);
370+
assertEquals(expected, v1);
371+
v1.setStringValue("New value");
372+
mapper.save(v1);
373+
Mixed v2 = mapper.load(Mixed.class, hashKey, rangeKey);
374+
assertEquals(v1, v2);
375+
Mixed v2_2 = mapper.load(Mixed.class, hashKey, rangeKey);
376+
377+
v2.getIntSet().add(-37);
378+
mapper.save(v2);
379+
Mixed v3 = mapper.load(Mixed.class, hashKey, rangeKey);
380+
assertEquals(v2, v3);
381+
assertTrue(v3.getIntSet().contains(-37));
382+
383+
// This should fail due to optimistic locking
384+
v2_2.getIntSet().add(38);
385+
try {
386+
mapper.save(v2_2);
387+
fail("Expected ConditionalCheckFailedException");
388+
} catch (ConditionalCheckFailedException ex) {
389+
// Expected exception
390+
}
391+
392+
// Force the update with clobber
393+
clobberMapper.save(v2_2);
394+
Mixed clobbered = mapper.load(Mixed.class, hashKey, rangeKey);
395+
assertEquals(v2_2, clobbered);
396+
assertTrue(clobbered.getIntSet().contains(38));
397+
assertFalse(clobbered.getIntSet().contains(-37));
398+
}
399+
326400
@Test
327401
public void leadingAndTrailingZeros() {
328402
DynamoDBMapper mapper = new DynamoDBMapper(client, CLOBBER_CONFIG, new AttributeEncryptor(symProv));

src/test/java/com/amazonaws/services/dynamodbv2/datamodeling/encryption/DynamoDBEncryptorTest.java

+29
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.security.NoSuchProviderException;
3232
import java.security.Security;
3333
import java.security.SignatureException;
34+
import java.util.Arrays;
3435
import java.util.Collection;
3536
import java.util.Collections;
3637
import java.util.HashMap;
@@ -96,6 +97,34 @@ public void setUp() throws Exception {
9697
attribs.put("rangeKey", new AttributeValue().withN("7"));
9798
attribs.put("version", new AttributeValue().withN("0"));
9899

100+
// New(er) data types
101+
attribs.put("booleanTrue", new AttributeValue().withBOOL(true));
102+
attribs.put("booleanFalse", new AttributeValue().withBOOL(false));
103+
attribs.put("nullValue", new AttributeValue().withNULL(true));
104+
Map<String, AttributeValue> tmpMap = new HashMap<>(attribs);
105+
attribs.put("listValue", new AttributeValue().withL(
106+
new AttributeValue().withS("I'm a string"),
107+
new AttributeValue().withN("42"),
108+
new AttributeValue().withS("Another string"),
109+
new AttributeValue().withNS("1", "4", "7"),
110+
new AttributeValue().withM(tmpMap),
111+
new AttributeValue().withL(
112+
new AttributeValue().withN("123"),
113+
new AttributeValue().withNS("1", "200", "10", "15", "0"),
114+
new AttributeValue().withSS("Goodbye", "Cruel", "World", "!")
115+
)));
116+
tmpMap = new HashMap<>();
117+
tmpMap.put("another string", new AttributeValue().withS("All around the cobbler's bench"));
118+
tmpMap.put("next line", new AttributeValue().withSS("the monkey", "chased", "the weasel"));
119+
tmpMap.put("more lyrics", new AttributeValue().withL(
120+
new AttributeValue().withS("the monkey"),
121+
new AttributeValue().withS("thought twas"),
122+
new AttributeValue().withS("all in fun")
123+
));
124+
tmpMap.put("weasel", new AttributeValue().withM(Collections.singletonMap("pop", new AttributeValue().withBOOL(true))));
125+
attribs.put("song", new AttributeValue().withM(tmpMap));
126+
127+
99128
context = new EncryptionContext.Builder()
100129
.withTableName("TableName")
101130
.withHashKeyName("hashKey")

src/test/java/com/amazonaws/services/dynamodbv2/testing/AttrMatcher.java

+31-4
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,40 @@ public boolean matches(Object item) {
6060

6161
public static boolean attrEquals(AttributeValue e, AttributeValue a) {
6262
if (!isEqual(e.getB(), a.getB()) ||
63+
!isEqual(e.getBOOL(), a.getBOOL()) ||
64+
!isSetEqual(e.getBS(), a.getBS()) ||
6365
!isEqual(e.getN(), a.getN()) ||
66+
!isSetEqual(e.getNS(), a.getNS()) ||
67+
!isEqual(e.getNULL(), a.getNULL()) ||
6468
!isEqual(e.getS(), a.getS()) ||
65-
!isEqual(e.getBS(), a.getBS()) ||
66-
!isEqual(e.getNS(), a.getNS()) ||
67-
!isEqual(e.getSS(), a.getSS())) {
69+
!isSetEqual(e.getSS(), a.getSS())) {
6870
return false;
6971
}
72+
// Recursive types need special handling
73+
if (e.getM() == null ^ a.getM() == null) {
74+
return false;
75+
} else if (e.getM() != null) {
76+
if (!e.getM().keySet().equals(a.getM().keySet())) {
77+
return false;
78+
}
79+
for (final String key : e.getM().keySet()) {
80+
if (!attrEquals(e.getM().get(key), a.getM().get(key))) {
81+
return false;
82+
}
83+
}
84+
}
85+
if (e.getL() == null ^ a.getL() == null) {
86+
return false;
87+
} else if (e.getL() != null) {
88+
if (e.getL().size() != a.getL().size()) {
89+
return false;
90+
}
91+
for (int x = 0; x < e.getL().size(); x++) {
92+
if (!attrEquals(e.getL().get(x), a.getL().get(x))) {
93+
return false;
94+
}
95+
}
96+
}
7097
return true;
7198
}
7299

@@ -82,7 +109,7 @@ private static boolean isEqual(Object o1, Object o2) {
82109
return o1.equals(o2);
83110
}
84111

85-
private static <T> boolean isEqual(Collection<T> c1, Collection<T> c2) {
112+
private static <T> boolean isSetEqual(Collection<T> c1, Collection<T> c2) {
86113
if(c1 == null ^ c2 == null) {
87114
return false;
88115
}

0 commit comments

Comments
 (0)