import datetime

import boto3
import pytest
from botocore.exceptions import ClientError

from moto import mock_ec2, mock_kms, mock_rds
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID

DEFAULT_REGION = "us-west-2"


@mock_rds
def test_create_database():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        LicenseModel="license-included",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
        VpcSecurityGroupIds=["sg-123456"],
        EnableCloudwatchLogsExports=["audit", "error"],
    )
    db_instance = database["DBInstance"]
    assert db_instance["AllocatedStorage"] == 10
    assert db_instance["DBInstanceClass"] == "db.m1.small"
    assert db_instance["LicenseModel"] == "license-included"
    assert db_instance["MasterUsername"] == "root"
    assert db_instance["DBSecurityGroups"][0]["DBSecurityGroupName"] == "my_sg"
    assert db_instance["DBInstanceArn"] == (
        f"arn:aws:rds:us-west-2:{ACCOUNT_ID}:db:db-master-1"
    )
    assert db_instance["DBInstanceStatus"] == "available"
    assert db_instance["DBName"] == "staging-postgres"
    assert db_instance["DBInstanceIdentifier"] == "db-master-1"
    assert db_instance["IAMDatabaseAuthenticationEnabled"] is False
    assert "db-" in db_instance["DbiResourceId"]
    assert db_instance["CopyTagsToSnapshot"] is False
    assert isinstance(db_instance["InstanceCreateTime"], datetime.datetime)
    assert db_instance["VpcSecurityGroups"][0]["VpcSecurityGroupId"] == "sg-123456"
    assert db_instance["DeletionProtection"] is False
    assert db_instance["EnabledCloudwatchLogsExports"] == ["audit", "error"]
    assert db_instance["Endpoint"]["Port"] == 1234
    assert db_instance["DbInstancePort"] == 1234


@mock_rds
def test_database_with_deletion_protection_cannot_be_deleted():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        DeletionProtection=True,
    )
    db_instance = database["DBInstance"]
    assert db_instance["DBInstanceClass"] == "db.m1.small"
    assert db_instance["DeletionProtection"] is True


@mock_rds
def test_create_database_no_allocated_storage():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
    )
    db_instance = database["DBInstance"]
    assert db_instance["Engine"] == "postgres"
    assert db_instance["StorageType"] == "gp2"
    assert db_instance["AllocatedStorage"] == 20
    assert db_instance["PreferredMaintenanceWindow"] == "wed:06:38-wed:07:08"


@mock_rds
def test_create_database_invalid_preferred_maintenance_window_more_24_hours():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError) as ex:
        conn.create_db_instance(
            DBInstanceIdentifier="db-master-1",
            Engine="postgres",
            DBName="staging-postgres",
            DBInstanceClass="db.m1.small",
            PreferredMaintenanceWindow="mon:16:00-tue:17:00",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert err["Message"] == "Maintenance window must be less than 24 hours."


@mock_rds
def test_create_database_invalid_preferred_maintenance_window_less_30_mins():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError) as ex:
        conn.create_db_instance(
            DBInstanceIdentifier="db-master-1",
            Engine="postgres",
            DBName="staging-postgres",
            DBInstanceClass="db.m1.small",
            PreferredMaintenanceWindow="mon:16:00-mon:16:05",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert err["Message"] == "The maintenance window must be at least 30 minutes."


@mock_rds
def test_create_database_invalid_preferred_maintenance_window_value():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError) as ex:
        conn.create_db_instance(
            DBInstanceIdentifier="db-master-1",
            Engine="postgres",
            DBName="staging-postgres",
            DBInstanceClass="db.m1.small",
            PreferredMaintenanceWindow="sim:16:00-mon:16:30",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert "Invalid day:hour:minute" in err["Message"]


@mock_rds
def test_create_database_invalid_preferred_maintenance_window_format():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError) as ex:
        conn.create_db_instance(
            DBInstanceIdentifier="db-master-1",
            Engine="postgres",
            DBName="staging-postgres",
            DBInstanceClass="db.m1.small",
            PreferredMaintenanceWindow="mon:16tue:17:00",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert (
        "Should be specified as a range ddd:hh24:mi-ddd:hh24:mi "
        "(24H Clock UTC). Example: Sun:23:45-Mon:00:15"
    ) in err["Message"]


@mock_rds
def test_create_database_preferred_backup_window_overlap_no_spill():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError) as ex:
        conn.create_db_instance(
            DBInstanceIdentifier="db-master-1",
            Engine="postgres",
            DBName="staging-postgres",
            DBInstanceClass="db.m1.small",
            PreferredMaintenanceWindow="wed:18:00-wed:22:00",
            PreferredBackupWindow="20:00-20:30",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert (
        "The backup window and maintenance window must not overlap." in err["Message"]
    )


@mock_rds
def test_create_database_preferred_backup_window_overlap_maintenance_window_spill():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError) as ex:
        conn.create_db_instance(
            DBInstanceIdentifier="db-master-1",
            Engine="postgres",
            DBName="staging-postgres",
            DBInstanceClass="db.m1.small",
            PreferredMaintenanceWindow="wed:18:00-thu:01:00",
            PreferredBackupWindow="00:00-00:30",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert (
        "The backup window and maintenance window must not overlap." in err["Message"]
    )


@mock_rds
def test_create_database_preferred_backup_window_overlap_backup_window_spill():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError) as ex:
        conn.create_db_instance(
            DBInstanceIdentifier="db-master-1",
            Engine="postgres",
            DBName="staging-postgres",
            DBInstanceClass="db.m1.small",
            PreferredMaintenanceWindow="thu:00:00-thu:14:00",
            PreferredBackupWindow="23:50-00:20",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert (
        "The backup window and maintenance window must not overlap." in err["Message"]
    )


@mock_rds
def test_create_database_preferred_backup_window_overlap_both_spill():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError) as ex:
        conn.create_db_instance(
            DBInstanceIdentifier="db-master-1",
            Engine="postgres",
            DBName="staging-postgres",
            DBInstanceClass="db.m1.small",
            PreferredMaintenanceWindow="wed:18:00-thu:01:00",
            PreferredBackupWindow="23:50-00:20",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert (
        "The backup window and maintenance window must not overlap." in err["Message"]
    )


@mock_rds
def test_create_database_valid_preferred_maintenance_window_format():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        PreferredMaintenanceWindow="sun:16:00-sun:16:30",
    )
    db_instance = database["DBInstance"]
    assert db_instance["DBInstanceClass"] == "db.m1.small"
    assert db_instance["PreferredMaintenanceWindow"] == "sun:16:00-sun:16:30"


@mock_rds
def test_create_database_valid_preferred_maintenance_window_uppercase_format():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        PreferredMaintenanceWindow="MON:16:00-TUE:01:30",
    )
    db_instance = database["DBInstance"]
    assert db_instance["DBInstanceClass"] == "db.m1.small"
    assert db_instance["PreferredMaintenanceWindow"] == "mon:16:00-tue:01:30"


@mock_rds
def test_create_database_non_existing_option_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.create_db_instance(
            DBInstanceIdentifier="db-master-1",
            AllocatedStorage=10,
            Engine="postgres",
            DBName="staging-postgres",
            DBInstanceClass="db.m1.small",
            OptionGroupName="non-existing",
        )


@mock_rds
def test_create_database_with_option_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_option_group(
        OptionGroupName="my-og",
        EngineName="mysql",
        MajorEngineVersion="5.6",
        OptionGroupDescription="test option group",
    )
    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        OptionGroupName="my-og",
    )
    db_instance = database["DBInstance"]
    assert db_instance["AllocatedStorage"] == 10
    assert db_instance["DBInstanceClass"] == "db.m1.small"
    assert db_instance["DBName"] == "staging-postgres"
    assert db_instance["OptionGroupMemberships"][0]["OptionGroupName"] == "my-og"


@mock_rds
def test_stop_database():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        LicenseModel="license-included",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    mydb = conn.describe_db_instances(
        DBInstanceIdentifier=database["DBInstance"]["DBInstanceIdentifier"]
    )["DBInstances"][0]
    assert mydb["DBInstanceStatus"] == "available"
    # test stopping database should shutdown
    response = conn.stop_db_instance(DBInstanceIdentifier=mydb["DBInstanceIdentifier"])
    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
    assert response["DBInstance"]["DBInstanceStatus"] == "stopped"
    # test rdsclient error when trying to stop an already stopped database
    with pytest.raises(ClientError):
        conn.stop_db_instance(DBInstanceIdentifier=mydb["DBInstanceIdentifier"])
    # test stopping a stopped database with snapshot should error and no
    # snapshot should exist for that call
    with pytest.raises(ClientError):
        conn.stop_db_instance(
            DBInstanceIdentifier=mydb["DBInstanceIdentifier"],
            DBSnapshotIdentifier="rocky4570-rds-snap",
        )
    response = conn.describe_db_snapshots()
    assert response["DBSnapshots"] == []


