diff --git a/pytest_tests/testsuites/services/s3_gate/test_s3_bucket.py b/pytest_tests/testsuites/services/s3_gate/test_s3_bucket.py index 0824f44a..4bb43aa7 100644 --- a/pytest_tests/testsuites/services/s3_gate/test_s3_bucket.py +++ b/pytest_tests/testsuites/services/s3_gate/test_s3_bucket.py @@ -1,3 +1,4 @@ +import string from datetime import datetime, timedelta import allure @@ -6,8 +7,15 @@ from frostfs_testlib import reporter from frostfs_testlib.s3 import S3ClientWrapper, VersioningStatus from frostfs_testlib.steps.s3 import s3_helper from frostfs_testlib.storage.dataclasses.object_size import ObjectSize +from frostfs_testlib.utils import string_utils from frostfs_testlib.utils.file_utils import generate_file +VALID_SYMBOLS_WITHOUT_DOT = string.ascii_lowercase + string.digits + "-" +VALID_AND_INVALID_SYMBOLS = string.ascii_letters + string.punctuation + +# TODO: The dot symbol is temporarily not supported. +VALID_SYMBOLS_WITH_DOT = VALID_SYMBOLS_WITHOUT_DOT + "." + @pytest.mark.nightly @pytest.mark.s3_gate @@ -140,3 +148,95 @@ class TestS3GateBucket: s3_client.delete_bucket(bucket) with pytest.raises(Exception, match=r".*Not Found.*"): s3_client.head_bucket(bucket) + + @allure.title("Create bucket with valid name length (s3_client={s3_client}, length={length})") + @pytest.mark.parametrize("length", [3, 4, 32, 62, 63]) + def test_s3_create_bucket_with_valid_length(self, s3_client: S3ClientWrapper, length: int): + bucket_name = string_utils.random_string(length, VALID_SYMBOLS_WITHOUT_DOT) + while not (bucket_name[0].isalnum() and bucket_name[-1].isalnum()): + bucket_name = string_utils.random_string(length, VALID_SYMBOLS_WITHOUT_DOT) + + with reporter.step("Create bucket with valid name length"): + s3_client.create_bucket(bucket_name) + + with reporter.step("Check bucket name in buckets"): + assert bucket_name in s3_client.list_buckets() + + @allure.title("[NEGATIVE] Bucket with invalid name length should not be created (s3_client={s3_client}, length={length})") + @pytest.mark.parametrize("length", [2, 64, 254, 255, 256]) + def test_s3_create_bucket_with_invalid_length(self, s3_client: S3ClientWrapper, length: int): + bucket_name = string_utils.random_string(length, VALID_SYMBOLS_WITHOUT_DOT) + while not (bucket_name[0].isalnum() and bucket_name[-1].isalnum()): + bucket_name = string_utils.random_string(length, VALID_SYMBOLS_WITHOUT_DOT) + + with reporter.step("Create bucket with invalid name length and catch exception"): + with pytest.raises(Exception, match=".*(?:InvalidBucketName|Invalid bucket name).*"): + s3_client.create_bucket(bucket_name) + + @allure.title("[NEGATIVE] Bucket with invalid name should not be created (s3_client={s3_client}, bucket_name={bucket_name})") + @pytest.mark.parametrize( + "bucket_name", + [ + "BUCKET-1", + "buckeT-2", + # The following case for AWS CLI is not handled correctly + # "-bucket-3", + "bucket-4-", + ".bucket-5", + "bucket-6.", + "bucket..7", + "bucket+8", + "bucket_9", + "bucket 10", + "127.10.5.11", + "xn--bucket-12", + "bucket-13-s3alias", + # The following names can be used in FrostFS but are prohibited by the AWS specification. + # "sthree-bucket-14" + # "sthree-configurator-bucket-15" + # "amzn-s3-demo-bucket-16" + # "sthree-bucket-17" + # "bucket-18--ol-s3" + # "bucket-19--x-s3" + # "bucket-20.mrap" + ], + ) + def test_s3_create_bucket_with_invalid_name(self, s3_client: S3ClientWrapper, bucket_name: str): + with reporter.step("Create bucket with invalid name and catch exception"): + with pytest.raises(Exception, match=".*(?:InvalidBucketName|Invalid bucket name).*"): + s3_client.create_bucket(bucket_name) + + @allure.title("Non-empty bucket is available after attempting to delete it (s3_client={s3_client})") + def test_s3_check_availability_non_empty_bucket_after_deleting( + self, + s3_client: S3ClientWrapper, + simple_object_size: ObjectSize, + ): + bucket = string_utils.unique_name("bucket-") + object_path = generate_file(simple_object_size.value) + object_name = s3_helper.object_key_from_file_path(object_path) + + with reporter.step("Create bucket"): + s3_client.create_bucket(bucket) + + with reporter.step("Check that bucket is created"): + buckets = s3_client.list_buckets() + assert buckets, f"Expected non-empty bucket list, got {buckets}" + assert bucket in buckets, f"Bucket {bucket} not found in bucket list {buckets}" + + with reporter.step("Put object into bucket"): + s3_client.put_object(bucket, object_path) + + with reporter.step("Check that object appears in bucket"): + objects = s3_client.list_objects(bucket) + assert objects, f"Expected bucket with object, got empty {objects}" + assert object_name in objects, f"Object {object_name} not found in bucket object list {objects}" + + with reporter.step("Try to delete not empty bucket and get error"): + with pytest.raises(Exception, match=r".*The bucket you tried to delete is not empty.*"): + s3_client.delete_bucket(bucket) + + with reporter.step("Check bucket availability"): + objects = s3_client.list_objects(bucket) + assert objects, f"Expected bucket with object, got empty {objects}" + assert object_name in objects, f"Object {object_name} not found in bucket object list {objects}"