Repository: getmoto/moto

Issue:
mock_dynamodb2 throws ConditionalCheckFailedException with simple put_item
Hi, I'm trying to understand how to use the mock_dynamodb2 decorator, if I run it as is with my current code it's throwing `ConditionalCheckFailedException` with write operations. Here there is a simple tests showing what happens:

```python
'''broken_test.py'''
import boto
import botocore
import boto3
import moto

@moto.mock_dynamodb2
def test_dynamodb_put():
    '''Test data abstraction layer.'''
    assert boto.__version__ == '2.46.1'
    assert botocore.__version__ == '1.5.26'
    assert boto3.__version__ == '1.4.4'
    assert moto.__version__ == '0.4.31'

    dyn = boto3.client('dynamodb')
    dyn.create_table(
        TableName='test',
        AttributeDefinitions=[
            {'AttributeName':'email', 'AttributeType':'S'},
        ],
        KeySchema=[{'AttributeName':'email', 'KeyType':'S'}],
        ProvisionedThroughput={'ReadCapacityUnits':5, 'WriteCapacityUnits':5},
    )
    dyn.put_item(
        TableName='test',
        Item={'email': {'S': 'some@email.com'}})
```

```
$ py.test
======================================================== test session starts ========================================================
platform darwin -- Python 2.7.10, pytest-3.0.7, py-1.4.33, pluggy-0.4.0
rootdir: ./backend, inifile:
collected 1 items

broken_test.py F

============================================================= FAILURES ==============================================================
_________________________________________________________ test_dynamodb_put _________________________________________________________

    @moto.mock_dynamodb2
    def test_dynamodb_put():
        '''Test data abstraction layer.'''
        assert version.startswith('2.7.10 ')
        assert boto.__version__ == '2.46.1'
        assert botocore.__version__ == '1.5.26'
        assert boto3.__version__ == '1.4.4'
        assert moto.__version__ == '0.4.31'

        dyn = boto3.client('dynamodb')
        dyn.create_table(
            TableName='test',
            AttributeDefinitions=[
                {'AttributeName':'email', 'AttributeType':'S'},
            ],
            KeySchema=[{'AttributeName':'email', 'KeyType':'S'}],
            ProvisionedThroughput={'ReadCapacityUnits':5, 'WriteCapacityUnits':5},
        )
        dyn.put_item(
            TableName='test',
>           Item={'email': {'S': 'some@email.com'}})

broken_test.py:27:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../../../Library/Python/2.7/lib/python/site-packages/botocore/client.py:253: in _api_call
    return self._make_api_call(operation_name, kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <botocore.client.DynamoDB object at 0x106efc610>, operation_name = 'PutItem'
api_params = {'Item': {'email': {'S': 'some@email.com'}}, 'TableName': 'test'}

    def _make_api_call(self, operation_name, api_params):
        operation_model = self._service_model.operation_model(operation_name)
        request_context = {
            'client_region': self.meta.region_name,
            'client_config': self.meta.config,
            'has_streaming_input': operation_model.has_streaming_input
        }
        request_dict = self._convert_to_request_dict(
            api_params, operation_model, context=request_context)

        handler, event_response = self.meta.events.emit_until_response(
            'before-call.{endpoint_prefix}.{operation_name}'.format(
                endpoint_prefix=self._service_model.endpoint_prefix,
                operation_name=operation_name),
            model=operation_model, params=request_dict,
            request_signer=self._request_signer, context=request_context)

        if event_response is not None:
            http, parsed_response = event_response
        else:
            http, parsed_response = self._endpoint.make_request(
                operation_model, request_dict)

        self.meta.events.emit(
            'after-call.{endpoint_prefix}.{operation_name}'.format(
                endpoint_prefix=self._service_model.endpoint_prefix,
                operation_name=operation_name),
            http_response=http, parsed=parsed_response,
            model=operation_model, context=request_context
        )

        if http.status_code >= 300:
            error_code = parsed_response.get("Error", {}).get("Code")
            error_class = self.exceptions.from_code(error_code)
>           raise error_class(parsed_response, operation_name)
E           ConditionalCheckFailedException: An error occurred (ConditionalCheckFailedException) when calling the PutItem operation:

../../../Library/Python/2.7/lib/python/site-packages/botocore/client.py:543: ConditionalCheckFailedException
===================================================== 1 failed in 0.73 seconds ======================================================
```

I'm can't understand which condition is not met. I'm testing on MacOS using mocks, installed Moto via pip. Thanks for your help