@mock_rds
def test_start_database():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        LicenseModel="license-included",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    mydb = conn.describe_db_instances(
        DBInstanceIdentifier=database["DBInstance"]["DBInstanceIdentifier"]
    )["DBInstances"][0]
    assert mydb["DBInstanceStatus"] == "available"
    # test starting an already started database should error
    with pytest.raises(ClientError):
        conn.start_db_instance(DBInstanceIdentifier=mydb["DBInstanceIdentifier"])
    # stop and test start - should go from stopped to available, create
    # snapshot and check snapshot
    response = conn.stop_db_instance(
        DBInstanceIdentifier=mydb["DBInstanceIdentifier"],
        DBSnapshotIdentifier="rocky4570-rds-snap",
    )
    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
    assert response["DBInstance"]["DBInstanceStatus"] == "stopped"
    response = conn.describe_db_snapshots()
    assert response["DBSnapshots"][0]["DBSnapshotIdentifier"] == "rocky4570-rds-snap"
    response = conn.start_db_instance(DBInstanceIdentifier=mydb["DBInstanceIdentifier"])
    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
    assert response["DBInstance"]["DBInstanceStatus"] == "available"
    # starting database should not remove snapshot
    response = conn.describe_db_snapshots()
    assert response["DBSnapshots"][0]["DBSnapshotIdentifier"] == "rocky4570-rds-snap"
    # test stopping database, create snapshot with existing snapshot already
    # created should throw error
    with pytest.raises(ClientError):
        conn.stop_db_instance(
            DBInstanceIdentifier=mydb["DBInstanceIdentifier"],
            DBSnapshotIdentifier="rocky4570-rds-snap",
        )
    # test stopping database not invoking snapshot should succeed.
    response = conn.stop_db_instance(DBInstanceIdentifier=mydb["DBInstanceIdentifier"])
    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
    assert response["DBInstance"]["DBInstanceStatus"] == "stopped"


@mock_rds
def test_fail_to_stop_multi_az_and_sqlserver():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="sqlserver-ee",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        LicenseModel="license-included",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
        MultiAZ=True,
    )

    mydb = conn.describe_db_instances(
        DBInstanceIdentifier=database["DBInstance"]["DBInstanceIdentifier"]
    )["DBInstances"][0]
    assert mydb["DBInstanceStatus"] == "available"
    # multi-az databases arent allowed to be shutdown at this time.
    with pytest.raises(ClientError):
        conn.stop_db_instance(DBInstanceIdentifier=mydb["DBInstanceIdentifier"])
    # multi-az databases arent allowed to be started up at this time.
    with pytest.raises(ClientError):
        conn.start_db_instance(DBInstanceIdentifier=mydb["DBInstanceIdentifier"])


@mock_rds
def test_stop_multi_az_postgres():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        LicenseModel="license-included",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
        MultiAZ=True,
    )

    mydb = conn.describe_db_instances(
        DBInstanceIdentifier=database["DBInstance"]["DBInstanceIdentifier"]
    )["DBInstances"][0]
    assert mydb["DBInstanceStatus"] == "available"

    response = conn.stop_db_instance(DBInstanceIdentifier=mydb["DBInstanceIdentifier"])
    assert response["ResponseMetadata"]["HTTPStatusCode"] == 200
    assert response["DBInstance"]["DBInstanceStatus"] == "stopped"


@mock_rds
def test_fail_to_stop_readreplica():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        LicenseModel="license-included",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )

    replica = conn.create_db_instance_read_replica(
        DBInstanceIdentifier="db-replica-1",
        SourceDBInstanceIdentifier="db-master-1",
        DBInstanceClass="db.m1.small",
    )

    mydb = conn.describe_db_instances(
        DBInstanceIdentifier=replica["DBInstance"]["DBInstanceIdentifier"]
    )["DBInstances"][0]
    assert mydb["DBInstanceStatus"] == "available"
    # read-replicas are not allowed to be stopped at this time.
    with pytest.raises(ClientError):
        conn.stop_db_instance(DBInstanceIdentifier=mydb["DBInstanceIdentifier"])
    # read-replicas are not allowed to be started at this time.
    with pytest.raises(ClientError):
        conn.start_db_instance(DBInstanceIdentifier=mydb["DBInstanceIdentifier"])


@mock_rds
def test_get_databases():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)

    instances = conn.describe_db_instances()
    assert len(list(instances["DBInstances"])) == 0

    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-2",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
        DeletionProtection=True,
    )
    instances = conn.describe_db_instances()
    assert len(list(instances["DBInstances"])) == 2

    instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
    assert len(list(instances["DBInstances"])) == 1
    assert instances["DBInstances"][0]["DBInstanceIdentifier"] == "db-master-1"
    assert instances["DBInstances"][0]["DeletionProtection"] is False
    assert instances["DBInstances"][0]["DBInstanceArn"] == (
        f"arn:aws:rds:us-west-2:{ACCOUNT_ID}:db:db-master-1"
    )

    instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-2")
    assert instances["DBInstances"][0]["DeletionProtection"] is True
    assert instances["DBInstances"][0]["Endpoint"]["Port"] == 1234
    assert instances["DBInstances"][0]["DbInstancePort"] == 1234


@mock_rds
def test_get_databases_paginated():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)

    for i in range(51):
        conn.create_db_instance(
            AllocatedStorage=5,
            Port=5432,
            DBInstanceIdentifier=f"rds{i}",
            DBInstanceClass="db.t1.micro",
            Engine="postgres",
        )

    resp = conn.describe_db_instances()
    assert len(resp["DBInstances"]) == 50
    assert resp["Marker"] == resp["DBInstances"][-1]["DBInstanceIdentifier"]

    resp2 = conn.describe_db_instances(Marker=resp["Marker"])
    assert len(resp2["DBInstances"]) == 1

    resp3 = conn.describe_db_instances(MaxRecords=100)
    assert len(resp3["DBInstances"]) == 51


@mock_rds
def test_describe_non_existent_database():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.describe_db_instances(DBInstanceIdentifier="not-a-db")


@mock_rds
def test_modify_db_instance():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
    assert instances["DBInstances"][0]["AllocatedStorage"] == 10
    conn.modify_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=20,
        ApplyImmediately=True,
        VpcSecurityGroupIds=["sg-123456"],
    )
    instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
    assert instances["DBInstances"][0]["AllocatedStorage"] == 20
    assert instances["DBInstances"][0]["PreferredMaintenanceWindow"] == (
        "wed:06:38-wed:07:08"
    )
    assert (
        instances["DBInstances"][0]["VpcSecurityGroups"][0]["VpcSecurityGroupId"]
        == "sg-123456"
    )


@mock_rds
def test_modify_db_instance_not_existent_db_parameter_group_name():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
    assert instances["DBInstances"][0]["AllocatedStorage"] == 10
    with pytest.raises(ClientError):
        conn.modify_db_instance(
            DBInstanceIdentifier="db-master-1",
            DBParameterGroupName="test-sqlserver-se-2017",
        )


@mock_rds
def test_modify_db_instance_valid_preferred_maintenance_window():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
    conn.modify_db_instance(
        DBInstanceIdentifier="db-master-1",
        PreferredMaintenanceWindow="sun:16:00-sun:16:30",
    )
    instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
    assert instances["DBInstances"][0]["PreferredMaintenanceWindow"] == (
        "sun:16:00-sun:16:30"
    )


@mock_rds
def test_modify_db_instance_valid_preferred_maintenance_window_uppercase():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
    conn.modify_db_instance(
        DBInstanceIdentifier="db-master-1",
        PreferredMaintenanceWindow="SUN:16:00-SUN:16:30",
    )
    instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
    assert instances["DBInstances"][0]["PreferredMaintenanceWindow"] == (
        "sun:16:00-sun:16:30"
    )


