2022-02-10 20:09:45 +00:00
import boto3
from botocore . exceptions import ClientError
from datetime import datetime
import pytest
2023-11-04 10:37:32 +00:00
from moto import mock_dynamodb
2022-08-13 09:49:43 +00:00
from moto . core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
2023-11-04 10:37:32 +00:00
from . import dynamodb_aws_verified
2022-02-10 20:09:45 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
2022-02-10 20:09:45 +00:00
def test_create_table_standard ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
client . create_table (
TableName = " messages " ,
KeySchema = [
{ " AttributeName " : " id " , " KeyType " : " HASH " } ,
{ " AttributeName " : " subject " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " id " , " AttributeType " : " S " } ,
{ " AttributeName " : " subject " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 5 } ,
)
actual = client . describe_table ( TableName = " messages " ) [ " Table " ]
2023-07-13 10:21:47 +00:00
assert actual [ " AttributeDefinitions " ] == [
{ " AttributeName " : " id " , " AttributeType " : " S " } ,
{ " AttributeName " : " subject " , " AttributeType " : " S " } ,
]
assert isinstance ( actual [ " CreationDateTime " ] , datetime )
assert actual [ " GlobalSecondaryIndexes " ] == [ ]
assert actual [ " LocalSecondaryIndexes " ] == [ ]
assert actual [ " ProvisionedThroughput " ] == {
" NumberOfDecreasesToday " : 0 ,
" ReadCapacityUnits " : 1 ,
" WriteCapacityUnits " : 5 ,
}
assert actual [ " TableSizeBytes " ] == 0
assert actual [ " TableName " ] == " messages "
assert actual [ " TableStatus " ] == " ACTIVE "
assert (
actual [ " TableArn " ] == f " arn:aws:dynamodb:us-east-1: { ACCOUNT_ID } :table/messages "
2022-02-10 20:09:45 +00:00
)
2023-07-13 10:21:47 +00:00
assert actual [ " KeySchema " ] == [
{ " AttributeName " : " id " , " KeyType " : " HASH " } ,
{ " AttributeName " : " subject " , " KeyType " : " RANGE " } ,
]
assert actual [ " ItemCount " ] == 0
2022-02-10 20:09:45 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
2022-02-10 20:09:45 +00:00
def test_create_table_with_local_index ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
client . create_table (
TableName = " messages " ,
KeySchema = [
{ " AttributeName " : " id " , " KeyType " : " HASH " } ,
{ " AttributeName " : " subject " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " id " , " AttributeType " : " S " } ,
{ " AttributeName " : " subject " , " AttributeType " : " S " } ,
{ " AttributeName " : " threads " , " AttributeType " : " S " } ,
] ,
LocalSecondaryIndexes = [
{
" IndexName " : " threads_index " ,
" KeySchema " : [
{ " AttributeName " : " id " , " KeyType " : " HASH " } ,
{ " AttributeName " : " threads " , " KeyType " : " RANGE " } ,
] ,
" Projection " : { " ProjectionType " : " ALL " } ,
}
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 5 } ,
)
actual = client . describe_table ( TableName = " messages " ) [ " Table " ]
2023-07-13 10:21:47 +00:00
assert actual [ " AttributeDefinitions " ] == [
{ " AttributeName " : " id " , " AttributeType " : " S " } ,
{ " AttributeName " : " subject " , " AttributeType " : " S " } ,
{ " AttributeName " : " threads " , " AttributeType " : " S " } ,
]
assert isinstance ( actual [ " CreationDateTime " ] , datetime )
assert actual [ " GlobalSecondaryIndexes " ] == [ ]
assert actual [ " LocalSecondaryIndexes " ] == [
{
" IndexName " : " threads_index " ,
" KeySchema " : [
{ " AttributeName " : " id " , " KeyType " : " HASH " } ,
{ " AttributeName " : " threads " , " KeyType " : " RANGE " } ,
] ,
" Projection " : { " ProjectionType " : " ALL " } ,
}
]
assert actual [ " ProvisionedThroughput " ] == {
" NumberOfDecreasesToday " : 0 ,
" ReadCapacityUnits " : 1 ,
" WriteCapacityUnits " : 5 ,
}
assert actual [ " TableSizeBytes " ] == 0
assert actual [ " TableName " ] == " messages "
assert actual [ " TableStatus " ] == " ACTIVE "
assert (
actual [ " TableArn " ] == f " arn:aws:dynamodb:us-east-1: { ACCOUNT_ID } :table/messages "
2022-02-10 20:09:45 +00:00
)
2023-07-13 10:21:47 +00:00
assert actual [ " KeySchema " ] == [
{ " AttributeName " : " id " , " KeyType " : " HASH " } ,
{ " AttributeName " : " subject " , " KeyType " : " RANGE " } ,
]
assert actual [ " ItemCount " ] == 0
2022-02-10 20:09:45 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
2022-02-10 20:09:45 +00:00
def test_create_table_with_gsi ( ) :
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
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 " } ,
] ,
BillingMode = " PAY_PER_REQUEST " ,
GlobalSecondaryIndexes = [
{
" IndexName " : " test_gsi " ,
" KeySchema " : [ { " AttributeName " : " subject " , " KeyType " : " HASH " } ] ,
" Projection " : { " ProjectionType " : " ALL " } ,
}
] ,
)
2023-07-13 10:21:47 +00:00
assert table [ " TableDescription " ] [ " GlobalSecondaryIndexes " ] == [
{
" KeySchema " : [ { " KeyType " : " HASH " , " AttributeName " : " subject " } ] ,
" IndexName " : " test_gsi " ,
" Projection " : { " ProjectionType " : " ALL " } ,
" IndexStatus " : " ACTIVE " ,
" ProvisionedThroughput " : {
" ReadCapacityUnits " : 0 ,
" WriteCapacityUnits " : 0 ,
} ,
}
]
2022-02-10 20:09:45 +00:00
table = dynamodb . create_table (
TableName = " users2 " ,
KeySchema = [
{ " AttributeName " : " forum_name " , " KeyType " : " HASH " } ,
{ " AttributeName " : " subject " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " forum_name " , " AttributeType " : " S " } ,
{ " AttributeName " : " subject " , " AttributeType " : " S " } ,
] ,
BillingMode = " PAY_PER_REQUEST " ,
GlobalSecondaryIndexes = [
{
" IndexName " : " test_gsi " ,
" KeySchema " : [ { " AttributeName " : " subject " , " KeyType " : " HASH " } ] ,
" Projection " : { " ProjectionType " : " ALL " } ,
" ProvisionedThroughput " : {
" ReadCapacityUnits " : 3 ,
" WriteCapacityUnits " : 5 ,
} ,
}
] ,
)
2023-07-13 10:21:47 +00:00
assert table [ " TableDescription " ] [ " GlobalSecondaryIndexes " ] == [
{
" KeySchema " : [ { " KeyType " : " HASH " , " AttributeName " : " subject " } ] ,
" IndexName " : " test_gsi " ,
" Projection " : { " ProjectionType " : " ALL " } ,
" IndexStatus " : " ACTIVE " ,
" ProvisionedThroughput " : {
" ReadCapacityUnits " : 3 ,
" WriteCapacityUnits " : 5 ,
} ,
}
]
2022-02-10 20:09:45 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
2022-02-10 20:09:45 +00:00
def test_create_table_with_stream_specification ( ) :
conn = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
resp = conn . create_table (
TableName = " test-streams " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
StreamSpecification = {
" StreamEnabled " : True ,
" StreamViewType " : " NEW_AND_OLD_IMAGES " ,
} ,
)
2023-07-13 10:21:47 +00:00
assert resp [ " TableDescription " ] [ " StreamSpecification " ] == {
" StreamEnabled " : True ,
" StreamViewType " : " NEW_AND_OLD_IMAGES " ,
}
assert " LatestStreamLabel " in resp [ " TableDescription " ]
assert " LatestStreamArn " in resp [ " TableDescription " ]
2022-02-10 20:09:45 +00:00
resp = conn . delete_table ( TableName = " test-streams " )
2023-07-13 10:21:47 +00:00
assert " StreamSpecification " in resp [ " TableDescription " ]
2022-02-10 20:09:45 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
2022-02-10 20:09:45 +00:00
def test_create_table_with_tags ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
resp = client . create_table (
TableName = " test-streams " ,
KeySchema = [ { " AttributeName " : " id " , " KeyType " : " HASH " } ] ,
AttributeDefinitions = [ { " AttributeName " : " id " , " AttributeType " : " S " } ] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 1 , " WriteCapacityUnits " : 1 } ,
Tags = [ { " Key " : " tk " , " Value " : " tv " } ] ,
)
resp = client . list_tags_of_resource (
ResourceArn = resp [ " TableDescription " ] [ " TableArn " ]
)
2023-07-13 10:21:47 +00:00
assert resp [ " Tags " ] == [ { " Key " : " tk " , " Value " : " tv " } ]
2022-02-10 20:09:45 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
2022-02-10 20:09:45 +00:00
def test_create_table_pay_per_request ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
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 " ,
)
actual = client . describe_table ( TableName = " test1 " ) [ " Table " ]
2023-07-13 10:21:47 +00:00
assert actual [ " BillingModeSummary " ] == { " BillingMode " : " PAY_PER_REQUEST " }
assert actual [ " ProvisionedThroughput " ] == {
" NumberOfDecreasesToday " : 0 ,
" ReadCapacityUnits " : 0 ,
" WriteCapacityUnits " : 0 ,
}
2022-02-10 20:09:45 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
2022-02-10 20:09:45 +00:00
def test_create_table__provisioned_throughput ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
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 " : 2 , " WriteCapacityUnits " : 3 } ,
)
actual = client . describe_table ( TableName = " test1 " ) [ " Table " ]
2023-07-13 10:21:47 +00:00
assert actual [ " BillingModeSummary " ] == { " BillingMode " : " PROVISIONED " }
assert actual [ " ProvisionedThroughput " ] == {
" NumberOfDecreasesToday " : 0 ,
" ReadCapacityUnits " : 2 ,
" WriteCapacityUnits " : 3 ,
}
2022-02-10 20:09:45 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
2022-02-10 20:09:45 +00:00
def test_create_table_without_specifying_throughput ( ) :
dynamodb_client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
with pytest . raises ( ClientError ) as exc :
dynamodb_client . create_table (
TableName = " my-table " ,
AttributeDefinitions = [
{ " AttributeName " : " some_field " , " AttributeType " : " S " }
] ,
KeySchema = [ { " AttributeName " : " some_field " , " KeyType " : " HASH " } ] ,
BillingMode = " PROVISIONED " ,
StreamSpecification = { " StreamEnabled " : False , " StreamViewType " : " NEW_IMAGE " } ,
)
err = exc . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert err [ " Code " ] == " ValidationException "
assert (
err [ " Message " ]
== " One or more parameter values were invalid: ReadCapacityUnits and WriteCapacityUnits must both be specified when BillingMode is PROVISIONED "
2022-02-10 20:09:45 +00:00
)
2022-03-09 17:57:25 +00:00
@mock_dynamodb
2022-02-10 20:09:45 +00:00
def test_create_table_error_pay_per_request_with_provisioned_param ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
with pytest . raises ( ClientError ) as exc :
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 } ,
BillingMode = " PAY_PER_REQUEST " ,
)
err = exc . value . response [ " Error " ]
2023-07-13 10:21:47 +00:00
assert err [ " Code " ] == " ValidationException "
assert (
err [ " Message " ]
== " ProvisionedThroughput cannot be specified when BillingMode is PAY_PER_REQUEST "
2022-02-10 20:09:45 +00:00
)
2022-03-09 17:57:25 +00:00
@mock_dynamodb
2022-02-10 20:09:45 +00:00
def test_create_table_with_ssespecification__false ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
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 " ,
SSESpecification = { " Enabled " : False } ,
)
actual = client . describe_table ( TableName = " test1 " ) [ " Table " ]
2023-07-13 10:21:47 +00:00
assert " SSEDescription " not in actual
2022-02-10 20:09:45 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
2022-02-10 20:09:45 +00:00
def test_create_table_with_ssespecification__true ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
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 " ,
SSESpecification = { " Enabled " : True } ,
)
actual = client . describe_table ( TableName = " test1 " ) [ " Table " ]
2023-07-13 10:21:47 +00:00
assert " SSEDescription " in actual
assert actual [ " SSEDescription " ] [ " Status " ] == " ENABLED "
assert actual [ " SSEDescription " ] [ " SSEType " ] == " KMS "
# Default KMS key for DynamoDB
assert actual [ " SSEDescription " ] [ " KMSMasterKeyArn " ] . startswith ( " arn:aws:kms " )
2022-02-10 20:09:45 +00:00
2022-03-09 17:57:25 +00:00
@mock_dynamodb
2022-02-10 20:09:45 +00:00
def test_create_table_with_ssespecification__custom_kms_key ( ) :
client = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
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 " ,
SSESpecification = { " Enabled " : True , " KMSMasterKeyId " : " custom-kms-key " } ,
)
actual = client . describe_table ( TableName = " test1 " ) [ " Table " ]
2023-07-13 10:21:47 +00:00
assert " SSEDescription " in actual
assert actual [ " SSEDescription " ] [ " Status " ] == " ENABLED "
assert actual [ " SSEDescription " ] [ " SSEType " ] == " KMS "
assert actual [ " SSEDescription " ] [ " KMSMasterKeyArn " ] == " custom-kms-key "
2023-02-11 11:51:37 +00:00
2023-11-04 10:37:32 +00:00
@pytest.mark.aws_verified
@dynamodb_aws_verified ( create_table = False )
2023-02-11 11:51:37 +00:00
def test_create_table__specify_non_key_column ( ) :
2023-11-04 10:37:32 +00:00
dynamodb = boto3 . client ( " dynamodb " , region_name = " us-east-1 " )
with pytest . raises ( ClientError ) as exc :
dynamodb . create_table (
TableName = " unknown-key-type " ,
KeySchema = [
{ " AttributeName " : " pk " , " KeyType " : " HASH " } ,
{ " AttributeName " : " sk " , " KeyType " : " SORT " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " pk " , " AttributeType " : " S " } ,
{ " AttributeName " : " sk " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
)
err = exc . value . response [ " Error " ]
assert err [ " Code " ] == " ValidationException "
assert (
err [ " Message " ]
== " 1 validation error detected: Value ' SORT ' at ' keySchema.2.member.keyType ' failed to satisfy constraint: Member must satisfy enum value set: [HASH, RANGE] "
2023-02-11 11:51:37 +00:00
)
2023-11-04 10:37:32 +00:00
# Verify we get the same message for Global Secondary Indexes
with pytest . raises ( ClientError ) as exc :
dynamodb . create_table (
TableName = " unknown-key-type " ,
KeySchema = [
{ " AttributeName " : " pk " , " KeyType " : " HASH " } ,
{ " AttributeName " : " sk " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " pk " , " AttributeType " : " S " } ,
{ " AttributeName " : " sk " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
GlobalSecondaryIndexes = [
{
" IndexName " : " TestGSI " ,
# Note that the attributes are not declared, which is also invalid
# But AWS trips over the KeyType=SORT first
" KeySchema " : [
{ " AttributeName " : " n/a " , " KeyType " : " HASH " } ,
{ " AttributeName " : " sth " , " KeyType " : " SORT " } ,
] ,
" Projection " : { " ProjectionType " : " ALL " } ,
" ProvisionedThroughput " : {
" ReadCapacityUnits " : 5 ,
" WriteCapacityUnits " : 5 ,
} ,
}
] ,
)
err = exc . value . response [ " Error " ]
assert err [ " Code " ] == " ValidationException "
assert (
err [ " Message " ]
== " 1 validation error detected: Value ' SORT ' at ' globalSecondaryIndexes.1.member.keySchema.2.member.keyType ' failed to satisfy constraint: Member must satisfy enum value set: [HASH, RANGE] "
)
2023-02-11 11:51:37 +00:00
2023-11-04 10:37:32 +00:00
# Verify we get the same message for Local Secondary Indexes
with pytest . raises ( ClientError ) as exc :
dynamodb . create_table (
TableName = " unknown-key-type " ,
KeySchema = [
{ " AttributeName " : " pk " , " KeyType " : " HASH " } ,
{ " AttributeName " : " sk " , " KeyType " : " RANGE " } ,
] ,
AttributeDefinitions = [
{ " AttributeName " : " pk " , " AttributeType " : " S " } ,
{ " AttributeName " : " sk " , " AttributeType " : " S " } ,
] ,
ProvisionedThroughput = { " ReadCapacityUnits " : 5 , " WriteCapacityUnits " : 5 } ,
LocalSecondaryIndexes = [
{
" IndexName " : " test_lsi " ,
" KeySchema " : [
{ " AttributeName " : " pk " , " KeyType " : " HASH " } ,
{ " AttributeName " : " lsi_range_key " , " KeyType " : " SORT " } ,
] ,
" Projection " : { " ProjectionType " : " ALL " } ,
}
] ,
)
err = exc . value . response [ " Error " ]
assert err [ " Code " ] == " ValidationException "
assert (
err [ " Message " ]
== " 1 validation error detected: Value ' SORT ' at ' localSecondaryIndexes.1.member.keySchema.2.member.keyType ' failed to satisfy constraint: Member must satisfy enum value set: [HASH, RANGE] "
)