import boto3

import sure  # noqa # pylint: disable=unused-import
from botocore.exceptions import ClientError
from datetime import datetime
import pytest

from moto import mock_s3


@mock_s3
def test_lifecycle_with_filters():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    # Create a lifecycle rule with a Filter (no tags):
    lfc = {
        "Rules": [
            {
                "Expiration": {"Days": 7},
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["Filter"]["Prefix"] == ""
    assert not result["Rules"][0]["Filter"].get("And")
    assert not result["Rules"][0]["Filter"].get("Tag")
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Prefix"]

    # Without any prefixes and an empty filter (this is by default a prefix for the whole bucket):
    lfc = {
        "Rules": [
            {
                "Expiration": {"Days": 7},
                "ID": "wholebucket",
                "Filter": {},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Prefix"]

    # If we remove the filter -- and don't specify a Prefix, then this is bad:
    lfc["Rules"][0].pop("Filter")
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"

    # With a tag:
    lfc["Rules"][0]["Filter"] = {"Tag": {"Key": "mytag", "Value": "mytagvalue"}}
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Filter"]["Prefix"]
    assert not result["Rules"][0]["Filter"].get("And")
    assert result["Rules"][0]["Filter"]["Tag"]["Key"] == "mytag"
    assert result["Rules"][0]["Filter"]["Tag"]["Value"] == "mytagvalue"
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Prefix"]

    # With And (single tag):
    lfc["Rules"][0]["Filter"] = {
        "And": {
            "Prefix": "some/prefix",
            "Tags": [{"Key": "mytag", "Value": "mytagvalue"}],
        }
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert not result["Rules"][0]["Filter"].get("Prefix")
    assert result["Rules"][0]["Filter"]["And"]["Prefix"] == "some/prefix"
    assert len(result["Rules"][0]["Filter"]["And"]["Tags"]) == 1
    assert result["Rules"][0]["Filter"]["And"]["Tags"][0]["Key"] == "mytag"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][0]["Value"] == "mytagvalue"
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Prefix"]

    # With multiple And tags:
    lfc["Rules"][0]["Filter"]["And"] = {
        "Prefix": "some/prefix",
        "Tags": [
            {"Key": "mytag", "Value": "mytagvalue"},
            {"Key": "mytag2", "Value": "mytagvalue2"},
        ],
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert not result["Rules"][0]["Filter"].get("Prefix")
    assert result["Rules"][0]["Filter"]["And"]["Prefix"] == "some/prefix"
    assert len(result["Rules"][0]["Filter"]["And"]["Tags"]) == 2
    assert result["Rules"][0]["Filter"]["And"]["Tags"][0]["Key"] == "mytag"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][0]["Value"] == "mytagvalue"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][1]["Key"] == "mytag2"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][1]["Value"] == "mytagvalue2"
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Prefix"]

    # And filter without Prefix but multiple Tags:
    lfc["Rules"][0]["Filter"]["And"] = {
        "Tags": [
            {"Key": "mytag", "Value": "mytagvalue"},
            {"Key": "mytag2", "Value": "mytagvalue2"},
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Filter"]["And"]["Prefix"]
    assert len(result["Rules"][0]["Filter"]["And"]["Tags"]) == 2
    assert result["Rules"][0]["Filter"]["And"]["Tags"][0]["Key"] == "mytag"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][0]["Value"] == "mytagvalue"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][1]["Key"] == "mytag2"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][1]["Value"] == "mytagvalue2"
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Prefix"]

    # Can't have both filter and prefix:
    lfc["Rules"][0]["Prefix"] = ""
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"

    lfc["Rules"][0]["Prefix"] = "some/path"
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"

    # No filters -- just a prefix:
    del lfc["Rules"][0]["Filter"]
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert not result["Rules"][0].get("Filter")
    assert result["Rules"][0]["Prefix"] == "some/path"

    # Can't have Tag, Prefix, and And in a filter:
    del lfc["Rules"][0]["Prefix"]
    lfc["Rules"][0]["Filter"] = {
        "Prefix": "some/prefix",
        "Tag": {"Key": "mytag", "Value": "mytagvalue"},
    }
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"

    lfc["Rules"][0]["Filter"] = {
        "Tag": {"Key": "mytag", "Value": "mytagvalue"},
        "And": {
            "Prefix": "some/prefix",
            "Tags": [
                {"Key": "mytag", "Value": "mytagvalue"},
                {"Key": "mytag2", "Value": "mytagvalue2"},
            ],
        },
    }
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"

    # Make sure multiple rules work:
    lfc = {
        "Rules": [
            {
                "Expiration": {"Days": 7},
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            },
            {
                "Expiration": {"Days": 10},
                "ID": "Tags",
                "Filter": {"Tag": {"Key": "somekey", "Value": "somevalue"}},
                "Status": "Enabled",
            },
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")["Rules"]
    assert len(result) == 2
    assert result[0]["ID"] == "wholebucket"
    assert result[1]["ID"] == "Tags"


@mock_s3
def test_lifecycle_with_eodm():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    lfc = {
        "Rules": [
            {
                "Expiration": {"ExpiredObjectDeleteMarker": True},
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["Expiration"]["ExpiredObjectDeleteMarker"]

    # Set to False:
    lfc["Rules"][0]["Expiration"]["ExpiredObjectDeleteMarker"] = False
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert not result["Rules"][0]["Expiration"]["ExpiredObjectDeleteMarker"]

    # With failure:
    lfc["Rules"][0]["Expiration"]["Days"] = 7
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"
    del lfc["Rules"][0]["Expiration"]["Days"]

    lfc["Rules"][0]["Expiration"]["Date"] = datetime(2015, 1, 1)
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"


@mock_s3
def test_lifecycle_with_nve():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    lfc = {
        "Rules": [
            {
                "NoncurrentVersionExpiration": {"NoncurrentDays": 30},
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["NoncurrentVersionExpiration"]["NoncurrentDays"] == 30

    # Change NoncurrentDays:
    lfc["Rules"][0]["NoncurrentVersionExpiration"]["NoncurrentDays"] = 10
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["NoncurrentVersionExpiration"]["NoncurrentDays"] == 10

    # TODO: Add test for failures due to missing children


@mock_s3
def test_lifecycle_with_nvt():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    lfc = {
        "Rules": [
            {
                "NoncurrentVersionTransitions": [
                    {"NoncurrentDays": 30, "StorageClass": "ONEZONE_IA"}
                ],
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["NoncurrentVersionTransitions"][0]["NoncurrentDays"] == 30
    assert (
        result["Rules"][0]["NoncurrentVersionTransitions"][0]["StorageClass"]
        == "ONEZONE_IA"
    )

    # Change NoncurrentDays:
    lfc["Rules"][0]["NoncurrentVersionTransitions"][0]["NoncurrentDays"] = 10
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["NoncurrentVersionTransitions"][0]["NoncurrentDays"] == 10

    # Change StorageClass:
    lfc["Rules"][0]["NoncurrentVersionTransitions"][0]["StorageClass"] = "GLACIER"
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert (
        result["Rules"][0]["NoncurrentVersionTransitions"][0]["StorageClass"]
        == "GLACIER"
    )

    # With failures for missing children:
    del lfc["Rules"][0]["NoncurrentVersionTransitions"][0]["NoncurrentDays"]
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"
    lfc["Rules"][0]["NoncurrentVersionTransitions"][0]["NoncurrentDays"] = 30

    del lfc["Rules"][0]["NoncurrentVersionTransitions"][0]["StorageClass"]
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"


@mock_s3
def test_lifecycle_with_multiple_nvt():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    lfc = {
        "Rules": [
            {
                "NoncurrentVersionTransitions": [
                    {"NoncurrentDays": 30, "StorageClass": "ONEZONE_IA"},
                    {"NoncurrentDays": 50, "StorageClass": "GLACIER"},
                ],
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["NoncurrentVersionTransitions"][0] == {
        "NoncurrentDays": 30,
        "StorageClass": "ONEZONE_IA",
    }
    assert result["Rules"][0]["NoncurrentVersionTransitions"][1] == {
        "NoncurrentDays": 50,
        "StorageClass": "GLACIER",
    }


@mock_s3
def test_lifecycle_with_multiple_transitions():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    lfc = {
        "Rules": [
            {
                "Transitions": [
                    {"Days": 30, "StorageClass": "ONEZONE_IA"},
                    {"Days": 50, "StorageClass": "GLACIER"},
                ],
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["Transitions"][0] == {
        "Days": 30,
        "StorageClass": "ONEZONE_IA",
    }
    assert result["Rules"][0]["Transitions"][1] == {
        "Days": 50,
        "StorageClass": "GLACIER",
    }


@mock_s3
def test_lifecycle_with_aimu():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    lfc = {
        "Rules": [
            {
                "AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 7},
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert (
        result["Rules"][0]["AbortIncompleteMultipartUpload"]["DaysAfterInitiation"] == 7
    )

    # Change DaysAfterInitiation:
    lfc["Rules"][0]["AbortIncompleteMultipartUpload"]["DaysAfterInitiation"] = 30
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert (
        result["Rules"][0]["AbortIncompleteMultipartUpload"]["DaysAfterInitiation"]
        == 30
    )

    # TODO: Add test for failures due to missing children


@mock_s3
def test_lifecycle_with_glacier_transition_boto3():
    s3 = boto3.resource("s3", region_name="us-east-1")
    client = boto3.client("s3", region_name="us-east-1")
    s3.create_bucket(Bucket="foobar")

    client.put_bucket_lifecycle_configuration(
        Bucket="foobar",
        LifecycleConfiguration={
            "Rules": [
                {
                    "ID": "myid",
                    "Prefix": "",
                    "Status": "Enabled",
                    "Transitions": [{"Days": 30, "StorageClass": "GLACIER"}],
                }
            ]
        },
    )

    response = client.get_bucket_lifecycle_configuration(Bucket="foobar")
    response.should.have.key("Rules")
    rules = response["Rules"]
    rules.should.have.length_of(1)
    rules[0].should.have.key("ID").equal("myid")
    transition = rules[0]["Transitions"][0]
    transition["Days"].should.equal(30)
    transition["StorageClass"].should.equal("GLACIER")
    transition.shouldnt.have.key("Date")


@mock_s3
def test_lifecycle_multi_boto3():
    s3 = boto3.resource("s3", region_name="us-east-1")
    client = boto3.client("s3", region_name="us-east-1")
    s3.create_bucket(Bucket="foobar")

    date = "2022-10-12T00:00:00.000Z"
    sc = "GLACIER"

    client.put_bucket_lifecycle_configuration(
        Bucket="foobar",
        LifecycleConfiguration={
            "Rules": [
                {"ID": "1", "Prefix": "1/", "Status": "Enabled"},
                {
                    "ID": "2",
                    "Prefix": "2/",
                    "Status": "Enabled",
                    "Expiration": {"Days": 2},
                },
                {
                    "ID": "3",
                    "Prefix": "3/",
                    "Status": "Enabled",
                    "Expiration": {"Date": date},
                },
                {
                    "ID": "4",
                    "Prefix": "4/",
                    "Status": "Enabled",
                    "Transitions": [{"Days": 4, "StorageClass": "GLACIER"}],
                },
                {
                    "ID": "5",
                    "Prefix": "5/",
                    "Status": "Enabled",
                    "Transitions": [{"Date": date, "StorageClass": "GLACIER"}],
                },
            ]
        },
    )

    # read the lifecycle back
    rules = client.get_bucket_lifecycle_configuration(Bucket="foobar")["Rules"]

    for rule in rules:
        if rule["ID"] == "1":
            rule["Prefix"].should.equal("1/")
            rule.shouldnt.have.key("Expiration")
        elif rule["ID"] == "2":
            rule["Prefix"].should.equal("2/")
            rule["Expiration"]["Days"].should.equal(2)
        elif rule["ID"] == "3":
            rule["Prefix"].should.equal("3/")
            rule["Expiration"]["Date"].should.be.a(datetime)
            rule["Expiration"]["Date"].strftime("%Y-%m-%dT%H:%M:%S.000Z").should.equal(
                date
            )
        elif rule["ID"] == "4":
            rule["Prefix"].should.equal("4/")
            rule["Transitions"][0]["Days"].should.equal(4)
            rule["Transitions"][0]["StorageClass"].should.equal(sc)
        elif rule["ID"] == "5":
            rule["Prefix"].should.equal("5/")
            rule["Transitions"][0]["Date"].should.be.a(datetime)
            rule["Transitions"][0]["Date"].strftime(
                "%Y-%m-%dT%H:%M:%S.000Z"
            ).should.equal(date)
            rule["Transitions"][0]["StorageClass"].should.equal(sc)
        else:
            assert False, "Invalid rule id"


@mock_s3
def test_lifecycle_delete_boto3():
    s3 = boto3.resource("s3", region_name="us-east-1")
    client = boto3.client("s3", region_name="us-east-1")
    s3.create_bucket(Bucket="foobar")

    client.put_bucket_lifecycle_configuration(
        Bucket="foobar",
        LifecycleConfiguration={
            "Rules": [{"ID": "myid", "Prefix": "", "Status": "Enabled"}]
        },
    )

    client.delete_bucket_lifecycle(Bucket="foobar")

    with pytest.raises(ClientError) as ex:
        client.get_bucket_lifecycle_configuration(Bucket="foobar")
    ex.value.response["Error"]["Code"].should.equal("NoSuchLifecycleConfiguration")
    ex.value.response["Error"]["Message"].should.equal(
        "The lifecycle configuration does not exist"
    )

fb10d0aba002089d4b716e25c45b49019cba6b4a
<mask token>


@mock_s3
def test_lifecycle_with_filters():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'Expiration': {'Days': 7}, 'ID': 'wholebucket',
        'Filter': {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['Filter']['Prefix'] == ''
    assert not result['Rules'][0]['Filter'].get('And')
    assert not result['Rules'][0]['Filter'].get('Tag')
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc = {'Rules': [{'Expiration': {'Days': 7}, 'ID': 'wholebucket',
        'Filter': {}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0].pop('Filter')
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['Filter'] = {'Tag': {'Key': 'mytag', 'Value': 'mytagvalue'}
        }
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Filter']['Prefix']
    assert not result['Rules'][0]['Filter'].get('And')
    assert result['Rules'][0]['Filter']['Tag']['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['Tag']['Value'] == 'mytagvalue'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter'] = {'And': {'Prefix': 'some/prefix', 'Tags': [
        {'Key': 'mytag', 'Value': 'mytagvalue'}]}}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert not result['Rules'][0]['Filter'].get('Prefix')
    assert result['Rules'][0]['Filter']['And']['Prefix'] == 'some/prefix'
    assert len(result['Rules'][0]['Filter']['And']['Tags']) == 1
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Value'
        ] == 'mytagvalue'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter']['And'] = {'Prefix': 'some/prefix', 'Tags': [{
        'Key': 'mytag', 'Value': 'mytagvalue'}, {'Key': 'mytag2', 'Value':
        'mytagvalue2'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert not result['Rules'][0]['Filter'].get('Prefix')
    assert result['Rules'][0]['Filter']['And']['Prefix'] == 'some/prefix'
    assert len(result['Rules'][0]['Filter']['And']['Tags']) == 2
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Value'
        ] == 'mytagvalue'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Key'] == 'mytag2'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Value'
        ] == 'mytagvalue2'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter']['And'] = {'Tags': [{'Key': 'mytag', 'Value':
        'mytagvalue'}, {'Key': 'mytag2', 'Value': 'mytagvalue2'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Filter']['And']['Prefix']
    assert len(result['Rules'][0]['Filter']['And']['Tags']) == 2
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Value'
        ] == 'mytagvalue'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Key'] == 'mytag2'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Value'
        ] == 'mytagvalue2'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Prefix'] = ''
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['Prefix'] = 'some/path'
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    del lfc['Rules'][0]['Filter']
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert not result['Rules'][0].get('Filter')
    assert result['Rules'][0]['Prefix'] == 'some/path'
    del lfc['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter'] = {'Prefix': 'some/prefix', 'Tag': {'Key':
        'mytag', 'Value': 'mytagvalue'}}
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['Filter'] = {'Tag': {'Key': 'mytag', 'Value':
        'mytagvalue'}, 'And': {'Prefix': 'some/prefix', 'Tags': [{'Key':
        'mytag', 'Value': 'mytagvalue'}, {'Key': 'mytag2', 'Value':
        'mytagvalue2'}]}}
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc = {'Rules': [{'Expiration': {'Days': 7}, 'ID': 'wholebucket',
        'Filter': {'Prefix': ''}, 'Status': 'Enabled'}, {'Expiration': {
        'Days': 10}, 'ID': 'Tags', 'Filter': {'Tag': {'Key': 'somekey',
        'Value': 'somevalue'}}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')['Rules'
        ]
    assert len(result) == 2
    assert result[0]['ID'] == 'wholebucket'
    assert result[1]['ID'] == 'Tags'


<mask token>


@mock_s3
def test_lifecycle_with_nvt():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'NoncurrentVersionTransitions': [{'NoncurrentDays': 
        30, 'StorageClass': 'ONEZONE_IA'}], 'ID': 'wholebucket', 'Filter':
        {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0][
        'NoncurrentDays'] == 30
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass'
        ] == 'ONEZONE_IA'
    lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['NoncurrentDays'] = 10
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0][
        'NoncurrentDays'] == 10
    lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass'
        ] = 'GLACIER'
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass'
        ] == 'GLACIER'
    del lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['NoncurrentDays']
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['NoncurrentDays'] = 30
    del lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass']
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'


@mock_s3
def test_lifecycle_with_multiple_nvt():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'NoncurrentVersionTransitions': [{'NoncurrentDays': 
        30, 'StorageClass': 'ONEZONE_IA'}, {'NoncurrentDays': 50,
        'StorageClass': 'GLACIER'}], 'ID': 'wholebucket', 'Filter': {
        'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0] == {
        'NoncurrentDays': 30, 'StorageClass': 'ONEZONE_IA'}
    assert result['Rules'][0]['NoncurrentVersionTransitions'][1] == {
        'NoncurrentDays': 50, 'StorageClass': 'GLACIER'}


<mask token>


@mock_s3
def test_lifecycle_with_aimu():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'AbortIncompleteMultipartUpload': {
        'DaysAfterInitiation': 7}, 'ID': 'wholebucket', 'Filter': {'Prefix':
        ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['AbortIncompleteMultipartUpload'][
        'DaysAfterInitiation'] == 7
    lfc['Rules'][0]['AbortIncompleteMultipartUpload']['DaysAfterInitiation'
        ] = 30
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['AbortIncompleteMultipartUpload'][
        'DaysAfterInitiation'] == 30


<mask token>


@mock_s3
def test_lifecycle_delete_boto3():
    s3 = boto3.resource('s3', region_name='us-east-1')
    client = boto3.client('s3', region_name='us-east-1')
    s3.create_bucket(Bucket='foobar')
    client.put_bucket_lifecycle_configuration(Bucket='foobar',
        LifecycleConfiguration={'Rules': [{'ID': 'myid', 'Prefix': '',
        'Status': 'Enabled'}]})
    client.delete_bucket_lifecycle(Bucket='foobar')
    with pytest.raises(ClientError) as ex:
        client.get_bucket_lifecycle_configuration(Bucket='foobar')
    ex.value.response['Error']['Code'].should.equal(
        'NoSuchLifecycleConfiguration')
    ex.value.response['Error']['Message'].should.equal(
        'The lifecycle configuration does not exist')

<mask token>


@mock_s3
def test_lifecycle_with_filters():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'Expiration': {'Days': 7}, 'ID': 'wholebucket',
        'Filter': {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['Filter']['Prefix'] == ''
    assert not result['Rules'][0]['Filter'].get('And')
    assert not result['Rules'][0]['Filter'].get('Tag')
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc = {'Rules': [{'Expiration': {'Days': 7}, 'ID': 'wholebucket',
        'Filter': {}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0].pop('Filter')
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['Filter'] = {'Tag': {'Key': 'mytag', 'Value': 'mytagvalue'}
        }
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Filter']['Prefix']
    assert not result['Rules'][0]['Filter'].get('And')
    assert result['Rules'][0]['Filter']['Tag']['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['Tag']['Value'] == 'mytagvalue'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter'] = {'And': {'Prefix': 'some/prefix', 'Tags': [
        {'Key': 'mytag', 'Value': 'mytagvalue'}]}}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert not result['Rules'][0]['Filter'].get('Prefix')
    assert result['Rules'][0]['Filter']['And']['Prefix'] == 'some/prefix'
    assert len(result['Rules'][0]['Filter']['And']['Tags']) == 1
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Value'
        ] == 'mytagvalue'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter']['And'] = {'Prefix': 'some/prefix', 'Tags': [{
        'Key': 'mytag', 'Value': 'mytagvalue'}, {'Key': 'mytag2', 'Value':
        'mytagvalue2'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert not result['Rules'][0]['Filter'].get('Prefix')
    assert result['Rules'][0]['Filter']['And']['Prefix'] == 'some/prefix'
    assert len(result['Rules'][0]['Filter']['And']['Tags']) == 2
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Value'
        ] == 'mytagvalue'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Key'] == 'mytag2'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Value'
        ] == 'mytagvalue2'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter']['And'] = {'Tags': [{'Key': 'mytag', 'Value':
        'mytagvalue'}, {'Key': 'mytag2', 'Value': 'mytagvalue2'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Filter']['And']['Prefix']
    assert len(result['Rules'][0]['Filter']['And']['Tags']) == 2
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Value'
        ] == 'mytagvalue'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Key'] == 'mytag2'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Value'
        ] == 'mytagvalue2'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Prefix'] = ''
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['Prefix'] = 'some/path'
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    del lfc['Rules'][0]['Filter']
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert not result['Rules'][0].get('Filter')
    assert result['Rules'][0]['Prefix'] == 'some/path'
    del lfc['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter'] = {'Prefix': 'some/prefix', 'Tag': {'Key':
        'mytag', 'Value': 'mytagvalue'}}
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['Filter'] = {'Tag': {'Key': 'mytag', 'Value':
        'mytagvalue'}, 'And': {'Prefix': 'some/prefix', 'Tags': [{'Key':
        'mytag', 'Value': 'mytagvalue'}, {'Key': 'mytag2', 'Value':
        'mytagvalue2'}]}}
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc = {'Rules': [{'Expiration': {'Days': 7}, 'ID': 'wholebucket',
        'Filter': {'Prefix': ''}, 'Status': 'Enabled'}, {'Expiration': {
        'Days': 10}, 'ID': 'Tags', 'Filter': {'Tag': {'Key': 'somekey',
        'Value': 'somevalue'}}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')['Rules'
        ]
    assert len(result) == 2
    assert result[0]['ID'] == 'wholebucket'
    assert result[1]['ID'] == 'Tags'


<mask token>


@mock_s3
def test_lifecycle_with_nvt():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'NoncurrentVersionTransitions': [{'NoncurrentDays': 
        30, 'StorageClass': 'ONEZONE_IA'}], 'ID': 'wholebucket', 'Filter':
        {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0][
        'NoncurrentDays'] == 30
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass'
        ] == 'ONEZONE_IA'
    lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['NoncurrentDays'] = 10
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0][
        'NoncurrentDays'] == 10
    lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass'
        ] = 'GLACIER'
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass'
        ] == 'GLACIER'
    del lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['NoncurrentDays']
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['NoncurrentDays'] = 30
    del lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass']
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'


@mock_s3
def test_lifecycle_with_multiple_nvt():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'NoncurrentVersionTransitions': [{'NoncurrentDays': 
        30, 'StorageClass': 'ONEZONE_IA'}, {'NoncurrentDays': 50,
        'StorageClass': 'GLACIER'}], 'ID': 'wholebucket', 'Filter': {
        'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0] == {
        'NoncurrentDays': 30, 'StorageClass': 'ONEZONE_IA'}
    assert result['Rules'][0]['NoncurrentVersionTransitions'][1] == {
        'NoncurrentDays': 50, 'StorageClass': 'GLACIER'}


@mock_s3
def test_lifecycle_with_multiple_transitions():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'Transitions': [{'Days': 30, 'StorageClass':
        'ONEZONE_IA'}, {'Days': 50, 'StorageClass': 'GLACIER'}], 'ID':
        'wholebucket', 'Filter': {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['Transitions'][0] == {'Days': 30,
        'StorageClass': 'ONEZONE_IA'}
    assert result['Rules'][0]['Transitions'][1] == {'Days': 50,
        'StorageClass': 'GLACIER'}


@mock_s3
def test_lifecycle_with_aimu():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'AbortIncompleteMultipartUpload': {
        'DaysAfterInitiation': 7}, 'ID': 'wholebucket', 'Filter': {'Prefix':
        ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['AbortIncompleteMultipartUpload'][
        'DaysAfterInitiation'] == 7
    lfc['Rules'][0]['AbortIncompleteMultipartUpload']['DaysAfterInitiation'
        ] = 30
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['AbortIncompleteMultipartUpload'][
        'DaysAfterInitiation'] == 30


<mask token>


@mock_s3
def test_lifecycle_delete_boto3():
    s3 = boto3.resource('s3', region_name='us-east-1')
    client = boto3.client('s3', region_name='us-east-1')
    s3.create_bucket(Bucket='foobar')
    client.put_bucket_lifecycle_configuration(Bucket='foobar',
        LifecycleConfiguration={'Rules': [{'ID': 'myid', 'Prefix': '',
        'Status': 'Enabled'}]})
    client.delete_bucket_lifecycle(Bucket='foobar')
    with pytest.raises(ClientError) as ex:
        client.get_bucket_lifecycle_configuration(Bucket='foobar')
    ex.value.response['Error']['Code'].should.equal(
        'NoSuchLifecycleConfiguration')
    ex.value.response['Error']['Message'].should.equal(
        'The lifecycle configuration does not exist')

<mask token>


@mock_s3
def test_lifecycle_with_filters():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'Expiration': {'Days': 7}, 'ID': 'wholebucket',
        'Filter': {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['Filter']['Prefix'] == ''
    assert not result['Rules'][0]['Filter'].get('And')
    assert not result['Rules'][0]['Filter'].get('Tag')
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc = {'Rules': [{'Expiration': {'Days': 7}, 'ID': 'wholebucket',
        'Filter': {}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0].pop('Filter')
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['Filter'] = {'Tag': {'Key': 'mytag', 'Value': 'mytagvalue'}
        }
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Filter']['Prefix']
    assert not result['Rules'][0]['Filter'].get('And')
    assert result['Rules'][0]['Filter']['Tag']['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['Tag']['Value'] == 'mytagvalue'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter'] = {'And': {'Prefix': 'some/prefix', 'Tags': [
        {'Key': 'mytag', 'Value': 'mytagvalue'}]}}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert not result['Rules'][0]['Filter'].get('Prefix')
    assert result['Rules'][0]['Filter']['And']['Prefix'] == 'some/prefix'
    assert len(result['Rules'][0]['Filter']['And']['Tags']) == 1
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Value'
        ] == 'mytagvalue'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter']['And'] = {'Prefix': 'some/prefix', 'Tags': [{
        'Key': 'mytag', 'Value': 'mytagvalue'}, {'Key': 'mytag2', 'Value':
        'mytagvalue2'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert not result['Rules'][0]['Filter'].get('Prefix')
    assert result['Rules'][0]['Filter']['And']['Prefix'] == 'some/prefix'
    assert len(result['Rules'][0]['Filter']['And']['Tags']) == 2
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Value'
        ] == 'mytagvalue'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Key'] == 'mytag2'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Value'
        ] == 'mytagvalue2'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter']['And'] = {'Tags': [{'Key': 'mytag', 'Value':
        'mytagvalue'}, {'Key': 'mytag2', 'Value': 'mytagvalue2'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Filter']['And']['Prefix']
    assert len(result['Rules'][0]['Filter']['And']['Tags']) == 2
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Value'
        ] == 'mytagvalue'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Key'] == 'mytag2'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Value'
        ] == 'mytagvalue2'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Prefix'] = ''
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['Prefix'] = 'some/path'
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    del lfc['Rules'][0]['Filter']
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert not result['Rules'][0].get('Filter')
    assert result['Rules'][0]['Prefix'] == 'some/path'
    del lfc['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter'] = {'Prefix': 'some/prefix', 'Tag': {'Key':
        'mytag', 'Value': 'mytagvalue'}}
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['Filter'] = {'Tag': {'Key': 'mytag', 'Value':
        'mytagvalue'}, 'And': {'Prefix': 'some/prefix', 'Tags': [{'Key':
        'mytag', 'Value': 'mytagvalue'}, {'Key': 'mytag2', 'Value':
        'mytagvalue2'}]}}
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc = {'Rules': [{'Expiration': {'Days': 7}, 'ID': 'wholebucket',
        'Filter': {'Prefix': ''}, 'Status': 'Enabled'}, {'Expiration': {
        'Days': 10}, 'ID': 'Tags', 'Filter': {'Tag': {'Key': 'somekey',
        'Value': 'somevalue'}}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')['Rules'
        ]
    assert len(result) == 2
    assert result[0]['ID'] == 'wholebucket'
    assert result[1]['ID'] == 'Tags'


@mock_s3
def test_lifecycle_with_eodm():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'Expiration': {'ExpiredObjectDeleteMarker': True},
        'ID': 'wholebucket', 'Filter': {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['Expiration']['ExpiredObjectDeleteMarker']
    lfc['Rules'][0]['Expiration']['ExpiredObjectDeleteMarker'] = False
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert not result['Rules'][0]['Expiration']['ExpiredObjectDeleteMarker']
    lfc['Rules'][0]['Expiration']['Days'] = 7
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    del lfc['Rules'][0]['Expiration']['Days']
    lfc['Rules'][0]['Expiration']['Date'] = datetime(2015, 1, 1)
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'


@mock_s3
def test_lifecycle_with_nve():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'NoncurrentVersionExpiration': {'NoncurrentDays': 30},
        'ID': 'wholebucket', 'Filter': {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionExpiration']['NoncurrentDays'
        ] == 30
    lfc['Rules'][0]['NoncurrentVersionExpiration']['NoncurrentDays'] = 10
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionExpiration']['NoncurrentDays'
        ] == 10


@mock_s3
def test_lifecycle_with_nvt():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'NoncurrentVersionTransitions': [{'NoncurrentDays': 
        30, 'StorageClass': 'ONEZONE_IA'}], 'ID': 'wholebucket', 'Filter':
        {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0][
        'NoncurrentDays'] == 30
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass'
        ] == 'ONEZONE_IA'
    lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['NoncurrentDays'] = 10
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0][
        'NoncurrentDays'] == 10
    lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass'
        ] = 'GLACIER'
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass'
        ] == 'GLACIER'
    del lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['NoncurrentDays']
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['NoncurrentDays'] = 30
    del lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass']
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'


@mock_s3
def test_lifecycle_with_multiple_nvt():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'NoncurrentVersionTransitions': [{'NoncurrentDays': 
        30, 'StorageClass': 'ONEZONE_IA'}, {'NoncurrentDays': 50,
        'StorageClass': 'GLACIER'}], 'ID': 'wholebucket', 'Filter': {
        'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0] == {
        'NoncurrentDays': 30, 'StorageClass': 'ONEZONE_IA'}
    assert result['Rules'][0]['NoncurrentVersionTransitions'][1] == {
        'NoncurrentDays': 50, 'StorageClass': 'GLACIER'}


@mock_s3
def test_lifecycle_with_multiple_transitions():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'Transitions': [{'Days': 30, 'StorageClass':
        'ONEZONE_IA'}, {'Days': 50, 'StorageClass': 'GLACIER'}], 'ID':
        'wholebucket', 'Filter': {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['Transitions'][0] == {'Days': 30,
        'StorageClass': 'ONEZONE_IA'}
    assert result['Rules'][0]['Transitions'][1] == {'Days': 50,
        'StorageClass': 'GLACIER'}


@mock_s3
def test_lifecycle_with_aimu():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'AbortIncompleteMultipartUpload': {
        'DaysAfterInitiation': 7}, 'ID': 'wholebucket', 'Filter': {'Prefix':
        ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['AbortIncompleteMultipartUpload'][
        'DaysAfterInitiation'] == 7
    lfc['Rules'][0]['AbortIncompleteMultipartUpload']['DaysAfterInitiation'
        ] = 30
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['AbortIncompleteMultipartUpload'][
        'DaysAfterInitiation'] == 30


<mask token>


@mock_s3
def test_lifecycle_delete_boto3():
    s3 = boto3.resource('s3', region_name='us-east-1')
    client = boto3.client('s3', region_name='us-east-1')
    s3.create_bucket(Bucket='foobar')
    client.put_bucket_lifecycle_configuration(Bucket='foobar',
        LifecycleConfiguration={'Rules': [{'ID': 'myid', 'Prefix': '',
        'Status': 'Enabled'}]})
    client.delete_bucket_lifecycle(Bucket='foobar')
    with pytest.raises(ClientError) as ex:
        client.get_bucket_lifecycle_configuration(Bucket='foobar')
    ex.value.response['Error']['Code'].should.equal(
        'NoSuchLifecycleConfiguration')
    ex.value.response['Error']['Message'].should.equal(
        'The lifecycle configuration does not exist')

<mask token>


@mock_s3
def test_lifecycle_with_filters():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'Expiration': {'Days': 7}, 'ID': 'wholebucket',
        'Filter': {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['Filter']['Prefix'] == ''
    assert not result['Rules'][0]['Filter'].get('And')
    assert not result['Rules'][0]['Filter'].get('Tag')
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc = {'Rules': [{'Expiration': {'Days': 7}, 'ID': 'wholebucket',
        'Filter': {}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0].pop('Filter')
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['Filter'] = {'Tag': {'Key': 'mytag', 'Value': 'mytagvalue'}
        }
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Filter']['Prefix']
    assert not result['Rules'][0]['Filter'].get('And')
    assert result['Rules'][0]['Filter']['Tag']['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['Tag']['Value'] == 'mytagvalue'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter'] = {'And': {'Prefix': 'some/prefix', 'Tags': [
        {'Key': 'mytag', 'Value': 'mytagvalue'}]}}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert not result['Rules'][0]['Filter'].get('Prefix')
    assert result['Rules'][0]['Filter']['And']['Prefix'] == 'some/prefix'
    assert len(result['Rules'][0]['Filter']['And']['Tags']) == 1
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Value'
        ] == 'mytagvalue'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter']['And'] = {'Prefix': 'some/prefix', 'Tags': [{
        'Key': 'mytag', 'Value': 'mytagvalue'}, {'Key': 'mytag2', 'Value':
        'mytagvalue2'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert not result['Rules'][0]['Filter'].get('Prefix')
    assert result['Rules'][0]['Filter']['And']['Prefix'] == 'some/prefix'
    assert len(result['Rules'][0]['Filter']['And']['Tags']) == 2
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Value'
        ] == 'mytagvalue'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Key'] == 'mytag2'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Value'
        ] == 'mytagvalue2'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter']['And'] = {'Tags': [{'Key': 'mytag', 'Value':
        'mytagvalue'}, {'Key': 'mytag2', 'Value': 'mytagvalue2'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Filter']['And']['Prefix']
    assert len(result['Rules'][0]['Filter']['And']['Tags']) == 2
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Key'] == 'mytag'
    assert result['Rules'][0]['Filter']['And']['Tags'][0]['Value'
        ] == 'mytagvalue'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Key'] == 'mytag2'
    assert result['Rules'][0]['Filter']['And']['Tags'][1]['Value'
        ] == 'mytagvalue2'
    with pytest.raises(KeyError):
        assert result['Rules'][0]['Prefix']
    lfc['Rules'][0]['Prefix'] = ''
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['Prefix'] = 'some/path'
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    del lfc['Rules'][0]['Filter']
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert not result['Rules'][0].get('Filter')
    assert result['Rules'][0]['Prefix'] == 'some/path'
    del lfc['Rules'][0]['Prefix']
    lfc['Rules'][0]['Filter'] = {'Prefix': 'some/prefix', 'Tag': {'Key':
        'mytag', 'Value': 'mytagvalue'}}
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['Filter'] = {'Tag': {'Key': 'mytag', 'Value':
        'mytagvalue'}, 'And': {'Prefix': 'some/prefix', 'Tags': [{'Key':
        'mytag', 'Value': 'mytagvalue'}, {'Key': 'mytag2', 'Value':
        'mytagvalue2'}]}}
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc = {'Rules': [{'Expiration': {'Days': 7}, 'ID': 'wholebucket',
        'Filter': {'Prefix': ''}, 'Status': 'Enabled'}, {'Expiration': {
        'Days': 10}, 'ID': 'Tags', 'Filter': {'Tag': {'Key': 'somekey',
        'Value': 'somevalue'}}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')['Rules'
        ]
    assert len(result) == 2
    assert result[0]['ID'] == 'wholebucket'
    assert result[1]['ID'] == 'Tags'


@mock_s3
def test_lifecycle_with_eodm():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'Expiration': {'ExpiredObjectDeleteMarker': True},
        'ID': 'wholebucket', 'Filter': {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['Expiration']['ExpiredObjectDeleteMarker']
    lfc['Rules'][0]['Expiration']['ExpiredObjectDeleteMarker'] = False
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert not result['Rules'][0]['Expiration']['ExpiredObjectDeleteMarker']
    lfc['Rules'][0]['Expiration']['Days'] = 7
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    del lfc['Rules'][0]['Expiration']['Days']
    lfc['Rules'][0]['Expiration']['Date'] = datetime(2015, 1, 1)
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'


@mock_s3
def test_lifecycle_with_nve():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'NoncurrentVersionExpiration': {'NoncurrentDays': 30},
        'ID': 'wholebucket', 'Filter': {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionExpiration']['NoncurrentDays'
        ] == 30
    lfc['Rules'][0]['NoncurrentVersionExpiration']['NoncurrentDays'] = 10
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionExpiration']['NoncurrentDays'
        ] == 10


@mock_s3
def test_lifecycle_with_nvt():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'NoncurrentVersionTransitions': [{'NoncurrentDays': 
        30, 'StorageClass': 'ONEZONE_IA'}], 'ID': 'wholebucket', 'Filter':
        {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0][
        'NoncurrentDays'] == 30
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass'
        ] == 'ONEZONE_IA'
    lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['NoncurrentDays'] = 10
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0][
        'NoncurrentDays'] == 10
    lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass'
        ] = 'GLACIER'
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass'
        ] == 'GLACIER'
    del lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['NoncurrentDays']
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'
    lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['NoncurrentDays'] = 30
    del lfc['Rules'][0]['NoncurrentVersionTransitions'][0]['StorageClass']
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(Bucket='bucket',
            LifecycleConfiguration=lfc)
    assert err.value.response['Error']['Code'] == 'MalformedXML'


@mock_s3
def test_lifecycle_with_multiple_nvt():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'NoncurrentVersionTransitions': [{'NoncurrentDays': 
        30, 'StorageClass': 'ONEZONE_IA'}, {'NoncurrentDays': 50,
        'StorageClass': 'GLACIER'}], 'ID': 'wholebucket', 'Filter': {
        'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['NoncurrentVersionTransitions'][0] == {
        'NoncurrentDays': 30, 'StorageClass': 'ONEZONE_IA'}
    assert result['Rules'][0]['NoncurrentVersionTransitions'][1] == {
        'NoncurrentDays': 50, 'StorageClass': 'GLACIER'}


@mock_s3
def test_lifecycle_with_multiple_transitions():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'Transitions': [{'Days': 30, 'StorageClass':
        'ONEZONE_IA'}, {'Days': 50, 'StorageClass': 'GLACIER'}], 'ID':
        'wholebucket', 'Filter': {'Prefix': ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['Transitions'][0] == {'Days': 30,
        'StorageClass': 'ONEZONE_IA'}
    assert result['Rules'][0]['Transitions'][1] == {'Days': 50,
        'StorageClass': 'GLACIER'}


@mock_s3
def test_lifecycle_with_aimu():
    client = boto3.client('s3')
    client.create_bucket(Bucket='bucket', CreateBucketConfiguration={
        'LocationConstraint': 'us-west-1'})
    lfc = {'Rules': [{'AbortIncompleteMultipartUpload': {
        'DaysAfterInitiation': 7}, 'ID': 'wholebucket', 'Filter': {'Prefix':
        ''}, 'Status': 'Enabled'}]}
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['AbortIncompleteMultipartUpload'][
        'DaysAfterInitiation'] == 7
    lfc['Rules'][0]['AbortIncompleteMultipartUpload']['DaysAfterInitiation'
        ] = 30
    client.put_bucket_lifecycle_configuration(Bucket='bucket',
        LifecycleConfiguration=lfc)
    result = client.get_bucket_lifecycle_configuration(Bucket='bucket')
    assert len(result['Rules']) == 1
    assert result['Rules'][0]['AbortIncompleteMultipartUpload'][
        'DaysAfterInitiation'] == 30


@mock_s3
def test_lifecycle_with_glacier_transition_boto3():
    s3 = boto3.resource('s3', region_name='us-east-1')
    client = boto3.client('s3', region_name='us-east-1')
    s3.create_bucket(Bucket='foobar')
    client.put_bucket_lifecycle_configuration(Bucket='foobar',
        LifecycleConfiguration={'Rules': [{'ID': 'myid', 'Prefix': '',
        'Status': 'Enabled', 'Transitions': [{'Days': 30, 'StorageClass':
        'GLACIER'}]}]})
    response = client.get_bucket_lifecycle_configuration(Bucket='foobar')
    response.should.have.key('Rules')
    rules = response['Rules']
    rules.should.have.length_of(1)
    rules[0].should.have.key('ID').equal('myid')
    transition = rules[0]['Transitions'][0]
    transition['Days'].should.equal(30)
    transition['StorageClass'].should.equal('GLACIER')
    transition.shouldnt.have.key('Date')


@mock_s3
def test_lifecycle_multi_boto3():
    s3 = boto3.resource('s3', region_name='us-east-1')
    client = boto3.client('s3', region_name='us-east-1')
    s3.create_bucket(Bucket='foobar')
    date = '2022-10-12T00:00:00.000Z'
    sc = 'GLACIER'
    client.put_bucket_lifecycle_configuration(Bucket='foobar',
        LifecycleConfiguration={'Rules': [{'ID': '1', 'Prefix': '1/',
        'Status': 'Enabled'}, {'ID': '2', 'Prefix': '2/', 'Status':
        'Enabled', 'Expiration': {'Days': 2}}, {'ID': '3', 'Prefix': '3/',
        'Status': 'Enabled', 'Expiration': {'Date': date}}, {'ID': '4',
        'Prefix': '4/', 'Status': 'Enabled', 'Transitions': [{'Days': 4,
        'StorageClass': 'GLACIER'}]}, {'ID': '5', 'Prefix': '5/', 'Status':
        'Enabled', 'Transitions': [{'Date': date, 'StorageClass': 'GLACIER'
        }]}]})
    rules = client.get_bucket_lifecycle_configuration(Bucket='foobar')['Rules']
    for rule in rules:
        if rule['ID'] == '1':
            rule['Prefix'].should.equal('1/')
            rule.shouldnt.have.key('Expiration')
        elif rule['ID'] == '2':
            rule['Prefix'].should.equal('2/')
            rule['Expiration']['Days'].should.equal(2)
        elif rule['ID'] == '3':
            rule['Prefix'].should.equal('3/')
            rule['Expiration']['Date'].should.be.a(datetime)
            rule['Expiration']['Date'].strftime('%Y-%m-%dT%H:%M:%S.000Z'
                ).should.equal(date)
        elif rule['ID'] == '4':
            rule['Prefix'].should.equal('4/')
            rule['Transitions'][0]['Days'].should.equal(4)
            rule['Transitions'][0]['StorageClass'].should.equal(sc)
        elif rule['ID'] == '5':
            rule['Prefix'].should.equal('5/')
            rule['Transitions'][0]['Date'].should.be.a(datetime)
            rule['Transitions'][0]['Date'].strftime('%Y-%m-%dT%H:%M:%S.000Z'
                ).should.equal(date)
            rule['Transitions'][0]['StorageClass'].should.equal(sc)
        else:
            assert False, 'Invalid rule id'


@mock_s3
def test_lifecycle_delete_boto3():
    s3 = boto3.resource('s3', region_name='us-east-1')
    client = boto3.client('s3', region_name='us-east-1')
    s3.create_bucket(Bucket='foobar')
    client.put_bucket_lifecycle_configuration(Bucket='foobar',
        LifecycleConfiguration={'Rules': [{'ID': 'myid', 'Prefix': '',
        'Status': 'Enabled'}]})
    client.delete_bucket_lifecycle(Bucket='foobar')
    with pytest.raises(ClientError) as ex:
        client.get_bucket_lifecycle_configuration(Bucket='foobar')
    ex.value.response['Error']['Code'].should.equal(
        'NoSuchLifecycleConfiguration')
    ex.value.response['Error']['Message'].should.equal(
        'The lifecycle configuration does not exist')

import boto3

import sure  # noqa # pylint: disable=unused-import
from botocore.exceptions import ClientError
from datetime import datetime
import pytest

from moto import mock_s3


@mock_s3
def test_lifecycle_with_filters():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    # Create a lifecycle rule with a Filter (no tags):
    lfc = {
        "Rules": [
            {
                "Expiration": {"Days": 7},
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["Filter"]["Prefix"] == ""
    assert not result["Rules"][0]["Filter"].get("And")
    assert not result["Rules"][0]["Filter"].get("Tag")
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Prefix"]

    # Without any prefixes and an empty filter (this is by default a prefix for the whole bucket):
    lfc = {
        "Rules": [
            {
                "Expiration": {"Days": 7},
                "ID": "wholebucket",
                "Filter": {},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Prefix"]

    # If we remove the filter -- and don't specify a Prefix, then this is bad:
    lfc["Rules"][0].pop("Filter")
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"

    # With a tag:
    lfc["Rules"][0]["Filter"] = {"Tag": {"Key": "mytag", "Value": "mytagvalue"}}
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Filter"]["Prefix"]
    assert not result["Rules"][0]["Filter"].get("And")
    assert result["Rules"][0]["Filter"]["Tag"]["Key"] == "mytag"
    assert result["Rules"][0]["Filter"]["Tag"]["Value"] == "mytagvalue"
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Prefix"]

    # With And (single tag):
    lfc["Rules"][0]["Filter"] = {
        "And": {
            "Prefix": "some/prefix",
            "Tags": [{"Key": "mytag", "Value": "mytagvalue"}],
        }
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert not result["Rules"][0]["Filter"].get("Prefix")
    assert result["Rules"][0]["Filter"]["And"]["Prefix"] == "some/prefix"
    assert len(result["Rules"][0]["Filter"]["And"]["Tags"]) == 1
    assert result["Rules"][0]["Filter"]["And"]["Tags"][0]["Key"] == "mytag"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][0]["Value"] == "mytagvalue"
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Prefix"]

    # With multiple And tags:
    lfc["Rules"][0]["Filter"]["And"] = {
        "Prefix": "some/prefix",
        "Tags": [
            {"Key": "mytag", "Value": "mytagvalue"},
            {"Key": "mytag2", "Value": "mytagvalue2"},
        ],
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert not result["Rules"][0]["Filter"].get("Prefix")
    assert result["Rules"][0]["Filter"]["And"]["Prefix"] == "some/prefix"
    assert len(result["Rules"][0]["Filter"]["And"]["Tags"]) == 2
    assert result["Rules"][0]["Filter"]["And"]["Tags"][0]["Key"] == "mytag"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][0]["Value"] == "mytagvalue"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][1]["Key"] == "mytag2"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][1]["Value"] == "mytagvalue2"
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Prefix"]

    # And filter without Prefix but multiple Tags:
    lfc["Rules"][0]["Filter"]["And"] = {
        "Tags": [
            {"Key": "mytag", "Value": "mytagvalue"},
            {"Key": "mytag2", "Value": "mytagvalue2"},
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Filter"]["And"]["Prefix"]
    assert len(result["Rules"][0]["Filter"]["And"]["Tags"]) == 2
    assert result["Rules"][0]["Filter"]["And"]["Tags"][0]["Key"] == "mytag"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][0]["Value"] == "mytagvalue"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][1]["Key"] == "mytag2"
    assert result["Rules"][0]["Filter"]["And"]["Tags"][1]["Value"] == "mytagvalue2"
    with pytest.raises(KeyError):
        assert result["Rules"][0]["Prefix"]

    # Can't have both filter and prefix:
    lfc["Rules"][0]["Prefix"] = ""
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"

    lfc["Rules"][0]["Prefix"] = "some/path"
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"

    # No filters -- just a prefix:
    del lfc["Rules"][0]["Filter"]
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert not result["Rules"][0].get("Filter")
    assert result["Rules"][0]["Prefix"] == "some/path"

    # Can't have Tag, Prefix, and And in a filter:
    del lfc["Rules"][0]["Prefix"]
    lfc["Rules"][0]["Filter"] = {
        "Prefix": "some/prefix",
        "Tag": {"Key": "mytag", "Value": "mytagvalue"},
    }
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"

    lfc["Rules"][0]["Filter"] = {
        "Tag": {"Key": "mytag", "Value": "mytagvalue"},
        "And": {
            "Prefix": "some/prefix",
            "Tags": [
                {"Key": "mytag", "Value": "mytagvalue"},
                {"Key": "mytag2", "Value": "mytagvalue2"},
            ],
        },
    }
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"

    # Make sure multiple rules work:
    lfc = {
        "Rules": [
            {
                "Expiration": {"Days": 7},
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            },
            {
                "Expiration": {"Days": 10},
                "ID": "Tags",
                "Filter": {"Tag": {"Key": "somekey", "Value": "somevalue"}},
                "Status": "Enabled",
            },
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")["Rules"]
    assert len(result) == 2
    assert result[0]["ID"] == "wholebucket"
    assert result[1]["ID"] == "Tags"


@mock_s3
def test_lifecycle_with_eodm():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    lfc = {
        "Rules": [
            {
                "Expiration": {"ExpiredObjectDeleteMarker": True},
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["Expiration"]["ExpiredObjectDeleteMarker"]

    # Set to False:
    lfc["Rules"][0]["Expiration"]["ExpiredObjectDeleteMarker"] = False
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert not result["Rules"][0]["Expiration"]["ExpiredObjectDeleteMarker"]

    # With failure:
    lfc["Rules"][0]["Expiration"]["Days"] = 7
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"
    del lfc["Rules"][0]["Expiration"]["Days"]

    lfc["Rules"][0]["Expiration"]["Date"] = datetime(2015, 1, 1)
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"


@mock_s3
def test_lifecycle_with_nve():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    lfc = {
        "Rules": [
            {
                "NoncurrentVersionExpiration": {"NoncurrentDays": 30},
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["NoncurrentVersionExpiration"]["NoncurrentDays"] == 30

    # Change NoncurrentDays:
    lfc["Rules"][0]["NoncurrentVersionExpiration"]["NoncurrentDays"] = 10
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["NoncurrentVersionExpiration"]["NoncurrentDays"] == 10

    # TODO: Add test for failures due to missing children


@mock_s3
def test_lifecycle_with_nvt():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    lfc = {
        "Rules": [
            {
                "NoncurrentVersionTransitions": [
                    {"NoncurrentDays": 30, "StorageClass": "ONEZONE_IA"}
                ],
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["NoncurrentVersionTransitions"][0]["NoncurrentDays"] == 30
    assert (
        result["Rules"][0]["NoncurrentVersionTransitions"][0]["StorageClass"]
        == "ONEZONE_IA"
    )

    # Change NoncurrentDays:
    lfc["Rules"][0]["NoncurrentVersionTransitions"][0]["NoncurrentDays"] = 10
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["NoncurrentVersionTransitions"][0]["NoncurrentDays"] == 10

    # Change StorageClass:
    lfc["Rules"][0]["NoncurrentVersionTransitions"][0]["StorageClass"] = "GLACIER"
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert (
        result["Rules"][0]["NoncurrentVersionTransitions"][0]["StorageClass"]
        == "GLACIER"
    )

    # With failures for missing children:
    del lfc["Rules"][0]["NoncurrentVersionTransitions"][0]["NoncurrentDays"]
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"
    lfc["Rules"][0]["NoncurrentVersionTransitions"][0]["NoncurrentDays"] = 30

    del lfc["Rules"][0]["NoncurrentVersionTransitions"][0]["StorageClass"]
    with pytest.raises(ClientError) as err:
        client.put_bucket_lifecycle_configuration(
            Bucket="bucket", LifecycleConfiguration=lfc
        )
    assert err.value.response["Error"]["Code"] == "MalformedXML"


@mock_s3
def test_lifecycle_with_multiple_nvt():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    lfc = {
        "Rules": [
            {
                "NoncurrentVersionTransitions": [
                    {"NoncurrentDays": 30, "StorageClass": "ONEZONE_IA"},
                    {"NoncurrentDays": 50, "StorageClass": "GLACIER"},
                ],
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["NoncurrentVersionTransitions"][0] == {
        "NoncurrentDays": 30,
        "StorageClass": "ONEZONE_IA",
    }
    assert result["Rules"][0]["NoncurrentVersionTransitions"][1] == {
        "NoncurrentDays": 50,
        "StorageClass": "GLACIER",
    }


@mock_s3
def test_lifecycle_with_multiple_transitions():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    lfc = {
        "Rules": [
            {
                "Transitions": [
                    {"Days": 30, "StorageClass": "ONEZONE_IA"},
                    {"Days": 50, "StorageClass": "GLACIER"},
                ],
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert result["Rules"][0]["Transitions"][0] == {
        "Days": 30,
        "StorageClass": "ONEZONE_IA",
    }
    assert result["Rules"][0]["Transitions"][1] == {
        "Days": 50,
        "StorageClass": "GLACIER",
    }


@mock_s3
def test_lifecycle_with_aimu():
    client = boto3.client("s3")
    client.create_bucket(
        Bucket="bucket", CreateBucketConfiguration={"LocationConstraint": "us-west-1"}
    )

    lfc = {
        "Rules": [
            {
                "AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 7},
                "ID": "wholebucket",
                "Filter": {"Prefix": ""},
                "Status": "Enabled",
            }
        ]
    }
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert (
        result["Rules"][0]["AbortIncompleteMultipartUpload"]["DaysAfterInitiation"] == 7
    )

    # Change DaysAfterInitiation:
    lfc["Rules"][0]["AbortIncompleteMultipartUpload"]["DaysAfterInitiation"] = 30
    client.put_bucket_lifecycle_configuration(
        Bucket="bucket", LifecycleConfiguration=lfc
    )
    result = client.get_bucket_lifecycle_configuration(Bucket="bucket")
    assert len(result["Rules"]) == 1
    assert (
        result["Rules"][0]["AbortIncompleteMultipartUpload"]["DaysAfterInitiation"]
        == 30
    )

    # TODO: Add test for failures due to missing children


@mock_s3
def test_lifecycle_with_glacier_transition_boto3():
    s3 = boto3.resource("s3", region_name="us-east-1")
    client = boto3.client("s3", region_name="us-east-1")
    s3.create_bucket(Bucket="foobar")

    client.put_bucket_lifecycle_configuration(
        Bucket="foobar",
        LifecycleConfiguration={
            "Rules": [
                {
                    "ID": "myid",
                    "Prefix": "",
                    "Status": "Enabled",
                    "Transitions": [{"Days": 30, "StorageClass": "GLACIER"}],
                }
            ]
        },
    )

    response = client.get_bucket_lifecycle_configuration(Bucket="foobar")
    response.should.have.key("Rules")
    rules = response["Rules"]
    rules.should.have.length_of(1)
    rules[0].should.have.key("ID").equal("myid")
    transition = rules[0]["Transitions"][0]
    transition["Days"].should.equal(30)
    transition["StorageClass"].should.equal("GLACIER")
    transition.shouldnt.have.key("Date")


@mock_s3
def test_lifecycle_multi_boto3():
    s3 = boto3.resource("s3", region_name="us-east-1")
    client = boto3.client("s3", region_name="us-east-1")
    s3.create_bucket(Bucket="foobar")

    date = "2022-10-12T00:00:00.000Z"
    sc = "GLACIER"

    client.put_bucket_lifecycle_configuration(
        Bucket="foobar",
        LifecycleConfiguration={
            "Rules": [
                {"ID": "1", "Prefix": "1/", "Status": "Enabled"},
                {
                    "ID": "2",
                    "Prefix": "2/",
                    "Status": "Enabled",
                    "Expiration": {"Days": 2},
                },
                {
                    "ID": "3",
                    "Prefix": "3/",
                    "Status": "Enabled",
                    "Expiration": {"Date": date},
                },
                {
                    "ID": "4",
                    "Prefix": "4/",
                    "Status": "Enabled",
                    "Transitions": [{"Days": 4, "StorageClass": "GLACIER"}],
                },
                {
                    "ID": "5",
                    "Prefix": "5/",
                    "Status": "Enabled",
                    "Transitions": [{"Date": date, "StorageClass": "GLACIER"}],
                },
            ]
        },
    )

    # read the lifecycle back
    rules = client.get_bucket_lifecycle_configuration(Bucket="foobar")["Rules"]

    for rule in rules:
        if rule["ID"] == "1":
            rule["Prefix"].should.equal("1/")
            rule.shouldnt.have.key("Expiration")
        elif rule["ID"] == "2":
            rule["Prefix"].should.equal("2/")
            rule["Expiration"]["Days"].should.equal(2)
        elif rule["ID"] == "3":
            rule["Prefix"].should.equal("3/")
            rule["Expiration"]["Date"].should.be.a(datetime)
            rule["Expiration"]["Date"].strftime("%Y-%m-%dT%H:%M:%S.000Z").should.equal(
                date
            )
        elif rule["ID"] == "4":
            rule["Prefix"].should.equal("4/")
            rule["Transitions"][0]["Days"].should.equal(4)
            rule["Transitions"][0]["StorageClass"].should.equal(sc)
        elif rule["ID"] == "5":
            rule["Prefix"].should.equal("5/")
            rule["Transitions"][0]["Date"].should.be.a(datetime)
            rule["Transitions"][0]["Date"].strftime(
                "%Y-%m-%dT%H:%M:%S.000Z"
            ).should.equal(date)
            rule["Transitions"][0]["StorageClass"].should.equal(sc)
        else:
            assert False, "Invalid rule id"


@mock_s3
def test_lifecycle_delete_boto3():
    s3 = boto3.resource("s3", region_name="us-east-1")
    client = boto3.client("s3", region_name="us-east-1")
    s3.create_bucket(Bucket="foobar")

    client.put_bucket_lifecycle_configuration(
        Bucket="foobar",
        LifecycleConfiguration={
            "Rules": [{"ID": "myid", "Prefix": "", "Status": "Enabled"}]
        },
    )

    client.delete_bucket_lifecycle(Bucket="foobar")

    with pytest.raises(ClientError) as ex:
        client.get_bucket_lifecycle_configuration(Bucket="foobar")
    ex.value.response["Error"]["Code"].should.equal("NoSuchLifecycleConfiguration")
    ex.value.response["Error"]["Message"].should.equal(
        "The lifecycle configuration does not exist"
    )