@mock_rds
def test_modify_db_instance_invalid_preferred_maintenance_window_more_than_24_hours():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    with pytest.raises(ClientError) as ex:
        conn.modify_db_instance(
            DBInstanceIdentifier="db-master-1",
            PreferredMaintenanceWindow="sun:16:00-sat:16:30",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert err["Message"] == "Maintenance window must be less than 24 hours."


@mock_rds
def test_modify_db_instance_invalid_preferred_maintenance_window_less_than_30_mins():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    with pytest.raises(ClientError) as ex:
        conn.modify_db_instance(
            DBInstanceIdentifier="db-master-1",
            PreferredMaintenanceWindow="sun:16:00-sun:16:10",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert err["Message"] == "The maintenance window must be at least 30 minutes."


@mock_rds
def test_modify_db_instance_invalid_preferred_maintenance_window_value():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    with pytest.raises(ClientError) as ex:
        conn.modify_db_instance(
            DBInstanceIdentifier="db-master-1",
            PreferredMaintenanceWindow="sin:16:00-sun:16:30",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert "Invalid day:hour:minute value" in err["Message"]


@mock_rds
def test_modify_db_instance_invalid_preferred_maintenance_window_format():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    with pytest.raises(ClientError) as ex:
        conn.modify_db_instance(
            DBInstanceIdentifier="db-master-1",
            PreferredMaintenanceWindow="sun:16:00sun:16:30",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert (
        "Should be specified as a range ddd:hh24:mi-ddd:hh24:mi "
        "(24H Clock UTC). Example: Sun:23:45-Mon:00:15"
    ) in err["Message"]


@mock_rds
def test_modify_db_instance_maintenance_backup_window_no_spill():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    with pytest.raises(ClientError) as ex:
        conn.modify_db_instance(
            DBInstanceIdentifier="db-master-1",
            PreferredMaintenanceWindow="sun:16:00-sun:16:30",
            PreferredBackupWindow="15:50-16:20",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert (
        err["Message"] == "The backup window and maintenance window must not overlap."
    )


@mock_rds
def test_modify_db_instance_maintenance_backup_window_maintenance_spill():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    with pytest.raises(ClientError) as ex:
        conn.modify_db_instance(
            DBInstanceIdentifier="db-master-1",
            PreferredMaintenanceWindow="sun:16:00-mon:15:00",
            PreferredBackupWindow="00:00-00:30",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert err["Message"] == (
        "The backup window and maintenance window must not overlap."
    )


@mock_rds
def test_modify_db_instance_maintenance_backup_window_backup_spill():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    with pytest.raises(ClientError) as ex:
        conn.modify_db_instance(
            DBInstanceIdentifier="db-master-1",
            PreferredMaintenanceWindow="mon:00:00-mon:15:00",
            PreferredBackupWindow="23:50-00:20",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert err["Message"] == (
        "The backup window and maintenance window must not overlap."
    )


@mock_rds
def test_modify_db_instance_maintenance_backup_window_both_spill():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    with pytest.raises(ClientError) as ex:
        conn.modify_db_instance(
            DBInstanceIdentifier="db-master-1",
            PreferredMaintenanceWindow="sun:16:00-mon:15:00",
            PreferredBackupWindow="23:20-00:20",
        )
    err = ex.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert err["Message"] == (
        "The backup window and maintenance window must not overlap."
    )


@mock_rds
def test_rename_db_instance():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
    assert len(list(instances["DBInstances"])) == 1
    with pytest.raises(ClientError):
        conn.describe_db_instances(DBInstanceIdentifier="db-master-2")
    conn.modify_db_instance(
        DBInstanceIdentifier="db-master-1",
        NewDBInstanceIdentifier="db-master-2",
        ApplyImmediately=True,
    )
    with pytest.raises(ClientError):
        conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
    instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-2")
    assert len(list(instances["DBInstances"])) == 1


@mock_rds
def test_modify_non_existent_database():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.modify_db_instance(
            DBInstanceIdentifier="not-a-db", AllocatedStorage=20, ApplyImmediately=True
        )


@mock_rds
def test_reboot_db_instance():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    database = conn.reboot_db_instance(DBInstanceIdentifier="db-master-1")
    assert database["DBInstance"]["DBInstanceIdentifier"] == "db-master-1"


@mock_rds
def test_reboot_non_existent_database():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.reboot_db_instance(DBInstanceIdentifier="not-a-db")


@mock_rds
def test_delete_database():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    instances = conn.describe_db_instances()
    assert len(list(instances["DBInstances"])) == 0
    conn.create_db_instance(
        DBInstanceIdentifier="db-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    instances = conn.describe_db_instances()
    assert len(list(instances["DBInstances"])) == 1

    conn.delete_db_instance(
        DBInstanceIdentifier="db-1",
        FinalDBSnapshotIdentifier="primary-1-snapshot",
    )

    instances = conn.describe_db_instances()
    assert len(list(instances["DBInstances"])) == 0

    # Saved the snapshot
    snapshot = conn.describe_db_snapshots(DBInstanceIdentifier="db-1")["DBSnapshots"][0]
    assert snapshot["Engine"] == "postgres"
    assert snapshot["SnapshotType"] == "automated"


@mock_rds
def test_create_db_snapshots():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.create_db_snapshot(
            DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
        )

    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )

    snapshot = conn.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="g-1"
    ).get("DBSnapshot")

    assert snapshot.get("Engine") == "postgres"
    assert snapshot.get("DBInstanceIdentifier") == "db-primary-1"
    assert snapshot.get("DBSnapshotIdentifier") == "g-1"
    result = conn.list_tags_for_resource(ResourceName=snapshot["DBSnapshotArn"])
    assert result["TagList"] == []


@mock_rds
def test_create_db_snapshots_copy_tags():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.create_db_snapshot(
            DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
        )

    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
        CopyTagsToSnapshot=True,
        Tags=[{"Key": "foo", "Value": "bar"}, {"Key": "foo1", "Value": "bar1"}],
    )

    snapshot = conn.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="g-1"
    ).get("DBSnapshot")

    assert snapshot.get("Engine") == "postgres"
    assert snapshot.get("DBInstanceIdentifier") == "db-primary-1"
    assert snapshot.get("DBSnapshotIdentifier") == "g-1"
    result = conn.list_tags_for_resource(ResourceName=snapshot["DBSnapshotArn"])
    assert result["TagList"] == [
        {"Value": "bar", "Key": "foo"},
        {"Value": "bar1", "Key": "foo1"},
    ]


@mock_rds
def test_create_db_snapshots_with_tags():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )

    conn.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1",
        DBSnapshotIdentifier="g-1",
        Tags=[{"Key": "foo", "Value": "bar"}, {"Key": "foo1", "Value": "bar1"}],
    )

    snapshots = conn.describe_db_snapshots(DBInstanceIdentifier="db-primary-1").get(
        "DBSnapshots"
    )
    assert snapshots[0].get("DBSnapshotIdentifier") == "g-1"
    assert snapshots[0].get("TagList") == [
        {"Value": "bar", "Key": "foo"},
        {"Value": "bar1", "Key": "foo1"},
    ]


@mock_rds
def test_copy_db_snapshots():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)

    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )

    conn.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
    ).get("DBSnapshot")

    target_snapshot = conn.copy_db_snapshot(
        SourceDBSnapshotIdentifier="snapshot-1", TargetDBSnapshotIdentifier="snapshot-2"
    ).get("DBSnapshot")

    assert target_snapshot.get("Engine") == "postgres"
    assert target_snapshot.get("DBInstanceIdentifier") == "db-primary-1"
    assert target_snapshot.get("DBSnapshotIdentifier") == "snapshot-2"
    result = conn.list_tags_for_resource(ResourceName=target_snapshot["DBSnapshotArn"])
    assert result["TagList"] == []


@mock_rds
def test_describe_db_snapshots():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )

    created = conn.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
    ).get("DBSnapshot")

    assert created["Engine"] == "postgres"
    assert created["SnapshotType"] == "manual"

    by_database_id = conn.describe_db_snapshots(
        DBInstanceIdentifier="db-primary-1"
    ).get("DBSnapshots")
    by_snapshot_id = conn.describe_db_snapshots(DBSnapshotIdentifier="snapshot-1").get(
        "DBSnapshots"
    )
    assert by_snapshot_id == by_database_id

    snapshot = by_snapshot_id[0]
    assert snapshot == created

    conn.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-2"
    )
    snapshots = conn.describe_db_snapshots(DBInstanceIdentifier="db-primary-1").get(
        "DBSnapshots"
    )
    assert len(snapshots) == 2


@mock_rds
def test_promote_read_replica():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )

    conn.create_db_instance_read_replica(
        DBInstanceIdentifier="db-replica-1",
        SourceDBInstanceIdentifier="db-primary-1",
        DBInstanceClass="db.m1.small",
    )
    conn.promote_read_replica(DBInstanceIdentifier="db-replica-1")

    replicas = conn.describe_db_instances(DBInstanceIdentifier="db-primary-1").get(
        "ReadReplicaDBInstanceIdentifiers"
    )
    assert replicas is None


@mock_rds
def test_delete_db_snapshot():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    conn.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
    )

    _ = conn.describe_db_snapshots(DBSnapshotIdentifier="snapshot-1").get(
        "DBSnapshots"
    )[0]
    conn.delete_db_snapshot(DBSnapshotIdentifier="snapshot-1")
    with pytest.raises(ClientError):
        conn.describe_db_snapshots(DBSnapshotIdentifier="snapshot-1")


@mock_rds
def test_restore_db_instance_from_db_snapshot():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        DBSecurityGroups=["my_sg"],
    )
    assert len(conn.describe_db_instances()["DBInstances"]) == 1

    conn.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
    )

    # restore
    new_instance = conn.restore_db_instance_from_db_snapshot(
        DBInstanceIdentifier="db-restore-1", DBSnapshotIdentifier="snapshot-1"
    )["DBInstance"]
    assert new_instance["DBInstanceIdentifier"] == "db-restore-1"
    assert new_instance["DBInstanceClass"] == "db.m1.small"
    assert new_instance["StorageType"] == "gp2"
    assert new_instance["Engine"] == "postgres"
    assert new_instance["DBName"] == "staging-postgres"
    assert new_instance["DBParameterGroups"][0]["DBParameterGroupName"] == (
        "default.postgres9.3"
    )
    assert new_instance["DBSecurityGroups"] == [
        {"DBSecurityGroupName": "my_sg", "Status": "active"}
    ]
    assert new_instance["Endpoint"]["Port"] == 5432

    # Verify it exists
    assert len(conn.describe_db_instances()["DBInstances"]) == 2
    assert (
        len(
            conn.describe_db_instances(DBInstanceIdentifier="db-restore-1")[
                "DBInstances"
            ]
        )
        == 1
    )


