Commits

Jean-Tiare Le Bigot committed acd7437

add support + test for all RETURN_VALUES combinations close #3; 100% test coverage Yeeeeeeeah :)

Comments (0)

Files changed (6)

ddbmock/database/item.py

             raise ValueError('Field {} not found'.format(name))
 
         return key.read(field)
+
+    def __sub__(self, other):
+        # Thanks mnoel :)
+        return {k:v for k,v in self.iteritems() if k not in other or v != other[k]}

ddbmock/database/table.py

             raise ValidationException("UpdateItem can not alter the range_key.")
 
         self.data[hash_key][range_key].apply_actions(actions)
+        new = copy.deepcopy(self.data[hash_key][range_key])
 
         # If new item:
         if old.is_new():
             if self.range_key is not None:
                 self.data[hash_key][range_key][self.range_key.name] = range_key
 
-        return old
+        return old, new
 
     def put(self, item, expected):
         item = Item(item)

ddbmock/validators/types.py

     any(u'NONE', u'ALL_OLD', msg="If specified, return value may only be one of 'NONE' or 'ALL_OLD'"),
 )
 
+return_values_all = all(
+    default_to(u'NONE'),
+    any(u'NONE', u'ALL_OLD', u'UPDATED_OLD', u'ALL_NEW', u'UPDATED_NEW', msg="If specified, return value may only be one of 'ALL_OLD', 'UPDATED_OLD', 'ALL_NEW' or 'UPDATED_NEW'"),
+)
+
 consistent_read = all(
     default_to(False),
     boolean(msg="Consistent_read parameter must be a boolean"),

ddbmock/validators/update_item.py

 # -*- coding: utf-8 -*-
 
-from .types import table_name, optional, get_key_schema, return_values, expected_schema, attribute_update_schema
+from .types import table_name, optional, get_key_schema, return_values_all, expected_schema, attribute_update_schema
 
 post = {
     u'TableName': table_name,
     u'Key': get_key_schema,
     u'AttributeUpdates': attribute_update_schema,
     optional(u'Expected'): expected_schema, # It is optional but with a def value
-    optional(u'ReturnValues'): return_values, # It is optional but with a def value
+    optional(u'ReturnValues'): return_values_all, # It is optional but with a def value
 }

ddbmock/views/update_item.py

     name = post[u'TableName']
     table = DynamoDB().get_table(name)
 
-    ret = {
-        "ConsumedCapacityUnits": 1, #FIXME: stub
-        "Attributes": table.update_item(
+    old, new = table.update_item(
             post[u'Key'],
             post[u'AttributeUpdates'],
             post[u'Expected'],
-        ),
+    )
+
+    ret = {
+        "ConsumedCapacityUnits": 1, #FIXME: stub
     }
 
     if post[u'ReturnValues'] == "ALL_OLD":
-        return ret
-    else:
-        del ret["Attributes"]
-        return ret
+        ret["Attributes"] = old
+    elif post[u'ReturnValues'] == "ALL_NEW":
+        ret["Attributes"] = new
+    elif post[u'ReturnValues'] == "UPDATED_OLD":
+        ret["Attributes"] = old - new
+    elif post[u'ReturnValues'] == "UPDATED_NEW":
+        ret["Attributes"] = new - old
+
+    return ret
 
 # Pyramid route wrapper
 @view_config(route_name='update_item', renderer='json')
 def pyramid_update_item(request):
-    return update_item(request.json)
+    return update_item(request.json)

tests/functional/boto/test_update_item.py

 import unittest
 import boto
 from decimal import Decimal
+from copy import deepcopy as cp
 
 TABLE_NAME = 'Table-HR'
 TABLE_NAME2 = 'Table-H'
     TABLE_RK_NAME: {TABLE_RK_TYPE: RK_VALUE},
     FIELD_NAME: {u'B': u'THVkaWEgaXMgdGhlIGJlc3QgY29tcGFueSBldmVyIQ=='},
     FIELD_SET_NAME: {u'SS': [u'item1', u'item2', u'item3', u'item4']}
+}
 
-}
 ITEM2 = {
     TABLE_HK_NAME: {TABLE_HK_TYPE: HK_VALUE},
     u'relevant_data': {u'B': u'THVkaWEgaXMgdGhlIGJlc3QgY29tcGFueSBldmVyIQ=='},
         db.data[TABLE_NAME]  = self.t1
         db.data[TABLE_NAME2] = self.t2
 
-        self.t1.put(ITEM, {})
-        self.t2.put(ITEM2, {})
+        self.t1.put(cp(ITEM), {})
+        self.t2.put(cp(ITEM2), {})
 
     def tearDown(self):
         from ddbmock.database.db import DynamoDB
             }
         )
         self.assertEqual(expected1, self.t1.data[HK_VALUE][RK_VALUE][FIELD_SET_NAME])