Discussion:
@shaftoe did you solve this problem? I am seeing the same error when trying to delete an item that doesn't exist (idempotent call)

My setup is a bit different. I am running motto in a docker container and using java sdk client. I think the problem comes from the motto endpoint because the code works fine in production.
In my case there's a bug in moto/dynamodb2/responses.py:430. delete_item returns an error if the item doesn't exist.

I decided not to create a pull request for this because I don't know dynamodb contract for consumed read capacity for this operation. I also don't know the contract for returning attributes when the item doesn't exist.

Ref:
DeleteItemResult com.amazonaws.services.dynamodbv2.AmazonDynamoDB.deleteItem(DeleteItemRequest deleteItemRequest)

Deletes a single item in a table by primary key. You can perform a conditional delete operation that deletes the item if it exists, or if it has an expected attribute value.

In addition to deleting an item, you can also return the item's attribute values in the same operation, using the ReturnValues parameter.

Unless you specify conditions, the DeleteItem is an idempotent operation; running it multiple times on the same item or attribute does not result in an error response.

Conditional deletes are useful for deleting items only if specific conditions are met. If those conditions are met, DynamoDB performs the delete. Otherwise, the item is not deleted
@verkhoro nope, I didn't manage to go deeper, thanks for the update btw
@shaftoe The `ConditionalCheckFailedException` happens when we try `put` a key that already exists and we don't specify `overwrite=True`. Here's an example of how this is intended behavior: https://github.com/spulec/moto/blob/master/tests/test_dynamodb2/test_dynamodb_table_with_range_key.py#L902

That could probably be much clearer in the client, though. Perhaps you could suggest a better error message for users of the Moto library who encounter this?

Resolving patch:
diff --git a/moto/dynamodb2/models.py b/moto/dynamodb2/models.py
--- a/moto/dynamodb2/models.py
+++ b/moto/dynamodb2/models.py
@@ -659,7 +659,7 @@ def update_item(self, table_name, key, update_expression, attribute_updates, exp
         return item
 
     def delete_item(self, table_name, keys):
-        table = self.tables.get(table_name)
+        table = self.get_table(table_name)
         if not table:
             return None
         hash_key, range_key = self.get_keys_value(table, keys)
diff --git a/moto/dynamodb2/responses.py b/moto/dynamodb2/responses.py
--- a/moto/dynamodb2/responses.py
+++ b/moto/dynamodb2/responses.py
@@ -418,18 +418,19 @@ def delete_item(self):
         name = self.body['TableName']
         keys = self.body['Key']
         return_values = self.body.get('ReturnValues', '')
-        item = dynamodb_backend2.delete_item(name, keys)
-        if item:
-            if return_values == 'ALL_OLD':
-                item_dict = item.to_json()
-            else:
-                item_dict = {'Attributes': {}}
-            item_dict['ConsumedCapacityUnits'] = 0.5
-            return dynamo_json_dump(item_dict)
-        else:
+        table = dynamodb_backend2.get_table(name)
+        if not table:
             er = 'com.amazonaws.dynamodb.v20120810#ConditionalCheckFailedException'
             return self.error(er)
 
+        item = dynamodb_backend2.delete_item(name, keys)
+        if item and return_values == 'ALL_OLD':
+            item_dict = item.to_json()
+        else:
+            item_dict = {'Attributes': {}}
+        item_dict['ConsumedCapacityUnits'] = 0.5
+        return dynamo_json_dump(item_dict)
+
     def update_item(self):
         name = self.body['TableName']
         key = self.body['Key']


Test patch:
diff --git a/tests/test_dynamodb2/test_dynamodb_table_with_range_key.py b/tests/test_dynamodb2/test_dynamodb_table_with_range_key.py
--- a/tests/test_dynamodb2/test_dynamodb_table_with_range_key.py
+++ b/tests/test_dynamodb2/test_dynamodb_table_with_range_key.py
@@ -314,7 +314,8 @@ def test_delete_item():
     response.should.equal(True)
 
     table.count().should.equal(0)
-    item.delete().should.equal(False)
+    # Deletes are idempotent
+    item.delete().should.equal(True)
 
 
 @requires_boto_gte("2.9")
diff --git a/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py b/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py
--- a/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py
+++ b/tests/test_dynamodb2/test_dynamodb_table_without_range_key.py
@@ -199,7 +199,8 @@ def test_delete_item():
 
     table.count().should.equal(0)
 
-    item.delete().should.equal(False)
+    # Deletes are idempotent and 'False' here would imply an error condition
+    item.delete().should.equal(True)
 
 
 @requires_boto_gte("2.9")