@mock_rds
def test_restore_db_instance_to_point_in_time():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        DBSecurityGroups=["my_sg"],
    )
    assert len(conn.describe_db_instances()["DBInstances"]) == 1

    # restore
    new_instance = conn.restore_db_instance_to_point_in_time(
        SourceDBInstanceIdentifier="db-primary-1",
        TargetDBInstanceIdentifier="db-restore-1",
    )["DBInstance"]
    assert new_instance["DBInstanceIdentifier"] == "db-restore-1"
    assert new_instance["DBInstanceClass"] == "db.m1.small"
    assert new_instance["StorageType"] == "gp2"
    assert new_instance["Engine"] == "postgres"
    assert new_instance["DBName"] == "staging-postgres"
    assert new_instance["DBParameterGroups"][0]["DBParameterGroupName"] == (
        "default.postgres9.3"
    )
    assert new_instance["DBSecurityGroups"] == [
        {"DBSecurityGroupName": "my_sg", "Status": "active"}
    ]
    assert new_instance["Endpoint"]["Port"] == 5432

    # Verify it exists
    assert len(conn.describe_db_instances()["DBInstances"]) == 2
    assert (
        len(
            conn.describe_db_instances(DBInstanceIdentifier="db-restore-1")[
                "DBInstances"
            ]
        )
        == 1
    )


@mock_rds
def test_restore_db_instance_from_db_snapshot_and_override_params():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    assert len(conn.describe_db_instances()["DBInstances"]) == 1
    conn.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
    )

    # restore with some updated attributes
    new_instance = conn.restore_db_instance_from_db_snapshot(
        DBInstanceIdentifier="db-restore-1",
        DBSnapshotIdentifier="snapshot-1",
        Port=10000,
        VpcSecurityGroupIds=["new_vpc"],
    )["DBInstance"]
    assert new_instance["DBInstanceIdentifier"] == "db-restore-1"
    assert new_instance["DBParameterGroups"][0]["DBParameterGroupName"] == (
        "default.postgres9.3"
    )
    assert new_instance["DBSecurityGroups"] == [
        {"DBSecurityGroupName": "my_sg", "Status": "active"}
    ]
    assert new_instance["VpcSecurityGroups"] == [
        {"VpcSecurityGroupId": "new_vpc", "Status": "active"}
    ]
    assert new_instance["Endpoint"]["Port"] == 10000


@mock_rds
def test_create_option_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    option_group = conn.create_option_group(
        OptionGroupName="test",
        EngineName="mysql",
        MajorEngineVersion="5.6",
        OptionGroupDescription="test option group",
    )
    assert option_group["OptionGroup"]["OptionGroupName"] == "test"
    assert option_group["OptionGroup"]["EngineName"] == "mysql"
    assert option_group["OptionGroup"]["OptionGroupDescription"] == (
        "test option group"
    )
    assert option_group["OptionGroup"]["MajorEngineVersion"] == "5.6"
    assert option_group["OptionGroup"]["OptionGroupArn"] == (
        f"arn:aws:rds:us-west-2:{ACCOUNT_ID}:og:test"
    )


@mock_rds
def test_create_option_group_bad_engine_name():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.create_option_group(
            OptionGroupName="test",
            EngineName="invalid_engine",
            MajorEngineVersion="5.6",
            OptionGroupDescription="test invalid engine",
        )


@mock_rds
def test_create_option_group_bad_engine_major_version():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.create_option_group(
            OptionGroupName="test",
            EngineName="mysql",
            MajorEngineVersion="6.6.6",
            OptionGroupDescription="test invalid engine version",
        )


@mock_rds
def test_create_option_group_empty_description():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.create_option_group(
            OptionGroupName="test",
            EngineName="mysql",
            MajorEngineVersion="5.6",
            OptionGroupDescription="",
        )


@mock_rds
def test_create_option_group_duplicate():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_option_group(
        OptionGroupName="test",
        EngineName="mysql",
        MajorEngineVersion="5.6",
        OptionGroupDescription="test option group",
    )
    with pytest.raises(ClientError):
        conn.create_option_group(
            OptionGroupName="test",
            EngineName="mysql",
            MajorEngineVersion="5.6",
            OptionGroupDescription="test option group",
        )


@mock_rds
def test_describe_option_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_option_group(
        OptionGroupName="test",
        EngineName="mysql",
        MajorEngineVersion="5.6",
        OptionGroupDescription="test option group",
    )
    option_groups = conn.describe_option_groups(OptionGroupName="test")
    assert option_groups["OptionGroupsList"][0]["OptionGroupName"] == "test"


@mock_rds
def test_describe_non_existent_option_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.describe_option_groups(OptionGroupName="not-a-option-group")


@mock_rds
def test_delete_option_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_option_group(
        OptionGroupName="test",
        EngineName="mysql",
        MajorEngineVersion="5.6",
        OptionGroupDescription="test option group",
    )
    option_groups = conn.describe_option_groups(OptionGroupName="test")
    assert option_groups["OptionGroupsList"][0]["OptionGroupName"] == "test"
    conn.delete_option_group(OptionGroupName="test")
    with pytest.raises(ClientError):
        conn.describe_option_groups(OptionGroupName="test")


@mock_rds
def test_delete_non_existent_option_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.delete_option_group(OptionGroupName="non-existent")


@mock_rds
def test_describe_option_group_options():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    option_group_options = conn.describe_option_group_options(EngineName="sqlserver-ee")
    assert len(option_group_options["OptionGroupOptions"]) == 4
    option_group_options = conn.describe_option_group_options(
        EngineName="sqlserver-ee", MajorEngineVersion="11.00"
    )
    assert len(option_group_options["OptionGroupOptions"]) == 2
    option_group_options = conn.describe_option_group_options(
        EngineName="mysql", MajorEngineVersion="5.6"
    )
    assert len(option_group_options["OptionGroupOptions"]) == 1
    with pytest.raises(ClientError):
        conn.describe_option_group_options(EngineName="non-existent")
    with pytest.raises(ClientError):
        conn.describe_option_group_options(
            EngineName="mysql", MajorEngineVersion="non-existent"
        )


@mock_rds
def test_modify_option_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_option_group(
        OptionGroupName="test",
        EngineName="mysql",
        MajorEngineVersion="5.6",
        OptionGroupDescription="test option group",
    )
    # TODO: create option and validate before deleting.
    # if Someone can tell me how the hell to use this function
    # to add options to an option_group, I can finish coding this.
    result = conn.modify_option_group(
        OptionGroupName="test",
        OptionsToInclude=[],
        OptionsToRemove=["MEMCACHED"],
        ApplyImmediately=True,
    )
    assert result["OptionGroup"]["EngineName"] == "mysql"
    assert result["OptionGroup"]["Options"] == []
    assert result["OptionGroup"]["OptionGroupName"] == "test"


@mock_rds
def test_modify_option_group_no_options():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_option_group(
        OptionGroupName="test",
        EngineName="mysql",
        MajorEngineVersion="5.6",
        OptionGroupDescription="test option group",
    )
    with pytest.raises(ClientError):
        conn.modify_option_group(OptionGroupName="test")


@mock_rds
def test_modify_non_existent_option_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError) as client_err:
        conn.modify_option_group(
            OptionGroupName="non-existent",
            OptionsToInclude=[{"OptionName": "test-option"}],
        )
    assert client_err.value.response["Error"]["Message"] == (
        "Specified OptionGroupName: non-existent not found."
    )


@mock_rds
def test_delete_database_with_protection():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBInstanceClass="db.m1.small",
        DeletionProtection=True,
    )

    with pytest.raises(ClientError) as exc:
        conn.delete_db_instance(DBInstanceIdentifier="db-primary-1")
    err = exc.value.response["Error"]
    assert err["Message"] == "Can't delete Instance with protection enabled"


@mock_rds
def test_delete_non_existent_database():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError) as ex:
        conn.delete_db_instance(DBInstanceIdentifier="non-existent")
    assert ex.value.response["Error"]["Code"] == "DBInstanceNotFound"
    assert ex.value.response["Error"]["Message"] == "DBInstance non-existent not found."


@mock_rds
def test_list_tags_invalid_arn():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.list_tags_for_resource(ResourceName="arn:aws:rds:bad-arn")


@mock_rds
def test_list_tags_db():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:db:foo"
    )
    assert result["TagList"] == []
    test_instance = conn.create_db_instance(
        DBInstanceIdentifier="db-with-tags",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
        Tags=[{"Key": "foo", "Value": "bar"}, {"Key": "foo1", "Value": "bar1"}],
    )
    result = conn.list_tags_for_resource(
        ResourceName=test_instance["DBInstance"]["DBInstanceArn"]
    )
    assert result["TagList"] == [
        {"Value": "bar", "Key": "foo"},
        {"Value": "bar1", "Key": "foo1"},
    ]


@mock_rds
def test_add_tags_db():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-without-tags",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
        Tags=[{"Key": "foo", "Value": "bar"}, {"Key": "foo1", "Value": "bar1"}],
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:db:db-without-tags"
    )
    assert len(list(result["TagList"])) == 2
    conn.add_tags_to_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:db:db-without-tags",
        Tags=[{"Key": "foo", "Value": "fish"}, {"Key": "foo2", "Value": "bar2"}],
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:db:db-without-tags"
    )
    assert len(list(result["TagList"])) == 3


@mock_rds
def test_remove_tags_db():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-with-tags",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
        Tags=[{"Key": "foo", "Value": "bar"}, {"Key": "foo1", "Value": "bar1"}],
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:db:db-with-tags"
    )
    assert len(list(result["TagList"])) == 2
    conn.remove_tags_from_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:db:db-with-tags", TagKeys=["foo"]
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:db:db-with-tags"
    )
    assert len(result["TagList"]) == 1