+
+    def test_update_return_all_old(self):
+        from ddbmock import connect_boto
+        from boto.dynamodb.exceptions import DynamoDBValidationError
+
+        key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}}
+        ADD_VALUE = 1
+
+        db = connect_boto()
+        expected = cp(ITEM2)
+
+        # regular increment
+        ret = db.layer1.update_item(TABLE_NAME2, key, {
+                FIELD_NUM_NAME: {'Action': 'ADD', u'Value': {u'N': unicode(ADD_VALUE)}},
+            },
+            return_values=u'ALL_OLD',
+         )
+        self.assertEqual(expected, ret[u'Attributes'])
+
+    def test_update_return_all_new(self):
+        from ddbmock import connect_boto
+        from boto.dynamodb.exceptions import DynamoDBValidationError
+
+        key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}}
+        ADD_VALUE = 1
+
+        db = connect_boto()
+        expected = cp(ITEM2)
+        expected[FIELD_NUM_NAME][u'N'] = unicode(int(expected[FIELD_NUM_NAME][u'N']) + ADD_VALUE)
+
+        # regular increment
+        ret = db.layer1.update_item(TABLE_NAME2, key, {
+                FIELD_NUM_NAME: {'Action': 'ADD', u'Value': {u'N': unicode(ADD_VALUE)}},
+            },
+            return_values=u'ALL_NEW',
+        )
+        self.assertEqual(expected, ret[u'Attributes'])
+
+    def test_update_return_updated_old(self):
+        from ddbmock import connect_boto
+        from boto.dynamodb.exceptions import DynamoDBValidationError
+
+        key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}}
+        ADD_VALUE = 1
+
+        db = connect_boto()
+        expected = {FIELD_NUM_NAME: cp(ITEM2[FIELD_NUM_NAME])}
+
+        # regular increment
+        ret = db.layer1.update_item(TABLE_NAME2, key, {
+                FIELD_NUM_NAME: {'Action': 'ADD', u'Value': {u'N': unicode(ADD_VALUE)}},
+            },
+            return_values=u'UPDATED_OLD',
+         )
+        self.assertEqual(expected, ret[u'Attributes'])
+
+    def test_update_return_updated_new(self):
+        from ddbmock import connect_boto
+        from boto.dynamodb.exceptions import DynamoDBValidationError
+
+        key = {u"HashKeyElement": {TABLE_HK_TYPE: HK_VALUE}}
+        ADD_VALUE = 1
+
+        db = connect_boto()
+        expected = {FIELD_NUM_NAME: cp(ITEM2[FIELD_NUM_NAME])}
+        expected[FIELD_NUM_NAME][u'N'] = unicode(int(expected[FIELD_NUM_NAME][u'N']) + ADD_VALUE)
+
+        # regular increment
+        ret = db.layer1.update_item(TABLE_NAME2, key, {
+                FIELD_NUM_NAME: {'Action': 'ADD', u'Value': {u'N': unicode(ADD_VALUE)}},
+            },
+            return_values=u'UPDATED_NEW',
+         )
+        self.assertEqual(expected, ret[u'Attributes'])