moto/tests/test_dynamodb/test_dynamodb_create_table.py
2022-08-13 09:49:43 +00:00

407 lines
15 KiB
Python

import boto3
from botocore.exceptions import ClientError
import sure # noqa # pylint: disable=unused-import
from datetime import datetime
import pytest
from moto import mock_dynamodb
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
@mock_dynamodb
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"]
actual.should.have.key("AttributeDefinitions").equal(
[
{"AttributeName": "id", "AttributeType": "S"},
{"AttributeName": "subject", "AttributeType": "S"},
]
)
actual.should.have.key("CreationDateTime").be.a(datetime)
actual.should.have.key("GlobalSecondaryIndexes").equal([])
actual.should.have.key("LocalSecondaryIndexes").equal([])
actual.should.have.key("ProvisionedThroughput").equal(
{"NumberOfDecreasesToday": 0, "ReadCapacityUnits": 1, "WriteCapacityUnits": 5}
)
actual.should.have.key("TableSizeBytes").equal(0)
actual.should.have.key("TableName").equal("messages")
actual.should.have.key("TableStatus").equal("ACTIVE")
actual.should.have.key("TableArn").equal(
f"arn:aws:dynamodb:us-east-1:{ACCOUNT_ID}:table/messages"
)
actual.should.have.key("KeySchema").equal(
[
{"AttributeName": "id", "KeyType": "HASH"},
{"AttributeName": "subject", "KeyType": "RANGE"},
]
)
actual.should.have.key("ItemCount").equal(0)
@mock_dynamodb
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"]
actual.should.have.key("AttributeDefinitions").equal(
[
{"AttributeName": "id", "AttributeType": "S"},
{"AttributeName": "subject", "AttributeType": "S"},
{"AttributeName": "threads", "AttributeType": "S"},
]
)
actual.should.have.key("CreationDateTime").be.a(datetime)
actual.should.have.key("GlobalSecondaryIndexes").equal([])
actual.should.have.key("LocalSecondaryIndexes").equal(
[
{
"IndexName": "threads_index",
"KeySchema": [
{"AttributeName": "id", "KeyType": "HASH"},
{"AttributeName": "threads", "KeyType": "RANGE"},
],
"Projection": {"ProjectionType": "ALL"},
}
]
)
actual.should.have.key("ProvisionedThroughput").equal(
{"NumberOfDecreasesToday": 0, "ReadCapacityUnits": 1, "WriteCapacityUnits": 5}
)
actual.should.have.key("TableSizeBytes").equal(0)
actual.should.have.key("TableName").equal("messages")
actual.should.have.key("TableStatus").equal("ACTIVE")
actual.should.have.key("TableArn").equal(
f"arn:aws:dynamodb:us-east-1:{ACCOUNT_ID}:table/messages"
)
actual.should.have.key("KeySchema").equal(
[
{"AttributeName": "id", "KeyType": "HASH"},
{"AttributeName": "subject", "KeyType": "RANGE"},
]
)
actual.should.have.key("ItemCount").equal(0)
@mock_dynamodb
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"},
}
],
)
table["TableDescription"]["GlobalSecondaryIndexes"].should.equal(
[
{
"KeySchema": [{"KeyType": "HASH", "AttributeName": "subject"}],
"IndexName": "test_gsi",
"Projection": {"ProjectionType": "ALL"},
"IndexStatus": "ACTIVE",
"ProvisionedThroughput": {
"ReadCapacityUnits": 0,
"WriteCapacityUnits": 0,
},
}
]
)
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,
},
}
],
)
table["TableDescription"]["GlobalSecondaryIndexes"].should.equal(
[
{
"KeySchema": [{"KeyType": "HASH", "AttributeName": "subject"}],
"IndexName": "test_gsi",
"Projection": {"ProjectionType": "ALL"},
"IndexStatus": "ACTIVE",
"ProvisionedThroughput": {
"ReadCapacityUnits": 3,
"WriteCapacityUnits": 5,
},
}
]
)
@mock_dynamodb
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",
},
)
resp["TableDescription"].should.have.key("StreamSpecification")
resp["TableDescription"]["StreamSpecification"].should.equal(
{"StreamEnabled": True, "StreamViewType": "NEW_AND_OLD_IMAGES"}
)
resp["TableDescription"].should.contain("LatestStreamLabel")
resp["TableDescription"].should.contain("LatestStreamArn")
resp = conn.delete_table(TableName="test-streams")
resp["TableDescription"].should.contain("StreamSpecification")
@mock_dynamodb
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"]
)
resp.should.have.key("Tags").equals([{"Key": "tk", "Value": "tv"}])
@mock_dynamodb
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"]
actual.should.have.key("BillingModeSummary").equals(
{"BillingMode": "PAY_PER_REQUEST"}
)
actual.should.have.key("ProvisionedThroughput").equals(
{"NumberOfDecreasesToday": 0, "ReadCapacityUnits": 0, "WriteCapacityUnits": 0}
)
@mock_dynamodb
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"]
actual.should.have.key("BillingModeSummary").equals({"BillingMode": "PROVISIONED"})
actual.should.have.key("ProvisionedThroughput").equals(
{"NumberOfDecreasesToday": 0, "ReadCapacityUnits": 2, "WriteCapacityUnits": 3}
)
@mock_dynamodb
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"]
err["Code"].should.equal("ValidationException")
err["Message"].should.equal(
"One or more parameter values were invalid: ReadCapacityUnits and WriteCapacityUnits must both be specified when BillingMode is PROVISIONED"
)
@mock_dynamodb
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"]
err["Code"].should.equal("ValidationException")
err["Message"].should.equal(
"ProvisionedThroughput cannot be specified when BillingMode is PAY_PER_REQUEST"
)
@mock_dynamodb
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"]
actual.shouldnt.have.key("SSEDescription")
@mock_dynamodb
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"]
actual.should.have.key("SSEDescription")
actual["SSEDescription"].should.have.key("Status").equals("ENABLED")
actual["SSEDescription"].should.have.key("SSEType").equals("KMS")
actual["SSEDescription"].should.have.key("KMSMasterKeyArn").match(
"^arn:aws:kms"
) # Default KMS key for DynamoDB
@mock_dynamodb
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"]
actual.should.have.key("SSEDescription")
actual["SSEDescription"].should.have.key("Status").equals("ENABLED")
actual["SSEDescription"].should.have.key("SSEType").equals("KMS")
actual["SSEDescription"].should.have.key("KMSMasterKeyArn").equals("custom-kms-key")