@mock_rds
def test_list_tags_snapshot():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:snapshot:foo"
    )
    assert result["TagList"] == []
    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    snapshot = conn.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1",
        DBSnapshotIdentifier="snapshot-with-tags",
        Tags=[{"Key": "foo", "Value": "bar"}, {"Key": "foo1", "Value": "bar1"}],
    )
    result = conn.list_tags_for_resource(
        ResourceName=snapshot["DBSnapshot"]["DBSnapshotArn"]
    )
    assert result["TagList"] == [
        {"Value": "bar", "Key": "foo"},
        {"Value": "bar1", "Key": "foo1"},
    ]


@mock_rds
def test_add_tags_snapshot():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    conn.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1",
        DBSnapshotIdentifier="snapshot-without-tags",
        Tags=[{"Key": "foo", "Value": "bar"}, {"Key": "foo1", "Value": "bar1"}],
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:snapshot:snapshot-without-tags"
    )
    assert len(list(result["TagList"])) == 2
    conn.add_tags_to_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:snapshot:snapshot-without-tags",
        Tags=[{"Key": "foo", "Value": "fish"}, {"Key": "foo2", "Value": "bar2"}],
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:snapshot:snapshot-without-tags"
    )
    assert len(list(result["TagList"])) == 3


@mock_rds
def test_remove_tags_snapshot():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )
    conn.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1",
        DBSnapshotIdentifier="snapshot-with-tags",
        Tags=[{"Key": "foo", "Value": "bar"}, {"Key": "foo1", "Value": "bar1"}],
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:snapshot:snapshot-with-tags"
    )
    assert len(list(result["TagList"])) == 2
    conn.remove_tags_from_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:snapshot:snapshot-with-tags",
        TagKeys=["foo"],
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:snapshot:snapshot-with-tags"
    )
    assert len(result["TagList"]) == 1


@mock_rds
def test_add_tags_option_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_option_group(
        OptionGroupName="test",
        EngineName="mysql",
        MajorEngineVersion="5.6",
        OptionGroupDescription="test option group",
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:og:test"
    )
    assert len(list(result["TagList"])) == 0
    conn.add_tags_to_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:og:test",
        Tags=[{"Key": "foo", "Value": "fish"}, {"Key": "foo2", "Value": "bar2"}],
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:og:test"
    )
    assert len(list(result["TagList"])) == 2


@mock_rds
def test_remove_tags_option_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_option_group(
        OptionGroupName="test",
        EngineName="mysql",
        MajorEngineVersion="5.6",
        OptionGroupDescription="test option group",
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:og:test"
    )
    conn.add_tags_to_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:og:test",
        Tags=[{"Key": "foo", "Value": "fish"}, {"Key": "foo2", "Value": "bar2"}],
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:og:test"
    )
    assert len(list(result["TagList"])) == 2
    conn.remove_tags_from_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:og:test", TagKeys=["foo"]
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:og:test"
    )
    assert len(list(result["TagList"])) == 1


@mock_rds
def test_create_database_security_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)

    result = conn.create_db_security_group(
        DBSecurityGroupName="db_sg", DBSecurityGroupDescription="DB Security Group"
    )
    assert result["DBSecurityGroup"]["DBSecurityGroupName"] == "db_sg"
    assert (
        result["DBSecurityGroup"]["DBSecurityGroupDescription"] == "DB Security Group"
    )
    assert result["DBSecurityGroup"]["IPRanges"] == []


@mock_rds
def test_get_security_groups():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)

    result = conn.describe_db_security_groups()
    assert len(result["DBSecurityGroups"]) == 0

    conn.create_db_security_group(
        DBSecurityGroupName="db_sg1", DBSecurityGroupDescription="DB Security Group"
    )
    conn.create_db_security_group(
        DBSecurityGroupName="db_sg2", DBSecurityGroupDescription="DB Security Group"
    )

    result = conn.describe_db_security_groups()
    assert len(result["DBSecurityGroups"]) == 2

    result = conn.describe_db_security_groups(DBSecurityGroupName="db_sg1")
    assert len(result["DBSecurityGroups"]) == 1
    assert result["DBSecurityGroups"][0]["DBSecurityGroupName"] == "db_sg1"


@mock_rds
def test_get_non_existent_security_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.describe_db_security_groups(DBSecurityGroupName="not-a-sg")


@mock_rds
def test_delete_database_security_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_security_group(
        DBSecurityGroupName="db_sg", DBSecurityGroupDescription="DB Security Group"
    )

    result = conn.describe_db_security_groups()
    assert len(result["DBSecurityGroups"]) == 1

    conn.delete_db_security_group(DBSecurityGroupName="db_sg")
    result = conn.describe_db_security_groups()
    assert len(result["DBSecurityGroups"]) == 0


@mock_rds
def test_delete_non_existent_security_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.delete_db_security_group(DBSecurityGroupName="not-a-db")


@mock_rds
def test_security_group_authorize():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    security_group = conn.create_db_security_group(
        DBSecurityGroupName="db_sg", DBSecurityGroupDescription="DB Security Group"
    )
    assert security_group["DBSecurityGroup"]["IPRanges"] == []

    conn.authorize_db_security_group_ingress(
        DBSecurityGroupName="db_sg", CIDRIP="10.3.2.45/32"
    )

    result = conn.describe_db_security_groups(DBSecurityGroupName="db_sg")
    assert len(result["DBSecurityGroups"][0]["IPRanges"]) == 1
    assert result["DBSecurityGroups"][0]["IPRanges"] == [
        {"Status": "authorized", "CIDRIP": "10.3.2.45/32"}
    ]

    conn.authorize_db_security_group_ingress(
        DBSecurityGroupName="db_sg", CIDRIP="10.3.2.46/32"
    )
    result = conn.describe_db_security_groups(DBSecurityGroupName="db_sg")
    assert len(result["DBSecurityGroups"][0]["IPRanges"]) == 2
    assert result["DBSecurityGroups"][0]["IPRanges"] == [
        {"Status": "authorized", "CIDRIP": "10.3.2.45/32"},
        {"Status": "authorized", "CIDRIP": "10.3.2.46/32"},
    ]


@mock_rds
def test_add_security_group_to_database():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)

    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        DBInstanceClass="postgres",
        Engine="postgres",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
    )

    result = conn.describe_db_instances()
    assert result["DBInstances"][0]["DBSecurityGroups"] == []
    conn.create_db_security_group(
        DBSecurityGroupName="db_sg", DBSecurityGroupDescription="DB Security Group"
    )
    conn.modify_db_instance(
        DBInstanceIdentifier="db-master-1", DBSecurityGroups=["db_sg"]
    )
    result = conn.describe_db_instances()
    assert (
        result["DBInstances"][0]["DBSecurityGroups"][0]["DBSecurityGroupName"]
        == "db_sg"
    )


@mock_rds
def test_list_tags_security_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    result = conn.describe_db_subnet_groups()
    assert len(result["DBSubnetGroups"]) == 0

    security_group = conn.create_db_security_group(
        DBSecurityGroupName="db_sg",
        DBSecurityGroupDescription="DB Security Group",
        Tags=[{"Value": "bar", "Key": "foo"}, {"Value": "bar1", "Key": "foo1"}],
    )["DBSecurityGroup"]["DBSecurityGroupName"]
    resource = f"arn:aws:rds:us-west-2:1234567890:secgrp:{security_group}"
    result = conn.list_tags_for_resource(ResourceName=resource)
    assert result["TagList"] == [
        {"Value": "bar", "Key": "foo"},
        {"Value": "bar1", "Key": "foo1"},
    ]


@mock_rds
def test_add_tags_security_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    result = conn.describe_db_subnet_groups()
    assert len(result["DBSubnetGroups"]) == 0

    security_group = conn.create_db_security_group(
        DBSecurityGroupName="db_sg", DBSecurityGroupDescription="DB Security Group"
    )["DBSecurityGroup"]["DBSecurityGroupName"]

    resource = f"arn:aws:rds:us-west-2:1234567890:secgrp:{security_group}"
    conn.add_tags_to_resource(
        ResourceName=resource,
        Tags=[{"Value": "bar", "Key": "foo"}, {"Value": "bar1", "Key": "foo1"}],
    )

    result = conn.list_tags_for_resource(ResourceName=resource)
    assert result["TagList"] == [
        {"Value": "bar", "Key": "foo"},
        {"Value": "bar1", "Key": "foo1"},
    ]


@mock_rds
def test_remove_tags_security_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    result = conn.describe_db_subnet_groups()
    assert len(result["DBSubnetGroups"]) == 0

    security_group = conn.create_db_security_group(
        DBSecurityGroupName="db_sg",
        DBSecurityGroupDescription="DB Security Group",
        Tags=[{"Value": "bar", "Key": "foo"}, {"Value": "bar1", "Key": "foo1"}],
    )["DBSecurityGroup"]["DBSecurityGroupName"]

    resource = f"arn:aws:rds:us-west-2:1234567890:secgrp:{security_group}"
    conn.remove_tags_from_resource(ResourceName=resource, TagKeys=["foo"])

    result = conn.list_tags_for_resource(ResourceName=resource)
    assert result["TagList"] == [{"Value": "bar1", "Key": "foo1"}]


