2023-10-24 20:31:25 +00:00
import boto3
import pytest
2022-03-09 17:57:25 +00:00
import uuid
2023-10-24 20:31:25 +00:00
import re
from botocore . exceptions import ClientError
2022-03-09 17:57:25 +00:00
from datetime import datetime
from decimal import Decimal
from boto3 . dynamodb . conditions import Attr , Key
2023-09-15 17:17:57 +00:00
from boto3 . dynamodb . types import Binary
2022-04-09 21:45:25 +00:00
from moto import mock_dynamodb , settings
2022-08-13 09:49:43 +00:00
from moto . core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
2022-03-09 17:57:25 +00:00
from moto . dynamodb import dynamodb_backends
import moto . dynamodb . comparisons
import moto . dynamodb . models
2023-10-24 20:31:25 +00:00
from . import dynamodb_aws_verified
2013-02-20 01:39:04 +00:00
2013-03-10 19:46:27 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
@pytest.mark.parametrize (
" names " ,
[ [ ] , [ " TestTable " ] , [ " TestTable1 " , " TestTable2 " ] ] ,
ids = [ " no-table " , " one-table " , " multiple-tables " ] ,
)
def test_list_tables_boto3 ( names ) :
conn = boto3 . client ( " dynamodb " , region_name = " us-west-2 " )
for name in names :
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
2023-07-13 10:21:47 +00:00
assert conn . list_tables ( ) [ " TableNames " ] == names
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_list_tables_paginated ( ) :
conn = boto3 . client ( " dynamodb " , region_name = " us-west-2 " )
for name in [ " name1 " , " name2 " , " name3 " ] :
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
res = conn . list_tables ( Limit = 2 )
2023-07-13 10:21:47 +00:00
assert res [ " TableNames " ] == [ " name1 " , " name2 " ]
assert res [ " LastEvaluatedTableName " ] == " name2 "
2022-03-09 17:57:25 +00:00
res = conn . list_tables ( Limit = 1 , ExclusiveStartTableName = " name1 " )
2023-07-13 10:21:47 +00:00
assert res [ " TableNames " ] == [ " name2 " ]
assert res [ " LastEvaluatedTableName " ] == " name2 "
2022-03-09 17:57:25 +00:00
res = conn . list_tables ( ExclusiveStartTableName = " name1 " )
2023-07-13 10:21:47 +00:00
assert res [ " TableNames " ] == [ " name2 " , " name3 " ]
assert " LastEvaluatedTableName " not in res
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_describe_missing_table_boto3 ( ) :
conn = boto3 . client ( " dynamodb " , region_name = " us-west-2 " )
with pytest . raises ( ClientError ) as ex :
conn . describe_table ( TableName = " messages " )
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ResourceNotFoundException "
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
assert (
ex . value . response [ " Error " ] [ " Message " ]
== " Requested resource not found: Table: messages not found "
2023-02-27 23:05:50 +00:00
)
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_list_table_tags ( ) :
name = " TestTable "
conn = boto3 . client (
" dynamodb " ,
region_name = " us-west-2 " ,
aws_access_key_id = " ak " ,
aws_secret_access_key = " sk " ,
)
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table_description = conn . describe_table ( TableName = name )
arn = table_description [ " Table " ] [ " TableArn " ]
# Tag table
tags = [
{ " Key " : " TestTag " , " Value " : " TestValue " } ,
{ " Key " : " TestTag2 " , " Value " : " TestValue2 " } ,
]
conn . tag_resource ( ResourceArn = arn , Tags = tags )
# Check tags
resp = conn . list_tags_of_resource ( ResourceArn = arn )
assert resp [ " Tags " ] == tags
# Remove 1 tag
conn . untag_resource ( ResourceArn = arn , TagKeys = [ " TestTag " ] )
# Check tags
resp = conn . list_tags_of_resource ( ResourceArn = arn )
assert resp [ " Tags " ] == [ { " Key " : " TestTag2 " , " Value " : " TestValue2 " } ]
@mock_dynamodb
def test_list_table_tags_empty ( ) :
name = " TestTable "
conn = boto3 . client (
" dynamodb " ,
region_name = " us-west-2 " ,
aws_access_key_id = " ak " ,
aws_secret_access_key = " sk " ,
)
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table_description = conn . describe_table ( TableName = name )
arn = table_description [ " Table " ] [ " TableArn " ]
resp = conn . list_tags_of_resource ( ResourceArn = arn )
assert resp [ " Tags " ] == [ ]
@mock_dynamodb
def test_list_table_tags_paginated ( ) :
name = " TestTable "
conn = boto3 . client (
" dynamodb " ,
region_name = " us-west-2 " ,
aws_access_key_id = " ak " ,
aws_secret_access_key = " sk " ,
)
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table_description = conn . describe_table ( TableName = name )
arn = table_description [ " Table " ] [ " TableArn " ]
for i in range ( 11 ) :
2022-11-17 22:41:08 +00:00
tags = [ { " Key " : f " TestTag { i } " , " Value " : " TestValue " } ]
2022-03-09 17:57:25 +00:00
conn . tag_resource ( ResourceArn = arn , Tags = tags )
resp = conn . list_tags_of_resource ( ResourceArn = arn )
assert len ( resp [ " Tags " ] ) == 10
assert " NextToken " in resp . keys ( )
resp2 = conn . list_tags_of_resource ( ResourceArn = arn , NextToken = resp [ " NextToken " ] )
assert len ( resp2 [ " Tags " ] ) == 1
assert " NextToken " not in resp2 . keys ( )
@mock_dynamodb
def test_list_not_found_table_tags ( ) :
conn = boto3 . client (
" dynamodb " ,
region_name = " us-west-2 " ,
aws_access_key_id = " ak " ,
aws_secret_access_key = " sk " ,
)
arn = " DymmyArn "
try :
conn . list_tags_of_resource ( ResourceArn = arn )
except ClientError as exception :
assert exception . response [ " Error " ] [ " Code " ] == " ResourceNotFoundException "
@mock_dynamodb
def test_item_add_empty_string_hash_key_exception ( ) :
name = " TestTable "
conn = boto3 . client (
" dynamodb " ,
region_name = " us-west-2 " ,
aws_access_key_id = " ak " ,
aws_secret_access_key = " sk " ,
)
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " forum_name " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " forum_name " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
with pytest . raises ( ClientError ) as ex :
conn . put_item (
TableName = name ,
Item = {
" forum_name " : { " S " : " " } ,
" subject " : { " S " : " Check this out! " } ,
" Body " : { " S " : " http://url_to_lolcat.gif " } ,
" SentBy " : { " S " : " someone@somewhere.edu " } ,
" ReceivedTime " : { " S " : " 12/9/2011 11:36:03 PM " } ,
} ,
)
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ValidationException "
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
assert (
ex . value . response [ " Error " ] [ " Message " ]
== " One or more parameter values were invalid: An AttributeValue may not contain an empty string. Key: forum_name "
2022-03-09 17:57:25 +00:00
)
@mock_dynamodb
def test_item_add_empty_string_range_key_exception ( ) :
name = " TestTable "
conn = boto3 . client (
" dynamodb " ,
region_name = " us-west-2 " ,
aws_access_key_id = " ak " ,
aws_secret_access_key = " sk " ,
)
conn . create_table (
TableName = name ,
KeySchema = [
{ " AttributeName " : " forum_name " , " KeyType " : " HASH " } ,
{ " AttributeName " : " ReceivedTime " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " forum_name " , " AttributeType " : " S " } ,
{ " AttributeName " : " ReceivedTime " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
with pytest . raises ( ClientError ) as ex :
conn . put_item (
TableName = name ,
Item = {
" forum_name " : { " S " : " LOLCat Forum " } ,
" subject " : { " S " : " Check this out! " } ,
" Body " : { " S " : " http://url_to_lolcat.gif " } ,
" SentBy " : { " S " : " someone@somewhere.edu " } ,
" ReceivedTime " : { " S " : " " } ,
} ,
)
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ValidationException "
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
assert (
ex . value . response [ " Error " ] [ " Message " ]
== " One or more parameter values were invalid: An AttributeValue may not contain an empty string. Key: ReceivedTime "
2022-03-09 17:57:25 +00:00
)
@mock_dynamodb
def test_item_add_empty_string_attr_no_exception ( ) :
name = " TestTable "
conn = boto3 . client (
" dynamodb " ,
region_name = " us-west-2 " ,
aws_access_key_id = " ak " ,
aws_secret_access_key = " sk " ,
)
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " forum_name " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " forum_name " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
conn . put_item (
TableName = name ,
Item = {
" forum_name " : { " S " : " LOLCat Forum " } ,
" subject " : { " S " : " Check this out! " } ,
" Body " : { " S " : " http://url_to_lolcat.gif " } ,
" SentBy " : { " S " : " " } ,
" ReceivedTime " : { " S " : " 12/9/2011 11:36:03 PM " } ,
} ,
)
@mock_dynamodb
def test_update_item_with_empty_string_attr_no_exception ( ) :
name = " TestTable "
conn = boto3 . client (
" dynamodb " ,
region_name = " us-west-2 " ,
aws_access_key_id = " ak " ,
aws_secret_access_key = " sk " ,
)
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " forum_name " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " forum_name " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
conn . put_item (
TableName = name ,
Item = {
" forum_name " : { " S " : " LOLCat Forum " } ,
" subject " : { " S " : " Check this out! " } ,
" Body " : { " S " : " http://url_to_lolcat.gif " } ,
" SentBy " : { " S " : " test " } ,
" ReceivedTime " : { " S " : " 12/9/2011 11:36:03 PM " } ,
} ,
)
conn . update_item (
TableName = name ,
Key = { " forum_name " : { " S " : " LOLCat Forum " } } ,
UpdateExpression = " set Body=:Body " ,
ExpressionAttributeValues = { " :Body " : { " S " : " " } } ,
)
@mock_dynamodb
def test_query_invalid_table ( ) :
conn = boto3 . client (
" dynamodb " ,
region_name = " us-west-2 " ,
aws_access_key_id = " ak " ,
aws_secret_access_key = " sk " ,
)
try :
conn . query (
TableName = " invalid_table " ,
KeyConditionExpression = " index1 = :partitionkeyval " ,
ExpressionAttributeValues = { " :partitionkeyval " : { " S " : " test " } } ,
)
except ClientError as exception :
assert exception . response [ " Error " ] [ " Code " ] == " ResourceNotFoundException "
@mock_dynamodb
def test_put_item_with_special_chars ( ) :
name = " TestTable "
conn = boto3 . client (
" dynamodb " ,
region_name = " us-west-2 " ,
aws_access_key_id = " ak " ,
aws_secret_access_key = " sk " ,
)
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " forum_name " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " forum_name " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
conn . put_item (
TableName = name ,
Item = {
" forum_name " : { " S " : " LOLCat Forum " } ,
" subject " : { " S " : " Check this out! " } ,
" Body " : { " S " : " http://url_to_lolcat.gif " } ,
" SentBy " : { " S " : " test " } ,
" ReceivedTime " : { " S " : " 12/9/2011 11:36:03 PM " } ,
' " ' : { " S " : " foo " } ,
} ,
)
@mock_dynamodb
def test_put_item_with_streams ( ) :
name = " TestTable "
conn = boto3 . client (
" dynamodb " ,
region_name = " us-west-2 " ,
aws_access_key_id = " ak " ,
aws_secret_access_key = " sk " ,
)
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " forum_name " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " forum_name " , " AttributeType " : " S " } ] ,
StreamSpecification = {
" StreamEnabled " : True ,
" StreamViewType " : " NEW_AND_OLD_IMAGES " ,
} ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
conn . put_item (
TableName = name ,
Item = {
" forum_name " : { " S " : " LOLCat Forum " } ,
" subject " : { " S " : " Check this out! " } ,
" Body " : { " S " : " http://url_to_lolcat.gif " } ,
" SentBy " : { " S " : " test " } ,
" Data " : { " M " : { " Key1 " : { " S " : " Value1 " } , " Key2 " : { " S " : " Value2 " } } } ,
} ,
)
result = conn . get_item ( TableName = name , Key = { " forum_name " : { " S " : " LOLCat Forum " } } )
2023-07-13 10:21:47 +00:00
assert result [ " Item " ] == {
" forum_name " : { " S " : " LOLCat Forum " } ,
" subject " : { " S " : " Check this out! " } ,
" Body " : { " S " : " http://url_to_lolcat.gif " } ,
" SentBy " : { " S " : " test " } ,
" Data " : { " M " : { " Key1 " : { " S " : " Value1 " } , " Key2 " : { " S " : " Value2 " } } } ,
}
2022-04-09 21:45:25 +00:00
if not settings . TEST_SERVER_MODE :
2022-08-13 09:49:43 +00:00
table = dynamodb_backends [ ACCOUNT_ID ] [ " us-west-2 " ] . get_table ( name )
2023-07-13 10:21:47 +00:00
assert len ( table . stream_shard . items ) == 1
2022-04-09 21:45:25 +00:00
stream_record = table . stream_shard . items [ 0 ] . record
2023-07-13 10:21:47 +00:00
assert stream_record [ " eventName " ] == " INSERT "
assert stream_record [ " dynamodb " ] [ " SizeBytes " ] == 447
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_basic_projection_expression_using_get_item ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
2022-08-24 10:48:59 +00:00
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
2022-03-09 17:57:25 +00:00
# Create the DynamoDB table.
table = dynamodb . create_table (
TableName = " users " ,
KeySchema = [
{ " AttributeName " : " forum_name " , " KeyType " : " HASH " } ,
{ " AttributeName " : " subject " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " forum_name " , " AttributeType " : " S " } ,
{ " AttributeName " : " subject " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item (
Item = { " forum_name " : " the-key " , " subject " : " 123 " , " body " : " some test message " }
)
table . put_item (
Item = {
" forum_name " : " not-the-key " ,
" subject " : " 123 " ,
" body " : " some other test message " ,
}
)
result = table . get_item (
Key = { " forum_name " : " the-key " , " subject " : " 123 " } ,
ProjectionExpression = " body, subject " ,
)
2023-07-13 10:21:47 +00:00
assert result [ " Item " ] == { " subject " : " 123 " , " body " : " some test message " }
2022-03-09 17:57:25 +00:00
# The projection expression should not remove data from storage
result = table . get_item ( Key = { " forum_name " : " the-key " , " subject " : " 123 " } )
2023-07-13 10:21:47 +00:00
assert result [ " Item " ] == {
" forum_name " : " the-key " ,
" subject " : " 123 " ,
" body " : " some test message " ,
}
2022-03-09 17:57:25 +00:00
2022-08-24 10:48:59 +00:00
# Running this against AWS DDB gives an exception so make sure it also fails.:
with pytest . raises ( client . exceptions . ClientError ) :
# botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the GetItem
# operation: "Can not use both expression and non-expression parameters in the same request:
# Non-expression parameters: {AttributesToGet} Expression parameters: {ProjectionExpression}"
table . get_item (
Key = { " forum_name " : " the-key " , " subject " : " 123 " } ,
ProjectionExpression = " body " ,
AttributesToGet = [ " body " ] ,
)
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_basic_projection_expressions_using_scan ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
dynamodb . create_table (
TableName = " users " ,
KeySchema = [
{ " AttributeName " : " forum_name " , " KeyType " : " HASH " } ,
{ " AttributeName " : " subject " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " forum_name " , " AttributeType " : " S " } ,
{ " AttributeName " : " subject " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item (
Item = { " forum_name " : " the-key " , " subject " : " 123 " , " body " : " some test message " }
)
table . put_item (
Item = {
" forum_name " : " not-the-key " ,
" subject " : " 123 " ,
" body " : " some other test message " ,
}
)
# Test a scan returning all items
results = table . scan (
FilterExpression = Key ( " forum_name " ) . eq ( " the-key " ) ,
ProjectionExpression = " body, subject " ,
)
assert " body " in results [ " Items " ] [ 0 ]
assert results [ " Items " ] [ 0 ] [ " body " ] == " some test message "
assert " subject " in results [ " Items " ] [ 0 ]
table . put_item (
Item = {
" forum_name " : " the-key " ,
" subject " : " 1234 " ,
" body " : " yet another test message " ,
}
)
results = table . scan (
FilterExpression = Key ( " forum_name " ) . eq ( " the-key " ) , ProjectionExpression = " body "
)
bodies = [ item [ " body " ] for item in results [ " Items " ] ]
2023-07-13 10:21:47 +00:00
assert " some test message " in bodies
assert " yet another test message " in bodies
2022-03-09 17:57:25 +00:00
assert " subject " not in results [ " Items " ] [ 0 ]
assert " forum_name " not in results [ " Items " ] [ 0 ]
assert " subject " not in results [ " Items " ] [ 1 ]
assert " forum_name " not in results [ " Items " ] [ 1 ]
# The projection expression should not remove data from storage
results = table . query ( KeyConditionExpression = Key ( " forum_name " ) . eq ( " the-key " ) )
assert " subject " in results [ " Items " ] [ 0 ]
assert " body " in results [ " Items " ] [ 1 ]
assert " forum_name " in results [ " Items " ] [ 1 ]
@mock_dynamodb
def test_nested_projection_expression_using_get_item ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
dynamodb . create_table (
TableName = " users " ,
KeySchema = [ { " AttributeName " : " forum_name " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " forum_name " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item (
Item = {
" forum_name " : " key1 " ,
" nested " : {
" level1 " : { " id " : " id1 " , " att " : " irrelevant " } ,
" level2 " : { " id " : " id2 " , " include " : " all " } ,
" level3 " : { " id " : " irrelevant " } ,
} ,
" foo " : " bar " ,
}
)
table . put_item (
Item = {
" forum_name " : " key2 " ,
" nested " : { " id " : " id2 " , " incode " : " code2 " } ,
" foo " : " bar " ,
}
)
# Test a get_item returning all items
result = table . get_item (
Key = { " forum_name " : " key1 " } ,
ProjectionExpression = " nested.level1.id, nested.level2 " ,
) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result == {
" nested " : { " level1 " : { " id " : " id1 " } , " level2 " : { " id " : " id2 " , " include " : " all " } }
}
2022-03-09 17:57:25 +00:00
# Assert actual data has not been deleted
result = table . get_item ( Key = { " forum_name " : " key1 " } ) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result == {
" foo " : " bar " ,
" forum_name " : " key1 " ,
" nested " : {
" level1 " : { " id " : " id1 " , " att " : " irrelevant " } ,
" level2 " : { " id " : " id2 " , " include " : " all " } ,
" level3 " : { " id " : " irrelevant " } ,
} ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_basic_projection_expressions_using_query ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
dynamodb . create_table (
TableName = " users " ,
KeySchema = [
{ " AttributeName " : " forum_name " , " KeyType " : " HASH " } ,
{ " AttributeName " : " subject " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " forum_name " , " AttributeType " : " S " } ,
{ " AttributeName " : " subject " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item (
Item = { " forum_name " : " the-key " , " subject " : " 123 " , " body " : " some test message " }
)
table . put_item (
Item = {
" forum_name " : " not-the-key " ,
" subject " : " 123 " ,
" body " : " some other test message " ,
}
)
# Test a query returning all items
result = table . query (
KeyConditionExpression = Key ( " forum_name " ) . eq ( " the-key " ) ,
ProjectionExpression = " body, subject " ,
) [ " Items " ] [ 0 ]
assert " body " in result
assert result [ " body " ] == " some test message "
assert " subject " in result
assert " forum_name " not in result
table . put_item (
Item = {
" forum_name " : " the-key " ,
" subject " : " 1234 " ,
" body " : " yet another test message " ,
}
)
items = table . query (
KeyConditionExpression = Key ( " forum_name " ) . eq ( " the-key " ) ,
ProjectionExpression = " body " ,
) [ " Items " ]
assert " body " in items [ 0 ]
assert " subject " not in items [ 0 ]
assert items [ 0 ] [ " body " ] == " some test message "
assert " body " in items [ 1 ]
assert " subject " not in items [ 1 ]
assert items [ 1 ] [ " body " ] == " yet another test message "
# The projection expression should not remove data from storage
items = table . query ( KeyConditionExpression = Key ( " forum_name " ) . eq ( " the-key " ) ) [ " Items " ]
assert " subject " in items [ 0 ]
assert " body " in items [ 1 ]
assert " forum_name " in items [ 1 ]
@mock_dynamodb
def test_nested_projection_expression_using_query ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
dynamodb . create_table (
TableName = " users " ,
2023-07-13 10:21:47 +00:00
KeySchema = [ { " AttributeName " : " name " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " name " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item (
Item = {
2023-07-13 10:21:47 +00:00
" name " : " key1 " ,
2022-03-09 17:57:25 +00:00
" nested " : {
" level1 " : { " id " : " id1 " , " att " : " irrelevant " } ,
" level2 " : { " id " : " id2 " , " include " : " all " } ,
" level3 " : { " id " : " irrelevant " } ,
} ,
" foo " : " bar " ,
}
)
table . put_item (
Item = {
2023-07-13 10:21:47 +00:00
" name " : " key2 " ,
2022-03-09 17:57:25 +00:00
" nested " : { " id " : " id2 " , " incode " : " code2 " } ,
" foo " : " bar " ,
}
)
# Test a query returning all items
result = table . query (
2023-07-13 10:21:47 +00:00
KeyConditionExpression = Key ( " name " ) . eq ( " key1 " ) ,
2022-03-09 17:57:25 +00:00
ProjectionExpression = " nested.level1.id, nested.level2 " ,
) [ " Items " ] [ 0 ]
assert " nested " in result
2023-07-13 10:21:47 +00:00
assert result [ " nested " ] == {
" level1 " : { " id " : " id1 " } ,
" level2 " : { " id " : " id2 " , " include " : " all " } ,
}
2022-03-09 17:57:25 +00:00
assert " foo " not in result
# Assert actual data has not been deleted
2023-07-13 10:21:47 +00:00
result = table . query ( KeyConditionExpression = Key ( " name " ) . eq ( " key1 " ) ) [ " Items " ] [ 0 ]
assert result == {
" foo " : " bar " ,
" name " : " key1 " ,
" nested " : {
" level1 " : { " id " : " id1 " , " att " : " irrelevant " } ,
" level2 " : { " id " : " id2 " , " include " : " all " } ,
" level3 " : { " id " : " irrelevant " } ,
} ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_nested_projection_expression_using_scan ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
dynamodb . create_table (
TableName = " users " ,
KeySchema = [ { " AttributeName " : " forum_name " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " forum_name " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item (
Item = {
" forum_name " : " key1 " ,
" nested " : {
" level1 " : { " id " : " id1 " , " att " : " irrelevant " } ,
" level2 " : { " id " : " id2 " , " include " : " all " } ,
" level3 " : { " id " : " irrelevant " } ,
} ,
" foo " : " bar " ,
}
)
table . put_item (
Item = {
" forum_name " : " key2 " ,
" nested " : { " id " : " id2 " , " incode " : " code2 " } ,
" foo " : " bar " ,
}
)
# Test a scan
results = table . scan (
FilterExpression = Key ( " forum_name " ) . eq ( " key1 " ) ,
ProjectionExpression = " nested.level1.id, nested.level2 " ,
) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert results == [
{
" nested " : {
" level1 " : { " id " : " id1 " } ,
" level2 " : { " include " : " all " , " id " : " id2 " } ,
2022-03-09 17:57:25 +00:00
}
2023-07-13 10:21:47 +00:00
}
]
2022-03-09 17:57:25 +00:00
# Assert original data is still there
results = table . scan ( FilterExpression = Key ( " forum_name " ) . eq ( " key1 " ) ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert results == [
{
" forum_name " : " key1 " ,
" foo " : " bar " ,
" nested " : {
" level1 " : { " att " : " irrelevant " , " id " : " id1 " } ,
" level2 " : { " include " : " all " , " id " : " id2 " } ,
" level3 " : { " id " : " irrelevant " } ,
} ,
}
]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_basic_projection_expression_using_get_item_with_attr_expression_names ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
table = dynamodb . create_table (
TableName = " users " ,
KeySchema = [
{ " AttributeName " : " forum_name " , " KeyType " : " HASH " } ,
{ " AttributeName " : " subject " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " forum_name " , " AttributeType " : " S " } ,
{ " AttributeName " : " subject " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item (
Item = {
" forum_name " : " the-key " ,
" subject " : " 123 " ,
" body " : " some test message " ,
" attachment " : " something " ,
}
)
table . put_item (
Item = {
" forum_name " : " not-the-key " ,
" subject " : " 123 " ,
" body " : " some other test message " ,
" attachment " : " something " ,
}
)
result = table . get_item (
Key = { " forum_name " : " the-key " , " subject " : " 123 " } ,
ProjectionExpression = " #rl, #rt, subject " ,
ExpressionAttributeNames = { " #rl " : " body " , " #rt " : " attachment " } ,
)
2023-07-13 10:21:47 +00:00
assert result [ " Item " ] == {
" subject " : " 123 " ,
" body " : " some test message " ,
" attachment " : " something " ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_basic_projection_expressions_using_query_with_attr_expression_names ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
table = dynamodb . create_table (
TableName = " users " ,
KeySchema = [
{ " AttributeName " : " forum_name " , " KeyType " : " HASH " } ,
{ " AttributeName " : " subject " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " forum_name " , " AttributeType " : " S " } ,
{ " AttributeName " : " subject " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item (
Item = {
" forum_name " : " the-key " ,
" subject " : " 123 " ,
" body " : " some test message " ,
" attachment " : " something " ,
}
)
table . put_item (
Item = {
" forum_name " : " not-the-key " ,
" subject " : " 123 " ,
" body " : " some other test message " ,
" attachment " : " something " ,
}
)
# Test a query returning all items
results = table . query (
KeyConditionExpression = Key ( " forum_name " ) . eq ( " the-key " ) ,
ProjectionExpression = " #rl, #rt, subject " ,
ExpressionAttributeNames = { " #rl " : " body " , " #rt " : " attachment " } ,
)
assert results [ " Items " ] [ 0 ] [ " body " ] == " some test message "
assert results [ " Items " ] [ 0 ] [ " subject " ] == " 123 "
assert results [ " Items " ] [ 0 ] [ " attachment " ] == " something "
@mock_dynamodb
def test_nested_projection_expression_using_get_item_with_attr_expression ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
dynamodb . create_table (
TableName = " users " ,
KeySchema = [ { " AttributeName " : " forum_name " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " forum_name " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item (
Item = {
" forum_name " : " key1 " ,
" nested " : {
" level1 " : { " id " : " id1 " , " att " : " irrelevant " } ,
2023-08-21 21:52:58 +00:00
" level.2 " : { " id " : " id2 " , " include " : " all " } ,
2023-06-08 17:10:14 +00:00
" level3 " : {
" id " : " irrelevant " ,
" children " : [ { " Name " : " child_a " } , { " Name " : " child_b " } ] ,
} ,
2022-03-09 17:57:25 +00:00
} ,
" foo " : " bar " ,
}
)
table . put_item (
Item = {
" forum_name " : " key2 " ,
" nested " : { " id " : " id2 " , " incode " : " code2 " } ,
" foo " : " bar " ,
}
)
# Test a get_item returning all items
result = table . get_item (
Key = { " forum_name " : " key1 " } ,
ProjectionExpression = " #nst.level1.id, #nst.#lvl2 " ,
2023-08-21 21:52:58 +00:00
ExpressionAttributeNames = { " #nst " : " nested " , " #lvl2 " : " level.2 " } ,
2022-03-09 17:57:25 +00:00
) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result == {
2023-08-21 21:52:58 +00:00
" nested " : { " level1 " : { " id " : " id1 " } , " level.2 " : { " id " : " id2 " , " include " : " all " } }
2023-07-13 10:21:47 +00:00
}
2022-03-09 17:57:25 +00:00
# Assert actual data has not been deleted
result = table . get_item ( Key = { " forum_name " : " key1 " } ) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result == {
" foo " : " bar " ,
" forum_name " : " key1 " ,
" nested " : {
" level1 " : { " id " : " id1 " , " att " : " irrelevant " } ,
2023-08-21 21:52:58 +00:00
" level.2 " : { " id " : " id2 " , " include " : " all " } ,
2023-07-13 10:21:47 +00:00
" level3 " : {
" id " : " irrelevant " ,
" children " : [ { " Name " : " child_a " } , { " Name " : " child_b " } ] ,
2022-03-09 17:57:25 +00:00
} ,
2023-07-13 10:21:47 +00:00
} ,
}
2022-03-09 17:57:25 +00:00
2023-06-08 17:10:14 +00:00
# Test a get_item retrieving children
result = table . get_item (
Key = { " forum_name " : " key1 " } ,
ProjectionExpression = " nested.level3.children[0].Name " ,
) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result == { " nested " : { " level3 " : { " children " : [ { " Name " : " child_a " } ] } } }
2023-06-08 17:10:14 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_nested_projection_expression_using_query_with_attr_expression_names ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
dynamodb . create_table (
TableName = " users " ,
2023-07-13 10:21:47 +00:00
KeySchema = [ { " AttributeName " : " name " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " name " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item (
Item = {
2023-07-13 10:21:47 +00:00
" name " : " key1 " ,
2022-03-09 17:57:25 +00:00
" nested " : {
" level1 " : { " id " : " id1 " , " att " : " irrelevant " } ,
" level2 " : { " id " : " id2 " , " include " : " all " } ,
" level3 " : { " id " : " irrelevant " } ,
} ,
" foo " : " bar " ,
}
)
table . put_item (
Item = {
2023-07-13 10:21:47 +00:00
" name " : " key2 " ,
2022-03-09 17:57:25 +00:00
" nested " : { " id " : " id2 " , " incode " : " code2 " } ,
" foo " : " bar " ,
}
)
# Test a query returning all items
result = table . query (
2023-07-13 10:21:47 +00:00
KeyConditionExpression = Key ( " name " ) . eq ( " key1 " ) ,
2022-03-09 17:57:25 +00:00
ProjectionExpression = " #nst.level1.id, #nst.#lvl2 " ,
ExpressionAttributeNames = { " #nst " : " nested " , " #lvl2 " : " level2 " } ,
) [ " Items " ] [ 0 ]
2023-07-13 10:21:47 +00:00
assert result [ " nested " ] == {
" level1 " : { " id " : " id1 " } ,
" level2 " : { " id " : " id2 " , " include " : " all " } ,
}
2022-03-09 17:57:25 +00:00
assert " foo " not in result
# Assert actual data has not been deleted
2023-07-13 10:21:47 +00:00
result = table . query ( KeyConditionExpression = Key ( " name " ) . eq ( " key1 " ) ) [ " Items " ] [ 0 ]
assert result == {
" foo " : " bar " ,
" name " : " key1 " ,
" nested " : {
" level1 " : { " id " : " id1 " , " att " : " irrelevant " } ,
" level2 " : { " id " : " id2 " , " include " : " all " } ,
" level3 " : { " id " : " irrelevant " } ,
} ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_basic_projection_expressions_using_scan_with_attr_expression_names ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
table = dynamodb . create_table (
TableName = " users " ,
KeySchema = [
{ " AttributeName " : " forum_name " , " KeyType " : " HASH " } ,
{ " AttributeName " : " subject " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " forum_name " , " AttributeType " : " S " } ,
{ " AttributeName " : " subject " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item (
Item = {
" forum_name " : " the-key " ,
" subject " : " 123 " ,
" body " : " some test message " ,
" attachment " : " something " ,
}
)
table . put_item (
Item = {
" forum_name " : " not-the-key " ,
" subject " : " 123 " ,
" body " : " some other test message " ,
" attachment " : " something " ,
}
)
# Test a scan returning all items
results = table . scan (
FilterExpression = Key ( " forum_name " ) . eq ( " the-key " ) ,
ProjectionExpression = " #rl, #rt, subject " ,
ExpressionAttributeNames = { " #rl " : " body " , " #rt " : " attachment " } ,
)
assert " body " in results [ " Items " ] [ 0 ]
assert " attachment " in results [ " Items " ] [ 0 ]
assert " subject " in results [ " Items " ] [ 0 ]
assert " form_name " not in results [ " Items " ] [ 0 ]
# Test without a FilterExpression
results = table . scan (
ProjectionExpression = " #rl, #rt, subject " ,
ExpressionAttributeNames = { " #rl " : " body " , " #rt " : " attachment " } ,
)
assert " body " in results [ " Items " ] [ 0 ]
assert " attachment " in results [ " Items " ] [ 0 ]
assert " subject " in results [ " Items " ] [ 0 ]
assert " form_name " not in results [ " Items " ] [ 0 ]
@mock_dynamodb
def test_nested_projection_expression_using_scan_with_attr_expression_names ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
dynamodb . create_table (
TableName = " users " ,
KeySchema = [ { " AttributeName " : " forum_name " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " forum_name " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item (
Item = {
" forum_name " : " key1 " ,
" nested " : {
" level1 " : { " id " : " id1 " , " att " : " irrelevant " } ,
" level2 " : { " id " : " id2 " , " include " : " all " } ,
" level3 " : { " id " : " irrelevant " } ,
} ,
" foo " : " bar " ,
}
)
table . put_item (
Item = {
" forum_name " : " key2 " ,
" nested " : { " id " : " id2 " , " incode " : " code2 " } ,
" foo " : " bar " ,
}
)
# Test a scan
results = table . scan (
FilterExpression = Key ( " forum_name " ) . eq ( " key1 " ) ,
ProjectionExpression = " nested.level1.id, nested.level2 " ,
ExpressionAttributeNames = { " #nst " : " nested " , " #lvl2 " : " level2 " } ,
) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert results == [
{
" nested " : {
" level1 " : { " id " : " id1 " } ,
" level2 " : { " include " : " all " , " id " : " id2 " } ,
2022-03-09 17:57:25 +00:00
}
2023-07-13 10:21:47 +00:00
}
]
2022-03-09 17:57:25 +00:00
# Assert original data is still there
results = table . scan ( FilterExpression = Key ( " forum_name " ) . eq ( " key1 " ) ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert results == [
{
" forum_name " : " key1 " ,
" foo " : " bar " ,
" nested " : {
" level1 " : { " att " : " irrelevant " , " id " : " id1 " } ,
" level2 " : { " include " : " all " , " id " : " id2 " } ,
" level3 " : { " id " : " irrelevant " } ,
} ,
}
]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_put_empty_item ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
2022-03-10 14:39:59 +00:00
AttributeDefinitions = [ { " AttributeName " : " structure_id " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
TableName = " test " ,
2022-03-10 14:39:59 +00:00
KeySchema = [ { " AttributeName " : " structure_id " , " KeyType " : " HASH " } ] ,
2022-03-09 17:57:25 +00:00
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
table = dynamodb . Table ( " test " )
with pytest . raises ( ClientError ) as ex :
table . put_item ( Item = { } )
2023-07-13 10:21:47 +00:00
assert (
ex . value . response [ " Error " ] [ " Message " ]
== " One or more parameter values were invalid: Missing the key structure_id in the item "
2022-03-09 17:57:25 +00:00
)
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ValidationException "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_put_item_nonexisting_hash_key ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
2022-03-10 14:39:59 +00:00
AttributeDefinitions = [ { " AttributeName " : " structure_id " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
TableName = " test " ,
2022-03-10 14:39:59 +00:00
KeySchema = [ { " AttributeName " : " structure_id " , " KeyType " : " HASH " } ] ,
2022-03-09 17:57:25 +00:00
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
table = dynamodb . Table ( " test " )
with pytest . raises ( ClientError ) as ex :
table . put_item ( Item = { " a_terribly_misguided_id_attribute " : " abcdef " } )
2023-07-13 10:21:47 +00:00
assert (
ex . value . response [ " Error " ] [ " Message " ]
== " One or more parameter values were invalid: Missing the key structure_id in the item "
2022-03-09 17:57:25 +00:00
)
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ValidationException "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_put_item_nonexisting_range_key ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
AttributeDefinitions = [
{ " AttributeName " : " structure_id " , " AttributeType " : " S " } ,
{ " AttributeName " : " added_at " , " AttributeType " : " N " } ,
] ,
TableName = " test " ,
KeySchema = [
{ " AttributeName " : " structure_id " , " KeyType " : " HASH " } ,
{ " AttributeName " : " added_at " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
table = dynamodb . Table ( " test " )
with pytest . raises ( ClientError ) as ex :
table . put_item ( Item = { " structure_id " : " abcdef " } )
2023-07-13 10:21:47 +00:00
assert (
ex . value . response [ " Error " ] [ " Message " ]
== " One or more parameter values were invalid: Missing the key added_at in the item "
2022-03-09 17:57:25 +00:00
)
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ValidationException "
2022-03-09 17:57:25 +00:00
def test_filter_expression ( ) :
row1 = moto . dynamodb . models . Item (
hash_key = None ,
range_key = None ,
attrs = {
" Id " : { " N " : " 8 " } ,
" Subs " : { " N " : " 5 " } ,
" Des " : { " S " : " Some description " } ,
" KV " : { " SS " : [ " test1 " , " test2 " ] } ,
} ,
)
row2 = moto . dynamodb . models . Item (
hash_key = None ,
range_key = None ,
attrs = {
" Id " : { " N " : " 8 " } ,
" Subs " : { " N " : " 10 " } ,
" Des " : { " S " : " A description " } ,
" KV " : { " SS " : [ " test3 " , " test4 " ] } ,
} ,
)
# NOT test 1
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" NOT attribute_not_exists(Id) " , { } , { }
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
2022-03-09 17:57:25 +00:00
# NOT test 2
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" NOT (Id = :v0) " , { } , { " :v0 " : { " N " : " 8 " } }
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is False # Id = 8 so should be false
2022-03-09 17:57:25 +00:00
# AND test
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" Id > :v0 AND Subs < :v1 " , { } , { " :v0 " : { " N " : " 5 " } , " :v1 " : { " N " : " 7 " } }
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
assert filter_expr . expr ( row2 ) is False
2022-03-09 17:57:25 +00:00
# lowercase AND test
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" Id > :v0 and Subs < :v1 " , { } , { " :v0 " : { " N " : " 5 " } , " :v1 " : { " N " : " 7 " } }
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
assert filter_expr . expr ( row2 ) is False
2022-03-09 17:57:25 +00:00
# OR test
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" Id = :v0 OR Id=:v1 " , { } , { " :v0 " : { " N " : " 5 " } , " :v1 " : { " N " : " 8 " } }
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
2022-03-09 17:57:25 +00:00
# BETWEEN test
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" Id BETWEEN :v0 AND :v1 " , { } , { " :v0 " : { " N " : " 5 " } , " :v1 " : { " N " : " 10 " } }
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
2022-03-09 17:57:25 +00:00
# BETWEEN integer test
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" Id BETWEEN :v0 AND :v1 " , { } , { " :v0 " : { " N " : " 0 " } , " :v1 " : { " N " : " 10 " } }
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
2022-03-09 17:57:25 +00:00
# PAREN test
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" Id = :v0 AND (Subs = :v0 OR Subs = :v1) " ,
{ } ,
{ " :v0 " : { " N " : " 8 " } , " :v1 " : { " N " : " 5 " } } ,
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
2022-03-09 17:57:25 +00:00
# IN test
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" Id IN (:v0, :v1, :v2) " ,
{ } ,
{ " :v0 " : { " N " : " 7 " } , " :v1 " : { " N " : " 8 " } , " :v2 " : { " N " : " 9 " } } ,
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
2022-03-09 17:57:25 +00:00
# attribute function tests (with extra spaces)
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" attribute_exists(Id) AND attribute_not_exists (UnknownAttribute) " , { } , { }
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
2022-03-09 17:57:25 +00:00
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" attribute_type(Id, :v0) " , { } , { " :v0 " : { " S " : " N " } }
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
2022-03-09 17:57:25 +00:00
# beginswith function test
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" begins_with(Des, :v0) " , { } , { " :v0 " : { " S " : " Some " } }
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
assert filter_expr . expr ( row2 ) is False
2013-02-19 13:26:05 +00:00
2022-03-09 17:57:25 +00:00
# contains function test
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" contains(KV, :v0) " , { } , { " :v0 " : { " S " : " test1 " } }
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
assert filter_expr . expr ( row2 ) is False
2022-03-09 17:57:25 +00:00
# size function test
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" size(Des) > size(KV) " , { } , { }
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
2022-03-09 17:57:25 +00:00
# Expression from @batkuip
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" (#n0 < :v0 AND attribute_not_exists(#n1)) " ,
{ " #n0 " : " Subs " , " #n1 " : " fanout_ts " } ,
{ " :v0 " : { " N " : " 7 " } } ,
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
2022-03-09 17:57:25 +00:00
# Expression from to check contains on string value
filter_expr = moto . dynamodb . comparisons . get_filter_expression (
" contains(#n0, :v0) " , { " #n0 " : " Des " } , { " :v0 " : { " S " : " Some " } }
)
2023-07-13 10:21:47 +00:00
assert filter_expr . expr ( row1 ) is True
assert filter_expr . expr ( row2 ) is False
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_query_filter ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
client . put_item (
TableName = " test1 " ,
Item = {
" client " : { " S " : " client1 " } ,
" app " : { " S " : " app1 " } ,
" nested " : {
" M " : {
" version " : { " S " : " version1 " } ,
" contents " : { " L " : [ { " S " : " value1 " } , { " S " : " value2 " } ] } ,
}
} ,
} ,
)
client . put_item (
TableName = " test1 " ,
Item = {
" client " : { " S " : " client1 " } ,
" app " : { " S " : " app2 " } ,
" nested " : {
" M " : {
" version " : { " S " : " version2 " } ,
" contents " : { " L " : [ { " S " : " value1 " } , { " S " : " value2 " } ] } ,
}
} ,
} ,
)
table = dynamodb . Table ( " test1 " )
response = table . query ( KeyConditionExpression = Key ( " client " ) . eq ( " client1 " ) )
assert response [ " Count " ] == 2
response = table . query (
KeyConditionExpression = Key ( " client " ) . eq ( " client1 " ) ,
FilterExpression = Attr ( " app " ) . eq ( " app2 " ) ,
)
assert response [ " Count " ] == 1
assert response [ " Items " ] [ 0 ] [ " app " ] == " app2 "
response = table . query (
KeyConditionExpression = Key ( " client " ) . eq ( " client1 " ) ,
FilterExpression = Attr ( " app " ) . contains ( " app " ) ,
)
assert response [ " Count " ] == 2
response = table . query (
KeyConditionExpression = Key ( " client " ) . eq ( " client1 " ) ,
FilterExpression = Attr ( " nested.version " ) . contains ( " version " ) ,
)
assert response [ " Count " ] == 2
response = table . query (
KeyConditionExpression = Key ( " client " ) . eq ( " client1 " ) ,
FilterExpression = Attr ( " nested.contents[0] " ) . eq ( " value1 " ) ,
)
assert response [ " Count " ] == 2
@mock_dynamodb
def test_query_filter_overlapping_expression_prefixes ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
client . put_item (
TableName = " test1 " ,
Item = {
" client " : { " S " : " client1 " } ,
" app " : { " S " : " app1 " } ,
" nested " : {
" M " : {
" version " : { " S " : " version1 " } ,
" contents " : { " L " : [ { " S " : " value1 " } , { " S " : " value2 " } ] } ,
}
} ,
} ,
)
table = dynamodb . Table ( " test1 " )
response = table . query (
KeyConditionExpression = Key ( " client " ) . eq ( " client1 " ) & Key ( " app " ) . eq ( " app1 " ) ,
ProjectionExpression = " #1, #10, nested " ,
ExpressionAttributeNames = { " #1 " : " client " , " #10 " : " app " } ,
)
assert response [ " Count " ] == 1
assert response [ " Items " ] [ 0 ] == {
" client " : " client1 " ,
" app " : " app1 " ,
" nested " : { " version " : " version1 " , " contents " : [ " value1 " , " value2 " ] } ,
}
@mock_dynamodb
def test_scan_filter ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
client . put_item (
TableName = " test1 " , Item = { " client " : { " S " : " client1 " } , " app " : { " S " : " app1 " } }
)
table = dynamodb . Table ( " test1 " )
response = table . scan ( FilterExpression = Attr ( " app " ) . eq ( " app2 " ) )
assert response [ " Count " ] == 0
response = table . scan ( FilterExpression = Attr ( " app " ) . eq ( " app1 " ) )
assert response [ " Count " ] == 1
response = table . scan ( FilterExpression = Attr ( " app " ) . ne ( " app2 " ) )
assert response [ " Count " ] == 1
response = table . scan ( FilterExpression = Attr ( " app " ) . ne ( " app1 " ) )
assert response [ " Count " ] == 0
@mock_dynamodb
def test_scan_filter2 ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " N " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
client . put_item (
TableName = " test1 " , Item = { " client " : { " S " : " client1 " } , " app " : { " N " : " 1 " } }
)
response = client . scan (
TableName = " test1 " ,
Select = " ALL_ATTRIBUTES " ,
FilterExpression = " #tb >= :dt " ,
ExpressionAttributeNames = { " #tb " : " app " } ,
ExpressionAttributeValues = { " :dt " : { " N " : str ( 1 ) } } ,
)
assert response [ " Count " ] == 1
@mock_dynamodb
def test_scan_filter3 ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " N " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
client . put_item (
TableName = " test1 " ,
Item = { " client " : { " S " : " client1 " } , " app " : { " N " : " 1 " } , " active " : { " BOOL " : True } } ,
)
table = dynamodb . Table ( " test1 " )
response = table . scan ( FilterExpression = Attr ( " active " ) . eq ( True ) )
assert response [ " Count " ] == 1
response = table . scan ( FilterExpression = Attr ( " active " ) . ne ( True ) )
assert response [ " Count " ] == 0
response = table . scan ( FilterExpression = Attr ( " active " ) . ne ( False ) )
assert response [ " Count " ] == 1
response = table . scan ( FilterExpression = Attr ( " app " ) . ne ( 1 ) )
assert response [ " Count " ] == 0
response = table . scan ( FilterExpression = Attr ( " app " ) . ne ( 2 ) )
assert response [ " Count " ] == 1
@mock_dynamodb
def test_scan_filter4 ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " N " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
table = dynamodb . Table ( " test1 " )
response = table . scan (
FilterExpression = Attr ( " epoch_ts " ) . lt ( 7 ) & Attr ( " fanout_ts " ) . not_exists ( )
)
# Just testing
assert response [ " Count " ] == 0
@mock_dynamodb
def test_scan_filter_should_not_return_non_existing_attributes ( ) :
table_name = " my-table "
item = { " partitionKey " : " pk-2 " , " my-attr " : 42 }
# Create table
res = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
res . create_table (
TableName = table_name ,
KeySchema = [ { " AttributeName " : " partitionKey " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " partitionKey " , " AttributeType " : " S " } ] ,
BillingMode = " PAY_PER_REQUEST " ,
)
table = res . Table ( table_name )
# Insert items
table . put_item ( Item = { " partitionKey " : " pk-1 " } )
table . put_item ( Item = item )
# Verify a few operations
# Assert we only find the item that has this attribute
2023-07-13 10:21:47 +00:00
assert table . scan ( FilterExpression = Attr ( " my-attr " ) . lt ( 43 ) ) [ " Items " ] == [ item ]
assert table . scan ( FilterExpression = Attr ( " my-attr " ) . lte ( 42 ) ) [ " Items " ] == [ item ]
assert table . scan ( FilterExpression = Attr ( " my-attr " ) . gte ( 42 ) ) [ " Items " ] == [ item ]
assert table . scan ( FilterExpression = Attr ( " my-attr " ) . gt ( 41 ) ) [ " Items " ] == [ item ]
2022-03-09 17:57:25 +00:00
# Sanity check that we can't find the item if the FE is wrong
2023-07-13 10:21:47 +00:00
assert table . scan ( FilterExpression = Attr ( " my-attr " ) . gt ( 43 ) ) [ " Items " ] == [ ]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_bad_scan_filter ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
table = dynamodb . Table ( " test1 " )
# Bad expression
2022-04-09 21:45:25 +00:00
with pytest . raises ( ClientError ) as exc :
2022-03-09 17:57:25 +00:00
table . scan ( FilterExpression = " client test " )
2023-07-13 10:21:47 +00:00
assert exc . value . response [ " Error " ] [ " Code " ] == " ValidationException "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_duplicate_create ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
2023-07-13 10:21:47 +00:00
with pytest . raises ( ClientError ) as exc :
2022-03-09 17:57:25 +00:00
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
2023-07-13 10:21:47 +00:00
err = exc . value . response [ " Error " ]
assert err [ " Code " ] == " ResourceInUseException "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_delete_table ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
client . delete_table ( TableName = " test1 " )
resp = client . list_tables ( )
2023-07-13 10:21:47 +00:00
assert len ( resp [ " TableNames " ] ) == 0
2022-03-09 17:57:25 +00:00
2023-07-13 10:21:47 +00:00
with pytest . raises ( ClientError ) as err :
2022-03-09 17:57:25 +00:00
client . delete_table ( TableName = " test1 " )
2023-07-13 10:21:47 +00:00
assert err . value . response [ " Error " ] [ " Code " ] == " ResourceNotFoundException "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_delete_item ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
client . put_item (
TableName = " test1 " , Item = { " client " : { " S " : " client1 " } , " app " : { " S " : " app1 " } }
)
client . put_item (
TableName = " test1 " , Item = { " client " : { " S " : " client1 " } , " app " : { " S " : " app2 " } }
)
table = dynamodb . Table ( " test1 " )
response = table . scan ( )
assert response [ " Count " ] == 2
# Test ReturnValues validation
with pytest . raises ( ClientError ) as ex :
table . delete_item (
Key = { " client " : " client1 " , " app " : " app1 " } , ReturnValues = " ALL_NEW "
)
err = ex . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert err [ " Code " ] == " ValidationException "
assert err [ " Message " ] == " Return values set to invalid value "
2022-03-09 17:57:25 +00:00
# Test deletion and returning old value
response = table . delete_item (
Key = { " client " : " client1 " , " app " : " app1 " } , ReturnValues = " ALL_OLD "
)
2023-07-13 10:21:47 +00:00
assert " client " in response [ " Attributes " ]
assert " app " in response [ " Attributes " ]
2022-03-09 17:57:25 +00:00
response = table . scan ( )
assert response [ " Count " ] == 1
# Test deletion returning nothing
response = table . delete_item ( Key = { " client " : " client1 " , " app " : " app2 " } )
2023-07-13 10:21:47 +00:00
assert len ( response [ " Attributes " ] ) == 0
2022-03-09 17:57:25 +00:00
response = table . scan ( )
assert response [ " Count " ] == 0
2023-06-27 21:06:49 +00:00
@mock_dynamodb
def test_delete_item_error ( ) :
# Setup
client = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
BillingMode = " PAY_PER_REQUEST " ,
)
table = client . Table ( " test1 " )
table . delete ( )
# Execute
with pytest . raises ( ClientError ) as ex :
table . delete_item (
Key = { " client " : " client1 " , " app " : " app1 " } ,
)
# Verify
err = ex . value . response [ " Error " ]
assert err [ " Code " ] == " ResourceNotFoundException "
assert err [ " Message " ] == " Requested resource not found "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_describe_limits ( ) :
client = boto3 . client ( " dynamodb " , region_name = " eu-central-1 " )
resp = client . describe_limits ( )
2023-07-13 10:21:47 +00:00
assert resp [ " AccountMaxReadCapacityUnits " ] == 20000
assert resp [ " AccountMaxWriteCapacityUnits " ] == 20000
assert resp [ " TableMaxWriteCapacityUnits " ] == 10000
assert resp [ " TableMaxReadCapacityUnits " ] == 10000
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_set_ttl ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
client . update_time_to_live (
TableName = " test1 " ,
TimeToLiveSpecification = { " Enabled " : True , " AttributeName " : " expire " } ,
)
resp = client . describe_time_to_live ( TableName = " test1 " )
2023-07-13 10:21:47 +00:00
assert resp [ " TimeToLiveDescription " ] [ " TimeToLiveStatus " ] == " ENABLED "
assert resp [ " TimeToLiveDescription " ] [ " AttributeName " ] == " expire "
2022-03-09 17:57:25 +00:00
client . update_time_to_live (
TableName = " test1 " ,
TimeToLiveSpecification = { " Enabled " : False , " AttributeName " : " expire " } ,
)
resp = client . describe_time_to_live ( TableName = " test1 " )
2023-07-13 10:21:47 +00:00
assert resp [ " TimeToLiveDescription " ] [ " TimeToLiveStatus " ] == " DISABLED "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_describe_continuous_backups ( ) :
# given
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
table_name = client . create_table (
TableName = " test " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
BillingMode = " PAY_PER_REQUEST " ,
) [ " TableDescription " ] [ " TableName " ]
# when
response = client . describe_continuous_backups ( TableName = table_name )
# then
2023-07-13 10:21:47 +00:00
assert response [ " ContinuousBackupsDescription " ] == {
" ContinuousBackupsStatus " : " ENABLED " ,
" PointInTimeRecoveryDescription " : { " PointInTimeRecoveryStatus " : " DISABLED " } ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_describe_continuous_backups_errors ( ) :
# given
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
# when
2023-11-09 23:17:46 +00:00
with pytest . raises ( ClientError ) as e :
2022-03-09 17:57:25 +00:00
client . describe_continuous_backups ( TableName = " not-existing-table " )
# then
ex = e . value
2023-07-13 10:21:47 +00:00
assert ex . operation_name == " DescribeContinuousBackups "
assert ex . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
assert ex . response [ " Error " ] [ " Code " ] == " TableNotFoundException "
assert ex . response [ " Error " ] [ " Message " ] == " Table not found: not-existing-table "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_continuous_backups ( ) :
# given
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
table_name = client . create_table (
TableName = " test " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
BillingMode = " PAY_PER_REQUEST " ,
) [ " TableDescription " ] [ " TableName " ]
# when
response = client . update_continuous_backups (
TableName = table_name ,
PointInTimeRecoverySpecification = { " PointInTimeRecoveryEnabled " : True } ,
)
# then
2023-07-13 10:21:47 +00:00
assert (
response [ " ContinuousBackupsDescription " ] [ " ContinuousBackupsStatus " ] == " ENABLED "
2022-03-09 17:57:25 +00:00
)
point_in_time = response [ " ContinuousBackupsDescription " ] [
" PointInTimeRecoveryDescription "
]
earliest_datetime = point_in_time [ " EarliestRestorableDateTime " ]
2023-07-13 10:21:47 +00:00
assert isinstance ( earliest_datetime , datetime )
2022-03-09 17:57:25 +00:00
latest_datetime = point_in_time [ " LatestRestorableDateTime " ]
2023-07-13 10:21:47 +00:00
assert isinstance ( latest_datetime , datetime )
assert point_in_time [ " PointInTimeRecoveryStatus " ] == " ENABLED "
2022-03-09 17:57:25 +00:00
# when
# a second update should not change anything
response = client . update_continuous_backups (
TableName = table_name ,
PointInTimeRecoverySpecification = { " PointInTimeRecoveryEnabled " : True } ,
)
# then
2023-07-13 10:21:47 +00:00
assert (
response [ " ContinuousBackupsDescription " ] [ " ContinuousBackupsStatus " ] == " ENABLED "
2022-03-09 17:57:25 +00:00
)
point_in_time = response [ " ContinuousBackupsDescription " ] [
" PointInTimeRecoveryDescription "
]
2023-07-13 10:21:47 +00:00
assert point_in_time [ " EarliestRestorableDateTime " ] == earliest_datetime
assert point_in_time [ " LatestRestorableDateTime " ] == latest_datetime
assert point_in_time [ " PointInTimeRecoveryStatus " ] == " ENABLED "
2022-03-09 17:57:25 +00:00
# when
response = client . update_continuous_backups (
TableName = table_name ,
PointInTimeRecoverySpecification = { " PointInTimeRecoveryEnabled " : False } ,
)
# then
2023-07-13 10:21:47 +00:00
assert response [ " ContinuousBackupsDescription " ] == {
" ContinuousBackupsStatus " : " ENABLED " ,
" PointInTimeRecoveryDescription " : { " PointInTimeRecoveryStatus " : " DISABLED " } ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_continuous_backups_errors ( ) :
# given
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
# when
2023-11-09 23:17:46 +00:00
with pytest . raises ( ClientError ) as e :
2022-03-09 17:57:25 +00:00
client . update_continuous_backups (
TableName = " not-existing-table " ,
PointInTimeRecoverySpecification = { " PointInTimeRecoveryEnabled " : True } ,
)
# then
ex = e . value
2023-07-13 10:21:47 +00:00
assert ex . operation_name == " UpdateContinuousBackups "
assert ex . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
assert ex . response [ " Error " ] [ " Code " ] == " TableNotFoundException "
assert ex . response [ " Error " ] [ " Message " ] == " Table not found: not-existing-table "
2022-03-09 17:57:25 +00:00
2023-01-07 11:35:14 +00:00
# https://github.com/getmoto/moto/issues/1043
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_query_missing_expr_names ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
client . create_table (
TableName = " test1 " ,
AttributeDefinitions = [
{ " AttributeName " : " client " , " AttributeType " : " S " } ,
{ " AttributeName " : " app " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " client " , " KeyType " : " HASH " } ,
{ " AttributeName " : " app " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 123 , " WriteCapacityUnits " : 123 } ,
)
client . put_item (
TableName = " test1 " , Item = { " client " : { " S " : " test1 " } , " app " : { " S " : " test1 " } }
)
client . put_item (
TableName = " test1 " , Item = { " client " : { " S " : " test2 " } , " app " : { " S " : " test2 " } }
)
resp = client . query (
TableName = " test1 " ,
KeyConditionExpression = " client=:client " ,
ExpressionAttributeValues = { " :client " : { " S " : " test1 " } } ,
)
2023-07-13 10:21:47 +00:00
assert resp [ " Count " ] == 1
assert resp [ " Items " ] [ 0 ] [ " client " ] [ " S " ] == " test1 "
2022-03-09 17:57:25 +00:00
2023-01-07 11:35:14 +00:00
# https://github.com/getmoto/moto/issues/2328
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_item_with_list ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
dynamodb . create_table (
TableName = " Table " ,
KeySchema = [ { " AttributeName " : " key " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " key " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
)
table = dynamodb . Table ( " Table " )
table . update_item (
Key = { " key " : " the-key " } ,
AttributeUpdates = { " list " : { " Value " : [ 1 , 2 ] , " Action " : " PUT " } } ,
)
resp = table . get_item ( Key = { " key " : " the-key " } )
2023-07-13 10:21:47 +00:00
assert resp [ " Item " ] == { " key " : " the-key " , " list " : [ 1 , 2 ] }
2022-03-09 17:57:25 +00:00
2023-01-07 11:35:14 +00:00
# https://github.com/getmoto/moto/issues/2328
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_item_with_no_action_passed_with_list ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
dynamodb . create_table (
TableName = " Table " ,
KeySchema = [ { " AttributeName " : " key " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " key " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
)
table = dynamodb . Table ( " Table " )
table . update_item (
Key = { " key " : " the-key " } ,
# Do not pass 'Action' key, in order to check that the
# parameter's default value will be used.
AttributeUpdates = { " list " : { " Value " : [ 1 , 2 ] } } ,
)
resp = table . get_item ( Key = { " key " : " the-key " } )
2023-07-13 10:21:47 +00:00
assert resp [ " Item " ] == { " key " : " the-key " , " list " : [ 1 , 2 ] }
2022-03-09 17:57:25 +00:00
2023-01-07 11:35:14 +00:00
# https://github.com/getmoto/moto/issues/1342
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_item_on_map ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
dynamodb . create_table (
TableName = " users " ,
KeySchema = [
{ " AttributeName " : " forum_name " , " KeyType " : " HASH " } ,
{ " AttributeName " : " subject " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " forum_name " , " AttributeType " : " S " } ,
{ " AttributeName " : " subject " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item (
Item = {
" forum_name " : " the-key " ,
" subject " : " 123 " ,
" body " : { " nested " : { " data " : " test " } } ,
}
)
resp = table . scan ( )
2023-07-13 10:21:47 +00:00
assert resp [ " Items " ] [ 0 ] [ " body " ] == { " nested " : { " data " : " test " } }
2022-03-09 17:57:25 +00:00
# Nonexistent nested attributes are supported for existing top-level attributes.
table . update_item (
Key = { " forum_name " : " the-key " , " subject " : " 123 " } ,
UpdateExpression = " SET body.#nested.#data = :tb " ,
2022-03-10 14:39:59 +00:00
ExpressionAttributeNames = { " #nested " : " nested " , " #data " : " data " } ,
2022-03-09 17:57:25 +00:00
ExpressionAttributeValues = { " :tb " : " new_value " } ,
)
# Running this against AWS DDB gives an exception so make sure it also fails.:
with pytest . raises ( client . exceptions . ClientError ) :
# botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the UpdateItem
# operation: The document path provided in the update expression is invalid for update
table . update_item (
Key = { " forum_name " : " the-key " , " subject " : " 123 " } ,
UpdateExpression = " SET body.#nested.#nonexistentnested.#data = :tb2 " ,
ExpressionAttributeNames = {
" #nested " : " nested " ,
" #nonexistentnested " : " nonexistentnested " ,
" #data " : " data " ,
} ,
ExpressionAttributeValues = { " :tb2 " : " other_value " } ,
)
table . update_item (
Key = { " forum_name " : " the-key " , " subject " : " 123 " } ,
UpdateExpression = " SET body.#nested.#nonexistentnested = :tb2 " ,
ExpressionAttributeNames = {
" #nested " : " nested " ,
" #nonexistentnested " : " nonexistentnested " ,
} ,
ExpressionAttributeValues = { " :tb2 " : { " data " : " other_value " } } ,
)
resp = table . scan ( )
2023-07-13 10:21:47 +00:00
assert resp [ " Items " ] [ 0 ] [ " body " ] == {
" nested " : { " data " : " new_value " , " nonexistentnested " : { " data " : " other_value " } }
}
2022-03-09 17:57:25 +00:00
# Test nested value for a nonexistent attribute throws a ClientError.
with pytest . raises ( client . exceptions . ClientError ) :
table . update_item (
Key = { " forum_name " : " the-key " , " subject " : " 123 " } ,
UpdateExpression = " SET nonexistent.#nested = :tb " ,
ExpressionAttributeNames = { " #nested " : " nested " } ,
ExpressionAttributeValues = { " :tb " : " new_value " } ,
)
2023-01-07 11:35:14 +00:00
# https://github.com/getmoto/moto/issues/1358
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_if_not_exists ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
# Create the DynamoDB table.
dynamodb . create_table (
TableName = " users " ,
KeySchema = [
{ " AttributeName " : " forum_name " , " KeyType " : " HASH " } ,
{ " AttributeName " : " subject " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " forum_name " , " AttributeType " : " S " } ,
{ " AttributeName " : " subject " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " users " )
table . put_item ( Item = { " forum_name " : " the-key " , " subject " : " 123 " } )
table . update_item (
Key = { " forum_name " : " the-key " , " subject " : " 123 " } ,
# if_not_exists without space
UpdateExpression = " SET created_at=if_not_exists(created_at,:created_at) " ,
ExpressionAttributeValues = { " :created_at " : 123 } ,
)
resp = table . scan ( )
assert resp [ " Items " ] [ 0 ] [ " created_at " ] == 123
table . update_item (
Key = { " forum_name " : " the-key " , " subject " : " 123 " } ,
# if_not_exists with space
UpdateExpression = " SET created_at = if_not_exists (created_at, :created_at) " ,
ExpressionAttributeValues = { " :created_at " : 456 } ,
)
resp = table . scan ( )
# Still the original value
assert resp [ " Items " ] [ 0 ] [ " created_at " ] == 123
2023-01-07 11:35:14 +00:00
# https://github.com/getmoto/moto/issues/1937
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_return_attributes ( ) :
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " moto-test " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
)
def update ( col , to , rv ) :
return dynamodb . update_item (
TableName = " moto-test " ,
Key = { " id " : { " S " : " foo " } } ,
AttributeUpdates = { col : { " Value " : { " S " : to } , " Action " : " PUT " } } ,
ReturnValues = rv ,
)
r = update ( " col1 " , " val1 " , " ALL_NEW " )
assert r [ " Attributes " ] == { " id " : { " S " : " foo " } , " col1 " : { " S " : " val1 " } }
r = update ( " col1 " , " val2 " , " ALL_OLD " )
assert r [ " Attributes " ] == { " id " : { " S " : " foo " } , " col1 " : { " S " : " val1 " } }
r = update ( " col2 " , " val3 " , " UPDATED_NEW " )
assert r [ " Attributes " ] == { " col2 " : { " S " : " val3 " } }
r = update ( " col2 " , " val4 " , " UPDATED_OLD " )
assert r [ " Attributes " ] == { " col2 " : { " S " : " val3 " } }
r = update ( " col1 " , " val5 " , " NONE " )
assert r [ " Attributes " ] == { }
with pytest . raises ( ClientError ) as ex :
update ( " col1 " , " val6 " , " WRONG " )
err = ex . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert err [ " Code " ] == " ValidationException "
assert err [ " Message " ] == " Return values set to invalid value "
2022-03-09 17:57:25 +00:00
2023-01-07 11:35:14 +00:00
# https://github.com/getmoto/moto/issues/3448
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_return_updated_new_attributes_when_same ( ) :
dynamo_client = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
dynamo_client . create_table (
TableName = " moto-test " ,
KeySchema = [ { " AttributeName " : " HashKey1 " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " HashKey1 " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
)
dynamodb_table = dynamo_client . Table ( " moto-test " )
dynamodb_table . put_item (
Item = { " HashKey1 " : " HashKeyValue1 " , " listValuedAttribute1 " : [ " a " , " b " ] }
)
def update ( col , to , rv ) :
return dynamodb_table . update_item (
TableName = " moto-test " ,
Key = { " HashKey1 " : " HashKeyValue1 " } ,
UpdateExpression = " SET listValuedAttribute1=: " + col ,
ExpressionAttributeValues = { " : " + col : to } ,
ReturnValues = rv ,
)
r = update ( " a " , [ " a " , " c " ] , " UPDATED_NEW " )
assert r [ " Attributes " ] == { " listValuedAttribute1 " : [ " a " , " c " ] }
r = update ( " a " , { " a " , " c " } , " UPDATED_NEW " )
assert r [ " Attributes " ] == { " listValuedAttribute1 " : { " a " , " c " } }
r = update ( " a " , { 1 , 2 } , " UPDATED_NEW " )
assert r [ " Attributes " ] == { " listValuedAttribute1 " : { 1 , 2 } }
with pytest . raises ( ClientError ) as ex :
update ( " a " , [ " a " , " c " ] , " WRONG " )
err = ex . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert err [ " Code " ] == " ValidationException "
assert err [ " Message " ] == " Return values set to invalid value "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_put_return_attributes ( ) :
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " moto-test " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
)
r = dynamodb . put_item (
TableName = " moto-test " ,
Item = { " id " : { " S " : " foo " } , " col1 " : { " S " : " val1 " } } ,
ReturnValues = " NONE " ,
)
assert " Attributes " not in r
r = dynamodb . put_item (
TableName = " moto-test " ,
Item = { " id " : { " S " : " foo " } , " col1 " : { " S " : " val2 " } } ,
ReturnValues = " ALL_OLD " ,
)
assert r [ " Attributes " ] == { " id " : { " S " : " foo " } , " col1 " : { " S " : " val1 " } }
with pytest . raises ( ClientError ) as ex :
dynamodb . put_item (
TableName = " moto-test " ,
Item = { " id " : { " S " : " foo " } , " col1 " : { " S " : " val3 " } } ,
ReturnValues = " ALL_NEW " ,
)
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ValidationException "
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
assert ex . value . response [ " Error " ] [ " Message " ] == " Return values set to invalid value "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
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 " } ] ,
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 " ,
}
@mock_dynamodb
def test_query_gsi_with_range_key ( ) :
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [
{ " AttributeName " : " id " , " AttributeType " : " S " } ,
{ " AttributeName " : " gsi_hash_key " , " AttributeType " : " S " } ,
{ " AttributeName " : " gsi_range_key " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
GlobalSecondaryIndexes = [
{
" IndexName " : " test_gsi " ,
" KeySchema " : [
{ " AttributeName " : " gsi_hash_key " , " KeyType " : " HASH " } ,
{ " AttributeName " : " gsi_range_key " , " KeyType " : " RANGE " } ,
] ,
" Projection " : { " ProjectionType " : " ALL " } ,
" ProvisionedThroughput " : {
" ReadCapacityUnits " : 1 ,
" WriteCapacityUnits " : 1 ,
} ,
}
] ,
)
dynamodb . put_item (
TableName = " test " ,
Item = {
" id " : { " S " : " test1 " } ,
" gsi_hash_key " : { " S " : " key1 " } ,
" gsi_range_key " : { " S " : " range1 " } ,
} ,
)
dynamodb . put_item (
TableName = " test " , Item = { " id " : { " S " : " test2 " } , " gsi_hash_key " : { " S " : " key1 " } }
)
res = dynamodb . query (
TableName = " test " ,
IndexName = " test_gsi " ,
KeyConditionExpression = " gsi_hash_key = :gsi_hash_key and gsi_range_key = :gsi_range_key " ,
ExpressionAttributeValues = {
" :gsi_hash_key " : { " S " : " key1 " } ,
" :gsi_range_key " : { " S " : " range1 " } ,
} ,
)
2023-07-13 10:21:47 +00:00
assert res [ " Count " ] == 1
assert res [ " Items " ] [ 0 ] == {
" id " : { " S " : " test1 " } ,
" gsi_hash_key " : { " S " : " key1 " } ,
" gsi_range_key " : { " S " : " range1 " } ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_scan_by_non_exists_index ( ) :
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [
{ " AttributeName " : " id " , " AttributeType " : " S " } ,
{ " AttributeName " : " gsi_col " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
GlobalSecondaryIndexes = [
{
" IndexName " : " test_gsi " ,
" KeySchema " : [ { " AttributeName " : " gsi_col " , " KeyType " : " HASH " } ] ,
" Projection " : { " ProjectionType " : " ALL " } ,
" ProvisionedThroughput " : {
" ReadCapacityUnits " : 1 ,
" WriteCapacityUnits " : 1 ,
} ,
}
] ,
)
with pytest . raises ( ClientError ) as ex :
dynamodb . scan ( TableName = " test " , IndexName = " non_exists_index " )
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ValidationException "
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
assert (
ex . value . response [ " Error " ] [ " Message " ]
== " The table does not have the specified index: non_exists_index "
2022-03-09 17:57:25 +00:00
)
@mock_dynamodb
def test_query_by_non_exists_index ( ) :
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [
{ " AttributeName " : " id " , " AttributeType " : " S " } ,
{ " AttributeName " : " gsi_col " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
GlobalSecondaryIndexes = [
{
" IndexName " : " test_gsi " ,
" KeySchema " : [ { " AttributeName " : " gsi_col " , " KeyType " : " HASH " } ] ,
" Projection " : { " ProjectionType " : " ALL " } ,
" ProvisionedThroughput " : {
" ReadCapacityUnits " : 1 ,
" WriteCapacityUnits " : 1 ,
} ,
}
] ,
)
with pytest . raises ( ClientError ) as ex :
dynamodb . query (
TableName = " test " ,
IndexName = " non_exists_index " ,
KeyConditionExpression = " CarModel=M " ,
)
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ResourceNotFoundException "
assert (
ex . value . response [ " Error " ] [ " Message " ]
== " Invalid index: non_exists_index for table: test. Available indexes are: test_gsi "
2022-03-09 17:57:25 +00:00
)
@mock_dynamodb
def test_index_with_unknown_attributes_should_fail ( ) :
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
expected_exception = (
" Some index key attributes are not defined in AttributeDefinitions. "
)
with pytest . raises ( ClientError ) as ex :
dynamodb . create_table (
AttributeDefinitions = [
{ " AttributeName " : " customer_nr " , " AttributeType " : " S " } ,
{ " AttributeName " : " last_name " , " AttributeType " : " S " } ,
] ,
TableName = " table_with_missing_attribute_definitions " ,
KeySchema = [
{ " AttributeName " : " customer_nr " , " KeyType " : " HASH " } ,
{ " AttributeName " : " last_name " , " KeyType " : " RANGE " } ,
] ,
LocalSecondaryIndexes = [
{
" IndexName " : " indexthataddsanadditionalattribute " ,
" KeySchema " : [
{ " AttributeName " : " customer_nr " , " KeyType " : " HASH " } ,
{ " AttributeName " : " postcode " , " KeyType " : " RANGE " } ,
] ,
" Projection " : { " ProjectionType " : " ALL " } ,
}
] ,
BillingMode = " PAY_PER_REQUEST " ,
)
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ValidationException "
assert expected_exception in ex . value . response [ " Error " ] [ " Message " ]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_list_index__set_existing_index ( ) :
table_name = " test_list_index_access "
client = create_table_with_list ( table_name )
client . put_item (
TableName = table_name ,
Item = {
" id " : { " S " : " foo " } ,
" itemlist " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } , { " S " : " bar3 " } ] } ,
} ,
)
client . update_item (
TableName = table_name ,
Key = { " id " : { " S " : " foo " } } ,
UpdateExpression = " set itemlist[1]=:Item " ,
ExpressionAttributeValues = { " :Item " : { " S " : " bar2_update " } } ,
)
#
result = client . get_item ( TableName = table_name , Key = { " id " : { " S " : " foo " } } ) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result [ " id " ] == { " S " : " foo " }
assert result [ " itemlist " ] == {
" L " : [ { " S " : " bar1 " } , { " S " : " bar2_update " } , { " S " : " bar3 " } ]
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_list_index__set_existing_nested_index ( ) :
table_name = " test_list_index_access "
client = create_table_with_list ( table_name )
client . put_item (
TableName = table_name ,
Item = {
" id " : { " S " : " foo2 " } ,
" itemmap " : {
" M " : { " itemlist " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } , { " S " : " bar3 " } ] } }
} ,
} ,
)
client . update_item (
TableName = table_name ,
Key = { " id " : { " S " : " foo2 " } } ,
UpdateExpression = " set itemmap.itemlist[1]=:Item " ,
ExpressionAttributeValues = { " :Item " : { " S " : " bar2_update " } } ,
)
#
result = client . get_item ( TableName = table_name , Key = { " id " : { " S " : " foo2 " } } ) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result [ " id " ] == { " S " : " foo2 " }
assert result [ " itemmap " ] [ " M " ] [ " itemlist " ] [ " L " ] == [
{ " S " : " bar1 " } ,
{ " S " : " bar2_update " } ,
{ " S " : " bar3 " } ,
]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_list_index__set_index_out_of_range ( ) :
table_name = " test_list_index_access "
client = create_table_with_list ( table_name )
client . put_item (
TableName = table_name ,
Item = {
" id " : { " S " : " foo " } ,
" itemlist " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } , { " S " : " bar3 " } ] } ,
} ,
)
client . update_item (
TableName = table_name ,
Key = { " id " : { " S " : " foo " } } ,
UpdateExpression = " set itemlist[10]=:Item " ,
ExpressionAttributeValues = { " :Item " : { " S " : " bar10 " } } ,
)
#
result = client . get_item ( TableName = table_name , Key = { " id " : { " S " : " foo " } } ) [ " Item " ]
assert result [ " id " ] == { " S " : " foo " }
assert result [ " itemlist " ] == {
" L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } , { " S " : " bar3 " } , { " S " : " bar10 " } ]
}
@mock_dynamodb
def test_update_list_index__set_nested_index_out_of_range ( ) :
table_name = " test_list_index_access "
client = create_table_with_list ( table_name )
client . put_item (
TableName = table_name ,
Item = {
" id " : { " S " : " foo2 " } ,
" itemmap " : {
" M " : { " itemlist " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } , { " S " : " bar3 " } ] } }
} ,
} ,
)
client . update_item (
TableName = table_name ,
Key = { " id " : { " S " : " foo2 " } } ,
UpdateExpression = " set itemmap.itemlist[10]=:Item " ,
ExpressionAttributeValues = { " :Item " : { " S " : " bar10 " } } ,
)
#
result = client . get_item ( TableName = table_name , Key = { " id " : { " S " : " foo2 " } } ) [ " Item " ]
assert result [ " id " ] == { " S " : " foo2 " }
assert result [ " itemmap " ] [ " M " ] [ " itemlist " ] [ " L " ] == [
{ " S " : " bar1 " } ,
{ " S " : " bar2 " } ,
{ " S " : " bar3 " } ,
{ " S " : " bar10 " } ,
]
@mock_dynamodb
def test_update_list_index__set_double_nested_index ( ) :
table_name = " test_list_index_access "
client = create_table_with_list ( table_name )
client . put_item (
TableName = table_name ,
Item = {
" id " : { " S " : " foo2 " } ,
" itemmap " : {
" M " : {
" itemlist " : {
" L " : [
{ " M " : { " foo " : { " S " : " bar11 " } , " foos " : { " S " : " bar12 " } } } ,
{ " M " : { " foo " : { " S " : " bar21 " } , " foos " : { " S " : " bar21 " } } } ,
]
}
}
} ,
} ,
)
client . update_item (
TableName = table_name ,
Key = { " id " : { " S " : " foo2 " } } ,
UpdateExpression = " set itemmap.itemlist[1].foos=:Item " ,
ExpressionAttributeValues = { " :Item " : { " S " : " bar22 " } } ,
)
#
result = client . get_item ( TableName = table_name , Key = { " id " : { " S " : " foo2 " } } ) [ " Item " ]
assert result [ " id " ] == { " S " : " foo2 " }
2023-07-13 10:21:47 +00:00
assert len ( result [ " itemmap " ] [ " M " ] [ " itemlist " ] [ " L " ] ) == 2
assert result [ " itemmap " ] [ " M " ] [ " itemlist " ] [ " L " ] [ 0 ] == {
" M " : { " foo " : { " S " : " bar11 " } , " foos " : { " S " : " bar12 " } }
} # unchanged
assert result [ " itemmap " ] [ " M " ] [ " itemlist " ] [ " L " ] [ 1 ] == {
" M " : { " foo " : { " S " : " bar21 " } , " foos " : { " S " : " bar22 " } }
} # updated
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_list_index__set_index_of_a_string ( ) :
table_name = " test_list_index_access "
client = create_table_with_list ( table_name )
client . put_item (
TableName = table_name , Item = { " id " : { " S " : " foo2 " } , " itemstr " : { " S " : " somestring " } }
)
with pytest . raises ( ClientError ) as ex :
client . update_item (
TableName = table_name ,
Key = { " id " : { " S " : " foo2 " } } ,
UpdateExpression = " set itemstr[1]=:Item " ,
ExpressionAttributeValues = { " :Item " : { " S " : " string_update " } } ,
)
client . get_item ( TableName = table_name , Key = { " id " : { " S " : " foo2 " } } ) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ValidationException "
assert (
ex . value . response [ " Error " ] [ " Message " ]
== " The document path provided in the update expression is invalid for update "
2022-03-09 17:57:25 +00:00
)
@mock_dynamodb
def test_remove_top_level_attribute ( ) :
table_name = " test_remove "
client = create_table_with_list ( table_name )
client . put_item (
TableName = table_name , Item = { " id " : { " S " : " foo " } , " item " : { " S " : " bar " } }
)
client . update_item (
TableName = table_name ,
Key = { " id " : { " S " : " foo " } } ,
UpdateExpression = " REMOVE #i " ,
ExpressionAttributeNames = { " #i " : " item " } ,
)
#
result = client . get_item ( TableName = table_name , Key = { " id " : { " S " : " foo " } } ) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result == { " id " : { " S " : " foo " } }
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_remove_top_level_attribute_non_existent ( ) :
"""
Remove statements do not require attribute to exist they silently pass
"""
table_name = " test_remove "
client = create_table_with_list ( table_name )
ddb_item = { " id " : { " S " : " foo " } , " item " : { " S " : " bar " } }
client . put_item ( TableName = table_name , Item = ddb_item )
client . update_item (
TableName = table_name ,
Key = { " id " : { " S " : " foo " } } ,
UpdateExpression = " REMOVE non_existent_attribute " ,
ExpressionAttributeNames = { " #i " : " item " } ,
)
result = client . get_item ( TableName = table_name , Key = { " id " : { " S " : " foo " } } ) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result == ddb_item
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_remove_list_index__remove_existing_index ( ) :
table_name = " test_list_index_access "
client = create_table_with_list ( table_name )
client . put_item (
TableName = table_name ,
Item = {
" id " : { " S " : " foo " } ,
" itemlist " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } , { " S " : " bar3 " } ] } ,
} ,
)
client . update_item (
TableName = table_name ,
Key = { " id " : { " S " : " foo " } } ,
UpdateExpression = " REMOVE itemlist[1] " ,
)
#
result = client . get_item ( TableName = table_name , Key = { " id " : { " S " : " foo " } } ) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result [ " id " ] == { " S " : " foo " }
assert result [ " itemlist " ] == { " L " : [ { " S " : " bar1 " } , { " S " : " bar3 " } ] }
2022-03-09 17:57:25 +00:00
2022-12-14 11:07:34 +00:00
@mock_dynamodb
def test_remove_list_index__remove_multiple_indexes ( ) :
table_name = " remove-test "
create_table_with_list ( table_name )
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
table = dynamodb . Table ( table_name )
table . put_item (
Item = {
" id " : " woop " ,
" bla " : [ " 1 " , " 2 " , " 3 " , " 4 " , " 5 " ] ,
} ,
)
table . update_item (
Key = { " id " : " woop " } , UpdateExpression = " REMOVE bla[0], bla[1], bla[2] "
)
result = table . get_item ( Key = { " id " : " woop " } )
item = result [ " Item " ]
assert item [ " bla " ] == [ " 4 " , " 5 " ]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_remove_list_index__remove_existing_nested_index ( ) :
table_name = " test_list_index_access "
client = create_table_with_list ( table_name )
client . put_item (
TableName = table_name ,
Item = {
" id " : { " S " : " foo2 " } ,
" itemmap " : { " M " : { " itemlist " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } ] } } } ,
} ,
)
client . update_item (
TableName = table_name ,
Key = { " id " : { " S " : " foo2 " } } ,
UpdateExpression = " REMOVE itemmap.itemlist[1] " ,
)
#
result = client . get_item ( TableName = table_name , Key = { " id " : { " S " : " foo2 " } } ) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result [ " id " ] == { " S " : " foo2 " }
assert result [ " itemmap " ] [ " M " ] [ " itemlist " ] [ " L " ] == [ { " S " : " bar1 " } ]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_remove_list_index__remove_existing_double_nested_index ( ) :
table_name = " test_list_index_access "
client = create_table_with_list ( table_name )
client . put_item (
TableName = table_name ,
Item = {
" id " : { " S " : " foo2 " } ,
" itemmap " : {
" M " : {
" itemlist " : {
" L " : [
{ " M " : { " foo00 " : { " S " : " bar1 " } , " foo01 " : { " S " : " bar2 " } } } ,
{ " M " : { " foo10 " : { " S " : " bar1 " } , " foo11 " : { " S " : " bar2 " } } } ,
]
}
}
} ,
} ,
)
client . update_item (
TableName = table_name ,
Key = { " id " : { " S " : " foo2 " } } ,
UpdateExpression = " REMOVE itemmap.itemlist[1].foo10 " ,
)
#
result = client . get_item ( TableName = table_name , Key = { " id " : { " S " : " foo2 " } } ) [ " Item " ]
assert result [ " id " ] == { " S " : " foo2 " }
2023-07-13 10:21:47 +00:00
# untouched
assert result [ " itemmap " ] [ " M " ] [ " itemlist " ] [ " L " ] [ 0 ] [ " M " ] == {
" foo00 " : { " S " : " bar1 " } ,
" foo01 " : { " S " : " bar2 " } ,
}
# changed
assert result [ " itemmap " ] [ " M " ] [ " itemlist " ] [ " L " ] [ 1 ] [ " M " ] == { " foo11 " : { " S " : " bar2 " } }
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_remove_list_index__remove_index_out_of_range ( ) :
table_name = " test_list_index_access "
client = create_table_with_list ( table_name )
client . put_item (
TableName = table_name ,
Item = {
" id " : { " S " : " foo " } ,
" itemlist " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } , { " S " : " bar3 " } ] } ,
} ,
)
client . update_item (
TableName = table_name ,
Key = { " id " : { " S " : " foo " } } ,
UpdateExpression = " REMOVE itemlist[10] " ,
)
#
result = client . get_item ( TableName = table_name , Key = { " id " : { " S " : " foo " } } ) [ " Item " ]
assert result [ " id " ] == { " S " : " foo " }
assert result [ " itemlist " ] == { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } , { " S " : " bar3 " } ] }
def create_table_with_list ( table_name ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
client . create_table (
TableName = table_name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
BillingMode = " PAY_PER_REQUEST " ,
)
return client
@mock_dynamodb
def test_sorted_query_with_numerical_sort_key ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " CarCollection " ,
KeySchema = [
{ " AttributeName " : " CarModel " , " KeyType " : " HASH " } ,
{ " AttributeName " : " CarPrice " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " CarModel " , " AttributeType " : " S " } ,
{ " AttributeName " : " CarPrice " , " AttributeType " : " N " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
)
def create_item ( price ) :
return { " CarModel " : " M " , " CarPrice " : price }
table = dynamodb . Table ( " CarCollection " )
items = list ( map ( create_item , [ 2 , 1 , 10 , 3 ] ) )
for item in items :
table . put_item ( Item = item )
response = table . query ( KeyConditionExpression = Key ( " CarModel " ) . eq ( " M " ) )
response_items = response [ " Items " ]
assert len ( items ) == len ( response_items )
assert all ( isinstance ( item [ " CarPrice " ] , Decimal ) for item in response_items )
response_prices = [ item [ " CarPrice " ] for item in response_items ]
expected_prices = [ Decimal ( item [ " CarPrice " ] ) for item in items ]
expected_prices . sort ( )
assert (
expected_prices == response_prices
) , " result items are not sorted by numerical value "
2023-01-07 11:35:14 +00:00
# https://github.com/getmoto/moto/issues/1874
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_item_size_is_under_400KB ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " moto-test " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
)
table = dynamodb . Table ( " moto-test " )
large_item = " x " * 410 * 1000
assert_failure_due_to_item_size (
func = client . put_item ,
TableName = " moto-test " ,
Item = { " id " : { " S " : " foo " } , " cont " : { " S " : large_item } } ,
)
assert_failure_due_to_item_size (
func = table . put_item , Item = { " id " : " bar " , " cont " : large_item }
)
assert_failure_due_to_item_size_to_update (
func = client . update_item ,
TableName = " moto-test " ,
Key = { " id " : { " S " : " foo2 " } } ,
UpdateExpression = " set cont=:Item " ,
ExpressionAttributeValues = { " :Item " : { " S " : large_item } } ,
)
# Assert op fails when updating a nested item
assert_failure_due_to_item_size (
func = table . put_item , Item = { " id " : " bar " , " itemlist " : [ { " cont " : large_item } ] }
)
assert_failure_due_to_item_size (
func = client . put_item ,
TableName = " moto-test " ,
Item = {
" id " : { " S " : " foo " } ,
" itemlist " : { " L " : [ { " M " : { " item1 " : { " S " : large_item } } } ] } ,
} ,
)
def assert_failure_due_to_item_size ( func , * * kwargs ) :
with pytest . raises ( ClientError ) as ex :
func ( * * kwargs )
2023-07-13 10:21:47 +00:00
err = ex . value . response [ " Error " ]
assert err [ " Code " ] == " ValidationException "
assert err [ " Message " ] == " Item size has exceeded the maximum allowed size "
2022-03-09 17:57:25 +00:00
def assert_failure_due_to_item_size_to_update ( func , * * kwargs ) :
with pytest . raises ( ClientError ) as ex :
func ( * * kwargs )
2023-07-13 10:21:47 +00:00
err = ex . value . response [ " Error " ]
assert err [ " Code " ] == " ValidationException "
assert err [ " Message " ] == " Item size to update has exceeded the maximum allowed size "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_supports_complex_expression_attribute_values ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
client . create_table (
AttributeDefinitions = [ { " AttributeName " : " SHA256 " , " AttributeType " : " S " } ] ,
TableName = " TestTable " ,
KeySchema = [ { " AttributeName " : " SHA256 " , " KeyType " : " HASH " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
client . update_item (
TableName = " TestTable " ,
Key = { " SHA256 " : { " S " : " sha-of-file " } } ,
UpdateExpression = (
" SET MD5 = :md5, " " MyStringSet = :string_set, " " MyMap = :map "
) ,
ExpressionAttributeValues = {
" :md5 " : { " S " : " md5-of-file " } ,
" :string_set " : { " SS " : [ " string1 " , " string2 " ] } ,
" :map " : { " M " : { " EntryKey " : { " SS " : [ " thing1 " , " thing2 " ] } } } ,
} ,
)
result = client . get_item (
TableName = " TestTable " , Key = { " SHA256 " : { " S " : " sha-of-file " } }
) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result == {
" MyStringSet " : { " SS " : [ " string1 " , " string2 " ] } ,
" MyMap " : { " M " : { " EntryKey " : { " SS " : [ " thing1 " , " thing2 " ] } } } ,
" SHA256 " : { " S " : " sha-of-file " } ,
" MD5 " : { " S " : " md5-of-file " } ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_supports_list_append ( ) :
# Verify whether the list_append operation works as expected
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
client . create_table (
AttributeDefinitions = [ { " AttributeName " : " SHA256 " , " AttributeType " : " S " } ] ,
TableName = " TestTable " ,
KeySchema = [ { " AttributeName " : " SHA256 " , " KeyType " : " HASH " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
client . put_item (
TableName = " TestTable " ,
Item = { " SHA256 " : { " S " : " sha-of-file " } , " crontab " : { " L " : [ { " S " : " bar1 " } ] } } ,
)
# Update item using list_append expression
updated_item = client . update_item (
TableName = " TestTable " ,
Key = { " SHA256 " : { " S " : " sha-of-file " } } ,
UpdateExpression = " SET crontab = list_append(crontab, :i) " ,
ExpressionAttributeValues = { " :i " : { " L " : [ { " S " : " bar2 " } ] } } ,
ReturnValues = " UPDATED_NEW " ,
)
# Verify updated item is correct
2023-07-13 10:21:47 +00:00
assert updated_item [ " Attributes " ] == {
" crontab " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } ] }
}
2022-03-09 17:57:25 +00:00
# Verify item is appended to the existing list
result = client . get_item (
TableName = " TestTable " , Key = { " SHA256 " : { " S " : " sha-of-file " } }
) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result == {
" SHA256 " : { " S " : " sha-of-file " } ,
" crontab " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } ] } ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_supports_nested_list_append ( ) :
# Verify whether we can append a list that's inside a map
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
client . create_table (
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
TableName = " TestTable " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
client . put_item (
TableName = " TestTable " ,
Item = {
" id " : { " S " : " nested_list_append " } ,
" a " : { " M " : { " b " : { " L " : [ { " S " : " bar1 " } ] } } } ,
} ,
)
# Update item using list_append expression
updated_item = client . update_item (
TableName = " TestTable " ,
Key = { " id " : { " S " : " nested_list_append " } } ,
UpdateExpression = " SET a.#b = list_append(a.#b, :i) " ,
ExpressionAttributeValues = { " :i " : { " L " : [ { " S " : " bar2 " } ] } } ,
ExpressionAttributeNames = { " #b " : " b " } ,
ReturnValues = " UPDATED_NEW " ,
)
# Verify updated item is correct
2023-07-13 10:21:47 +00:00
assert updated_item [ " Attributes " ] == {
" a " : { " M " : { " b " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } ] } } }
}
2022-03-09 17:57:25 +00:00
result = client . get_item (
TableName = " TestTable " , Key = { " id " : { " S " : " nested_list_append " } }
) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result == {
" id " : { " S " : " nested_list_append " } ,
" a " : { " M " : { " b " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } ] } } } ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_supports_multiple_levels_nested_list_append ( ) :
# Verify whether we can append a list that's inside a map that's inside a map (Inception!)
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
client . create_table (
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
TableName = " TestTable " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
client . put_item (
TableName = " TestTable " ,
Item = {
" id " : { " S " : " nested_list_append " } ,
" a " : { " M " : { " b " : { " M " : { " c " : { " L " : [ { " S " : " bar1 " } ] } } } } } ,
} ,
)
# Update item using list_append expression
updated_item = client . update_item (
TableName = " TestTable " ,
Key = { " id " : { " S " : " nested_list_append " } } ,
UpdateExpression = " SET a.#b.c = list_append(a.#b.#c, :i) " ,
ExpressionAttributeValues = { " :i " : { " L " : [ { " S " : " bar2 " } ] } } ,
ExpressionAttributeNames = { " #b " : " b " , " #c " : " c " } ,
ReturnValues = " UPDATED_NEW " ,
)
# Verify updated item is correct
2023-07-13 10:21:47 +00:00
assert updated_item [ " Attributes " ] == {
" a " : { " M " : { " b " : { " M " : { " c " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } ] } } } } }
}
2022-03-09 17:57:25 +00:00
# Verify item is appended to the existing list
result = client . get_item (
TableName = " TestTable " , Key = { " id " : { " S " : " nested_list_append " } }
) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result == {
" id " : { " S " : " nested_list_append " } ,
" a " : { " M " : { " b " : { " M " : { " c " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } ] } } } } } ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_supports_nested_list_append_onto_another_list ( ) :
# Verify whether we can take the contents of one list, and use that to fill another list
# Note that the contents of the other list is completely overwritten
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
client . create_table (
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
TableName = " TestTable " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
client . put_item (
TableName = " TestTable " ,
Item = {
" id " : { " S " : " list_append_another " } ,
" a " : { " M " : { " b " : { " L " : [ { " S " : " bar1 " } ] } , " c " : { " L " : [ { " S " : " car1 " } ] } } } ,
} ,
)
# Update item using list_append expression
updated_item = client . update_item (
TableName = " TestTable " ,
Key = { " id " : { " S " : " list_append_another " } } ,
UpdateExpression = " SET a.#c = list_append(a.#b, :i) " ,
ExpressionAttributeValues = { " :i " : { " L " : [ { " S " : " bar2 " } ] } } ,
ExpressionAttributeNames = { " #b " : " b " , " #c " : " c " } ,
ReturnValues = " UPDATED_NEW " ,
)
# Verify updated item is correct
2023-07-13 10:21:47 +00:00
assert updated_item [ " Attributes " ] == {
" a " : { " M " : { " c " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } ] } } }
}
2022-03-09 17:57:25 +00:00
# Verify item is appended to the existing list
result = client . get_item (
TableName = " TestTable " , Key = { " id " : { " S " : " list_append_another " } }
) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert result == {
" id " : { " S " : " list_append_another " } ,
" a " : {
" M " : {
" b " : { " L " : [ { " S " : " bar1 " } ] } ,
" c " : { " L " : [ { " S " : " bar1 " } , { " S " : " bar2 " } ] } ,
}
} ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_supports_list_append_maps ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-west-1 " )
client . create_table (
AttributeDefinitions = [
{ " AttributeName " : " id " , " AttributeType " : " S " } ,
{ " AttributeName " : " rid " , " AttributeType " : " S " } ,
] ,
TableName = " TestTable " ,
KeySchema = [
{ " AttributeName " : " id " , " KeyType " : " HASH " } ,
{ " AttributeName " : " rid " , " KeyType " : " RANGE " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
client . put_item (
TableName = " TestTable " ,
Item = {
" id " : { " S " : " nested_list_append " } ,
" rid " : { " S " : " range_key " } ,
" a " : { " L " : [ { " M " : { " b " : { " S " : " bar1 " } } } ] } ,
} ,
)
# Update item using list_append expression
updated_item = client . update_item (
TableName = " TestTable " ,
Key = { " id " : { " S " : " nested_list_append " } , " rid " : { " S " : " range_key " } } ,
UpdateExpression = " SET a = list_append(a, :i) " ,
ExpressionAttributeValues = { " :i " : { " L " : [ { " M " : { " b " : { " S " : " bar2 " } } } ] } } ,
ReturnValues = " UPDATED_NEW " ,
)
# Verify updated item is correct
2023-07-13 10:21:47 +00:00
assert updated_item [ " Attributes " ] == {
" a " : { " L " : [ { " M " : { " b " : { " S " : " bar1 " } } } , { " M " : { " b " : { " S " : " bar2 " } } } ] }
}
2022-03-09 17:57:25 +00:00
# Verify item is appended to the existing list
result = client . query (
TableName = " TestTable " ,
KeyConditionExpression = " id = :i AND begins_with(rid, :r) " ,
ExpressionAttributeValues = {
" :i " : { " S " : " nested_list_append " } ,
" :r " : { " S " : " range_key " } ,
} ,
) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert result == [
{
" a " : { " L " : [ { " M " : { " b " : { " S " : " bar1 " } } } , { " M " : { " b " : { " S " : " bar2 " } } } ] } ,
" rid " : { " S " : " range_key " } ,
" id " : { " S " : " nested_list_append " } ,
}
]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_supports_nested_update_if_nested_value_not_exists ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
name = " TestTable "
dynamodb . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " user_id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " user_id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( name )
table . put_item (
2022-03-10 14:39:59 +00:00
Item = { " user_id " : " 1234 " , " friends " : { " 5678 " : { " name " : " friend_5678 " } } }
2022-03-09 17:57:25 +00:00
)
table . update_item (
Key = { " user_id " : " 1234 " } ,
2022-03-10 14:39:59 +00:00
ExpressionAttributeNames = { " #friends " : " friends " , " #friendid " : " 0000 " } ,
ExpressionAttributeValues = { " :friend " : { " name " : " friend_0000 " } } ,
2022-03-09 17:57:25 +00:00
UpdateExpression = " SET #friends.#friendid = :friend " ,
ReturnValues = " UPDATED_NEW " ,
)
item = table . get_item ( Key = { " user_id " : " 1234 " } ) [ " Item " ]
assert item == {
" user_id " : " 1234 " ,
2022-03-10 14:39:59 +00:00
" friends " : { " 5678 " : { " name " : " friend_5678 " } , " 0000 " : { " name " : " friend_0000 " } } ,
2022-03-09 17:57:25 +00:00
}
@mock_dynamodb
def test_update_supports_list_append_with_nested_if_not_exists_operation ( ) :
dynamo = boto3 . resource ( " dynamodb " , region_name = " us-west-1 " )
table_name = " test "
dynamo . create_table (
TableName = table_name ,
AttributeDefinitions = [ { " AttributeName " : " Id " , " AttributeType " : " S " } ] ,
KeySchema = [ { " AttributeName " : " Id " , " KeyType " : " HASH " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 20 , " WriteCapacityUnits " : 20 } ,
)
table = dynamo . Table ( table_name )
table . put_item ( Item = { " Id " : " item-id " , " nest1 " : { " nest2 " : { } } } )
updated_item = table . update_item (
Key = { " Id " : " item-id " } ,
UpdateExpression = " SET nest1.nest2.event_history = list_append(if_not_exists(nest1.nest2.event_history, :empty_list), :new_value) " ,
ExpressionAttributeValues = { " :empty_list " : [ ] , " :new_value " : [ " some_value " ] } ,
ReturnValues = " UPDATED_NEW " ,
)
# Verify updated item is correct
2023-07-13 10:21:47 +00:00
assert updated_item [ " Attributes " ] == {
" nest1 " : { " nest2 " : { " event_history " : [ " some_value " ] } }
}
2022-03-09 17:57:25 +00:00
2023-07-13 10:21:47 +00:00
assert table . get_item ( Key = { " Id " : " item-id " } ) [ " Item " ] == {
" Id " : " item-id " ,
" nest1 " : { " nest2 " : { " event_history " : [ " some_value " ] } } ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_supports_list_append_with_nested_if_not_exists_operation_and_property_already_exists ( ) :
dynamo = boto3 . resource ( " dynamodb " , region_name = " us-west-1 " )
table_name = " test "
dynamo . create_table (
TableName = table_name ,
AttributeDefinitions = [ { " AttributeName " : " Id " , " AttributeType " : " S " } ] ,
KeySchema = [ { " AttributeName " : " Id " , " KeyType " : " HASH " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 20 , " WriteCapacityUnits " : 20 } ,
)
table = dynamo . Table ( table_name )
table . put_item ( Item = { " Id " : " item-id " , " event_history " : [ " other_value " ] } )
updated_item = table . update_item (
Key = { " Id " : " item-id " } ,
UpdateExpression = " SET event_history = list_append(if_not_exists(event_history, :empty_list), :new_value) " ,
ExpressionAttributeValues = { " :empty_list " : [ ] , " :new_value " : [ " some_value " ] } ,
ReturnValues = " UPDATED_NEW " ,
)
# Verify updated item is correct
2023-07-13 10:21:47 +00:00
assert updated_item [ " Attributes " ] == {
" event_history " : [ " other_value " , " some_value " ]
}
2022-03-09 17:57:25 +00:00
2023-07-13 10:21:47 +00:00
assert table . get_item ( Key = { " Id " : " item-id " } ) [ " Item " ] == {
" Id " : " item-id " ,
" event_history " : [ " other_value " , " some_value " ] ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_item_if_original_value_is_none ( ) :
dynamo = boto3 . resource ( " dynamodb " , region_name = " eu-central-1 " )
dynamo . create_table (
AttributeDefinitions = [ { " AttributeName " : " job_id " , " AttributeType " : " S " } ] ,
TableName = " origin-rbu-dev " ,
KeySchema = [ { " AttributeName " : " job_id " , " KeyType " : " HASH " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
)
table = dynamo . Table ( " origin-rbu-dev " )
table . put_item ( Item = { " job_id " : " a " , " job_name " : None } )
table . update_item (
Key = { " job_id " : " a " } ,
UpdateExpression = " SET job_name = :output " ,
ExpressionAttributeValues = { " :output " : " updated " } ,
)
2023-07-13 10:21:47 +00:00
assert table . scan ( ) [ " Items " ] [ 0 ] [ " job_name " ] == " updated "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_nested_item_if_original_value_is_none ( ) :
dynamo = boto3 . resource ( " dynamodb " , region_name = " eu-central-1 " )
dynamo . create_table (
AttributeDefinitions = [ { " AttributeName " : " job_id " , " AttributeType " : " S " } ] ,
TableName = " origin-rbu-dev " ,
KeySchema = [ { " AttributeName " : " job_id " , " KeyType " : " HASH " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
)
table = dynamo . Table ( " origin-rbu-dev " )
table . put_item ( Item = { " job_id " : " a " , " job_details " : { " job_name " : None } } )
updated_item = table . update_item (
Key = { " job_id " : " a " } ,
UpdateExpression = " SET job_details.job_name = :output " ,
ExpressionAttributeValues = { " :output " : " updated " } ,
ReturnValues = " UPDATED_NEW " ,
)
# Verify updated item is correct
2023-07-13 10:21:47 +00:00
assert updated_item [ " Attributes " ] == { " job_details " : { " job_name " : " updated " } }
2022-03-09 17:57:25 +00:00
2023-07-13 10:21:47 +00:00
assert table . scan ( ) [ " Items " ] [ 0 ] [ " job_details " ] [ " job_name " ] == " updated "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_allow_update_to_item_with_different_type ( ) :
dynamo = boto3 . resource ( " dynamodb " , region_name = " eu-central-1 " )
dynamo . create_table (
AttributeDefinitions = [ { " AttributeName " : " job_id " , " AttributeType " : " S " } ] ,
TableName = " origin-rbu-dev " ,
KeySchema = [ { " AttributeName " : " job_id " , " KeyType " : " HASH " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
)
table = dynamo . Table ( " origin-rbu-dev " )
table . put_item ( Item = { " job_id " : " a " , " job_details " : { " job_name " : { " nested " : " yes " } } } )
table . put_item ( Item = { " job_id " : " b " , " job_details " : { " job_name " : { " nested " : " yes " } } } )
updated_item = table . update_item (
Key = { " job_id " : " a " } ,
UpdateExpression = " SET job_details.job_name = :output " ,
ExpressionAttributeValues = { " :output " : " updated " } ,
ReturnValues = " UPDATED_NEW " ,
)
# Verify updated item is correct
2023-07-13 10:21:47 +00:00
assert updated_item [ " Attributes " ] == { " job_details " : { " job_name " : " updated " } }
2022-03-09 17:57:25 +00:00
2023-07-13 10:21:47 +00:00
assert (
table . get_item ( Key = { " job_id " : " a " } ) [ " Item " ] [ " job_details " ] [ " job_name " ]
== " updated "
)
assert table . get_item ( Key = { " job_id " : " b " } ) [ " Item " ] [ " job_details " ] [ " job_name " ] == {
" nested " : " yes "
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_query_catches_when_no_filters ( ) :
dynamo = boto3 . resource ( " dynamodb " , region_name = " eu-central-1 " )
dynamo . create_table (
AttributeDefinitions = [ { " AttributeName " : " job_id " , " AttributeType " : " S " } ] ,
TableName = " origin-rbu-dev " ,
KeySchema = [ { " AttributeName " : " job_id " , " KeyType " : " HASH " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
)
table = dynamo . Table ( " origin-rbu-dev " )
with pytest . raises ( ClientError ) as ex :
table . query ( TableName = " original-rbu-dev " )
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ValidationException "
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
assert (
ex . value . response [ " Error " ] [ " Message " ]
== " Either KeyConditions or QueryFilter should be present "
2022-03-09 17:57:25 +00:00
)
@mock_dynamodb
def test_invalid_transact_get_items ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test1 " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = dynamodb . Table ( " test1 " )
2022-03-10 14:39:59 +00:00
table . put_item ( Item = { " id " : " 1 " , " val " : " 1 " } )
table . put_item ( Item = { " id " : " 1 " , " val " : " 2 " } )
2022-03-09 17:57:25 +00:00
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
with pytest . raises ( ClientError ) as ex :
client . transact_get_items (
TransactItems = [
{ " Get " : { " Key " : { " id " : { " S " : " 1 " } } , " TableName " : " test1 " } }
for i in range ( 26 )
]
)
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
assert (
" Member must have length less than or equal to 25 "
in ex . value . response [ " Error " ] [ " Message " ]
2022-03-09 17:57:25 +00:00
)
with pytest . raises ( ClientError ) as ex :
client . transact_get_items (
TransactItems = [
2022-03-10 14:39:59 +00:00
{ " Get " : { " Key " : { " id " : { " S " : " 1 " } } , " TableName " : " test1 " } } ,
{ " Get " : { " Key " : { " id " : { " S " : " 1 " } } , " TableName " : " non_exists_table " } } ,
2022-03-09 17:57:25 +00:00
]
)
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ResourceNotFoundException "
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
assert ex . value . response [ " Error " ] [ " Message " ] == " Requested resource not found "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_valid_transact_get_items ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test1 " ,
KeySchema = [
{ " AttributeName " : " id " , " KeyType " : " HASH " } ,
{ " AttributeName " : " sort_key " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " id " , " AttributeType " : " S " } ,
{ " AttributeName " : " sort_key " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table1 = dynamodb . Table ( " test1 " )
2022-03-10 14:39:59 +00:00
table1 . put_item ( Item = { " id " : " 1 " , " sort_key " : " 1 " } )
table1 . put_item ( Item = { " id " : " 1 " , " sort_key " : " 2 " } )
2022-03-09 17:57:25 +00:00
dynamodb . create_table (
TableName = " test2 " ,
KeySchema = [
{ " AttributeName " : " id " , " KeyType " : " HASH " } ,
{ " AttributeName " : " sort_key " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " id " , " AttributeType " : " S " } ,
{ " AttributeName " : " sort_key " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table2 = dynamodb . Table ( " test2 " )
2022-03-10 14:39:59 +00:00
table2 . put_item ( Item = { " id " : " 1 " , " sort_key " : " 1 " } )
2022-03-09 17:57:25 +00:00
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
res = client . transact_get_items (
TransactItems = [
{
" Get " : {
" Key " : { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 1 " } } ,
" TableName " : " test1 " ,
}
} ,
{
" Get " : {
" Key " : { " id " : { " S " : " non_exists_key " } , " sort_key " : { " S " : " 2 " } } ,
" TableName " : " test1 " ,
}
} ,
]
)
2023-07-13 10:21:47 +00:00
assert res [ " Responses " ] [ 0 ] [ " Item " ] == { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 1 " } }
assert len ( res [ " Responses " ] ) == 2
assert res [ " Responses " ] [ 1 ] == { }
2022-03-09 17:57:25 +00:00
res = client . transact_get_items (
TransactItems = [
{
" Get " : {
" Key " : { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 1 " } } ,
" TableName " : " test1 " ,
}
} ,
{
" Get " : {
" Key " : { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 2 " } } ,
" TableName " : " test1 " ,
}
} ,
{
" Get " : {
" Key " : { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 1 " } } ,
" TableName " : " test2 " ,
}
} ,
]
)
2023-07-13 10:21:47 +00:00
assert res [ " Responses " ] [ 0 ] [ " Item " ] == { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 1 " } }
2022-03-09 17:57:25 +00:00
2023-07-13 10:21:47 +00:00
assert res [ " Responses " ] [ 1 ] [ " Item " ] == { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 2 " } }
2022-03-09 17:57:25 +00:00
2023-07-13 10:21:47 +00:00
assert res [ " Responses " ] [ 2 ] [ " Item " ] == { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 1 " } }
2022-03-09 17:57:25 +00:00
res = client . transact_get_items (
TransactItems = [
{
" Get " : {
" Key " : { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 1 " } } ,
" TableName " : " test1 " ,
}
} ,
{
" Get " : {
" Key " : { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 2 " } } ,
" TableName " : " test1 " ,
}
} ,
{
" Get " : {
" Key " : { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 1 " } } ,
" TableName " : " test2 " ,
}
} ,
] ,
ReturnConsumedCapacity = " TOTAL " ,
)
2023-07-13 10:21:47 +00:00
assert res [ " ConsumedCapacity " ] [ 0 ] == {
" TableName " : " test1 " ,
" CapacityUnits " : 4.0 ,
" ReadCapacityUnits " : 4.0 ,
}
2022-03-09 17:57:25 +00:00
2023-07-13 10:21:47 +00:00
assert res [ " ConsumedCapacity " ] [ 1 ] == {
" TableName " : " test2 " ,
" CapacityUnits " : 2.0 ,
" ReadCapacityUnits " : 2.0 ,
}
2022-03-09 17:57:25 +00:00
res = client . transact_get_items (
TransactItems = [
{
" Get " : {
" Key " : { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 1 " } } ,
" TableName " : " test1 " ,
}
} ,
{
" Get " : {
" Key " : { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 2 " } } ,
" TableName " : " test1 " ,
}
} ,
{
" Get " : {
" Key " : { " id " : { " S " : " 1 " } , " sort_key " : { " S " : " 1 " } } ,
" TableName " : " test2 " ,
}
} ,
] ,
ReturnConsumedCapacity = " INDEXES " ,
)
2023-07-13 10:21:47 +00:00
assert res [ " ConsumedCapacity " ] [ 0 ] == {
" TableName " : " test1 " ,
" CapacityUnits " : 4.0 ,
" ReadCapacityUnits " : 4.0 ,
" Table " : { " CapacityUnits " : 4.0 , " ReadCapacityUnits " : 4.0 } ,
}
2022-03-09 17:57:25 +00:00
2023-07-13 10:21:47 +00:00
assert res [ " ConsumedCapacity " ] [ 1 ] == {
" TableName " : " test2 " ,
" CapacityUnits " : 2.0 ,
" ReadCapacityUnits " : 2.0 ,
" Table " : { " CapacityUnits " : 2.0 , " ReadCapacityUnits " : 2.0 } ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_gsi_verify_negative_number_order ( ) :
table_schema = {
" KeySchema " : [ { " AttributeName " : " partitionKey " , " KeyType " : " HASH " } ] ,
" GlobalSecondaryIndexes " : [
{
" IndexName " : " GSI-K1 " ,
" KeySchema " : [
{ " AttributeName " : " gsiK1PartitionKey " , " KeyType " : " HASH " } ,
{ " AttributeName " : " gsiK1SortKey " , " KeyType " : " RANGE " } ,
] ,
2022-03-10 14:39:59 +00:00
" Projection " : { " ProjectionType " : " KEYS_ONLY " } ,
2022-03-09 17:57:25 +00:00
}
] ,
" AttributeDefinitions " : [
{ " AttributeName " : " partitionKey " , " AttributeType " : " S " } ,
{ " AttributeName " : " gsiK1PartitionKey " , " AttributeType " : " S " } ,
{ " AttributeName " : " gsiK1SortKey " , " AttributeType " : " N " } ,
] ,
}
item1 = {
" partitionKey " : " pk-1 " ,
" gsiK1PartitionKey " : " gsi-k1 " ,
" gsiK1SortKey " : Decimal ( " -0.6 " ) ,
}
item2 = {
" partitionKey " : " pk-2 " ,
" gsiK1PartitionKey " : " gsi-k1 " ,
" gsiK1SortKey " : Decimal ( " -0.7 " ) ,
}
item3 = {
" partitionKey " : " pk-3 " ,
" gsiK1PartitionKey " : " gsi-k1 " ,
" gsiK1SortKey " : Decimal ( " 0.7 " ) ,
}
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test-table " , BillingMode = " PAY_PER_REQUEST " , * * table_schema
)
table = dynamodb . Table ( " test-table " )
table . put_item ( Item = item3 )
table . put_item ( Item = item1 )
table . put_item ( Item = item2 )
resp = table . query (
2022-03-10 14:39:59 +00:00
KeyConditionExpression = Key ( " gsiK1PartitionKey " ) . eq ( " gsi-k1 " ) , IndexName = " GSI-K1 "
2022-03-09 17:57:25 +00:00
)
# Items should be ordered with the lowest number first
2023-07-13 10:21:47 +00:00
assert [ float ( item [ " gsiK1SortKey " ] ) for item in resp [ " Items " ] ] == [ - 0.7 , - 0.6 , 0.7 ]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_transact_write_items_put ( ) :
table_schema = {
" KeySchema " : [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
" AttributeDefinitions " : [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
}
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test-table " , BillingMode = " PAY_PER_REQUEST " , * * table_schema
)
# Put multiple items
dynamodb . transact_write_items (
TransactItems = [
{
" Put " : {
2022-03-10 14:39:59 +00:00
" Item " : { " id " : { " S " : f " foo { str ( i ) } " } , " foo " : { " S " : " bar " } } ,
2022-03-09 17:57:25 +00:00
" TableName " : " test-table " ,
}
}
for i in range ( 0 , 5 )
]
)
# Assert all are present
items = dynamodb . scan ( TableName = " test-table " ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( items ) == 5
2022-03-09 17:57:25 +00:00
2023-10-24 20:31:25 +00:00
@pytest.mark.aws_verified
2023-11-04 10:37:32 +00:00
@dynamodb_aws_verified ( )
2023-10-24 20:31:25 +00:00
def test_transact_write_items_put_conditional_expressions ( table_name = None ) :
2022-03-09 17:57:25 +00:00
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
2023-10-24 20:31:25 +00:00
dynamodb . put_item ( TableName = table_name , Item = { " pk " : { " S " : " foo2 " } } )
2022-03-09 17:57:25 +00:00
# Put multiple items
with pytest . raises ( ClientError ) as ex :
dynamodb . transact_write_items (
TransactItems = [
{
" Put " : {
" Item " : {
2023-10-24 20:31:25 +00:00
" pk " : { " S " : f " foo { i } " } ,
2022-03-09 17:57:25 +00:00
" foo " : { " S " : " bar " } ,
} ,
2023-10-24 20:31:25 +00:00
" TableName " : table_name ,
2022-03-09 17:57:25 +00:00
" ConditionExpression " : " #i <> :i " ,
2023-10-24 20:31:25 +00:00
" ExpressionAttributeNames " : { " #i " : " pk " } ,
2022-03-09 17:57:25 +00:00
" ExpressionAttributeValues " : {
" :i " : {
" S " : " foo2 "
} # This item already exist, so the ConditionExpression should fail
} ,
}
}
for i in range ( 0 , 5 )
]
)
# Assert the exception is correct
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " TransactionCanceledException "
2022-04-09 21:45:25 +00:00
reasons = ex . value . response [ " CancellationReasons " ]
2023-07-13 10:21:47 +00:00
assert len ( reasons ) == 5
assert {
" Code " : " ConditionalCheckFailed " ,
" Message " : " The conditional request failed " ,
} in reasons
assert { " Code " : " None " } in reasons
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
2022-03-09 17:57:25 +00:00
# Assert all are present
2023-10-24 20:31:25 +00:00
items = dynamodb . scan ( TableName = table_name ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( items ) == 1
2023-10-24 20:31:25 +00:00
assert items [ 0 ] == { " pk " : { " S " : " foo2 " } }
2022-03-09 17:57:25 +00:00
2023-10-24 20:31:25 +00:00
@pytest.mark.aws_verified
2023-11-04 10:37:32 +00:00
@dynamodb_aws_verified ( )
2023-10-24 20:31:25 +00:00
def test_transact_write_items_failure__return_item ( table_name = None ) :
2022-11-18 21:51:51 +00:00
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
2023-10-24 20:31:25 +00:00
dynamodb . put_item ( TableName = table_name , Item = { " pk " : { " S " : " foo2 " } } )
2022-11-18 21:51:51 +00:00
# Put multiple items
with pytest . raises ( ClientError ) as ex :
dynamodb . transact_write_items (
TransactItems = [
{
" Put " : {
" Item " : {
2023-10-24 20:31:25 +00:00
" pk " : { " S " : f " foo { i } " } ,
2022-11-18 21:51:51 +00:00
" foo " : { " S " : " bar " } ,
} ,
2023-10-24 20:31:25 +00:00
" TableName " : table_name ,
2022-11-18 21:51:51 +00:00
" ConditionExpression " : " #i <> :i " ,
2023-10-24 20:31:25 +00:00
" ExpressionAttributeNames " : { " #i " : " pk " } ,
# This man right here - should return item as part of error message
2022-11-18 21:51:51 +00:00
" ReturnValuesOnConditionCheckFailure " : " ALL_OLD " ,
" ExpressionAttributeValues " : {
" :i " : {
" S " : " foo2 "
} # This item already exist, so the ConditionExpression should fail
} ,
}
}
for i in range ( 0 , 5 )
]
)
# Assert the exception is correct
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " TransactionCanceledException "
2022-11-18 21:51:51 +00:00
reasons = ex . value . response [ " CancellationReasons " ]
2023-07-13 10:21:47 +00:00
assert len ( reasons ) == 5
assert {
" Code " : " ConditionalCheckFailed " ,
" Message " : " The conditional request failed " ,
2023-10-24 20:31:25 +00:00
" Item " : { " pk " : { " S " : " foo2 " } } ,
2023-07-13 10:21:47 +00:00
} in reasons
assert { " Code " : " None " } in reasons
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
2022-11-18 21:51:51 +00:00
# Assert all are present
2023-10-24 20:31:25 +00:00
items = dynamodb . scan ( TableName = table_name ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( items ) == 1
2023-10-24 20:31:25 +00:00
assert items [ 0 ] == { " pk " : { " S " : " foo2 " } }
2022-11-18 21:51:51 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_transact_write_items_conditioncheck_passes ( ) :
table_schema = {
" KeySchema " : [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
" AttributeDefinitions " : [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
}
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test-table " , BillingMode = " PAY_PER_REQUEST " , * * table_schema
)
# Insert an item without email address
2022-03-10 14:39:59 +00:00
dynamodb . put_item ( TableName = " test-table " , Item = { " id " : { " S " : " foo " } } )
2022-03-09 17:57:25 +00:00
# Put an email address, after verifying it doesn't exist yet
dynamodb . transact_write_items (
TransactItems = [
{
" ConditionCheck " : {
" Key " : { " id " : { " S " : " foo " } } ,
" TableName " : " test-table " ,
" ConditionExpression " : " attribute_not_exists(#e) " ,
" ExpressionAttributeNames " : { " #e " : " email_address " } ,
}
} ,
{
" Put " : {
" Item " : {
" id " : { " S " : " foo " } ,
" email_address " : { " S " : " test@moto.com " } ,
} ,
" TableName " : " test-table " ,
}
} ,
]
)
# Assert all are present
items = dynamodb . scan ( TableName = " test-table " ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( items ) == 1
assert items [ 0 ] == { " email_address " : { " S " : " test@moto.com " } , " id " : { " S " : " foo " } }
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_transact_write_items_conditioncheck_fails ( ) :
table_schema = {
" KeySchema " : [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
" AttributeDefinitions " : [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
}
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test-table " , BillingMode = " PAY_PER_REQUEST " , * * table_schema
)
# Insert an item with email address
dynamodb . put_item (
TableName = " test-table " ,
Item = { " id " : { " S " : " foo " } , " email_address " : { " S " : " test@moto.com " } } ,
)
# Try to put an email address, but verify whether it exists
# ConditionCheck should fail
with pytest . raises ( ClientError ) as ex :
dynamodb . transact_write_items (
TransactItems = [
{
" ConditionCheck " : {
" Key " : { " id " : { " S " : " foo " } } ,
" TableName " : " test-table " ,
" ConditionExpression " : " attribute_not_exists(#e) " ,
" ExpressionAttributeNames " : { " #e " : " email_address " } ,
}
} ,
{
" Put " : {
" Item " : {
" id " : { " S " : " foo " } ,
" email_address " : { " S " : " update@moto.com " } ,
} ,
" TableName " : " test-table " ,
}
} ,
]
)
# Assert the exception is correct
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " TransactionCanceledException "
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
2022-03-09 17:57:25 +00:00
# Assert the original email address is still present
items = dynamodb . scan ( TableName = " test-table " ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( items ) == 1
assert items [ 0 ] == { " email_address " : { " S " : " test@moto.com " } , " id " : { " S " : " foo " } }
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_transact_write_items_delete ( ) :
table_schema = {
" KeySchema " : [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
" AttributeDefinitions " : [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
}
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test-table " , BillingMode = " PAY_PER_REQUEST " , * * table_schema
)
# Insert an item
2022-03-10 14:39:59 +00:00
dynamodb . put_item ( TableName = " test-table " , Item = { " id " : { " S " : " foo " } } )
2022-03-09 17:57:25 +00:00
# Delete the item
dynamodb . transact_write_items (
TransactItems = [
2022-03-10 14:39:59 +00:00
{ " Delete " : { " Key " : { " id " : { " S " : " foo " } } , " TableName " : " test-table " } }
2022-03-09 17:57:25 +00:00
]
)
# Assert the item is deleted
items = dynamodb . scan ( TableName = " test-table " ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( items ) == 0
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_transact_write_items_delete_with_successful_condition_expression ( ) :
table_schema = {
" KeySchema " : [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
" AttributeDefinitions " : [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
}
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test-table " , BillingMode = " PAY_PER_REQUEST " , * * table_schema
)
# Insert an item without email address
2022-03-10 14:39:59 +00:00
dynamodb . put_item ( TableName = " test-table " , Item = { " id " : { " S " : " foo " } } )
2022-03-09 17:57:25 +00:00
# ConditionExpression will pass - no email address has been specified yet
dynamodb . transact_write_items (
TransactItems = [
{
" Delete " : {
2022-03-10 14:39:59 +00:00
" Key " : { " id " : { " S " : " foo " } } ,
2022-03-09 17:57:25 +00:00
" TableName " : " test-table " ,
" ConditionExpression " : " attribute_not_exists(#e) " ,
" ExpressionAttributeNames " : { " #e " : " email_address " } ,
}
}
]
)
# Assert the item is deleted
items = dynamodb . scan ( TableName = " test-table " ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( items ) == 0
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_transact_write_items_delete_with_failed_condition_expression ( ) :
table_schema = {
" KeySchema " : [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
" AttributeDefinitions " : [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
}
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test-table " , BillingMode = " PAY_PER_REQUEST " , * * table_schema
)
# Insert an item with email address
dynamodb . put_item (
TableName = " test-table " ,
Item = { " id " : { " S " : " foo " } , " email_address " : { " S " : " test@moto.com " } } ,
)
# Try to delete an item that does not have an email address
# ConditionCheck should fail
with pytest . raises ( ClientError ) as ex :
dynamodb . transact_write_items (
TransactItems = [
{
" Delete " : {
2022-03-10 14:39:59 +00:00
" Key " : { " id " : { " S " : " foo " } } ,
2022-03-09 17:57:25 +00:00
" TableName " : " test-table " ,
" ConditionExpression " : " attribute_not_exists(#e) " ,
" ExpressionAttributeNames " : { " #e " : " email_address " } ,
}
}
]
)
# Assert the exception is correct
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " TransactionCanceledException "
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
2022-03-09 17:57:25 +00:00
# Assert the original item is still present
items = dynamodb . scan ( TableName = " test-table " ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( items ) == 1
assert items [ 0 ] == { " email_address " : { " S " : " test@moto.com " } , " id " : { " S " : " foo " } }
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_transact_write_items_update ( ) :
table_schema = {
" KeySchema " : [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
" AttributeDefinitions " : [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
}
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test-table " , BillingMode = " PAY_PER_REQUEST " , * * table_schema
)
# Insert an item
dynamodb . put_item ( TableName = " test-table " , Item = { " id " : { " S " : " foo " } } )
# Update the item
dynamodb . transact_write_items (
TransactItems = [
{
" Update " : {
" Key " : { " id " : { " S " : " foo " } } ,
" TableName " : " test-table " ,
" UpdateExpression " : " SET #e = :v " ,
" ExpressionAttributeNames " : { " #e " : " email_address " } ,
" ExpressionAttributeValues " : { " :v " : { " S " : " test@moto.com " } } ,
}
}
]
)
# Assert the item is updated
items = dynamodb . scan ( TableName = " test-table " ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( items ) == 1
assert items [ 0 ] == { " id " : { " S " : " foo " } , " email_address " : { " S " : " test@moto.com " } }
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_transact_write_items_update_with_failed_condition_expression ( ) :
table_schema = {
" KeySchema " : [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
" AttributeDefinitions " : [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
}
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test-table " , BillingMode = " PAY_PER_REQUEST " , * * table_schema
)
# Insert an item with email address
dynamodb . put_item (
TableName = " test-table " ,
Item = { " id " : { " S " : " foo " } , " email_address " : { " S " : " test@moto.com " } } ,
)
# Try to update an item that does not have an email address
# ConditionCheck should fail
with pytest . raises ( ClientError ) as ex :
dynamodb . transact_write_items (
TransactItems = [
{
" Update " : {
" Key " : { " id " : { " S " : " foo " } } ,
" TableName " : " test-table " ,
" UpdateExpression " : " SET #e = :v " ,
" ConditionExpression " : " attribute_not_exists(#e) " ,
" ExpressionAttributeNames " : { " #e " : " email_address " } ,
" ExpressionAttributeValues " : { " :v " : { " S " : " update@moto.com " } } ,
}
}
]
)
# Assert the exception is correct
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " TransactionCanceledException "
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
2022-03-09 17:57:25 +00:00
# Assert the original item is still present
items = dynamodb . scan ( TableName = " test-table " ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( items ) == 1
assert items [ 0 ] == { " email_address " : { " S " : " test@moto.com " } , " id " : { " S " : " foo " } }
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_dynamodb_max_1mb_limit ( ) :
ddb = boto3 . resource ( " dynamodb " , region_name = " eu-west-1 " )
table_name = " populated-mock-table "
table = ddb . create_table (
TableName = table_name ,
KeySchema = [
{ " AttributeName " : " partition_key " , " KeyType " : " HASH " } ,
{ " AttributeName " : " sort_key " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " partition_key " , " AttributeType " : " S " } ,
{ " AttributeName " : " sort_key " , " AttributeType " : " S " } ,
] ,
BillingMode = " PAY_PER_REQUEST " ,
)
# Populate the table
items = [
{
" partition_key " : " partition_key_val " , # size=30
" sort_key " : " sort_key_value____ " + str ( i ) , # size=30
}
for i in range ( 10000 , 29999 )
]
with table . batch_writer ( ) as batch :
for item in items :
batch . put_item ( Item = item )
response = table . query (
KeyConditionExpression = Key ( " partition_key " ) . eq ( " partition_key_val " )
)
# We shouldn't get everything back - the total result set is well over 1MB
2023-07-13 10:21:47 +00:00
assert len ( items ) > response [ " Count " ]
assert response [ " LastEvaluatedKey " ] is not None
2022-03-09 17:57:25 +00:00
def assert_raise_syntax_error ( client_error , token , near ) :
"""
Assert whether a client_error is as expected Syntax error . Syntax error looks like : ` syntax_error_template `
Args :
client_error ( ClientError ) : The ClientError exception that was raised
token ( str ) : The token that ws unexpected
near ( str ) : The part in the expression that shows where the error occurs it generally has the preceding token the
optional separation and the problematic token .
"""
syntax_error_template = (
' Invalid UpdateExpression: Syntax error; token: " {token} " , near: " {near} " '
)
expected_syntax_error = syntax_error_template . format ( token = token , near = near )
2023-07-13 10:21:47 +00:00
assert client_error [ " Code " ] == " ValidationException "
assert expected_syntax_error == client_error [ " Message " ]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_expression_with_numeric_literal_instead_of_value ( ) :
"""
DynamoDB requires literals to be passed in as values . If they are put literally in the expression a token error will
be raised
"""
dynamodb = boto3 . client ( " dynamodb " , region_name = " eu-west-1 " )
dynamodb . create_table (
TableName = " moto-test " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
BillingMode = " PAY_PER_REQUEST " ,
)
2023-07-13 10:21:47 +00:00
with pytest . raises ( ClientError ) as exc :
2022-03-09 17:57:25 +00:00
dynamodb . update_item (
TableName = " moto-test " ,
Key = { " id " : { " S " : " 1 " } } ,
UpdateExpression = " SET MyStr = myNum + 1 " ,
)
2023-07-13 10:21:47 +00:00
err = exc . value . response [ " Error " ]
assert_raise_syntax_error ( err , " 1 " , " + 1 " )
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_expression_with_multiple_set_clauses_must_be_comma_separated ( ) :
"""
An UpdateExpression can have multiple set clauses but if they are passed in without the separating comma .
"""
dynamodb = boto3 . client ( " dynamodb " , region_name = " eu-west-1 " )
dynamodb . create_table (
TableName = " moto-test " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
BillingMode = " PAY_PER_REQUEST " ,
)
2023-07-13 10:21:47 +00:00
with pytest . raises ( ClientError ) as exc :
2022-03-09 17:57:25 +00:00
dynamodb . update_item (
TableName = " moto-test " ,
Key = { " id " : { " S " : " 1 " } } ,
UpdateExpression = " SET MyStr = myNum Mystr2 myNum2 " ,
)
2023-07-13 10:21:47 +00:00
err = exc . value . response [ " Error " ]
assert_raise_syntax_error ( err , " Mystr2 " , " myNum Mystr2 myNum2 " )
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_list_tables_exclusive_start_table_name_empty ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
resp = client . list_tables ( Limit = 1 , ExclusiveStartTableName = " whatever " )
2023-07-13 10:21:47 +00:00
assert len ( resp [ " TableNames " ] ) == 0
2022-03-09 17:57:25 +00:00
def assert_correct_client_error (
client_error , code , message_template , message_values = None , braces = None
) :
"""
Assert whether a client_error is as expected . Allow for a list of values to be passed into the message
Args :
client_error ( ClientError ) : The ClientError exception that was raised
code ( str ) : The code for the error ( e . g . ValidationException )
message_template ( str ) : Error message template . if message_values is not None then this template has a { values }
as placeholder . For example :
' Value provided in ExpressionAttributeValues unused in expressions: keys: {values} '
message_values ( list of str | None ) : The values that are passed in the error message
braces ( list of str | None ) : List of length 2 with opening and closing brace for the values . By default it will be
surrounded by curly brackets
"""
braces = braces or [ " { " , " } " ]
assert client_error . response [ " Error " ] [ " Code " ] == code
if message_values is not None :
2022-11-17 22:41:08 +00:00
values_string = f " { braces [ 0 ] } (?P<values>.*) { braces [ 1 ] } "
2022-03-09 17:57:25 +00:00
re_msg = re . compile ( message_template . format ( values = values_string ) )
match_result = re_msg . match ( client_error . response [ " Error " ] [ " Message " ] )
assert match_result is not None
values_string = match_result . groupdict ( ) [ " values " ]
values = [ key for key in values_string . split ( " , " ) ]
assert len ( message_values ) == len ( values )
for value in message_values :
assert value in values
else :
assert client_error . response [ " Error " ] [ " Message " ] == message_template
def create_simple_table_and_return_client ( ) :
dynamodb = boto3 . client ( " dynamodb " , region_name = " eu-west-1 " )
dynamodb . create_table (
TableName = " moto-test " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
)
dynamodb . put_item (
TableName = " moto-test " ,
2022-03-10 14:39:59 +00:00
Item = { " id " : { " S " : " 1 " } , " myNum " : { " N " : " 1 " } , " MyStr " : { " S " : " 1 " } } ,
2022-03-09 17:57:25 +00:00
)
return dynamodb
2023-01-07 11:35:14 +00:00
# https://github.com/getmoto/moto/issues/2806
2022-03-09 17:57:25 +00:00
# https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_UpdateItem.html
# #DDB-UpdateItem-request-UpdateExpression
@mock_dynamodb
def test_update_item_with_attribute_in_right_hand_side_and_operation ( ) :
dynamodb = create_simple_table_and_return_client ( )
dynamodb . update_item (
TableName = " moto-test " ,
Key = { " id " : { " S " : " 1 " } } ,
UpdateExpression = " SET myNum = myNum+:val " ,
ExpressionAttributeValues = { " :val " : { " N " : " 3 " } } ,
)
result = dynamodb . get_item ( TableName = " moto-test " , Key = { " id " : { " S " : " 1 " } } )
assert result [ " Item " ] [ " myNum " ] [ " N " ] == " 4 "
dynamodb . update_item (
TableName = " moto-test " ,
Key = { " id " : { " S " : " 1 " } } ,
UpdateExpression = " SET myNum = myNum - :val " ,
ExpressionAttributeValues = { " :val " : { " N " : " 1 " } } ,
)
result = dynamodb . get_item ( TableName = " moto-test " , Key = { " id " : { " S " : " 1 " } } )
assert result [ " Item " ] [ " myNum " ] [ " N " ] == " 3 "
@mock_dynamodb
def test_non_existing_attribute_should_raise_exception ( ) :
"""
Does error message get correctly raised if attribute is referenced but it does not exist for the item .
"""
dynamodb = create_simple_table_and_return_client ( )
try :
dynamodb . update_item (
TableName = " moto-test " ,
Key = { " id " : { " S " : " 1 " } } ,
UpdateExpression = " SET MyStr = no_attr + MyStr " ,
)
assert False , " Validation exception not thrown "
except dynamodb . exceptions . ClientError as e :
assert_correct_client_error (
e ,
" ValidationException " ,
" The provided expression refers to an attribute that does not exist in the item " ,
)
@mock_dynamodb
def test_update_expression_with_plus_in_attribute_name ( ) :
"""
Does error message get correctly raised if attribute contains a plus and is passed in without an AttributeName . And
lhs & rhs are not attribute IDs by themselve .
"""
dynamodb = create_simple_table_and_return_client ( )
dynamodb . put_item (
TableName = " moto-test " ,
2022-03-10 14:39:59 +00:00
Item = { " id " : { " S " : " 1 " } , " my+Num " : { " S " : " 1 " } , " MyStr " : { " S " : " aaa " } } ,
2022-03-09 17:57:25 +00:00
)
try :
dynamodb . update_item (
TableName = " moto-test " ,
Key = { " id " : { " S " : " 1 " } } ,
UpdateExpression = " SET MyStr = my+Num " ,
)
assert False , " Validation exception not thrown "
except dynamodb . exceptions . ClientError as e :
assert_correct_client_error (
e ,
" ValidationException " ,
" The provided expression refers to an attribute that does not exist in the item " ,
)
@mock_dynamodb
def test_update_expression_with_minus_in_attribute_name ( ) :
"""
Does error message get correctly raised if attribute contains a minus and is passed in without an AttributeName . And
lhs & rhs are not attribute IDs by themselve .
"""
dynamodb = create_simple_table_and_return_client ( )
dynamodb . put_item (
TableName = " moto-test " ,
2022-03-10 14:39:59 +00:00
Item = { " id " : { " S " : " 1 " } , " my-Num " : { " S " : " 1 " } , " MyStr " : { " S " : " aaa " } } ,
2022-03-09 17:57:25 +00:00
)
try :
dynamodb . update_item (
TableName = " moto-test " ,
Key = { " id " : { " S " : " 1 " } } ,
UpdateExpression = " SET MyStr = my-Num " ,
)
assert False , " Validation exception not thrown "
except dynamodb . exceptions . ClientError as e :
assert_correct_client_error (
e ,
" ValidationException " ,
" The provided expression refers to an attribute that does not exist in the item " ,
)
@mock_dynamodb
def test_update_expression_with_space_in_attribute_name ( ) :
"""
Does error message get correctly raised if attribute contains a space and is passed in without an AttributeName . And
lhs & rhs are not attribute IDs by themselves .
"""
dynamodb = create_simple_table_and_return_client ( )
dynamodb . put_item (
TableName = " moto-test " ,
2022-03-10 14:39:59 +00:00
Item = { " id " : { " S " : " 1 " } , " my Num " : { " S " : " 1 " } , " MyStr " : { " S " : " aaa " } } ,
2022-03-09 17:57:25 +00:00
)
2023-07-13 10:21:47 +00:00
with pytest . raises ( ClientError ) as exc :
2022-03-09 17:57:25 +00:00
dynamodb . update_item (
TableName = " moto-test " ,
Key = { " id " : { " S " : " 1 " } } ,
UpdateExpression = " SET MyStr = my Num " ,
)
2023-07-13 10:21:47 +00:00
err = exc . value . response [ " Error " ]
assert_raise_syntax_error ( err , " Num " , " my Num " )
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_summing_up_2_strings_raises_exception ( ) :
"""
Update set supports different DynamoDB types but some operations are not supported . For example summing up 2 strings
raises an exception . It results in ClientError with code ValidationException :
Saying An operand in the update expression has an incorrect data type
"""
dynamodb = create_simple_table_and_return_client ( )
try :
dynamodb . update_item (
TableName = " moto-test " ,
Key = { " id " : { " S " : " 1 " } } ,
UpdateExpression = " SET MyStr = MyStr + MyStr " ,
)
assert False , " Validation exception not thrown "
except dynamodb . exceptions . ClientError as e :
assert_correct_client_error (
e ,
" ValidationException " ,
" An operand in the update expression has an incorrect data type " ,
)
2023-01-07 11:35:14 +00:00
# https://github.com/getmoto/moto/issues/2806
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_item_with_attribute_in_right_hand_side ( ) :
"""
After tokenization and building expression make sure referenced attributes are replaced with their current value
"""
dynamodb = create_simple_table_and_return_client ( )
# Make sure there are 2 values
dynamodb . put_item (
TableName = " moto-test " ,
Item = { " id " : { " S " : " 1 " } , " myVal1 " : { " S " : " Value1 " } , " myVal2 " : { " S " : " Value2 " } } ,
)
dynamodb . update_item (
TableName = " moto-test " ,
Key = { " id " : { " S " : " 1 " } } ,
UpdateExpression = " SET myVal1 = myVal2 " ,
)
result = dynamodb . get_item ( TableName = " moto-test " , Key = { " id " : { " S " : " 1 " } } )
assert result [ " Item " ] [ " myVal1 " ] [ " S " ] == result [ " Item " ] [ " myVal2 " ] [ " S " ] == " Value2 "
@mock_dynamodb
def test_multiple_updates ( ) :
dynamodb = create_simple_table_and_return_client ( )
dynamodb . put_item (
TableName = " moto-test " ,
Item = { " id " : { " S " : " 1 " } , " myNum " : { " N " : " 1 " } , " path " : { " N " : " 6 " } } ,
)
dynamodb . update_item (
TableName = " moto-test " ,
Key = { " id " : { " S " : " 1 " } } ,
UpdateExpression = " SET myNum = #p + :val, newAttr = myNum " ,
ExpressionAttributeValues = { " :val " : { " N " : " 1 " } } ,
ExpressionAttributeNames = { " #p " : " path " } ,
)
result = dynamodb . get_item ( TableName = " moto-test " , Key = { " id " : { " S " : " 1 " } } ) [ " Item " ]
expected_result = {
" myNum " : { " N " : " 7 " } ,
" newAttr " : { " N " : " 1 " } ,
" path " : { " N " : " 6 " } ,
" id " : { " S " : " 1 " } ,
}
assert result == expected_result
@mock_dynamodb
def test_update_item_atomic_counter ( ) :
table = " table_t "
ddb_mock = boto3 . client ( " dynamodb " , region_name = " eu-west-3 " )
ddb_mock . create_table (
TableName = table ,
KeySchema = [ { " AttributeName " : " t_id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " t_id " , " AttributeType " : " S " } ] ,
BillingMode = " PAY_PER_REQUEST " ,
)
key = { " t_id " : { " S " : " item1 " } }
ddb_mock . put_item (
TableName = table ,
Item = { " t_id " : { " S " : " item1 " } , " n_i " : { " N " : " 5 " } , " n_f " : { " N " : " 5.3 " } } ,
)
ddb_mock . update_item (
TableName = table ,
Key = key ,
UpdateExpression = " set n_i = n_i + :inc1, n_f = n_f + :inc2 " ,
ExpressionAttributeValues = { " :inc1 " : { " N " : " 1.2 " } , " :inc2 " : { " N " : " 0.05 " } } ,
)
updated_item = ddb_mock . get_item ( TableName = table , Key = key ) [ " Item " ]
2023-07-13 10:21:47 +00:00
assert updated_item [ " n_i " ] [ " N " ] == " 6.2 "
assert updated_item [ " n_f " ] [ " N " ] == " 5.35 "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_item_atomic_counter_return_values ( ) :
table = " table_t "
ddb_mock = boto3 . client ( " dynamodb " , region_name = " eu-west-3 " )
ddb_mock . create_table (
TableName = table ,
KeySchema = [ { " AttributeName " : " t_id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " t_id " , " AttributeType " : " S " } ] ,
BillingMode = " PAY_PER_REQUEST " ,
)
key = { " t_id " : { " S " : " item1 " } }
ddb_mock . put_item ( TableName = table , Item = { " t_id " : { " S " : " item1 " } , " v " : { " N " : " 5 " } } )
response = ddb_mock . update_item (
TableName = table ,
Key = key ,
UpdateExpression = " set v = v + :inc " ,
ExpressionAttributeValues = { " :inc " : { " N " : " 1 " } } ,
ReturnValues = " UPDATED_OLD " ,
)
2023-07-13 10:21:47 +00:00
# v has been updated, and should be returned here
assert response [ " Attributes " ] [ " v " ] [ " N " ] == " 5 "
2022-03-09 17:57:25 +00:00
# second update
response = ddb_mock . update_item (
TableName = table ,
Key = key ,
UpdateExpression = " set v = v + :inc " ,
ExpressionAttributeValues = { " :inc " : { " N " : " 1 " } } ,
ReturnValues = " UPDATED_OLD " ,
)
2023-07-13 10:21:47 +00:00
# v has been updated, and should be returned here
assert response [ " Attributes " ] [ " v " ] [ " N " ] == " 6 "
2022-03-09 17:57:25 +00:00
# third update
response = ddb_mock . update_item (
TableName = table ,
Key = key ,
UpdateExpression = " set v = v + :inc " ,
ExpressionAttributeValues = { " :inc " : { " N " : " 1 " } } ,
ReturnValues = " UPDATED_NEW " ,
)
2023-07-13 10:21:47 +00:00
# v has been updated, and should be returned here
assert response [ " Attributes " ] [ " v " ] [ " N " ] == " 8 "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_item_atomic_counter_from_zero ( ) :
table = " table_t "
ddb_mock = boto3 . client ( " dynamodb " , region_name = " eu-west-1 " )
ddb_mock . create_table (
TableName = table ,
KeySchema = [ { " AttributeName " : " t_id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " t_id " , " AttributeType " : " S " } ] ,
BillingMode = " PAY_PER_REQUEST " ,
)
key = { " t_id " : { " S " : " item1 " } }
2022-03-10 14:39:59 +00:00
ddb_mock . put_item ( TableName = table , Item = key )
2022-03-09 17:57:25 +00:00
ddb_mock . update_item (
TableName = table ,
Key = key ,
UpdateExpression = " add n_i :inc1, n_f :inc2 " ,
ExpressionAttributeValues = { " :inc1 " : { " N " : " 1.2 " } , " :inc2 " : { " N " : " -0.5 " } } ,
)
updated_item = ddb_mock . get_item ( TableName = table , Key = key ) [ " Item " ]
assert updated_item [ " n_i " ] [ " N " ] == " 1.2 "
assert updated_item [ " n_f " ] [ " N " ] == " -0.5 "
@mock_dynamodb
def test_update_item_add_to_non_existent_set ( ) :
table = " table_t "
ddb_mock = boto3 . client ( " dynamodb " , region_name = " eu-west-1 " )
ddb_mock . create_table (
TableName = table ,
KeySchema = [ { " AttributeName " : " t_id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " t_id " , " AttributeType " : " S " } ] ,
BillingMode = " PAY_PER_REQUEST " ,
)
key = { " t_id " : { " S " : " item1 " } }
2022-03-10 14:39:59 +00:00
ddb_mock . put_item ( TableName = table , Item = key )
2022-03-09 17:57:25 +00:00
ddb_mock . update_item (
TableName = table ,
Key = key ,
UpdateExpression = " add s_i :s1 " ,
ExpressionAttributeValues = { " :s1 " : { " SS " : [ " hello " ] } } ,
)
updated_item = ddb_mock . get_item ( TableName = table , Key = key ) [ " Item " ]
assert updated_item [ " s_i " ] [ " SS " ] == [ " hello " ]
@mock_dynamodb
def test_update_item_add_to_non_existent_number_set ( ) :
table = " table_t "
ddb_mock = boto3 . client ( " dynamodb " , region_name = " eu-west-1 " )
ddb_mock . create_table (
TableName = table ,
KeySchema = [ { " AttributeName " : " t_id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " t_id " , " AttributeType " : " S " } ] ,
BillingMode = " PAY_PER_REQUEST " ,
)
key = { " t_id " : { " S " : " item1 " } }
2022-03-10 14:39:59 +00:00
ddb_mock . put_item ( TableName = table , Item = key )
2022-03-09 17:57:25 +00:00
ddb_mock . update_item (
TableName = table ,
Key = key ,
UpdateExpression = " add s_i :s1 " ,
ExpressionAttributeValues = { " :s1 " : { " NS " : [ " 3 " ] } } ,
)
updated_item = ddb_mock . get_item ( TableName = table , Key = key ) [ " Item " ]
assert updated_item [ " s_i " ] [ " NS " ] == [ " 3 " ]
@mock_dynamodb
def test_transact_write_items_fails_with_transaction_canceled_exception ( ) :
table_schema = {
" KeySchema " : [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
" AttributeDefinitions " : [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
}
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test-table " , BillingMode = " PAY_PER_REQUEST " , * * table_schema
)
# Insert one item
dynamodb . put_item ( TableName = " test-table " , Item = { " id " : { " S " : " foo " } } )
# Update two items, the one that exists and another that doesn't
with pytest . raises ( ClientError ) as ex :
dynamodb . transact_write_items (
TransactItems = [
{
" Update " : {
" Key " : { " id " : { " S " : " foo " } } ,
" TableName " : " test-table " ,
" UpdateExpression " : " SET #k = :v " ,
" ConditionExpression " : " attribute_exists(id) " ,
" ExpressionAttributeNames " : { " #k " : " key " } ,
" ExpressionAttributeValues " : { " :v " : { " S " : " value " } } ,
}
} ,
{
" Update " : {
" Key " : { " id " : { " S " : " doesnotexist " } } ,
" TableName " : " test-table " ,
" UpdateExpression " : " SET #e = :v " ,
" ConditionExpression " : " attribute_exists(id) " ,
" ExpressionAttributeNames " : { " #e " : " key " } ,
" ExpressionAttributeValues " : { " :v " : { " S " : " value " } } ,
}
} ,
]
)
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " TransactionCanceledException "
assert ex . value . response [ " ResponseMetadata " ] [ " HTTPStatusCode " ] == 400
assert (
ex . value . response [ " Error " ] [ " Message " ]
== " Transaction cancelled, please refer cancellation reasons for specific reasons [None, ConditionalCheckFailed] "
2022-03-09 17:57:25 +00:00
)
@mock_dynamodb
def test_gsi_projection_type_keys_only ( ) :
table_schema = {
" KeySchema " : [ { " AttributeName " : " partitionKey " , " KeyType " : " HASH " } ] ,
" GlobalSecondaryIndexes " : [
{
" IndexName " : " GSI-K1 " ,
" KeySchema " : [
{ " AttributeName " : " gsiK1PartitionKey " , " KeyType " : " HASH " } ,
{ " AttributeName " : " gsiK1SortKey " , " KeyType " : " RANGE " } ,
] ,
2022-03-10 14:39:59 +00:00
" Projection " : { " ProjectionType " : " KEYS_ONLY " } ,
2022-03-09 17:57:25 +00:00
}
] ,
" AttributeDefinitions " : [
{ " AttributeName " : " partitionKey " , " AttributeType " : " S " } ,
{ " AttributeName " : " gsiK1PartitionKey " , " AttributeType " : " S " } ,
{ " AttributeName " : " gsiK1SortKey " , " AttributeType " : " S " } ,
] ,
}
item = {
" partitionKey " : " pk-1 " ,
" gsiK1PartitionKey " : " gsi-pk " ,
" gsiK1SortKey " : " gsi-sk " ,
" someAttribute " : " lore ipsum " ,
}
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test-table " , BillingMode = " PAY_PER_REQUEST " , * * table_schema
)
table = dynamodb . Table ( " test-table " )
table . put_item ( Item = item )
items = table . query (
2022-03-10 14:39:59 +00:00
KeyConditionExpression = Key ( " gsiK1PartitionKey " ) . eq ( " gsi-pk " ) , IndexName = " GSI-K1 "
2022-03-09 17:57:25 +00:00
) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( items ) == 1
2022-03-09 17:57:25 +00:00
# Item should only include GSI Keys and Table Keys, as per the ProjectionType
2023-07-13 10:21:47 +00:00
assert items [ 0 ] == {
" gsiK1PartitionKey " : " gsi-pk " ,
" gsiK1SortKey " : " gsi-sk " ,
" partitionKey " : " pk-1 " ,
}
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_gsi_projection_type_include ( ) :
table_schema = {
" KeySchema " : [ { " AttributeName " : " partitionKey " , " KeyType " : " HASH " } ] ,
" GlobalSecondaryIndexes " : [
{
" IndexName " : " GSI-INC " ,
" KeySchema " : [
{ " AttributeName " : " gsiK1PartitionKey " , " KeyType " : " HASH " } ,
{ " AttributeName " : " gsiK1SortKey " , " KeyType " : " RANGE " } ,
] ,
" Projection " : {
" ProjectionType " : " INCLUDE " ,
" NonKeyAttributes " : [ " projectedAttribute " ] ,
} ,
}
] ,
" AttributeDefinitions " : [
{ " AttributeName " : " partitionKey " , " AttributeType " : " S " } ,
{ " AttributeName " : " gsiK1PartitionKey " , " AttributeType " : " S " } ,
{ " AttributeName " : " gsiK1SortKey " , " AttributeType " : " S " } ,
] ,
}
item = {
" partitionKey " : " pk-1 " ,
" gsiK1PartitionKey " : " gsi-pk " ,
" gsiK1SortKey " : " gsi-sk " ,
" projectedAttribute " : " lore ipsum " ,
" nonProjectedAttribute " : " dolor sit amet " ,
}
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test-table " , BillingMode = " PAY_PER_REQUEST " , * * table_schema
)
table = dynamodb . Table ( " test-table " )
table . put_item ( Item = item )
items = table . query (
KeyConditionExpression = Key ( " gsiK1PartitionKey " ) . eq ( " gsi-pk " ) ,
IndexName = " GSI-INC " ,
) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( items ) == 1
2022-03-09 17:57:25 +00:00
# Item should only include keys and additionally projected attributes only
2023-07-13 10:21:47 +00:00
assert items [ 0 ] == {
" gsiK1PartitionKey " : " gsi-pk " ,
" gsiK1SortKey " : " gsi-sk " ,
" partitionKey " : " pk-1 " ,
" projectedAttribute " : " lore ipsum " ,
}
2022-03-09 17:57:25 +00:00
2023-02-22 11:16:54 +00:00
# Same when scanning the table
items = table . scan ( IndexName = " GSI-INC " ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert items [ 0 ] == {
" gsiK1PartitionKey " : " gsi-pk " ,
" gsiK1SortKey " : " gsi-sk " ,
" partitionKey " : " pk-1 " ,
" projectedAttribute " : " lore ipsum " ,
}
2023-02-22 11:16:54 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_lsi_projection_type_keys_only ( ) :
table_schema = {
" KeySchema " : [
{ " AttributeName " : " partitionKey " , " KeyType " : " HASH " } ,
{ " AttributeName " : " sortKey " , " KeyType " : " RANGE " } ,
] ,
" LocalSecondaryIndexes " : [
{
" IndexName " : " LSI " ,
" KeySchema " : [
{ " AttributeName " : " partitionKey " , " KeyType " : " HASH " } ,
{ " AttributeName " : " lsiK1SortKey " , " KeyType " : " RANGE " } ,
] ,
2022-03-10 14:39:59 +00:00
" Projection " : { " ProjectionType " : " KEYS_ONLY " } ,
2022-03-09 17:57:25 +00:00
}
] ,
" AttributeDefinitions " : [
{ " AttributeName " : " partitionKey " , " AttributeType " : " S " } ,
{ " AttributeName " : " sortKey " , " AttributeType " : " S " } ,
{ " AttributeName " : " lsiK1SortKey " , " AttributeType " : " S " } ,
] ,
}
item = {
" partitionKey " : " pk-1 " ,
" sortKey " : " sk-1 " ,
" lsiK1SortKey " : " lsi-sk " ,
" someAttribute " : " lore ipsum " ,
}
dynamodb = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
dynamodb . create_table (
TableName = " test-table " , BillingMode = " PAY_PER_REQUEST " , * * table_schema
)
table = dynamodb . Table ( " test-table " )
table . put_item ( Item = item )
items = table . query (
2022-03-10 14:39:59 +00:00
KeyConditionExpression = Key ( " partitionKey " ) . eq ( " pk-1 " ) , IndexName = " LSI "
2022-03-09 17:57:25 +00:00
) [ " Items " ]
# Item should only include GSI Keys and Table Keys, as per the ProjectionType
2023-07-13 10:21:47 +00:00
assert items == [
2022-03-09 17:57:25 +00:00
{ " partitionKey " : " pk-1 " , " sortKey " : " sk-1 " , " lsiK1SortKey " : " lsi-sk " }
2023-07-13 10:21:47 +00:00
]
2022-03-09 17:57:25 +00:00
2023-02-22 11:16:54 +00:00
# Same when scanning the table
items = table . scan ( IndexName = " LSI " ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert items [ 0 ] == {
" lsiK1SortKey " : " lsi-sk " ,
" partitionKey " : " pk-1 " ,
" sortKey " : " sk-1 " ,
}
2023-02-22 11:16:54 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
@pytest.mark.parametrize (
" attr_name " ,
[ " orders " , " #placeholder " ] ,
ids = [ " use attribute name " , " use expression attribute name " ] ,
)
def test_set_attribute_is_dropped_if_empty_after_update_expression ( attr_name ) :
table_name , item_key , set_item = " test-table " , " test-id " , " test-data "
expression_attribute_names = { " #placeholder " : " orders " }
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
client . create_table (
TableName = table_name ,
KeySchema = [ { " AttributeName " : " customer " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " customer " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
client . update_item (
TableName = table_name ,
Key = { " customer " : { " S " : item_key } } ,
2022-11-17 22:41:08 +00:00
UpdateExpression = f " ADD { attr_name } :order " ,
2022-03-09 17:57:25 +00:00
ExpressionAttributeNames = expression_attribute_names ,
ExpressionAttributeValues = { " :order " : { " SS " : [ set_item ] } } ,
)
resp = client . scan ( TableName = table_name , ProjectionExpression = " customer, orders " )
item = resp [ " Items " ] [ 0 ]
2023-07-13 10:21:47 +00:00
assert " customer " in item
assert " orders " in item
2022-03-09 17:57:25 +00:00
client . update_item (
TableName = table_name ,
Key = { " customer " : { " S " : item_key } } ,
2022-11-17 22:41:08 +00:00
UpdateExpression = f " DELETE { attr_name } :order " ,
2022-03-09 17:57:25 +00:00
ExpressionAttributeNames = expression_attribute_names ,
ExpressionAttributeValues = { " :order " : { " SS " : [ set_item ] } } ,
)
resp = client . scan ( TableName = table_name , ProjectionExpression = " customer, orders " )
item = resp [ " Items " ] [ 0 ]
2023-07-13 10:21:47 +00:00
assert " customer " in item
assert " orders " not in item
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_transact_get_items_should_return_empty_map_for_non_existent_item ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-west-2 " )
table_name = " test-table "
key_schema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ]
attribute_definitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ]
client . create_table (
TableName = table_name ,
KeySchema = key_schema ,
AttributeDefinitions = attribute_definitions ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
item = { " id " : { " S " : " 1 " } }
client . put_item ( TableName = table_name , Item = item )
items = client . transact_get_items (
TransactItems = [
{ " Get " : { " Key " : { " id " : { " S " : " 1 " } } , " TableName " : table_name } } ,
{ " Get " : { " Key " : { " id " : { " S " : " 2 " } } , " TableName " : table_name } } ,
]
) . get ( " Responses " , [ ] )
2023-07-13 10:21:47 +00:00
assert len ( items ) == 2
assert items [ 0 ] == { " Item " : item }
assert items [ 1 ] == { }
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_dynamodb_update_item_fails_on_string_sets ( ) :
dynamodb = boto3 . resource ( " dynamodb " , region_name = " eu-west-1 " )
client = boto3 . client ( " dynamodb " , region_name = " eu-west-1 " )
table = dynamodb . create_table (
TableName = " test " ,
2022-03-10 14:39:59 +00:00
KeySchema = [ { " AttributeName " : " record_id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " record_id " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
BillingMode = " PAY_PER_REQUEST " ,
)
table . meta . client . get_waiter ( " table_exists " ) . wait ( TableName = " test " )
2022-03-10 14:39:59 +00:00
attribute = { " test_field " : { " Value " : { " SS " : [ " test1 " , " test2 " ] } , " Action " : " PUT " } }
2022-03-09 17:57:25 +00:00
client . update_item (
TableName = " test " ,
Key = { " record_id " : { " S " : " testrecord " } } ,
AttributeUpdates = attribute ,
)
@mock_dynamodb
def test_update_item_add_to_list_using_legacy_attribute_updates ( ) :
resource = boto3 . resource ( " dynamodb " , region_name = " us-west-2 " )
resource . create_table (
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
TableName = " TestTable " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = resource . Table ( " TestTable " )
table . wait_until_exists ( )
2022-03-10 14:39:59 +00:00
table . put_item ( Item = { " id " : " list_add " , " attr " : [ " a " , " b " , " c " ] } )
2022-03-09 17:57:25 +00:00
table . update_item (
TableName = " TestTable " ,
Key = { " id " : " list_add " } ,
AttributeUpdates = { " attr " : { " Action " : " ADD " , " Value " : [ " d " , " e " ] } } ,
)
resp = table . get_item ( Key = { " id " : " list_add " } )
2023-07-13 10:21:47 +00:00
assert resp [ " Item " ] [ " attr " ] == [ " a " , " b " , " c " , " d " , " e " ]
2022-03-09 17:57:25 +00:00
2023-04-01 16:47:04 +00:00
@mock_dynamodb
def test_update_item_add_to_num_set_using_legacy_attribute_updates ( ) :
resource = boto3 . resource ( " dynamodb " , region_name = " us-west-2 " )
resource . create_table (
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
TableName = " TestTable " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = resource . Table ( " TestTable " )
table . wait_until_exists ( )
table . put_item ( Item = { " id " : " set_add " , " attr " : { 1 , 2 } } )
table . update_item (
TableName = " TestTable " ,
Key = { " id " : " set_add " } ,
AttributeUpdates = { " attr " : { " Action " : " PUT " , " Value " : { 1 , 2 , 3 } } } ,
)
table . update_item (
TableName = " TestTable " ,
Key = { " id " : " set_add " } ,
AttributeUpdates = { " attr " : { " Action " : " ADD " , " Value " : { 4 , 5 } } } ,
)
resp = table . get_item ( Key = { " id " : " set_add " } )
2023-07-13 10:21:47 +00:00
assert resp [ " Item " ] [ " attr " ] == { 1 , 2 , 3 , 4 , 5 }
2023-04-01 16:47:04 +00:00
table . update_item (
TableName = " TestTable " ,
Key = { " id " : " set_add " } ,
AttributeUpdates = { " attr " : { " Action " : " DELETE " , " Value " : { 2 , 3 } } } ,
)
resp = table . get_item ( Key = { " id " : " set_add " } )
2023-07-13 10:21:47 +00:00
assert resp [ " Item " ] [ " attr " ] == { 1 , 4 , 5 }
2023-04-01 16:47:04 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_get_item_for_non_existent_table_raises_error ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
with pytest . raises ( ClientError ) as ex :
client . get_item ( TableName = " non-existent " , Key = { " site-id " : { " S " : " foo " } } )
2023-07-13 10:21:47 +00:00
assert ex . value . response [ " Error " ] [ " Code " ] == " ResourceNotFoundException "
assert ex . value . response [ " Error " ] [ " Message " ] == " Requested resource not found "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_error_when_providing_expression_and_nonexpression_params ( ) :
client = boto3 . client ( " dynamodb " , " eu-central-1 " )
table_name = " testtable "
client . create_table (
TableName = table_name ,
2022-03-10 14:39:59 +00:00
KeySchema = [ { " AttributeName " : " pkey " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " pkey " , " AttributeType " : " S " } ] ,
2022-03-09 17:57:25 +00:00
BillingMode = " PAY_PER_REQUEST " ,
)
with pytest . raises ( ClientError ) as ex :
client . update_item (
TableName = table_name ,
Key = { " pkey " : { " S " : " testrecord " } } ,
AttributeUpdates = {
2022-03-10 14:39:59 +00:00
" test_field " : { " Value " : { " SS " : [ " test1 " , " test2 " ] } , " Action " : " PUT " }
2022-03-09 17:57:25 +00:00
} ,
UpdateExpression = " DELETE orders :order " ,
ExpressionAttributeValues = { " :order " : { " SS " : [ " item " ] } } ,
)
err = ex . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert err [ " Code " ] == " ValidationException "
assert (
err [ " Message " ]
== " Can not use both expression and non-expression parameters in the same request: Non-expression parameters: {AttributeUpdates} Expression parameters: {UpdateExpression} "
2022-03-09 17:57:25 +00:00
)
@mock_dynamodb
def test_attribute_item_delete ( ) :
name = " TestTable "
conn = boto3 . client ( " dynamodb " , region_name = " eu-west-1 " )
conn . create_table (
TableName = name ,
AttributeDefinitions = [ { " AttributeName " : " name " , " AttributeType " : " S " } ] ,
KeySchema = [ { " AttributeName " : " name " , " KeyType " : " HASH " } ] ,
BillingMode = " PAY_PER_REQUEST " ,
)
item_name = " foo "
conn . put_item (
TableName = name , Item = { " name " : { " S " : item_name } , " extra " : { " S " : " bar " } }
)
conn . update_item (
TableName = name ,
Key = { " name " : { " S " : item_name } } ,
AttributeUpdates = { " extra " : { " Action " : " DELETE " } } ,
)
items = conn . scan ( TableName = name ) [ " Items " ]
2023-07-13 10:21:47 +00:00
assert items == [ { " name " : { " S " : " foo " } } ]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_gsi_key_can_be_updated ( ) :
name = " TestTable "
conn = boto3 . client ( " dynamodb " , region_name = " eu-west-2 " )
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " main_key " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [
{ " AttributeName " : " main_key " , " AttributeType " : " S " } ,
{ " AttributeName " : " index_key " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
GlobalSecondaryIndexes = [
{
" IndexName " : " test_index " ,
" KeySchema " : [ { " AttributeName " : " index_key " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
" Projection " : { " ProjectionType " : " ALL " } ,
2022-03-09 17:57:25 +00:00
" ProvisionedThroughput " : {
" ReadCapacityUnits " : 1 ,
" WriteCapacityUnits " : 1 ,
} ,
}
] ,
)
conn . put_item (
TableName = name ,
Item = {
" main_key " : { " S " : " testkey1 " } ,
" extra_data " : { " S " : " testdata " } ,
" index_key " : { " S " : " indexkey1 " } ,
} ,
)
conn . update_item (
TableName = name ,
Key = { " main_key " : { " S " : " testkey1 " } } ,
UpdateExpression = " set index_key=:new_index_key " ,
ExpressionAttributeValues = { " :new_index_key " : { " S " : " new_value " } } ,
)
item = conn . scan ( TableName = name ) [ " Items " ] [ 0 ]
2023-07-13 10:21:47 +00:00
assert item [ " index_key " ] == { " S " : " new_value " }
assert item [ " main_key " ] == { " S " : " testkey1 " }
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_gsi_key_cannot_be_empty ( ) :
name = " TestTable "
conn = boto3 . client ( " dynamodb " , region_name = " eu-west-2 " )
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " main_key " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [
{ " AttributeName " : " main_key " , " AttributeType " : " S " } ,
{ " AttributeName " : " index_key " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
GlobalSecondaryIndexes = [
{
" IndexName " : " test_index " ,
" KeySchema " : [ { " AttributeName " : " index_key " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
" Projection " : { " ProjectionType " : " ALL " } ,
2022-03-09 17:57:25 +00:00
" ProvisionedThroughput " : {
" ReadCapacityUnits " : 1 ,
" WriteCapacityUnits " : 1 ,
} ,
}
] ,
)
conn . put_item (
TableName = name ,
Item = {
" main_key " : { " S " : " testkey1 " } ,
" extra_data " : { " S " : " testdata " } ,
" index_key " : { " S " : " indexkey1 " } ,
} ,
)
with pytest . raises ( ClientError ) as ex :
conn . update_item (
TableName = name ,
Key = { " main_key " : { " S " : " testkey1 " } } ,
UpdateExpression = " set index_key=:new_index_key " ,
ExpressionAttributeValues = { " :new_index_key " : { " S " : " " } } ,
)
err = ex . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert err [ " Code " ] == " ValidationException "
assert (
err [ " Message " ]
== " One or more parameter values are not valid. The update expression attempted to update a secondary index key to a value that is not supported. The AttributeValue for a key attribute cannot contain an empty string value. "
2022-03-09 17:57:25 +00:00
)
@mock_dynamodb
def test_create_backup_for_non_existent_table_raises_error ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
with pytest . raises ( ClientError ) as ex :
client . create_backup ( TableName = " non-existent " , BackupName = " backup " )
error = ex . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert error [ " Code " ] == " TableNotFoundException "
assert error [ " Message " ] == " Table not found: non-existent "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_create_backup ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
table_name = " test-table "
client . create_table (
TableName = table_name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
backup_name = " backup-test-table "
resp = client . create_backup ( TableName = table_name , BackupName = backup_name )
details = resp . get ( " BackupDetails " )
2023-07-13 10:21:47 +00:00
assert table_name in details [ " BackupArn " ]
assert details [ " BackupName " ] == backup_name
assert isinstance ( details [ " BackupSizeBytes " ] , int )
assert " BackupStatus " in details
assert details [ " BackupType " ] == " USER "
assert isinstance ( details [ " BackupCreationDateTime " ] , datetime )
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_create_multiple_backups_with_same_name ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
table_name = " test-table "
client . create_table (
TableName = table_name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
backup_name = " backup-test-table "
backup_arns = [ ]
for _ in range ( 4 ) :
backup = client . create_backup ( TableName = table_name , BackupName = backup_name ) . get (
" BackupDetails "
)
2023-07-13 10:21:47 +00:00
assert backup [ " BackupName " ] == backup_name
assert backup [ " BackupArn " ] not in backup_arns
2022-03-09 17:57:25 +00:00
backup_arns . append ( backup [ " BackupArn " ] )
@mock_dynamodb
def test_describe_backup_for_non_existent_backup_raises_error ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
non_existent_arn = " arn:aws:dynamodb:us-east-1:123456789012:table/table-name/backup/01623095754481-2cfcd6f9 "
with pytest . raises ( ClientError ) as ex :
client . describe_backup ( BackupArn = non_existent_arn )
error = ex . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert error [ " Code " ] == " BackupNotFoundException "
assert error [ " Message " ] == f " Backup not found: { non_existent_arn } "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_describe_backup ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
table_name = " test-table "
table = client . create_table (
TableName = table_name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
) . get ( " TableDescription " )
backup_name = " backup-test-table "
backup_arn = (
client . create_backup ( TableName = table_name , BackupName = backup_name )
. get ( " BackupDetails " )
. get ( " BackupArn " )
)
resp = client . describe_backup ( BackupArn = backup_arn )
description = resp . get ( " BackupDescription " )
details = description . get ( " BackupDetails " )
2023-07-13 10:21:47 +00:00
assert table_name in details [ " BackupArn " ]
assert details [ " BackupName " ] == backup_name
assert isinstance ( details [ " BackupSizeBytes " ] , int )
assert " BackupStatus " in details
assert details [ " BackupType " ] == " USER "
assert isinstance ( details [ " BackupCreationDateTime " ] , datetime )
2022-03-09 17:57:25 +00:00
source = description . get ( " SourceTableDetails " )
2023-07-13 10:21:47 +00:00
assert source [ " TableName " ] == table_name
assert source [ " TableArn " ] == table [ " TableArn " ]
assert isinstance ( source [ " TableSizeBytes " ] , int )
assert source [ " KeySchema " ] == table [ " KeySchema " ]
assert source [ " TableCreationDateTime " ] == table [ " CreationDateTime " ]
assert isinstance ( source [ " ProvisionedThroughput " ] , dict )
assert source [ " ItemCount " ] == table [ " ItemCount " ]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_list_backups_for_non_existent_table ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
resp = client . list_backups ( TableName = " non-existent " )
2023-07-13 10:21:47 +00:00
assert len ( resp [ " BackupSummaries " ] ) == 0
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_list_backups ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
table_names = [ " test-table-1 " , " test-table-2 " ]
backup_names = [ " backup-1 " , " backup-2 " ]
for table_name in table_names :
client . create_table (
TableName = table_name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
for backup_name in backup_names :
client . create_backup ( TableName = table_name , BackupName = backup_name )
resp = client . list_backups ( BackupType = " USER " )
2023-07-13 10:21:47 +00:00
assert len ( resp [ " BackupSummaries " ] ) == 4
2022-03-09 17:57:25 +00:00
for table_name in table_names :
resp = client . list_backups ( TableName = table_name )
2023-07-13 10:21:47 +00:00
assert len ( resp [ " BackupSummaries " ] ) == 2
2022-03-09 17:57:25 +00:00
for summary in resp [ " BackupSummaries " ] :
2023-07-13 10:21:47 +00:00
assert summary [ " TableName " ] == table_name
assert table_name in summary [ " TableArn " ]
assert summary [ " BackupName " ] in backup_names
assert " BackupArn " in summary
assert isinstance ( summary [ " BackupCreationDateTime " ] , datetime )
assert " BackupStatus " in summary
assert summary [ " BackupType " ] in [ " USER " , " SYSTEM " ]
assert isinstance ( summary [ " BackupSizeBytes " ] , int )
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_restore_table_from_non_existent_backup_raises_error ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
non_existent_arn = " arn:aws:dynamodb:us-east-1:123456789012:table/table-name/backup/01623095754481-2cfcd6f9 "
with pytest . raises ( ClientError ) as ex :
client . restore_table_from_backup (
TargetTableName = " from-backup " , BackupArn = non_existent_arn
)
error = ex . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert error [ " Code " ] == " BackupNotFoundException "
assert error [ " Message " ] == f " Backup not found: { non_existent_arn } "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_restore_table_from_backup_raises_error_when_table_already_exists ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
table_name = " test-table "
client . create_table (
TableName = table_name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
resp = client . create_backup ( TableName = table_name , BackupName = " backup " )
backup = resp . get ( " BackupDetails " )
with pytest . raises ( ClientError ) as ex :
client . restore_table_from_backup (
TargetTableName = table_name , BackupArn = backup [ " BackupArn " ]
)
error = ex . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert error [ " Code " ] == " TableAlreadyExistsException "
assert error [ " Message " ] == f " Table already exists: { table_name } "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_restore_table_from_backup ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
table_name = " test-table "
resp = client . create_table (
TableName = table_name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = resp . get ( " TableDescription " )
for i in range ( 5 ) :
2022-11-17 22:41:08 +00:00
client . put_item ( TableName = table_name , Item = { " id " : { " S " : f " item { i } " } } )
2022-03-09 17:57:25 +00:00
backup_arn = (
client . create_backup ( TableName = table_name , BackupName = " backup " )
. get ( " BackupDetails " )
. get ( " BackupArn " )
)
restored_table_name = " restored-from-backup "
restored = client . restore_table_from_backup (
TargetTableName = restored_table_name , BackupArn = backup_arn
) . get ( " TableDescription " )
2023-07-13 10:21:47 +00:00
assert restored [ " AttributeDefinitions " ] == table [ " AttributeDefinitions " ]
assert restored [ " TableName " ] == restored_table_name
assert restored [ " KeySchema " ] == table [ " KeySchema " ]
assert " TableStatus " in restored
assert restored [ " ItemCount " ] == 5
assert restored_table_name in restored [ " TableArn " ]
assert isinstance ( restored [ " RestoreSummary " ] , dict )
2022-03-09 17:57:25 +00:00
summary = restored . get ( " RestoreSummary " )
2023-07-13 10:21:47 +00:00
assert summary [ " SourceBackupArn " ] == backup_arn
assert summary [ " SourceTableArn " ] == table [ " TableArn " ]
assert isinstance ( summary [ " RestoreDateTime " ] , datetime )
assert summary [ " RestoreInProgress " ] is False
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_restore_table_to_point_in_time ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
table_name = " test-table "
resp = client . create_table (
TableName = table_name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table = resp . get ( " TableDescription " )
for i in range ( 5 ) :
2022-11-17 22:41:08 +00:00
client . put_item ( TableName = table_name , Item = { " id " : { " S " : f " item { i } " } } )
2022-03-09 17:57:25 +00:00
restored_table_name = " restored-from-pit "
restored = client . restore_table_to_point_in_time (
TargetTableName = restored_table_name , SourceTableName = table_name
) . get ( " TableDescription " )
2023-07-13 10:21:47 +00:00
assert restored [ " TableName " ] == restored_table_name
assert restored [ " KeySchema " ] == table [ " KeySchema " ]
assert " TableStatus " in restored
assert restored [ " ItemCount " ] == 5
assert restored_table_name in restored [ " TableArn " ]
assert isinstance ( restored [ " RestoreSummary " ] , dict )
2022-03-09 17:57:25 +00:00
summary = restored . get ( " RestoreSummary " )
2023-07-13 10:21:47 +00:00
assert summary [ " SourceTableArn " ] == table [ " TableArn " ]
assert isinstance ( summary [ " RestoreDateTime " ] , datetime )
assert summary [ " RestoreInProgress " ] is False
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_restore_table_to_point_in_time_raises_error_when_source_not_exist ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
table_name = " test-table "
restored_table_name = " restored-from-pit "
with pytest . raises ( ClientError ) as ex :
client . restore_table_to_point_in_time (
TargetTableName = restored_table_name , SourceTableName = table_name
)
error = ex . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert error [ " Code " ] == " SourceTableNotFoundException "
assert error [ " Message " ] == f " Source table not found: { table_name } "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_restore_table_to_point_in_time_raises_error_when_dest_exist ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
table_name = " test-table "
restored_table_name = " restored-from-pit "
client . create_table (
TableName = table_name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
client . create_table (
TableName = restored_table_name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
with pytest . raises ( ClientError ) as ex :
client . restore_table_to_point_in_time (
TargetTableName = restored_table_name , SourceTableName = table_name
)
error = ex . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert error [ " Code " ] == " TableAlreadyExistsException "
assert error [ " Message " ] == f " Table already exists: { restored_table_name } "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_delete_non_existent_backup_raises_error ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
non_existent_arn = " arn:aws:dynamodb:us-east-1:123456789012:table/table-name/backup/01623095754481-2cfcd6f9 "
with pytest . raises ( ClientError ) as ex :
client . delete_backup ( BackupArn = non_existent_arn )
error = ex . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert error [ " Code " ] == " BackupNotFoundException "
assert error [ " Message " ] == f " Backup not found: { non_existent_arn } "
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_delete_backup ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
table_name = " test-table-1 "
backup_names = [ " backup-1 " , " backup-2 " ]
client . create_table (
TableName = table_name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
for backup_name in backup_names :
client . create_backup ( TableName = table_name , BackupName = backup_name )
resp = client . list_backups ( TableName = table_name , BackupType = " USER " )
2023-07-13 10:21:47 +00:00
assert len ( resp [ " BackupSummaries " ] ) == 2
2022-03-09 17:57:25 +00:00
backup_to_delete = resp [ " BackupSummaries " ] [ 0 ] [ " BackupArn " ]
backup_deleted = client . delete_backup ( BackupArn = backup_to_delete ) . get (
" BackupDescription "
)
2023-07-13 10:21:47 +00:00
assert " SourceTableDetails " in backup_deleted
assert " BackupDetails " in backup_deleted
2022-03-09 17:57:25 +00:00
details = backup_deleted [ " BackupDetails " ]
2023-07-13 10:21:47 +00:00
assert details [ " BackupArn " ] == backup_to_delete
assert details [ " BackupName " ] in backup_names
assert details [ " BackupStatus " ] == " DELETED "
2022-03-09 17:57:25 +00:00
resp = client . list_backups ( TableName = table_name , BackupType = " USER " )
2023-07-13 10:21:47 +00:00
assert len ( resp [ " BackupSummaries " ] ) == 1
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_source_and_restored_table_items_are_not_linked ( ) :
client = boto3 . client ( " dynamodb " , " us-east-1 " )
def add_guids_to_table ( table , num_items ) :
guids = [ ]
for _ in range ( num_items ) :
guid = str ( uuid . uuid4 ( ) )
client . put_item ( TableName = table , Item = { " id " : { " S " : guid } } )
guids . append ( guid )
return guids
source_table_name = " source-table "
client . create_table (
TableName = source_table_name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
guids_original = add_guids_to_table ( source_table_name , 5 )
backup_arn = (
client . create_backup ( TableName = source_table_name , BackupName = " backup " )
. get ( " BackupDetails " )
. get ( " BackupArn " )
)
guids_added_after_backup = add_guids_to_table ( source_table_name , 5 )
restored_table_name = " restored-from-backup "
client . restore_table_from_backup (
TargetTableName = restored_table_name , BackupArn = backup_arn
)
guids_added_after_restore = add_guids_to_table ( restored_table_name , 5 )
source_table_items = client . scan ( TableName = source_table_name )
2023-07-13 10:21:47 +00:00
assert source_table_items [ " Count " ] == 10
2022-03-09 17:57:25 +00:00
source_table_guids = [ x [ " id " ] [ " S " ] for x in source_table_items [ " Items " ] ]
2023-07-13 10:21:47 +00:00
assert set ( source_table_guids ) == set ( guids_original ) | set (
guids_added_after_backup
2022-03-09 17:57:25 +00:00
)
restored_table_items = client . scan ( TableName = restored_table_name )
2023-07-13 10:21:47 +00:00
assert restored_table_items [ " Count " ] == 10
2022-03-09 17:57:25 +00:00
restored_table_guids = [ x [ " id " ] [ " S " ] for x in restored_table_items [ " Items " ] ]
2023-07-13 10:21:47 +00:00
assert set ( restored_table_guids ) == set ( guids_original ) | set (
guids_added_after_restore
2022-03-09 17:57:25 +00:00
)
@mock_dynamodb
@pytest.mark.parametrize ( " region " , [ " eu-central-1 " , " ap-south-1 " ] )
def test_describe_endpoints ( region ) :
client = boto3 . client ( " dynamodb " , region )
res = client . describe_endpoints ( ) [ " Endpoints " ]
2023-07-13 10:21:47 +00:00
assert res == [
{
" Address " : f " dynamodb. { region } .amazonaws.com " ,
" CachePeriodInMinutes " : 1440 ,
} ,
]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_update_non_existing_item_raises_error_and_does_not_contain_item_afterwards ( ) :
"""
2023-01-07 11:35:14 +00:00
https : / / github . com / getmoto / moto / issues / 3729
2022-03-09 17:57:25 +00:00
Exception is raised , but item was persisted anyway
Happened because we would create a placeholder , before validating / executing the UpdateExpression
: return :
"""
name = " TestTable "
conn = boto3 . client ( " dynamodb " , region_name = " us-west-2 " )
hkey = " primary_partition_key "
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : hkey , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : hkey , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
update_expression = {
" Key " : { hkey : " some_identification_string " } ,
" UpdateExpression " : " set #AA.#AB = :aa " ,
" ExpressionAttributeValues " : { " :aa " : " abc " } ,
" ExpressionAttributeNames " : { " #AA " : " some_dict " , " #AB " : " key1 " } ,
" ConditionExpression " : " attribute_not_exists(#AA.#AB) " ,
}
table = boto3 . resource ( " dynamodb " , region_name = " us-west-2 " ) . Table ( name )
with pytest . raises ( ClientError ) as err :
table . update_item ( * * update_expression )
2023-07-13 10:21:47 +00:00
assert err . value . response [ " Error " ] [ " Code " ] == " ValidationException "
2022-03-09 17:57:25 +00:00
2023-07-13 10:21:47 +00:00
assert len ( conn . scan ( TableName = name ) [ " Items " ] ) == 0
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_batch_write_item ( ) :
conn = boto3 . resource ( " dynamodb " , region_name = " us-west-2 " )
tables = [ f " table- { i } " for i in range ( 3 ) ]
for name in tables :
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
BillingMode = " PAY_PER_REQUEST " ,
)
conn . batch_write_item (
RequestItems = {
tables [ 0 ] : [ { " PutRequest " : { " Item " : { " id " : " 0 " } } } ] ,
tables [ 1 ] : [ { " PutRequest " : { " Item " : { " id " : " 1 " } } } ] ,
tables [ 2 ] : [ { " PutRequest " : { " Item " : { " id " : " 2 " } } } ] ,
}
)
for idx , name in enumerate ( tables ) :
table = conn . Table ( f " table- { idx } " )
res = table . get_item ( Key = { " id " : str ( idx ) } )
2023-07-13 10:21:47 +00:00
assert res [ " Item " ] == { " id " : str ( idx ) }
assert table . scan ( ) [ " Count " ] == 1
2022-03-09 17:57:25 +00:00
conn . batch_write_item (
RequestItems = {
tables [ 0 ] : [ { " DeleteRequest " : { " Key " : { " id " : " 0 " } } } ] ,
tables [ 1 ] : [ { " DeleteRequest " : { " Key " : { " id " : " 1 " } } } ] ,
tables [ 2 ] : [ { " DeleteRequest " : { " Key " : { " id " : " 2 " } } } ] ,
}
)
for idx , name in enumerate ( tables ) :
table = conn . Table ( f " table- { idx } " )
2023-07-13 10:21:47 +00:00
assert table . scan ( ) [ " Count " ] == 0
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_gsi_lastevaluatedkey ( ) :
2023-01-07 11:35:14 +00:00
# github.com/getmoto/moto/issues/3968
2022-03-09 17:57:25 +00:00
conn = boto3 . resource ( " dynamodb " , region_name = " us-west-2 " )
name = " test-table "
table = conn . Table ( name )
conn . create_table (
TableName = name ,
KeySchema = [ { " AttributeName " : " main_key " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [
{ " AttributeName " : " main_key " , " AttributeType " : " S " } ,
{ " AttributeName " : " index_key " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
GlobalSecondaryIndexes = [
{
" IndexName " : " test_index " ,
" KeySchema " : [ { " AttributeName " : " index_key " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
" Projection " : { " ProjectionType " : " ALL " } ,
2022-03-09 17:57:25 +00:00
" ProvisionedThroughput " : {
" ReadCapacityUnits " : 1 ,
" WriteCapacityUnits " : 1 ,
} ,
}
] ,
)
table . put_item (
Item = {
" main_key " : " testkey1 " ,
" extra_data " : " testdata " ,
" index_key " : " indexkey " ,
2022-03-10 14:39:59 +00:00
}
2022-03-09 17:57:25 +00:00
)
table . put_item (
Item = {
" main_key " : " testkey2 " ,
" extra_data " : " testdata " ,
" index_key " : " indexkey " ,
2022-03-10 14:39:59 +00:00
}
2022-03-09 17:57:25 +00:00
)
response = table . query (
Limit = 1 ,
KeyConditionExpression = Key ( " index_key " ) . eq ( " indexkey " ) ,
IndexName = " test_index " ,
)
items = response [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( items ) == 1
assert items [ 0 ] == {
" main_key " : " testkey1 " ,
" extra_data " : " testdata " ,
" index_key " : " indexkey " ,
}
2022-03-09 17:57:25 +00:00
last_evaluated_key = response [ " LastEvaluatedKey " ]
2023-07-13 10:21:47 +00:00
assert len ( last_evaluated_key ) == 2
assert last_evaluated_key == { " main_key " : " testkey1 " , " index_key " : " indexkey " }
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_filter_expression_execution_order ( ) :
2023-01-07 11:35:14 +00:00
# As mentioned here: https://github.com/getmoto/moto/issues/3909
2022-03-09 17:57:25 +00:00
# and documented here: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.FilterExpression
# the filter expression should be evaluated after the query.
# The same applies to scan operations:
# https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.FilterExpression
# If we set limit=1 and apply a filter expression whixh excludes the first result
# then we should get no items in response.
conn = boto3 . resource ( " dynamodb " , region_name = " us-west-2 " )
name = " test-filter-expression-table "
table = conn . Table ( name )
conn . create_table (
TableName = name ,
KeySchema = [
{ " AttributeName " : " hash_key " , " KeyType " : " HASH " } ,
{ " AttributeName " : " range_key " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " hash_key " , " AttributeType " : " S " } ,
{ " AttributeName " : " range_key " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
table . put_item (
2022-03-10 14:39:59 +00:00
Item = { " hash_key " : " keyvalue " , " range_key " : " A " , " filtered_attribute " : " Y " }
2022-03-09 17:57:25 +00:00
)
table . put_item (
2022-03-10 14:39:59 +00:00
Item = { " hash_key " : " keyvalue " , " range_key " : " B " , " filtered_attribute " : " Z " }
2022-03-09 17:57:25 +00:00
)
# test query
query_response_1 = table . query (
Limit = 1 ,
KeyConditionExpression = Key ( " hash_key " ) . eq ( " keyvalue " ) ,
FilterExpression = Attr ( " filtered_attribute " ) . eq ( " Z " ) ,
)
query_items_1 = query_response_1 [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( query_items_1 ) == 0
2022-03-09 17:57:25 +00:00
query_last_evaluated_key = query_response_1 [ " LastEvaluatedKey " ]
2023-07-13 10:21:47 +00:00
assert len ( query_last_evaluated_key ) == 2
assert query_last_evaluated_key == { " hash_key " : " keyvalue " , " range_key " : " A " }
2022-03-09 17:57:25 +00:00
query_response_2 = table . query (
Limit = 1 ,
KeyConditionExpression = Key ( " hash_key " ) . eq ( " keyvalue " ) ,
FilterExpression = Attr ( " filtered_attribute " ) . eq ( " Z " ) ,
ExclusiveStartKey = query_last_evaluated_key ,
)
query_items_2 = query_response_2 [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( query_items_2 ) == 1
assert query_items_2 [ 0 ] == {
" hash_key " : " keyvalue " ,
" filtered_attribute " : " Z " ,
" range_key " : " B " ,
}
2022-03-09 17:57:25 +00:00
# test scan
scan_response_1 = table . scan (
2022-03-10 14:39:59 +00:00
Limit = 1 , FilterExpression = Attr ( " filtered_attribute " ) . eq ( " Z " )
2022-03-09 17:57:25 +00:00
)
scan_items_1 = scan_response_1 [ " Items " ]
2023-07-13 10:21:47 +00:00
assert len ( scan_items_1 ) == 0
2022-03-09 17:57:25 +00:00
scan_last_evaluated_key = scan_response_1 [ " LastEvaluatedKey " ]
2023-07-13 10:21:47 +00:00
assert len ( scan_last_evaluated_key ) == 2
assert scan_last_evaluated_key == { " hash_key " : " keyvalue " , " range_key " : " A " }
2022-03-09 17:57:25 +00:00
scan_response_2 = table . scan (
Limit = 1 ,
FilterExpression = Attr ( " filtered_attribute " ) . eq ( " Z " ) ,
ExclusiveStartKey = query_last_evaluated_key ,
)
scan_items_2 = scan_response_2 [ " Items " ]
2023-07-13 10:21:47 +00:00
assert scan_items_2 == [
2022-03-09 17:57:25 +00:00
{ " hash_key " : " keyvalue " , " filtered_attribute " : " Z " , " range_key " : " B " }
2023-07-13 10:21:47 +00:00
]
2022-03-09 17:57:25 +00:00
@mock_dynamodb
def test_projection_expression_execution_order ( ) :
# projection expression needs to be applied after calculation of
# LastEvaluatedKey as it is possible for LastEvaluatedKey to
# include attributes which are not projected.
conn = boto3 . resource ( " dynamodb " , region_name = " us-west-2 " )
name = " test-projection-expression-with-gsi "
table = conn . Table ( name )
conn . create_table (
TableName = name ,
KeySchema = [
{ " AttributeName " : " hash_key " , " KeyType " : " HASH " } ,
{ " AttributeName " : " range_key " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " hash_key " , " AttributeType " : " S " } ,
{ " AttributeName " : " range_key " , " AttributeType " : " S " } ,
{ " AttributeName " : " index_key " , " AttributeType " : " S " } ,
] ,
GlobalSecondaryIndexes = [
{
" IndexName " : " test_index " ,
" KeySchema " : [ { " AttributeName " : " index_key " , " KeyType " : " HASH " } ] ,
2022-03-10 14:39:59 +00:00
" Projection " : { " ProjectionType " : " ALL " } ,
2022-03-09 17:57:25 +00:00
" ProvisionedThroughput " : {
" ReadCapacityUnits " : 1 ,
" WriteCapacityUnits " : 1 ,
} ,
}
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
2022-03-10 14:39:59 +00:00
table . put_item ( Item = { " hash_key " : " keyvalue " , " range_key " : " A " , " index_key " : " Z " } )
table . put_item ( Item = { " hash_key " : " keyvalue " , " range_key " : " B " , " index_key " : " Z " } )
2022-03-09 17:57:25 +00:00
# test query
# if projection expression is applied before LastEvaluatedKey is computed
# then this raises an exception.
table . query (
Limit = 1 ,
IndexName = " test_index " ,
KeyConditionExpression = Key ( " index_key " ) . eq ( " Z " ) ,
ProjectionExpression = " #a " ,
ExpressionAttributeNames = { " #a " : " hashKey " } ,
)
# if projection expression is applied before LastEvaluatedKey is computed
# then this raises an exception.
table . scan (
Limit = 1 ,
IndexName = " test_index " ,
ProjectionExpression = " #a " ,
ExpressionAttributeNames = { " #a " : " hashKey " } ,
2021-11-04 22:39:53 +00:00
)
2022-08-12 19:45:14 +00:00
2023-09-15 17:17:57 +00:00
@mock_dynamodb
def test_projection_expression_with_binary_attr ( ) :
dynamo_resource = boto3 . resource ( " dynamodb " , region_name = " us-east-1 " )
dynamo_resource . create_table (
TableName = " test " ,
AttributeDefinitions = [
{ " AttributeName " : " pk " , " AttributeType " : " S " } ,
{ " AttributeName " : " sk " , " AttributeType " : " S " } ,
] ,
KeySchema = [
{ " AttributeName " : " pk " , " KeyType " : " HASH " } ,
{ " AttributeName " : " sk " , " KeyType " : " RANGE " } ,
] ,
BillingMode = " PAY_PER_REQUEST " ,
)
table = dynamo_resource . Table ( " test " )
table . put_item ( Item = { " pk " : " pk " , " sk " : " sk " , " key " : b " value \xbf " } )
2023-09-18 21:22:03 +00:00
item = table . get_item (
2023-09-15 17:17:57 +00:00
Key = { " pk " : " pk " , " sk " : " sk " } ,
ExpressionAttributeNames = { " #key " : " key " } ,
ProjectionExpression = " #key " ,
2023-09-18 21:22:03 +00:00
) [ " Item " ]
assert item == { " key " : Binary ( b " value \xbf " ) }
item = table . scan ( ) [ " Items " ] [ 0 ]
assert item [ " key " ] == Binary ( b " value \xbf " )
item = table . query ( KeyConditionExpression = Key ( " pk " ) . eq ( " pk " ) ) [ " Items " ] [ 0 ]
assert item [ " key " ] == Binary ( b " value \xbf " )
2023-09-15 17:17:57 +00:00
2022-08-12 19:45:14 +00:00
@mock_dynamodb
def test_invalid_projection_expressions ( ) :
table_name = " test-projection-expressions-table "
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
client . create_table (
TableName = table_name ,
KeySchema = [ { " AttributeName " : " customer " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " customer " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
with pytest . raises (
ClientError ,
match = " ProjectionExpression: Attribute name is a reserved keyword; reserved keyword: name " ,
) :
client . scan ( TableName = table_name , ProjectionExpression = " name " )
with pytest . raises (
ClientError , match = " ProjectionExpression: Attribute name starts with a number "
) :
client . scan ( TableName = table_name , ProjectionExpression = " 3ame " )
with pytest . raises (
ClientError , match = " ProjectionExpression: Attribute name contains white space "
) :
client . scan ( TableName = table_name , ProjectionExpression = " na me " )
with pytest . raises (
ClientError ,
match = " ProjectionExpression: Attribute name is a reserved keyword; reserved keyword: name " ,
) :
client . get_item (
TableName = table_name ,
Key = { " customer " : { " S " : " a " } } ,
ProjectionExpression = " name " ,
)
with pytest . raises (
ClientError ,
match = " ProjectionExpression: Attribute name is a reserved keyword; reserved keyword: name " ,
) :
client . query (
TableName = table_name ,
KeyConditionExpression = " a " ,
ProjectionExpression = " name " ,
)
with pytest . raises (
ClientError ,
match = " ProjectionExpression: Attribute name is a reserved keyword; reserved keyword: name " ,
) :
client . scan ( TableName = table_name , ProjectionExpression = " not_a_keyword, name " )
with pytest . raises (
ClientError , match = " ProjectionExpression: Attribute name starts with a number "
) :
client . scan ( TableName = table_name , ProjectionExpression = " not_a_keyword, 3ame " )
with pytest . raises (
ClientError , match = " ProjectionExpression: Attribute name contains white space "
) :
client . scan ( TableName = table_name , ProjectionExpression = " not_a_keyword, na me " )