@@ -384,6 +384,8 @@ module {:options "-functionSyntax:4"} DdbEncryptionTestVectors {
384
384
BasicIoTestBatchWriteItem (c1, c2, globalRecords);
385
385
BasicIoTestPutItem (c1, c2, globalRecords);
386
386
BasicIoTestTransactWriteItems (c1, c2, globalRecords);
387
+ BasicIoTestUpdateItem (c1, c2, globalRecords);
388
+ BasicIoTestDeleteItem (c1, c2, globalRecords);
387
389
BasicIoTestExecuteStatement (c1, c2);
388
390
}
389
391
}
@@ -835,6 +837,108 @@ module {:options "-functionSyntax:4"} DdbEncryptionTestVectors {
835
837
BasicIoTestTransactGetItems (rClient, records);
836
838
}
837
839
840
+ method BasicIoTestUpdateItem (writeConfig : TableConfig , readConfig : TableConfig , records : seq <Record >)
841
+ {
842
+ var wClient :- expect newGazelle (writeConfig);
843
+ var rClient :- expect newGazelle (readConfig);
844
+ WriteAllRecords (wClient, records);
845
+
846
+ // Update each record by appending "-updated" to the partition key
847
+ for i := 0 to |records| {
848
+ :- expect Need (HashName in records[i].item, "");
849
+
850
+ // Get the current value of the partition key
851
+ var currentValue;
852
+ currentValue := records[i]. item[HashName]. N;
853
+
854
+ // Append "-updated" to the current value
855
+ var newValue := currentValue + "- updated";
856
+
857
+ // Create an update expression to update the partition key
858
+ var updateExpr := "SET #pk = :val";
859
+ var exprAttrNames := map ["#pk" := HashName];
860
+ var exprAttrValues := map [":val" := DDB. AttributeValue. S (newValue)];
861
+
862
+ var updateInput := DDB. UpdateItemInput (
863
+ TableName := TableName,
864
+ Key := map[HashName := records[i].item[HashName]],
865
+ UpdateExpression := Some(updateExpr),
866
+ ExpressionAttributeNames := Some (exprAttrNames),
867
+ ExpressionAttributeValues := Some (exprAttrValues),
868
+ ReturnValues := None,
869
+ ReturnConsumedCapacity := None,
870
+ ReturnItemCollectionMetrics := None,
871
+ ConditionExpression := None
872
+ );
873
+
874
+ var updateSignedItemResult := wClient. UpdateItem (updateInput);
875
+
876
+ expect updateSignedItemResult. Failure?, "UpdateItem should have failed for signed item. ";
877
+ // This error is of type DynamoDbEncryptionTransformsException
878
+ // but AWS SDK wraps it into its own type for which customers should be unwrapping.
879
+ // In test vectors, we still have to change the error from AWS SDK to dafny so it turns out to be OpaqueWithText.
880
+ expect updateSignedItemResult. error. OpaqueWithText?, "Error should have been of type OpaqueWithText";
881
+ var hasDynamoDbEncryptionTransformsException? := String. HasSubString (updateSignedItemResult.error.objMessage, "Update Expressions forbidden on signed attributes");
882
+ expect hasDynamoDbEncryptionTransformsException?. Some?;
883
+ }
884
+ }
885
+
886
+ method BasicIoTestDeleteItem (writeConfig : TableConfig , readConfig : TableConfig , records : seq <Record >)
887
+ {
888
+ var wClient :- expect newGazelle (writeConfig);
889
+ var rClient :- expect newGazelle (readConfig);
890
+ WriteAllRecords (wClient, records);
891
+
892
+ // Only delete half of the records to verify selective deletion
893
+ var deleteCount := |records| / 2;
894
+
895
+ for i := 0 to deleteCount {
896
+ :- expect Need (HashName in records[i].item, "");
897
+
898
+ var deleteInput := DDB. DeleteItemInput (
899
+ TableName := TableName,
900
+ Key := map[HashName := records[i].item[HashName]],
901
+ Expected := None,
902
+ ReturnValues := None,
903
+ ReturnConsumedCapacity := None,
904
+ ReturnItemCollectionMetrics := None,
905
+ ConditionExpression := None,
906
+ ExpressionAttributeNames := None,
907
+ ExpressionAttributeValues := None
908
+ );
909
+
910
+ var _ :- expect wClient. DeleteItem (deleteInput);
911
+ }
912
+
913
+ // Verify deletions were successful
914
+ for i := 0 to |records| {
915
+ :- expect Need (HashName in records[i].item, "");
916
+
917
+ var getInput := DDB. GetItemInput (
918
+ TableName := TableName,
919
+ Key := map[HashName := records[i].item[HashName]],
920
+ AttributesToGet := None,
921
+ ConsistentRead := None,
922
+ ReturnConsumedCapacity := None,
923
+ ProjectionExpression := None,
924
+ ExpressionAttributeNames := None
925
+ );
926
+
927
+ var out := rClient. GetItem (getInput);
928
+
929
+ if i < deleteCount {
930
+ // Deleted items should not be found
931
+ expect out. Success?;
932
+ expect out. value. Item. None?, "Item " + String. Base10Int2String (i) + " should have been deleted";
933
+ } else {
934
+ // Non-deleted items should still exist
935
+ expect out. Success?;
936
+ expect out. value. Item. Some?;
937
+ expect NormalizeItem (out.value.Item.value) == NormalizeItem (records[i].item);
938
+ }
939
+ }
940
+ }
941
+
838
942
method BasicIoTestExecuteStatement (writeConfig : TableConfig , readConfig : TableConfig )
839
943
{
840
944
var wClient :- expect newGazelle (writeConfig);
0 commit comments