@mock_ec2
@mock_rds
def test_create_database_subnet_group():
    vpc_conn = boto3.client("ec2", DEFAULT_REGION)
    vpc = vpc_conn.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]
    subnet1 = vpc_conn.create_subnet(VpcId=vpc["VpcId"], CidrBlock="10.0.1.0/24")[
        "Subnet"
    ]
    subnet2 = vpc_conn.create_subnet(VpcId=vpc["VpcId"], CidrBlock="10.0.2.0/24")[
        "Subnet"
    ]

    subnet_ids = [subnet1["SubnetId"], subnet2["SubnetId"]]
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    result = conn.create_db_subnet_group(
        DBSubnetGroupName="db_subnet",
        DBSubnetGroupDescription="my db subnet",
        SubnetIds=subnet_ids,
    )
    assert result["DBSubnetGroup"]["DBSubnetGroupName"] == "db_subnet"
    assert result["DBSubnetGroup"]["DBSubnetGroupDescription"] == "my db subnet"
    subnets = result["DBSubnetGroup"]["Subnets"]
    subnet_group_ids = [subnets[0]["SubnetIdentifier"], subnets[1]["SubnetIdentifier"]]
    assert list(subnet_group_ids) == subnet_ids


@mock_ec2
@mock_rds
def test_modify_database_subnet_group():
    vpc_conn = boto3.client("ec2", DEFAULT_REGION)
    vpc = vpc_conn.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]
    subnet1 = vpc_conn.create_subnet(VpcId=vpc["VpcId"], CidrBlock="10.0.1.0/24")[
        "Subnet"
    ]
    subnet2 = vpc_conn.create_subnet(VpcId=vpc["VpcId"], CidrBlock="10.0.2.0/24")[
        "Subnet"
    ]

    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_subnet_group(
        DBSubnetGroupName="db_subnet",
        DBSubnetGroupDescription="my db subnet",
        SubnetIds=[subnet1["SubnetId"]],
    )

    conn.modify_db_subnet_group(
        DBSubnetGroupName="db_subnet",
        DBSubnetGroupDescription="my updated desc",
        SubnetIds=[subnet1["SubnetId"], subnet2["SubnetId"]],
    )

    _ = conn.describe_db_subnet_groups()["DBSubnetGroups"]
    # FIXME: Group is deleted atm
    # TODO: we should check whether all attrs are persisted


@mock_ec2
@mock_rds
def test_create_database_in_subnet_group():
    vpc_conn = boto3.client("ec2", DEFAULT_REGION)
    vpc = vpc_conn.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]
    subnet = vpc_conn.create_subnet(VpcId=vpc["VpcId"], CidrBlock="10.0.1.0/24")[
        "Subnet"
    ]

    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_subnet_group(
        DBSubnetGroupName="db_subnet1",
        DBSubnetGroupDescription="my db subnet",
        SubnetIds=[subnet["SubnetId"]],
    )
    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSubnetGroupName="db_subnet1",
    )
    result = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
    assert result["DBInstances"][0]["DBSubnetGroup"]["DBSubnetGroupName"] == (
        "db_subnet1"
    )


@mock_ec2
@mock_rds
def test_describe_database_subnet_group():
    vpc_conn = boto3.client("ec2", DEFAULT_REGION)
    vpc = vpc_conn.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]
    subnet = vpc_conn.create_subnet(VpcId=vpc["VpcId"], CidrBlock="10.0.1.0/24")[
        "Subnet"
    ]

    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_subnet_group(
        DBSubnetGroupName="db_subnet1",
        DBSubnetGroupDescription="my db subnet",
        SubnetIds=[subnet["SubnetId"]],
    )
    conn.create_db_subnet_group(
        DBSubnetGroupName="db_subnet2",
        DBSubnetGroupDescription="my db subnet",
        SubnetIds=[subnet["SubnetId"]],
    )

    resp = conn.describe_db_subnet_groups()
    assert len(resp["DBSubnetGroups"]) == 2

    subnets = resp["DBSubnetGroups"][0]["Subnets"]
    assert len(subnets) == 1

    assert (
        len(
            list(
                conn.describe_db_subnet_groups(DBSubnetGroupName="db_subnet1")[
                    "DBSubnetGroups"
                ]
            )
        )
        == 1
    )

    with pytest.raises(ClientError):
        conn.describe_db_subnet_groups(DBSubnetGroupName="not-a-subnet")


@mock_ec2
@mock_rds
def test_delete_database_subnet_group():
    vpc_conn = boto3.client("ec2", DEFAULT_REGION)
    vpc = vpc_conn.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]
    subnet = vpc_conn.create_subnet(VpcId=vpc["VpcId"], CidrBlock="10.0.1.0/24")[
        "Subnet"
    ]

    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    result = conn.describe_db_subnet_groups()
    assert len(result["DBSubnetGroups"]) == 0

    conn.create_db_subnet_group(
        DBSubnetGroupName="db_subnet1",
        DBSubnetGroupDescription="my db subnet",
        SubnetIds=[subnet["SubnetId"]],
    )
    result = conn.describe_db_subnet_groups()
    assert len(result["DBSubnetGroups"]) == 1

    conn.delete_db_subnet_group(DBSubnetGroupName="db_subnet1")
    result = conn.describe_db_subnet_groups()
    assert len(result["DBSubnetGroups"]) == 0

    with pytest.raises(ClientError):
        conn.delete_db_subnet_group(DBSubnetGroupName="db_subnet1")


@mock_ec2
@mock_rds
def test_list_tags_database_subnet_group():
    vpc_conn = boto3.client("ec2", DEFAULT_REGION)
    vpc = vpc_conn.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]
    subnet = vpc_conn.create_subnet(VpcId=vpc["VpcId"], CidrBlock="10.0.1.0/24")[
        "Subnet"
    ]

    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    result = conn.describe_db_subnet_groups()
    assert len(result["DBSubnetGroups"]) == 0

    subnet = conn.create_db_subnet_group(
        DBSubnetGroupName="db_subnet1",
        DBSubnetGroupDescription="my db subnet",
        SubnetIds=[subnet["SubnetId"]],
        Tags=[{"Value": "bar", "Key": "foo"}, {"Value": "bar1", "Key": "foo1"}],
    )["DBSubnetGroup"]["DBSubnetGroupName"]
    result = conn.list_tags_for_resource(
        ResourceName=f"arn:aws:rds:us-west-2:1234567890:subgrp:{subnet}"
    )
    assert result["TagList"] == [
        {"Value": "bar", "Key": "foo"},
        {"Value": "bar1", "Key": "foo1"},
    ]


@mock_rds
def test_modify_tags_parameter_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    client_tags = [{"Key": "character_set_client", "Value": "utf-8"}]
    result = conn.create_db_parameter_group(
        DBParameterGroupName="test-sqlserver-2017",
        DBParameterGroupFamily="mysql5.6",
        Description="MySQL Group",
        Tags=client_tags,
    )
    resource = result["DBParameterGroup"]["DBParameterGroupArn"]
    result = conn.list_tags_for_resource(ResourceName=resource)
    assert result["TagList"] == client_tags
    server_tags = [{"Key": "character_set_server", "Value": "utf-8"}]
    conn.add_tags_to_resource(ResourceName=resource, Tags=server_tags)
    combined_tags = client_tags + server_tags
    result = conn.list_tags_for_resource(ResourceName=resource)
    assert result["TagList"] == combined_tags

    conn.remove_tags_from_resource(
        ResourceName=resource, TagKeys=["character_set_client"]
    )
    result = conn.list_tags_for_resource(ResourceName=resource)
    assert result["TagList"] == server_tags


@mock_rds
def test_modify_tags_event_subscription():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    tags = [{"Key": "hello", "Value": "world"}]
    result = conn.create_event_subscription(
        SubscriptionName="my-instance-events",
        SourceType="db-instance",
        EventCategories=["backup", "recovery"],
        SnsTopicArn="arn:aws:sns:us-east-1:123456789012:interesting-events",
        Tags=tags,
    )
    resource = result["EventSubscription"]["EventSubscriptionArn"]
    result = conn.list_tags_for_resource(ResourceName=resource)
    assert result["TagList"] == tags
    new_tags = [{"Key": "new_key", "Value": "new_value"}]
    conn.add_tags_to_resource(ResourceName=resource, Tags=new_tags)
    combined_tags = tags + new_tags
    result = conn.list_tags_for_resource(ResourceName=resource)
    assert result["TagList"] == combined_tags

    conn.remove_tags_from_resource(ResourceName=resource, TagKeys=["new_key"])
    result = conn.list_tags_for_resource(ResourceName=resource)
    assert result["TagList"] == tags


@mock_ec2
@mock_rds
def test_add_tags_database_subnet_group():
    vpc_conn = boto3.client("ec2", DEFAULT_REGION)
    vpc = vpc_conn.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]
    subnet = vpc_conn.create_subnet(VpcId=vpc["VpcId"], CidrBlock="10.0.1.0/24")[
        "Subnet"
    ]

    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    result = conn.describe_db_subnet_groups()
    assert len(result["DBSubnetGroups"]) == 0

    subnet = conn.create_db_subnet_group(
        DBSubnetGroupName="db_subnet1",
        DBSubnetGroupDescription="my db subnet",
        SubnetIds=[subnet["SubnetId"]],
        Tags=[],
    )["DBSubnetGroup"]["DBSubnetGroupName"]
    resource = f"arn:aws:rds:us-west-2:1234567890:subgrp:{subnet}"

    conn.add_tags_to_resource(
        ResourceName=resource,
        Tags=[{"Value": "bar", "Key": "foo"}, {"Value": "bar1", "Key": "foo1"}],
    )

    result = conn.list_tags_for_resource(ResourceName=resource)
    assert result["TagList"] == [
        {"Value": "bar", "Key": "foo"},
        {"Value": "bar1", "Key": "foo1"},
    ]


