From 25b5f03ea4b73f1ea298459fc60d2a971e767ee9 Mon Sep 17 00:00:00 2001 From: William Richard Date: Wed, 6 Jun 2018 12:09:09 -0400 Subject: [PATCH 1/3] These tests do not use pytest --- tests/test_dynamodb2/test_dynamodb.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py index 93188001..289208fd 100644 --- a/tests/test_dynamodb2/test_dynamodb.py +++ b/tests/test_dynamodb2/test_dynamodb.py @@ -6,7 +6,6 @@ import boto3 from boto3.dynamodb.conditions import Attr import sure # noqa import requests -from pytest import raises from moto import mock_dynamodb2, mock_dynamodb2_deprecated from moto.dynamodb2 import dynamodb_backend2 from boto.exception import JSONResponseError @@ -1119,7 +1118,7 @@ def test_update_item_on_map(): }) # Test nested value for a nonexistent attribute. - with raises(client.exceptions.ConditionalCheckFailedException): + with assert_raises(client.exceptions.ConditionalCheckFailedException): table.update_item(Key={ 'forum_name': 'the-key', 'subject': '123' From 014fbbb8cb1109bd8f75ea2cd0f8786436a1cb87 Mon Sep 17 00:00:00 2001 From: William Richard Date: Wed, 6 Jun 2018 12:50:29 -0400 Subject: [PATCH 2/3] Added a test that queries an index when created via the table resource update When you create an index via the table resource update, in python 3.6, it is saved as a `dict_value`, which causes an error - this test replicates this bug --- tests/test_dynamodb2/test_dynamodb.py | 97 ++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 2 deletions(-) diff --git a/tests/test_dynamodb2/test_dynamodb.py b/tests/test_dynamodb2/test_dynamodb.py index 289208fd..ab8f2585 100644 --- a/tests/test_dynamodb2/test_dynamodb.py +++ b/tests/test_dynamodb2/test_dynamodb.py @@ -1,16 +1,17 @@ from __future__ import unicode_literals, print_function +from decimal import Decimal + import six import boto import boto3 -from boto3.dynamodb.conditions import Attr +from boto3.dynamodb.conditions import Attr, Key import sure # noqa import requests from moto import mock_dynamodb2, mock_dynamodb2_deprecated from moto.dynamodb2 import dynamodb_backend2 from boto.exception import JSONResponseError from botocore.exceptions import ClientError -from boto3.dynamodb.conditions import Key from tests.helpers import requires_boto_gte import tests.backport_assert_raises @@ -1199,3 +1200,95 @@ def test_update_if_not_exists(): resp = table.scan() # Still the original value assert resp['Items'][0]['created_at'] == 123 + + +@mock_dynamodb2 +def test_query_global_secondary_index_when_created_via_update_table_resource(): + dynamodb = boto3.resource('dynamodb', region_name='us-east-1') + + # Create the DynamoDB table. + dynamodb.create_table( + TableName='users', + KeySchema=[ + { + 'AttributeName': 'user_id', + 'KeyType': 'HASH' + }, + ], + AttributeDefinitions=[ + { + 'AttributeName': 'user_id', + 'AttributeType': 'N', + }, + { + 'AttributeName': 'forum_name', + 'AttributeType': 'S' + }, + { + 'AttributeName': 'subject', + 'AttributeType': 'S' + }, + ], + ProvisionedThroughput={ + 'ReadCapacityUnits': 5, + 'WriteCapacityUnits': 5 + }, + ) + table = dynamodb.Table('users') + table.update( + AttributeDefinitions=[ + { + 'AttributeName': 'forum_name', + 'AttributeType': 'S' + }, + ], + GlobalSecondaryIndexUpdates=[ + {'Create': + { + 'IndexName': 'forum_name_index', + 'KeySchema': [ + { + 'AttributeName': 'forum_name', + 'KeyType': 'HASH', + }, + ], + 'Projection': { + 'ProjectionType': 'ALL', + }, + 'ProvisionedThroughput': { + 'ReadCapacityUnits': 5, + 'WriteCapacityUnits': 5 + }, + } + } + ] + ) + + next_user_id = 1 + for my_forum_name in ['cats', 'dogs']: + for my_subject in ['my pet is the cutest', 'wow look at what my pet did', "don't you love my pet?"]: + table.put_item(Item={'user_id': next_user_id, 'forum_name': my_forum_name, 'subject': my_subject}) + next_user_id += 1 + + # get all the cat users + forum_only_query_response = table.query( + IndexName='forum_name_index', + Select='ALL_ATTRIBUTES', + KeyConditionExpression=Key('forum_name').eq('cats'), + ) + forum_only_items = forum_only_query_response['Items'] + assert len(forum_only_items) == 3 + for item in forum_only_items: + assert item['forum_name'] == 'cats' + + # query all cat users with a particular subject + forum_and_subject_query_results = table.query( + IndexName='forum_name_index', + Select='ALL_ATTRIBUTES', + KeyConditionExpression=Key('forum_name').eq('cats'), + FilterExpression=Attr('subject').eq('my pet is the cutest'), + ) + forum_and_subject_items = forum_and_subject_query_results['Items'] + assert len(forum_and_subject_items) == 1 + assert forum_and_subject_items[0] == {'user_id': Decimal('1'), 'forum_name': 'cats', + 'subject': 'my pet is the cutest'} From 8b7875ec02a6387e15300ac75a8fa4a0b35c1d97 Mon Sep 17 00:00:00 2001 From: William Richard Date: Wed, 6 Jun 2018 12:56:19 -0400 Subject: [PATCH 3/3] Ensure that table.global_indexes and table.indexes are lists --- moto/dynamodb2/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/moto/dynamodb2/models.py b/moto/dynamodb2/models.py index c4aa1023..db6bf04a 100644 --- a/moto/dynamodb2/models.py +++ b/moto/dynamodb2/models.py @@ -706,7 +706,9 @@ class DynamoDBBackend(BaseBackend): gsis_by_name[gsi_to_create['IndexName']] = gsi_to_create - table.global_indexes = gsis_by_name.values() + # in python 3.6, dict.values() returns a dict_values object, but we expect it to be a list in other + # parts of the codebase + table.global_indexes = list(gsis_by_name.values()) return table def put_item(self, table_name, item_attrs, expected=None, overwrite=False):