TimestreamWrite - ensure test parity with AWS (#6971)

This commit is contained in:
Bert Blommers 2023-10-31 18:29:04 -01:00 committed by GitHub
parent d390aa673c
commit f8febb0daf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 205 additions and 94 deletions

View File

@ -1,5 +1,6 @@
from typing import Any, Dict, List, Iterable from typing import Any, Dict, List, Iterable
from moto.core import BaseBackend, BackendDict, BaseModel from moto.core import BaseBackend, BackendDict, BaseModel
from moto.core.utils import unix_time
from moto.utilities.tagging_service import TaggingService from moto.utilities.tagging_service import TaggingService
from .exceptions import ResourceNotFound from .exceptions import ResourceNotFound
@ -19,8 +20,8 @@ class TimestreamTable(BaseModel):
self.name = table_name self.name = table_name
self.db_name = db_name self.db_name = db_name
self.retention_properties = retention_properties or { self.retention_properties = retention_properties or {
"MemoryStoreRetentionPeriodInHours": 123, "MemoryStoreRetentionPeriodInHours": 6,
"MagneticStoreRetentionPeriodInDays": 123, "MagneticStoreRetentionPeriodInDays": 73000,
} }
self.magnetic_store_write_properties = magnetic_store_write_properties or {} self.magnetic_store_write_properties = magnetic_store_write_properties or {}
self.schema = schema or { self.schema = schema or {
@ -75,6 +76,8 @@ class TimestreamDatabase(BaseModel):
self.arn = ( self.arn = (
f"arn:aws:timestream:{self.region_name}:{account_id}:database/{self.name}" f"arn:aws:timestream:{self.region_name}:{account_id}:database/{self.name}"
) )
self.created_on = unix_time()
self.updated_on = unix_time()
self.tables: Dict[str, TimestreamTable] = dict() self.tables: Dict[str, TimestreamTable] = dict()
def update(self, kms_key_id: str) -> None: def update(self, kms_key_id: str) -> None:
@ -131,6 +134,8 @@ class TimestreamDatabase(BaseModel):
"DatabaseName": self.name, "DatabaseName": self.name,
"TableCount": len(self.tables.keys()), "TableCount": len(self.tables.keys()),
"KmsKeyId": self.kms_key_id, "KmsKeyId": self.kms_key_id,
"CreationTime": self.created_on,
"LastUpdatedTime": self.updated_on,
} }

View File

@ -0,0 +1,28 @@
import os
from functools import wraps
from moto import mock_timestreamwrite, mock_s3, mock_sts
def timestreamwrite_aws_verified(func):
"""
Function that is verified to work against AWS.
Can be run against AWS at any time by setting:
MOTO_TEST_ALLOW_AWS_REQUEST=true
If this environment variable is not set, the function runs in a `mock_timestreamwrite`, `mock_s3` and `mock_sts` context.
"""
@wraps(func)
def pagination_wrapper():
allow_aws_request = (
os.environ.get("MOTO_TEST_ALLOW_AWS_REQUEST", "false").lower() == "true"
)
if allow_aws_request:
return func()
else:
with mock_timestreamwrite(), mock_s3(), mock_sts():
return func()
return pagination_wrapper

View File

@ -1,23 +1,39 @@
import boto3 import boto3
from botocore.exceptions import ClientError
import pytest import pytest
from botocore.exceptions import ClientError
from uuid import uuid4
from moto import mock_timestreamwrite from moto import mock_timestreamwrite
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
from . import timestreamwrite_aws_verified
@mock_timestreamwrite
@pytest.mark.aws_verified
@timestreamwrite_aws_verified
def test_create_database_simple(): def test_create_database_simple():
ts = boto3.client("timestream-write", region_name="us-east-1") ts = boto3.client("timestream-write", region_name="us-east-1")
resp = ts.create_database(DatabaseName="mydatabase") db_name = "db_" + str(uuid4())[0:6]
database = resp["Database"]
assert database["Arn"] == ( identity = boto3.client("sts", region_name="us-east-1").get_caller_identity()
f"arn:aws:timestream:us-east-1:{ACCOUNT_ID}:database/mydatabase" account_id = identity["Account"]
)
assert database["DatabaseName"] == "mydatabase" try:
assert database["TableCount"] == 0 database = ts.create_database(DatabaseName=db_name)["Database"]
assert database["KmsKeyId"] == f"arn:aws:kms:us-east-1:{ACCOUNT_ID}:key/default_key"
assert (
database["Arn"]
== f"arn:aws:timestream:us-east-1:{account_id}:database/{db_name}"
)
assert db_name == db_name
assert database["TableCount"] == 0
assert database["KmsKeyId"].startswith(
f"arn:aws:kms:us-east-1:{account_id}:key/"
)
assert "CreationTime" in database
assert "LastUpdatedTime" in database
finally:
ts.delete_database(DatabaseName=db_name)
@mock_timestreamwrite @mock_timestreamwrite
@ -53,7 +69,8 @@ def test_describe_database():
assert database["KmsKeyId"] == "mykey" assert database["KmsKeyId"] == "mykey"
@mock_timestreamwrite @pytest.mark.aws_verified
@timestreamwrite_aws_verified
def test_describe_unknown_database(): def test_describe_unknown_database():
ts = boto3.client("timestream-write", region_name="us-east-1") ts = boto3.client("timestream-write", region_name="us-east-1")
with pytest.raises(ClientError) as exc: with pytest.raises(ClientError) as exc:
@ -72,6 +89,11 @@ def test_list_databases():
resp = ts.list_databases() resp = ts.list_databases()
databases = resp["Databases"] databases = resp["Databases"]
assert len(databases) == 2 assert len(databases) == 2
for db in databases:
db.pop("CreationTime")
db.pop("LastUpdatedTime")
assert { assert {
"Arn": f"arn:aws:timestream:us-east-1:{ACCOUNT_ID}:database/db_with", "Arn": f"arn:aws:timestream:us-east-1:{ACCOUNT_ID}:database/db_with",
"DatabaseName": "db_with", "DatabaseName": "db_with",

View File

@ -1,86 +1,125 @@
import boto3
import pytest
import time import time
import boto3
from botocore.exceptions import ClientError from botocore.exceptions import ClientError
import pytest from uuid import uuid4
from moto import mock_timestreamwrite, settings from moto import mock_timestreamwrite, settings
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
from . import timestreamwrite_aws_verified
@mock_timestreamwrite
@pytest.mark.aws_verified
@timestreamwrite_aws_verified
def test_create_table(): def test_create_table():
ts = boto3.client("timestream-write", region_name="us-east-1") ts = boto3.client("timestream-write", region_name="us-east-1")
ts.create_database(DatabaseName="mydatabase") db_name = "db_" + str(uuid4())[0:6]
table_name = "t_" + str(uuid4())[0:6]
resp = ts.create_table( identity = boto3.client("sts", region_name="us-east-1").get_caller_identity()
DatabaseName="mydatabase", account_id = identity["Account"]
TableName="mytable",
RetentionProperties={ try:
ts.create_database(DatabaseName=db_name)
resp = ts.create_table(
DatabaseName=db_name,
TableName=table_name,
RetentionProperties={
"MemoryStoreRetentionPeriodInHours": 7,
"MagneticStoreRetentionPeriodInDays": 42,
},
)
table = resp["Table"]
assert table["Arn"] == (
f"arn:aws:timestream:us-east-1:{account_id}:database/{db_name}/table/{table_name}"
)
assert table["TableName"] == table_name
assert table["DatabaseName"] == db_name
assert table["TableStatus"] == "ACTIVE"
assert table["RetentionProperties"] == {
"MemoryStoreRetentionPeriodInHours": 7, "MemoryStoreRetentionPeriodInHours": 7,
"MagneticStoreRetentionPeriodInDays": 42, "MagneticStoreRetentionPeriodInDays": 42,
}, }
) finally:
table = resp["Table"] ts.delete_table(DatabaseName=db_name, TableName=table_name)
assert table["Arn"] == ( ts.delete_database(DatabaseName=db_name)
f"arn:aws:timestream:us-east-1:{ACCOUNT_ID}:database/mydatabase/table/mytable"
)
assert table["TableName"] == "mytable"
assert table["DatabaseName"] == "mydatabase"
assert table["TableStatus"] == "ACTIVE"
assert table["RetentionProperties"] == {
"MemoryStoreRetentionPeriodInHours": 7,
"MagneticStoreRetentionPeriodInDays": 42,
}
@mock_timestreamwrite @pytest.mark.aws_verified
@timestreamwrite_aws_verified
def test_create_table__with_magnetic_store_write_properties(): def test_create_table__with_magnetic_store_write_properties():
ts = boto3.client("timestream-write", region_name="us-east-1") ts = boto3.client("timestream-write", region_name="us-east-1")
ts.create_database(DatabaseName="mydatabase") db_name = "db_" + str(uuid4())[0:6]
table_name = "t_" + str(uuid4())[0:6]
resp = ts.create_table( identity = boto3.client("sts", region_name="us-east-1").get_caller_identity()
DatabaseName="mydatabase", account_id = identity["Account"]
TableName="mytable",
MagneticStoreWriteProperties={ bucket_name = f"b-{str(uuid4())[0:6]}"
s3 = boto3.client("s3", region_name="us-east-1")
s3.create_bucket(Bucket=bucket_name)
try:
ts.create_database(DatabaseName=db_name)
table = ts.create_table(
DatabaseName=db_name,
TableName=table_name,
MagneticStoreWriteProperties={
"EnableMagneticStoreWrites": True,
"MagneticStoreRejectedDataLocation": {
"S3Configuration": {"BucketName": bucket_name}
},
},
)["Table"]
assert table["Arn"] == (
f"arn:aws:timestream:us-east-1:{account_id}:database/{db_name}/table/{table_name}"
)
assert table["TableName"] == table_name
assert table["DatabaseName"] == db_name
assert table["TableStatus"] == "ACTIVE"
assert table["MagneticStoreWriteProperties"] == {
"EnableMagneticStoreWrites": True, "EnableMagneticStoreWrites": True,
"MagneticStoreRejectedDataLocation": { "MagneticStoreRejectedDataLocation": {
"S3Configuration": {"BucketName": "hithere"} "S3Configuration": {"BucketName": bucket_name}
}, },
}, }
) finally:
table = resp["Table"] ts.delete_table(DatabaseName=db_name, TableName=table_name)
assert table["Arn"] == ( ts.delete_database(DatabaseName=db_name)
f"arn:aws:timestream:us-east-1:{ACCOUNT_ID}:database/mydatabase/table/mytable" s3.delete_bucket(Bucket=bucket_name)
)
assert table["TableName"] == "mytable"
assert table["DatabaseName"] == "mydatabase"
assert table["TableStatus"] == "ACTIVE"
assert table["MagneticStoreWriteProperties"] == {
"EnableMagneticStoreWrites": True,
"MagneticStoreRejectedDataLocation": {
"S3Configuration": {"BucketName": "hithere"}
},
}
@mock_timestreamwrite @pytest.mark.aws_verified
@timestreamwrite_aws_verified
def test_create_table_without_retention_properties(): def test_create_table_without_retention_properties():
ts = boto3.client("timestream-write", region_name="us-east-1") ts = boto3.client("timestream-write", region_name="us-east-1")
ts.create_database(DatabaseName="mydatabase") db_name = "db_" + str(uuid4())[0:6]
table_name = "t_" + str(uuid4())[0:6]
resp = ts.create_table(DatabaseName="mydatabase", TableName="mytable") identity = boto3.client("sts", region_name="us-east-1").get_caller_identity()
table = resp["Table"] account_id = identity["Account"]
assert table["Arn"] == (
f"arn:aws:timestream:us-east-1:{ACCOUNT_ID}:database/mydatabase/table/mytable" try:
) ts.create_database(DatabaseName=db_name)
assert table["TableName"] == "mytable"
assert table["DatabaseName"] == "mydatabase" table = ts.create_table(DatabaseName=db_name, TableName=table_name)["Table"]
assert table["TableStatus"] == "ACTIVE" assert table["Arn"] == (
assert table["RetentionProperties"] == { f"arn:aws:timestream:us-east-1:{account_id}:database/{db_name}/table/{table_name}"
"MemoryStoreRetentionPeriodInHours": 123, )
"MagneticStoreRetentionPeriodInDays": 123, assert table["TableName"] == table_name
} assert table["DatabaseName"] == db_name
assert table["TableStatus"] == "ACTIVE"
assert table["RetentionProperties"] == {
"MemoryStoreRetentionPeriodInHours": 6,
"MagneticStoreRetentionPeriodInDays": 73000,
}
finally:
ts.delete_table(DatabaseName=db_name, TableName=table_name)
ts.delete_database(DatabaseName=db_name)
@mock_timestreamwrite @mock_timestreamwrite
@ -110,16 +149,22 @@ def test_describe_table():
} }
@mock_timestreamwrite @pytest.mark.aws_verified
@timestreamwrite_aws_verified
def test_describe_unknown_database(): def test_describe_unknown_database():
ts = boto3.client("timestream-write", region_name="us-east-1") ts = boto3.client("timestream-write", region_name="us-east-1")
ts.create_database(DatabaseName="mydatabase") db_name = "db_" + str(uuid4())[0:6]
with pytest.raises(ClientError) as exc: try:
ts.describe_table(DatabaseName="mydatabase", TableName="unknown") ts.create_database(DatabaseName=db_name)
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFoundException" with pytest.raises(ClientError) as exc:
assert err["Message"] == "The table unknown does not exist." ts.describe_table(DatabaseName=db_name, TableName="unknown")
err = exc.value.response["Error"]
assert err["Code"] == "ResourceNotFoundException"
assert err["Message"] == "The table unknown does not exist."
finally:
ts.delete_database(DatabaseName=db_name)
@mock_timestreamwrite @mock_timestreamwrite

View File

@ -1,7 +1,11 @@
import boto3 import boto3
import pytest
from uuid import uuid4
from moto import mock_timestreamwrite from moto import mock_timestreamwrite
from . import timestreamwrite_aws_verified
@mock_timestreamwrite @mock_timestreamwrite
def test_list_tagging_for_table_without_tags(): def test_list_tagging_for_table_without_tags():
@ -62,26 +66,33 @@ def test_list_tagging_for_database_with_tags():
assert resp["Tags"] == [{"Key": "k1", "Value": "v1"}] assert resp["Tags"] == [{"Key": "k1", "Value": "v1"}]
@mock_timestreamwrite @pytest.mark.aws_verified
@timestreamwrite_aws_verified
def test_tag_and_untag_database(): def test_tag_and_untag_database():
ts = boto3.client("timestream-write", region_name="us-east-1") ts = boto3.client("timestream-write", region_name="us-east-1")
db_arn = ts.create_database( db_name = "db_" + str(uuid4())[0:6]
DatabaseName="mydatabase", Tags=[{"Key": "k1", "Value": "v1"}]
)["Database"]["Arn"]
ts.tag_resource( try:
ResourceARN=db_arn, db_arn = ts.create_database(
Tags=[{"Key": "k2", "Value": "v2"}, {"Key": "k3", "Value": "v3"}], DatabaseName=db_name, Tags=[{"Key": "k1", "Value": "v1"}]
) )["Database"]["Arn"]
resp = ts.list_tags_for_resource(ResourceARN=db_arn) ts.tag_resource(
assert resp["Tags"] == [ ResourceARN=db_arn,
{"Key": "k1", "Value": "v1"}, Tags=[{"Key": "k2", "Value": "v2"}, {"Key": "k3", "Value": "v3"}],
{"Key": "k2", "Value": "v2"}, )
{"Key": "k3", "Value": "v3"},
]
ts.untag_resource(ResourceARN=db_arn, TagKeys=["k2"]) tags = ts.list_tags_for_resource(ResourceARN=db_arn)["Tags"]
assert len(tags) == 3
assert {"Key": "k1", "Value": "v1"} in tags
assert {"Key": "k2", "Value": "v2"} in tags
assert {"Key": "k3", "Value": "v3"} in tags
resp = ts.list_tags_for_resource(ResourceARN=db_arn) ts.untag_resource(ResourceARN=db_arn, TagKeys=["k2"])
assert resp["Tags"] == [{"Key": "k1", "Value": "v1"}, {"Key": "k3", "Value": "v3"}]
tags = ts.list_tags_for_resource(ResourceARN=db_arn)["Tags"]
assert len(tags) == 2
assert {"Key": "k1", "Value": "v1"} in tags
assert {"Key": "k3", "Value": "v3"} in tags
finally:
ts.delete_database(DatabaseName=db_name)