@mock_ec2
@mock_rds
def test_remove_tags_database_subnet_group():
    vpc_conn = boto3.client("ec2", DEFAULT_REGION)
    vpc = vpc_conn.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]
    subnet = vpc_conn.create_subnet(VpcId=vpc["VpcId"], CidrBlock="10.0.1.0/24")[
        "Subnet"
    ]

    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    result = conn.describe_db_subnet_groups()
    assert len(result["DBSubnetGroups"]) == 0

    subnet = conn.create_db_subnet_group(
        DBSubnetGroupName="db_subnet1",
        DBSubnetGroupDescription="my db subnet",
        SubnetIds=[subnet["SubnetId"]],
        Tags=[{"Value": "bar", "Key": "foo"}, {"Value": "bar1", "Key": "foo1"}],
    )["DBSubnetGroup"]["DBSubnetGroupName"]
    resource = f"arn:aws:rds:us-west-2:1234567890:subgrp:{subnet}"

    conn.remove_tags_from_resource(ResourceName=resource, TagKeys=["foo"])

    result = conn.list_tags_for_resource(ResourceName=resource)
    assert result["TagList"] == [{"Value": "bar1", "Key": "foo1"}]


@mock_rds
def test_create_database_replica():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)

    conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )

    replica = conn.create_db_instance_read_replica(
        DBInstanceIdentifier="db-replica-1",
        SourceDBInstanceIdentifier="db-master-1",
        DBInstanceClass="db.m1.small",
    )
    assert replica["DBInstance"]["ReadReplicaSourceDBInstanceIdentifier"] == (
        "db-master-1"
    )
    assert replica["DBInstance"]["DBInstanceClass"] == "db.m1.small"
    assert replica["DBInstance"]["DBInstanceIdentifier"] == "db-replica-1"

    master = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
    assert master["DBInstances"][0]["ReadReplicaDBInstanceIdentifiers"] == (
        ["db-replica-1"]
    )
    replica = conn.describe_db_instances(DBInstanceIdentifier="db-replica-1")[
        "DBInstances"
    ][0]
    assert replica["ReadReplicaSourceDBInstanceIdentifier"] == "db-master-1"

    conn.delete_db_instance(DBInstanceIdentifier="db-replica-1", SkipFinalSnapshot=True)

    master = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")
    assert master["DBInstances"][0]["ReadReplicaDBInstanceIdentifiers"] == []


@mock_rds
def test_create_database_replica_cross_region():
    us1 = boto3.client("rds", region_name="us-east-1")
    us2 = boto3.client("rds", region_name=DEFAULT_REGION)

    source_id = "db-master-1"
    source_arn = us1.create_db_instance(
        DBInstanceIdentifier=source_id,
        AllocatedStorage=10,
        Engine="postgres",
        DBInstanceClass="db.m1.small",
    )["DBInstance"]["DBInstanceArn"]

    target_id = "db-replica-1"
    target_arn = us2.create_db_instance_read_replica(
        DBInstanceIdentifier=target_id,
        SourceDBInstanceIdentifier=source_arn,
        DBInstanceClass="db.m1.small",
    )["DBInstance"]["DBInstanceArn"]

    source_db = us1.describe_db_instances(DBInstanceIdentifier=source_id)[
        "DBInstances"
    ][0]
    assert source_db["ReadReplicaDBInstanceIdentifiers"] == [target_arn]

    target_db = us2.describe_db_instances(DBInstanceIdentifier=target_id)[
        "DBInstances"
    ][0]
    assert target_db["ReadReplicaSourceDBInstanceIdentifier"] == source_arn


@mock_rds
@mock_kms
def test_create_database_with_encrypted_storage():
    kms_conn = boto3.client("kms", region_name=DEFAULT_REGION)
    key = kms_conn.create_key(
        Policy="my RDS encryption policy",
        Description="RDS encryption key",
        KeyUsage="ENCRYPT_DECRYPT",
    )

    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
        StorageEncrypted=True,
        KmsKeyId=key["KeyMetadata"]["KeyId"],
    )

    assert database["DBInstance"]["StorageEncrypted"] is True
    assert database["DBInstance"]["KmsKeyId"] == key["KeyMetadata"]["KeyId"]


@mock_rds
def test_create_db_parameter_group():
    region = DEFAULT_REGION
    pg_name = "test"
    conn = boto3.client("rds", region_name=region)
    db_parameter_group = conn.create_db_parameter_group(
        DBParameterGroupName="test",
        DBParameterGroupFamily="mysql5.6",
        Description="test parameter group",
    )

    assert db_parameter_group["DBParameterGroup"]["DBParameterGroupName"] == "test"
    assert db_parameter_group["DBParameterGroup"]["DBParameterGroupFamily"] == (
        "mysql5.6"
    )
    assert db_parameter_group["DBParameterGroup"]["Description"] == (
        "test parameter group"
    )
    assert db_parameter_group["DBParameterGroup"]["DBParameterGroupArn"] == (
        f"arn:aws:rds:{region}:{ACCOUNT_ID}:pg:{pg_name}"
    )


@mock_rds
def test_create_db_instance_with_parameter_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_parameter_group(
        DBParameterGroupName="test",
        DBParameterGroupFamily="mysql5.6",
        Description="test parameter group",
    )

    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="mysql",
        DBInstanceClass="db.m1.small",
        DBParameterGroupName="test",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
    )

    assert len(database["DBInstance"]["DBParameterGroups"]) == 1
    assert database["DBInstance"]["DBParameterGroups"][0]["DBParameterGroupName"] == (
        "test"
    )
    assert database["DBInstance"]["DBParameterGroups"][0]["ParameterApplyStatus"] == (
        "in-sync"
    )


@mock_rds
def test_create_database_with_default_port():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        DBSecurityGroups=["my_sg"],
    )
    assert database["DBInstance"]["Endpoint"]["Port"] == 5432


@mock_rds
def test_modify_db_instance_with_parameter_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    database = conn.create_db_instance(
        DBInstanceIdentifier="db-master-1",
        AllocatedStorage=10,
        Engine="mysql",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
    )

    assert len(database["DBInstance"]["DBParameterGroups"]) == 1
    assert database["DBInstance"]["DBParameterGroups"][0]["DBParameterGroupName"] == (
        "default.mysql5.6"
    )
    assert database["DBInstance"]["DBParameterGroups"][0]["ParameterApplyStatus"] == (
        "in-sync"
    )

    conn.create_db_parameter_group(
        DBParameterGroupName="test",
        DBParameterGroupFamily="mysql5.6",
        Description="test parameter group",
    )
    conn.modify_db_instance(
        DBInstanceIdentifier="db-master-1",
        DBParameterGroupName="test",
        ApplyImmediately=True,
    )

    database = conn.describe_db_instances(DBInstanceIdentifier="db-master-1")[
        "DBInstances"
    ][0]
    assert len(database["DBParameterGroups"]) == 1
    assert database["DBParameterGroups"][0]["DBParameterGroupName"] == "test"
    assert database["DBParameterGroups"][0]["ParameterApplyStatus"] == "in-sync"


@mock_rds
def test_create_db_parameter_group_empty_description():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.create_db_parameter_group(
            DBParameterGroupName="test",
            DBParameterGroupFamily="mysql5.6",
            Description="",
        )


@mock_rds
def test_create_db_parameter_group_duplicate():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_parameter_group(
        DBParameterGroupName="test",
        DBParameterGroupFamily="mysql5.6",
        Description="test parameter group",
    )
    with pytest.raises(ClientError):
        conn.create_db_parameter_group(
            DBParameterGroupName="test",
            DBParameterGroupFamily="mysql5.6",
            Description="test parameter group",
        )


@mock_rds
def test_describe_db_parameter_group():
    region = DEFAULT_REGION
    pg_name = "test"
    conn = boto3.client("rds", region_name=region)
    conn.create_db_parameter_group(
        DBParameterGroupName=pg_name,
        DBParameterGroupFamily="mysql5.6",
        Description="test parameter group",
    )
    db_parameter_groups = conn.describe_db_parameter_groups(DBParameterGroupName="test")
    assert db_parameter_groups["DBParameterGroups"][0]["DBParameterGroupName"] == (
        "test"
    )
    assert db_parameter_groups["DBParameterGroups"][0]["DBParameterGroupArn"] == (
        f"arn:aws:rds:{region}:{ACCOUNT_ID}:pg:{pg_name}"
    )


@mock_rds
def test_describe_non_existent_db_parameter_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    db_parameter_groups = conn.describe_db_parameter_groups(DBParameterGroupName="test")
    assert len(db_parameter_groups["DBParameterGroups"]) == 0


@mock_rds
def test_delete_db_parameter_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_parameter_group(
        DBParameterGroupName="test",
        DBParameterGroupFamily="mysql5.6",
        Description="test parameter group",
    )
    db_parameter_groups = conn.describe_db_parameter_groups(DBParameterGroupName="test")
    assert db_parameter_groups["DBParameterGroups"][0]["DBParameterGroupName"] == (
        "test"
    )
    conn.delete_db_parameter_group(DBParameterGroupName="test")
    db_parameter_groups = conn.describe_db_parameter_groups(DBParameterGroupName="test")
    assert len(db_parameter_groups["DBParameterGroups"]) == 0


@mock_rds
def test_modify_db_parameter_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_parameter_group(
        DBParameterGroupName="test",
        DBParameterGroupFamily="mysql5.6",
        Description="test parameter group",
    )

    modify_result = conn.modify_db_parameter_group(
        DBParameterGroupName="test",
        Parameters=[
            {
                "ParameterName": "foo",
                "ParameterValue": "foo_val",
                "Description": "test param",
                "ApplyMethod": "immediate",
            }
        ],
    )

    assert modify_result["DBParameterGroupName"] == "test"

    db_parameters = conn.describe_db_parameters(DBParameterGroupName="test")
    assert db_parameters["Parameters"][0]["ParameterName"] == "foo"
    assert db_parameters["Parameters"][0]["ParameterValue"] == "foo_val"
    assert db_parameters["Parameters"][0]["Description"] == "test param"
    assert db_parameters["Parameters"][0]["ApplyMethod"] == "immediate"


@mock_rds
def test_delete_non_existent_db_parameter_group():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError):
        conn.delete_db_parameter_group(DBParameterGroupName="non-existent")


@mock_rds
def test_create_parameter_group_with_tags():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)
    conn.create_db_parameter_group(
        DBParameterGroupName="test",
        DBParameterGroupFamily="mysql5.6",
        Description="test parameter group",
        Tags=[{"Key": "foo", "Value": "bar"}],
    )
    result = conn.list_tags_for_resource(
        ResourceName="arn:aws:rds:us-west-2:1234567890:pg:test"
    )
    assert result["TagList"] == [{"Value": "bar", "Key": "foo"}]


@mock_rds
def test_create_db_with_iam_authentication():
    conn = boto3.client("rds", region_name=DEFAULT_REGION)

    database = conn.create_db_instance(
        DBInstanceIdentifier="rds",
        DBInstanceClass="db.t1.micro",
        Engine="postgres",
        EnableIAMDatabaseAuthentication=True,
    )

    db_instance = database["DBInstance"]
    assert db_instance["IAMDatabaseAuthenticationEnabled"] is True

    snapshot = conn.create_db_snapshot(
        DBInstanceIdentifier="rds", DBSnapshotIdentifier="snapshot"
    ).get("DBSnapshot")

    assert snapshot.get("IAMDatabaseAuthenticationEnabled") is True


@mock_rds
def test_create_db_instance_with_tags():
    client = boto3.client("rds", region_name=DEFAULT_REGION)
    tags = [{"Key": "foo", "Value": "bar"}, {"Key": "foo1", "Value": "bar1"}]
    db_instance_identifier = "test-db-instance"
    resp = client.create_db_instance(
        DBInstanceIdentifier=db_instance_identifier,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        Tags=tags,
    )
    assert resp["DBInstance"]["TagList"] == tags

    resp = client.describe_db_instances(DBInstanceIdentifier=db_instance_identifier)
    assert resp["DBInstances"][0]["TagList"] == tags


@mock_rds
def test_create_db_instance_without_availability_zone():
    region = "us-east-1"
    client = boto3.client("rds", region_name=region)
    db_instance_identifier = "test-db-instance"
    resp = client.create_db_instance(
        DBInstanceIdentifier=db_instance_identifier,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
    )
    assert region in resp["DBInstance"]["AvailabilityZone"]

    resp = client.describe_db_instances(DBInstanceIdentifier=db_instance_identifier)
    assert region in resp["DBInstances"][0]["AvailabilityZone"]


@mock_rds
def test_create_db_instance_with_availability_zone():
    region = "us-east-1"
    availability_zone = f"{region}c"
    client = boto3.client("rds", region_name=region)
    db_instance_identifier = "test-db-instance"
    resp = client.create_db_instance(
        DBInstanceIdentifier=db_instance_identifier,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        AvailabilityZone=availability_zone,
    )
    assert resp["DBInstance"]["AvailabilityZone"] == availability_zone

    resp = client.describe_db_instances(DBInstanceIdentifier=db_instance_identifier)
    assert resp["DBInstances"][0]["AvailabilityZone"] == availability_zone


@mock_rds
def test_validate_db_identifier():
    client = boto3.client("rds", region_name=DEFAULT_REGION)
    invalid_db_instance_identifier = "arn:aws:rds:eu-west-1:123456789012:db:mydb"

    with pytest.raises(ClientError) as exc:
        client.create_db_instance(
            DBInstanceIdentifier=invalid_db_instance_identifier,
            Engine="postgres",
            DBName="staging-postgres",
            DBInstanceClass="db.m1.small",
        )
    validation_helper(exc)

    with pytest.raises(ClientError) as exc:
        client.start_db_instance(
            DBInstanceIdentifier=invalid_db_instance_identifier,
        )
    validation_helper(exc)

    with pytest.raises(ClientError) as exc:
        client.stop_db_instance(
            DBInstanceIdentifier=invalid_db_instance_identifier,
        )
    validation_helper(exc)

    with pytest.raises(ClientError) as exc:
        client.delete_db_instance(
            DBInstanceIdentifier=invalid_db_instance_identifier,
        )
    validation_helper(exc)

    with pytest.raises(ClientError) as exc:
        client.delete_db_instance(
            DBInstanceIdentifier="valid-1-id" * 10,
        )
    validation_helper(exc)


@mock_rds
def test_createdb_instance_engine_with_invalid_value():
    client = boto3.client("rds", region_name=DEFAULT_REGION)
    with pytest.raises(ClientError) as exc:
        client.create_db_instance(
            DBInstanceIdentifier="test-db-instance",
            Engine="invalid-engine",
            DBName="staging-postgres",
            DBInstanceClass="db.m1.small",
        )

    err = exc.value.response["Error"]

    assert err["Code"] == "InvalidParameterValue"
    assert (
        err["Message"]
        == "Value invalid-engine for parameter Engine is invalid. Reason: engine invalid-engine not supported"
    )


@mock_rds
def test_describe_db_snapshot_attributes_default():
    client = boto3.client("rds", region_name="us-east-2")
    client.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )

    client.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
    ).get("DBSnapshot")

    resp = client.describe_db_snapshot_attributes(DBSnapshotIdentifier="snapshot-1")

    assert resp["DBSnapshotAttributesResult"]["DBSnapshotIdentifier"] == "snapshot-1"
    assert resp["DBSnapshotAttributesResult"]["DBSnapshotAttributes"] == []


@mock_rds
def test_describe_db_snapshot_attributes():
    client = boto3.client("rds", region_name="us-east-2")
    client.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )

    client.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
    ).get("DBSnapshot")

    resp = client.modify_db_snapshot_attribute(
        DBSnapshotIdentifier="snapshot-1",
        AttributeName="restore",
        ValuesToAdd=["Test", "Test2"],
    )

    resp = client.describe_db_snapshot_attributes(DBSnapshotIdentifier="snapshot-1")

    assert (
        resp["DBSnapshotAttributesResult"]["DBSnapshotAttributes"][0]["AttributeName"]
        == "restore"
    )
    assert resp["DBSnapshotAttributesResult"]["DBSnapshotAttributes"][0][
        "AttributeValues"
    ] == ["Test", "Test2"]


@mock_rds
def test_modify_db_snapshot_attribute():
    client = boto3.client("rds", region_name="us-east-2")
    client.create_db_instance(
        DBInstanceIdentifier="db-primary-1",
        AllocatedStorage=10,
        Engine="postgres",
        DBName="staging-postgres",
        DBInstanceClass="db.m1.small",
        MasterUsername="root",
        MasterUserPassword="hunter2",
        Port=1234,
        DBSecurityGroups=["my_sg"],
    )

    client.create_db_snapshot(
        DBInstanceIdentifier="db-primary-1", DBSnapshotIdentifier="snapshot-1"
    ).get("DBSnapshot")

    resp = client.modify_db_snapshot_attribute(
        DBSnapshotIdentifier="snapshot-1",
        AttributeName="restore",
        ValuesToAdd=["Test", "Test2"],
    )
    resp = client.modify_db_snapshot_attribute(
        DBSnapshotIdentifier="snapshot-1",
        AttributeName="restore",
        ValuesToRemove=["Test"],
    )

    assert (
        resp["DBSnapshotAttributesResult"]["DBSnapshotAttributes"][0]["AttributeName"]
        == "restore"
    )
    assert resp["DBSnapshotAttributesResult"]["DBSnapshotAttributes"][0][
        "AttributeValues"
    ] == ["Test2"]


def validation_helper(exc):
    err = exc.value.response["Error"]
    assert err["Code"] == "InvalidParameterValue"
    assert err["Message"] == (
        "The parameter DBInstanceIdentifier is not a valid identifier. "
        "Identifiers must begin with a letter; must contain only ASCII "
        "letters, digits, and hyphens; "
        "and must not end with a hyphen or contain two consecutive hyphens."
    )
