Repository: getmoto/moto
Pull request #6023 (2023-03-07 01:33:54)

Support CloudFormation and ECS ResourceTypeFilters in ResourceGroupsTaggingAPI get_resources()
Add support for cloudformation:stack, ecs:service, ecs:cluster, and ecs:task ResourceTypeFilters for ResourceGroupsTaggingAPI get_resources. 

codecov[bot]: # [Codecov](https://codecov.io/gh/getmoto/moto/pull/6023?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#6023](https://codecov.io/gh/getmoto/moto/pull/6023?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (aca7499) into [master](https://codecov.io/gh/getmoto/moto/commit/d525423f94897dad88dbd34aeefed7a5c2deebaf?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (d525423) will **decrease** coverage by `0.01%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##           master    #6023      +/-   ##
==========================================
- Coverage   96.23%   96.23%   -0.01%     
==========================================
  Files         765      765              
  Lines       75134    75187      +53     
==========================================
+ Hits        72303    72354      +51     
- Misses       2831     2833       +2     
```

| Flag | Coverage Δ | |
|---|---|---|
| servertests | `37.06% <48.07%> (+0.03%)` | :arrow_up: |
| unittests | `96.17% <100.00%> (+<0.01%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/getmoto/moto/pull/6023?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [moto/resourcegroupstaggingapi/models.py](https://codecov.io/gh/getmoto/moto/pull/6023?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bW90by9yZXNvdXJjZWdyb3Vwc3RhZ2dpbmdhcGkvbW9kZWxzLnB5) | `83.87% <100.00%> (-1.04%)` | :arrow_down: |
| [moto/emr/models.py](https://codecov.io/gh/getmoto/moto/pull/6023?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bW90by9lbXIvbW9kZWxzLnB5) | `97.20% <0.00%> (-0.29%)` | :arrow_down: |
| [moto/emrserverless/utils.py](https://codecov.io/gh/getmoto/moto/pull/6023?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bW90by9lbXJzZXJ2ZXJsZXNzL3V0aWxzLnB5) | `92.85% <0.00%> (-0.25%)` | :arrow_down: |
| [moto/ssm/models.py](https://codecov.io/gh/getmoto/moto/pull/6023?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bW90by9zc20vbW9kZWxzLnB5) | `94.91% <0.00%> (-0.12%)` | :arrow_down: |
| [moto/glue/responses.py](https://codecov.io/gh/getmoto/moto/pull/6023?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bW90by9nbHVlL3Jlc3BvbnNlcy5weQ==) | `99.69% <0.00%> (-0.01%)` | :arrow_down: |
| [moto/emr/responses.py](https://codecov.io/gh/getmoto/moto/pull/6023?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bW90by9lbXIvcmVzcG9uc2VzLnB5) | `99.45% <0.00%> (-0.01%)` | :arrow_down: |
| [moto/emr/utils.py](https://codecov.io/gh/getmoto/moto/pull/6023?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bW90by9lbXIvdXRpbHMucHk=) | `100.00% <0.00%> (ø)` | |
| [moto/events/models.py](https://codecov.io/gh/getmoto/moto/pull/6023?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bW90by9ldmVudHMvbW9kZWxzLnB5) | `94.76% <0.00%> (ø)` | |
| [moto/forecast/models.py](https://codecov.io/gh/getmoto/moto/pull/6023?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bW90by9mb3JlY2FzdC9tb2RlbHMucHk=) | `100.00% <0.00%> (ø)` | |
| ... and [22 more](https://codecov.io/gh/getmoto/moto/pull/6023?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)


Patch:
diff --git a/.github/workflows/data-update_ec2-instance-offerings.yml b/.github/workflows/data-update_ec2-instance-offerings.yml
index 39b749aba..984992836 100644
--- a/.github/workflows/data-update_ec2-instance-offerings.yml
+++ b/.github/workflows/data-update_ec2-instance-offerings.yml
@@ -38,13 +38,12 @@ jobs:
         role-to-assume: arn:aws:iam::486285699788:role/GithubActionsRole
 
     - name: Pull EC2 instance types from AWS
-      uses: technote-space/create-pr-action@v2
+      run: |
+        pip install boto3
+        scripts/ec2_get_instance_type_offerings.py
+    - name: Create PR
+      uses: peter-evans/create-pull-request@v4
       with:
-        EXECUTE_COMMANDS: |
-          pip install boto3
-          scripts/ec2_get_instance_type_offerings.py
-        COMMIT_MESSAGE: 'chore: update EC2 Instance Offerings'
-        COMMIT_NAME: 'Moto Admin'
-        COMMIT_EMAIL: 'admin@getmoto.org'
-        PR_BRANCH_NAME: 'chore-update-ec2-offerings-${{ github.run_number }}'
-        PR_TITLE: 'chore: update EC2 Instance Offerings'
+        branch: 'chore-update-ec2-offerings-${{ github.run_number }}'
+        title: 'chore: update EC2 Instance Offerings'
+        commit-message: 'chore: update EC2 Instance Offerings'
diff --git a/.github/workflows/data-update_ec2-instance-types.yml b/.github/workflows/data-update_ec2-instance-types.yml
index 2c1750649..a0762572e 100644
--- a/.github/workflows/data-update_ec2-instance-types.yml
+++ b/.github/workflows/data-update_ec2-instance-types.yml
@@ -37,13 +37,12 @@ jobs:
         role-to-assume: arn:aws:iam::486285699788:role/GithubActionsRole
 
     - name: Pull EC2 instance types from AWS
-      uses: technote-space/create-pr-action@v2
+      run: |
+        pip install boto3
+        scripts/get_instance_info.py
+    - name: Create PR
+      uses: peter-evans/create-pull-request@v4
       with:
-        EXECUTE_COMMANDS: |
-          pip install boto3
-          scripts/get_instance_info.py
-        COMMIT_MESSAGE: 'chore: update EC2 Instance Types'
-        COMMIT_NAME: 'Moto Admin'
-        COMMIT_EMAIL: 'admin@getmoto.org'
-        PR_BRANCH_NAME: 'chore-update-ec2-types-${{ github.run_number }}'
-        PR_TITLE: 'chore: update EC2 Instance Types'
+        branch: 'chore-update-ec2-types-${{ github.run_number }}'
+        title: 'chore: update EC2 Instance Types'
+        commit-message: 'chore: update EC2 Instance Types'
diff --git a/.github/workflows/data-update_ssm-default-amis.yml b/.github/workflows/data-update_ssm-default-amis.yml
index d4553b212..0f446dfba 100644
--- a/.github/workflows/data-update_ssm-default-amis.yml
+++ b/.github/workflows/data-update_ssm-default-amis.yml
@@ -37,14 +37,13 @@ jobs:
         role-to-assume: arn:aws:iam::486285699788:role/GithubActionsRole
 
     - name: Pull SSM default AMIs from AWS
-      uses: technote-space/create-pr-action@v2
+      run: |
+        pip install boto3
+        pip install moto[all]
+        python scripts/ssm_get_default_amis.py
+    - name: Create PR
+      uses: peter-evans/create-pull-request@v4
       with:
-        EXECUTE_COMMANDS: |
-          pip install boto3
-          pip install moto[all]
-          python scripts/ssm_get_default_amis.py
-        COMMIT_MESSAGE: "chore: update SSM default AMI's"
-        COMMIT_NAME: 'Moto Admin'
-        COMMIT_EMAIL: 'admin@getmoto.org'
-        PR_BRANCH_NAME: 'chore-update-ssm-amis-${{ github.run_number }}'
-        PR_TITLE: "chore: update SSM Instance AMI's"
+        branch: 'chore-update-ssm-amis-${{ github.run_number }}'
+        title: "chore: update SSM Instance AMI's"
+        commit-message: "chore: update SSM default AMI's"
diff --git a/.github/workflows/data-update_ssm-default-parameters.yml b/.github/workflows/data-update_ssm-default-parameters.yml
index f3f613cd0..bf77ee592 100644
--- a/.github/workflows/data-update_ssm-default-parameters.yml
+++ b/.github/workflows/data-update_ssm-default-parameters.yml
@@ -38,14 +38,13 @@ jobs:
         role-to-assume: arn:aws:iam::486285699788:role/GithubActionsRole
 
     - name: Pull SSM default Parameters from AWS
-      uses: technote-space/create-pr-action@v2
+      run: |
+        pip install boto3
+        pip install moto[all]
+        python scripts/ssm_get_default_params.py
+    - name: Create PR
+      uses: peter-evans/create-pull-request@v4
       with:
-        EXECUTE_COMMANDS: |
-          pip install boto3
-          pip install moto[all]
-          python scripts/ssm_get_default_params.py
-        COMMIT_MESSAGE: "chore: update SSM default parameters"
-        COMMIT_NAME: 'Moto Admin'
-        COMMIT_EMAIL: 'admin@getmoto.org'
-        PR_BRANCH_NAME: 'chore-update-ssm-params-${{ github.run_number }}'
-        PR_TITLE: "chore: update SSM Instance parameters"
+        branch: 'chore-update-ssm-params-${{ github.run_number }}'
+        title: "chore: update SSM Instance parameters"
+        commit-message: "chore: update SSM default parameters"
diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index f05450858..51718c2fe 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -2986,7 +2986,7 @@
 
 ## firehose
 <details>
-<summary>83% implemented</summary>
+<summary>100% implemented</summary>
 
 - [X] create_delivery_stream
 - [X] delete_delivery_stream
@@ -2995,8 +2995,8 @@
 - [X] list_tags_for_delivery_stream
 - [X] put_record
 - [X] put_record_batch
-- [ ] start_delivery_stream_encryption
-- [ ] stop_delivery_stream_encryption
+- [X] start_delivery_stream_encryption
+- [X] stop_delivery_stream_encryption
 - [X] tag_delivery_stream
 - [X] untag_delivery_stream
 - [X] update_destination
@@ -6055,11 +6055,11 @@
 
 ## secretsmanager
 <details>
-<summary>72% implemented</summary>
+<summary>81% implemented</summary>
 
 - [X] cancel_rotate_secret
 - [X] create_secret
-- [ ] delete_resource_policy
+- [X] delete_resource_policy
 - [X] delete_secret
 - [X] describe_secret
 - [X] get_random_password
@@ -6067,7 +6067,7 @@
 - [X] get_secret_value
 - [X] list_secret_version_ids
 - [X] list_secrets
-- [ ] put_resource_policy
+- [X] put_resource_policy
 - [X] put_secret_value
 - [ ] remove_regions_from_replication
 - [ ] replicate_secret_to_regions
diff --git a/docs/docs/services/firehose.rst b/docs/docs/services/firehose.rst
index b5c62e5d4..c472063d9 100644
--- a/docs/docs/services/firehose.rst
+++ b/docs/docs/services/firehose.rst
@@ -56,8 +56,8 @@ firehose
 - [X] put_record_batch
   Write multiple data records into a Kinesis Data firehose stream.
 
-- [ ] start_delivery_stream_encryption
-- [ ] stop_delivery_stream_encryption
+- [X] start_delivery_stream_encryption
+- [X] stop_delivery_stream_encryption
 - [X] tag_delivery_stream
   Add/update tags for specified delivery stream.
 
@@ -65,6 +65,4 @@ firehose
   Removes tags from specified delivery stream.
 
 - [X] update_destination
-  Updates specified destination of specified delivery stream.
-
 
diff --git a/docs/docs/services/secretsmanager.rst b/docs/docs/services/secretsmanager.rst
index fe4775f9e..9571d804b 100644
--- a/docs/docs/services/secretsmanager.rst
+++ b/docs/docs/services/secretsmanager.rst
@@ -27,7 +27,7 @@ secretsmanager
 
 - [X] cancel_rotate_secret
 - [X] create_secret
-- [ ] delete_resource_policy
+- [X] delete_resource_policy
 - [X] delete_secret
 - [X] describe_secret
 - [X] get_random_password
@@ -35,7 +35,11 @@ secretsmanager
 - [X] get_secret_value
 - [X] list_secret_version_ids
 - [X] list_secrets
-- [ ] put_resource_policy
+- [X] put_resource_policy
+  
+        The BlockPublicPolicy-parameter is not yet implemented
+        
+
 - [X] put_secret_value
 - [ ] remove_regions_from_replication
 - [ ] replicate_secret_to_regions
diff --git a/moto/apigateway/models.py b/moto/apigateway/models.py
index c6d8a1680..eb7935f33 100644
--- a/moto/apigateway/models.py
+++ b/moto/apigateway/models.py
@@ -1,5 +1,3 @@
-from __future__ import absolute_import
-
 import string
 import re
 import responses
diff --git a/moto/core/responses.py b/moto/core/responses.py
index 04897ca85..8fc794261 100644
--- a/moto/core/responses.py
+++ b/moto/core/responses.py
@@ -12,7 +12,11 @@ from collections import defaultdict, OrderedDict
 from moto import settings
 from moto.core.common_types import TYPE_RESPONSE, TYPE_IF_NONE
 from moto.core.exceptions import DryRunClientError
-from moto.core.utils import camelcase_to_underscores, method_names_from_class
+from moto.core.utils import (
+    camelcase_to_underscores,
+    method_names_from_class,
+    params_sort_function,
+)
 from moto.utilities.utils import load_resource
 from jinja2 import Environment, DictLoader, Template
 from typing import (
@@ -122,7 +126,6 @@ class _TemplateEnvironmentMixin(object):
 
 
 class ActionAuthenticatorMixin(object):
-
     request_count: ClassVar[int] = 0
 
     def _authenticate_and_authorize_action(self, iam_request_cls: type) -> None:
@@ -204,7 +207,6 @@ class ActionAuthenticatorMixin(object):
 
 
 class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
-
     default_region = "us-east-1"
     # to extract region, use [^.]
     # Note that the URL region can be anything, thanks to our MOTO_ALLOW_NONEXISTENT_REGION-config - so we can't have a very specific regex
@@ -687,7 +689,7 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
         }
         """
         params: Dict[str, Any] = {}
-        for k, v in sorted(self.querystring.items()):
+        for k, v in sorted(self.querystring.items(), key=params_sort_function):
             self._parse_param(k, v[0], params)
         return params
 
diff --git a/moto/core/utils.py b/moto/core/utils.py
index 7d4cf2f99..833359fbd 100644
--- a/moto/core/utils.py
+++ b/moto/core/utils.py
@@ -2,7 +2,7 @@ import datetime
 import inspect
 import re
 from botocore.exceptions import ClientError
-from typing import Any, Optional, List, Callable, Dict
+from typing import Any, Optional, List, Callable, Dict, Tuple
 from urllib.parse import urlparse
 from .common_types import TYPE_RESPONSE
 
@@ -307,3 +307,15 @@ def extract_region_from_aws_authorization(string: str) -> Optional[str]:
     if region == auth:
         return None
     return region
+
+
+def params_sort_function(item: Tuple[str, Any]) -> Tuple[str, Any]:
+    """
+    Comparison function used to sort params appropriately taking tags non
+    alphabetical order into consideration
+    """
+    key, _ = item
+    if key.startswith("Tags.member"):
+        member_num = int(key.split(".")[2])
+        return ("Tags.member", member_num)
+    return item
diff --git a/moto/firehose/models.py b/moto/firehose/models.py
index e6410dd82..f47af5774 100644
--- a/moto/firehose/models.py
+++ b/moto/firehose/models.py
@@ -1,10 +1,6 @@
 """FirehoseBackend class with methods for supported APIs.
 
 Incomplete list of unfinished items:
-  - The create_delivery_stream() argument
-    DeliveryStreamEncryptionConfigurationInput is not supported.
-  - The S3BackupMode argument is ignored as are most of the other
-    destination arguments.
   - Data record size and number of transactions are ignored.
   - Better validation of delivery destination parameters, e.g.,
     validation of the url for an http endpoint (boto3 does this).
@@ -116,12 +112,20 @@ class DeliveryStream(
 
     MAX_STREAMS_PER_REGION = 50
 
+    ALTERNATIVE_FIELD_NAMES = [
+        ("S3Configuration", "S3DestinationDescription"),
+        ("S3Update", "S3DestinationDescription"),
+        ("S3BackupConfiguration", "S3BackupDescription"),
+        ("S3BackupUpdate", "S3BackupDescription"),
+    ]
+
     def __init__(
         self,
         account_id: str,
         region: str,
         delivery_stream_name: str,
         delivery_stream_type: str,
+        encryption: Dict[str, Any],
         kinesis_stream_source_configuration: Dict[str, Any],
         destination_name: str,
         destination_config: Dict[str, Any],
@@ -131,6 +135,7 @@ class DeliveryStream(
         self.delivery_stream_type = (
             delivery_stream_type if delivery_stream_type else "DirectPut"
         )
+        self.delivery_stream_encryption_configuration = encryption
 
         self.source = kinesis_stream_source_configuration
         self.destinations: List[Dict[str, Any]] = [
@@ -139,18 +144,19 @@ class DeliveryStream(
                 destination_name: destination_config,
             }
         ]
+
         if destination_name == "ExtendedS3":
             # Add a S3 destination as well, minus a few ExtendedS3 fields.
             self.destinations[0]["S3"] = create_s3_destination_config(
                 destination_config
             )
-        elif "S3Configuration" in destination_config:
-            # S3Configuration becomes S3DestinationDescription for the
-            # other destinations.
-            self.destinations[0][destination_name][
-                "S3DestinationDescription"
-            ] = destination_config["S3Configuration"]
-            del self.destinations[0][destination_name]["S3Configuration"]
+
+        # S3Configuration becomes S3DestinationDescription for the
+        # other destinations. Same for S3Backup
+        for old, new in DeliveryStream.ALTERNATIVE_FIELD_NAMES:
+            if old in destination_config:
+                self.destinations[0][destination_name][new] = destination_config[old]
+                del self.destinations[0][destination_name][old]
 
         self.delivery_stream_status = "ACTIVE"
         self.delivery_stream_arn = f"arn:aws:firehose:{region}:{account_id}:deliverystream/{delivery_stream_name}"
@@ -213,12 +219,6 @@ class FirehoseBackend(BaseBackend):
             )
 
         # Rule out situations that are not yet implemented.
-        if delivery_stream_encryption_configuration_input:
-            warnings.warn(
-                "A delivery stream with server-side encryption enabled is not "
-                "yet implemented"
-            )
-
         if destination_name == "Splunk":
             warnings.warn("A Splunk destination delivery stream is not yet implemented")
 
@@ -247,13 +247,14 @@ class FirehoseBackend(BaseBackend):
         # by delivery stream name.  This instance will update the state and
         # create the ARN.
         delivery_stream = DeliveryStream(
-            self.account_id,
-            region,
-            delivery_stream_name,
-            delivery_stream_type,
-            kinesis_stream_source_configuration,
-            destination_name,
-            destination_config,
+            account_id=self.account_id,
+            region=region,
+            delivery_stream_name=delivery_stream_name,
+            delivery_stream_type=delivery_stream_type,
+            encryption=delivery_stream_encryption_configuration_input,
+            kinesis_stream_source_configuration=kinesis_stream_source_configuration,
+            destination_name=destination_name,
+            destination_config=destination_config,
         )
         self.tagger.tag_resource(delivery_stream.delivery_stream_arn, tags or [])
 
@@ -562,6 +563,27 @@ class FirehoseBackend(BaseBackend):
             delivery_stream.delivery_stream_arn, tag_keys
         )
 
+    def start_delivery_stream_encryption(
+        self, stream_name: str, encryption_config: Dict[str, Any]
+    ) -> None:
+        delivery_stream = self.delivery_streams.get(stream_name)
+        if not delivery_stream:
+            raise ResourceNotFoundException(
+                f"Firehose {stream_name} under account {self.account_id} not found."
+            )
+
+        delivery_stream.delivery_stream_encryption_configuration = encryption_config
+        delivery_stream.delivery_stream_encryption_configuration["Status"] = "ENABLED"
+
+    def stop_delivery_stream_encryption(self, stream_name: str) -> None:
+        delivery_stream = self.delivery_streams.get(stream_name)
+        if not delivery_stream:
+            raise ResourceNotFoundException(
+                f"Firehose {stream_name} under account {self.account_id} not found."
+            )
+
+        delivery_stream.delivery_stream_encryption_configuration["Status"] = "DISABLED"
+
     def update_destination(  # pylint: disable=unused-argument
         self,
         delivery_stream_name: str,
@@ -575,10 +597,7 @@ class FirehoseBackend(BaseBackend):
         splunk_destination_update: Dict[str, Any],
         http_endpoint_destination_update: Dict[str, Any],
     ) -> None:
-        """Updates specified destination of specified delivery stream."""
-        (destination_name, destination_config) = find_destination_config_in_args(
-            locals()
-        )
+        (dest_name, dest_config) = find_destination_config_in_args(locals())
 
         delivery_stream = self.delivery_streams.get(delivery_stream_name)
         if not delivery_stream:
@@ -586,7 +605,7 @@ class FirehoseBackend(BaseBackend):
                 f"Firehose {delivery_stream_name} under accountId {self.account_id} not found."
             )
 
-        if destination_name == "Splunk":
+        if dest_name == "Splunk":
             warnings.warn("A Splunk destination delivery stream is not yet implemented")
 
         if delivery_stream.version_id != current_delivery_stream_version_id:
@@ -609,35 +628,42 @@ class FirehoseBackend(BaseBackend):
         # Switching between Amazon ES and other services is not supported.
         # For an Amazon ES destination, you can only update to another Amazon
         # ES destination.  Same with HTTP.  Didn't test Splunk.
-        if (
-            destination_name == "Elasticsearch" and "Elasticsearch" not in destination
-        ) or (destination_name == "HttpEndpoint" and "HttpEndpoint" not in destination):
+        if (dest_name == "Elasticsearch" and "Elasticsearch" not in destination) or (
+            dest_name == "HttpEndpoint" and "HttpEndpoint" not in destination
+        ):
             raise InvalidArgumentException(
-                f"Changing the destination type to or from {destination_name} "
+                f"Changing the destination type to or from {dest_name} "
                 f"is not supported at this time."
             )
 
         # If this is a different type of destination configuration,
         # the existing configuration is reset first.
-        if destination_name in destination:
-            delivery_stream.destinations[destination_idx][destination_name].update(
-                destination_config
-            )
+        if dest_name in destination:
+            delivery_stream.destinations[destination_idx][dest_name].update(dest_config)
         else:
             delivery_stream.destinations[destination_idx] = {
                 "destination_id": destination_id,
-                destination_name: destination_config,
+                dest_name: dest_config,
             }
 
+        # Some names in the Update-request differ from the names used by Describe
+        for old, new in DeliveryStream.ALTERNATIVE_FIELD_NAMES:
+            if old in dest_config:
+                if new not in delivery_stream.destinations[destination_idx][dest_name]:
+                    delivery_stream.destinations[destination_idx][dest_name][new] = {}
+                delivery_stream.destinations[destination_idx][dest_name][new].update(
+                    dest_config[old]
+                )
+
         # Once S3 is updated to an ExtendedS3 destination, both remain in
         # the destination.  That means when one is updated, the other needs
         # to be updated as well.  The problem is that they don't have the
         # same fields.
-        if destination_name == "ExtendedS3":
+        if dest_name == "ExtendedS3":
             delivery_stream.destinations[destination_idx][
                 "S3"
-            ] = create_s3_destination_config(destination_config)
-        elif destination_name == "S3" and "ExtendedS3" in destination:
+            ] = create_s3_destination_config(dest_config)
+        elif dest_name == "S3" and "ExtendedS3" in destination:
             destination["ExtendedS3"] = {
                 k: v
                 for k, v in destination["S3"].items()
diff --git a/moto/firehose/responses.py b/moto/firehose/responses.py
index 2f858692d..c36125fd5 100644
--- a/moto/firehose/responses.py
+++ b/moto/firehose/responses.py
@@ -109,3 +109,18 @@ class FirehoseResponse(BaseResponse):
             self._get_param("HttpEndpointDestinationUpdate"),
         )
         return json.dumps({})
+
+    def start_delivery_stream_encryption(self) -> str:
+        stream_name = self._get_param("DeliveryStreamName")
+        encryption_config = self._get_param(
+            "DeliveryStreamEncryptionConfigurationInput"
+        )
+        self.firehose_backend.start_delivery_stream_encryption(
+            stream_name, encryption_config
+        )
+        return "{}"
+
+    def stop_delivery_stream_encryption(self) -> str:
+        stream_name = self._get_param("DeliveryStreamName")
+        self.firehose_backend.stop_delivery_stream_encryption(stream_name)
+        return "{}"
diff --git a/moto/glue/models.py b/moto/glue/models.py
index a0b3566aa..6abbf4aa9 100644
--- a/moto/glue/models.py
+++ b/moto/glue/models.py
@@ -324,6 +324,9 @@ class GlueBackend(BaseBackend):
         glue_version,
         number_of_workers,
         worker_type,
+        code_gen_configuration_nodes,
+        execution_class,
+        source_control_details,
     ):
         self.jobs[name] = FakeJob(
             name,
@@ -345,6 +348,9 @@ class GlueBackend(BaseBackend):
             glue_version,
             number_of_workers,
             worker_type,
+            code_gen_configuration_nodes,
+            execution_class,
+            source_control_details,
             backend=self,
         )
         return name
@@ -1070,6 +1076,9 @@ class FakeJob:
         glue_version=None,
         number_of_workers=None,
         worker_type=None,
+        code_gen_configuration_nodes=None,
+        execution_class=None,
+        source_control_details=None,
         backend=None,
     ):
         self.name = name
@@ -1090,6 +1099,9 @@ class FakeJob:
         self.glue_version = glue_version
         self.number_of_workers = number_of_workers
         self.worker_type = worker_type
+        self.code_gen_configuration_nodes = code_gen_configuration_nodes
+        self.execution_class = execution_class or "STANDARD"
+        self.source_control_details = source_control_details
         self.created_on = datetime.utcnow()
         self.last_modified_on = datetime.utcnow()
         self.arn = (
@@ -1125,6 +1137,9 @@ class FakeJob:
             "SecurityConfiguration": self.security_configuration,
             "NotificationProperty": self.notification_property,
             "GlueVersion": self.glue_version,
+            "CodeGenConfigurationNodes": self.code_gen_configuration_nodes,
+            "ExecutionClass": self.execution_class,
+            "SourceControlDetails": self.source_control_details,
         }
 
     def start_job_run(self):
diff --git a/moto/glue/responses.py b/moto/glue/responses.py
index 50c009eab..934b1ed76 100644
--- a/moto/glue/responses.py
+++ b/moto/glue/responses.py
@@ -309,6 +309,9 @@ class GlueResponse(BaseResponse):
         glue_version = self._get_param("GlueVersion")
         number_of_workers = self._get_int_param("NumberOfWorkers")
         worker_type = self._get_param("WorkerType")
+        code_gen_configuration_nodes = self._get_param("CodeGenConfigurationNodes")
+        execution_class = self._get_param("ExecutionClass")
+        source_control_details = self._get_param("SourceControlDetails")
         name = self.glue_backend.create_job(
             name=name,
             description=description,
@@ -329,6 +332,9 @@ class GlueResponse(BaseResponse):
             glue_version=glue_version,
             number_of_workers=number_of_workers,
             worker_type=worker_type,
+            code_gen_configuration_nodes=code_gen_configuration_nodes,
+            execution_class=execution_class,
+            source_control_details=source_control_details,
         )
         return json.dumps(dict(Name=name))
 
@@ -514,3 +520,6 @@ class GlueResponse(BaseResponse):
                 "CrawlersNotFound": crawlers_not_found,
             }
         )
+
+    def get_partition_indexes(self):
+        return json.dumps({"PartitionIndexDescriptorList": []})
diff --git a/moto/guardduty/__init__.py b/moto/guardduty/__init__.py
index 68e09dedd..c9d9d1c16 100644
--- a/moto/guardduty/__init__.py
+++ b/moto/guardduty/__init__.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
 from .models import guardduty_backends
 from ..core.models import base_decorator
 
diff --git a/moto/guardduty/models.py b/moto/guardduty/models.py
index f5056537c..7fad4a360 100644
--- a/moto/guardduty/models.py
+++ b/moto/guardduty/models.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 from moto.core import BaseBackend, BackendDict, BaseModel
 from moto.moto_api._internal import mock_random
 from datetime import datetime
diff --git a/moto/guardduty/responses.py b/moto/guardduty/responses.py
index 50f39e5de..7980bb1c3 100644
--- a/moto/guardduty/responses.py
+++ b/moto/guardduty/responses.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 from moto.core.responses import BaseResponse
 from .models import guardduty_backends
 import json
diff --git a/moto/guardduty/urls.py b/moto/guardduty/urls.py
index 3ce2153ca..2b11aa66a 100644
--- a/moto/guardduty/urls.py
+++ b/moto/guardduty/urls.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 from .responses import GuardDutyResponse
 
 response = GuardDutyResponse()
diff --git a/moto/managedblockchain/models.py b/moto/managedblockchain/models.py
index 6dc5c9be4..4456f95a0 100644
--- a/moto/managedblockchain/models.py
+++ b/moto/managedblockchain/models.py
@@ -1,5 +1,3 @@
-from __future__ import division
-
 import datetime
 import re
 
diff --git a/moto/meteringmarketplace/__init__.py b/moto/meteringmarketplace/__init__.py
index cb50b9840..73497d4e4 100644
--- a/moto/meteringmarketplace/__init__.py
+++ b/moto/meteringmarketplace/__init__.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 from .models import meteringmarketplace_backends
 from ..core.models import base_decorator
 
diff --git a/moto/meteringmarketplace/urls.py b/moto/meteringmarketplace/urls.py
index a351525e2..64abc56b5 100644
--- a/moto/meteringmarketplace/urls.py
+++ b/moto/meteringmarketplace/urls.py
@@ -1,4 +1,3 @@
-from __future__ import unicode_literals
 from .responses import MarketplaceMeteringResponse
 
 url_bases = [
diff --git a/moto/resourcegroupstaggingapi/models.py b/moto/resourcegroupstaggingapi/models.py
index 0514901c6..4abb7893f 100644
--- a/moto/resourcegroupstaggingapi/models.py
+++ b/moto/resourcegroupstaggingapi/models.py
@@ -13,6 +13,7 @@ from moto.glacier import glacier_backends
 from moto.redshift import redshift_backends
 from moto.emr import emr_backends
 from moto.awslambda import lambda_backends
+from moto.ecs import ecs_backends
 
 # Left: EC2 ElastiCache RDS ELB CloudFront WorkSpaces Lambda EMR Glacier Kinesis Redshift Route53
 # StorageGateway DynamoDB MachineLearning ACM DirectConnect DirectoryService CloudHSM
@@ -106,6 +107,13 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
         """
         return lambda_backends[self.account_id][self.region_name]
 
+    @property
+    def ecs_backend(self):
+        """
+        :rtype: moto.ecs.models.EcsnBackend
+        """
+        return ecs_backends[self.account_id][self.region_name]
+
     def _get_resources_generator(self, tag_filters=None, resource_type_filters=None):
         # Look at
         # https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
@@ -143,7 +151,19 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
             else:
                 return True
 
-        # Do S3, resource type s3
+        def format_tags(tags):
+            result = []
+            for key, value in tags.items():
+                result.append({"Key": key, "Value": value})
+            return result
+
+        def format_tag_keys(tags, keys):
+            result = []
+            for tag in tags:
+                result.append({"Key": tag[keys[0]], "Value": tag[keys[1]]})
+            return result
+
+        # S3
         if not resource_type_filters or "s3" in resource_type_filters:
             for bucket in self.s3_backend.buckets.values():
                 tags = self.s3_backend.tagger.list_tags_for_resource(bucket.arn)["Tags"]
@@ -153,12 +173,45 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
                     continue
                 yield {"ResourceARN": "arn:aws:s3:::" + bucket.name, "Tags": tags}
 
-        # EC2 tags
-        def get_ec2_tags(res_id):
-            result = []
-            for key, value in self.ec2_backend.tags.get(res_id, {}).items():
-                result.append({"Key": key, "Value": value})
-            return result
+        # CloudFormation
+        if not resource_type_filters or "cloudformation:stack" in resource_type_filters:
+
+            try:
+                from moto.cloudformation import cloudformation_backends
+
+                backend = cloudformation_backends[self.account_id][self.region_name]
+
+                for stack in backend.stacks.values():
+                    tags = format_tags(stack.tags)
+                    if not tag_filter(tags):
+                        continue
+                    yield {"ResourceARN": f"{stack.stack_id}", "Tags": tags}
+
+            except Exception:
+                pass
+
+        # ECS
+        if not resource_type_filters or "ecs:service" in resource_type_filters:
+            for service in self.ecs_backend.services.values():
+                tags = format_tag_keys(service.tags, ["key", "value"])
+                if not tag_filter(tags):
+                    continue
+                yield {"ResourceARN": f"{service.physical_resource_id}", "Tags": tags}
+
+        if not resource_type_filters or "ecs:cluster" in resource_type_filters:
+            for cluster in self.ecs_backend.clusters.values():
+                tags = format_tag_keys(cluster.tags, ["key", "value"])
+                if not tag_filter(tags):
+                    continue
+                yield {"ResourceARN": f"{cluster.arn}", "Tags": tags}
+
+        if not resource_type_filters or "ecs:task" in resource_type_filters:
+            for task_dict in self.ecs_backend.tasks.values():
+                for task in task_dict.values():
+                    tags = format_tag_keys(task.tags, ["key", "value"])
+                    if not tag_filter(tags):
+                        continue
+                    yield {"ResourceARN": f"{task.task_arn}", "Tags": tags}
 
         # EC2 AMI, resource type ec2:image
         if (
@@ -167,7 +220,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
             or "ec2:image" in resource_type_filters
         ):
             for ami in self.ec2_backend.amis.values():
-                tags = get_ec2_tags(ami.id)
+                tags = format_tags(self.ec2_backend.tags.get(ami.id, {}))
 
                 if not tags or not tag_filter(
                     tags
@@ -186,7 +239,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
         ):
             for reservation in self.ec2_backend.reservations.values():
                 for instance in reservation.instances:
-                    tags = get_ec2_tags(instance.id)
+                    tags = format_tags(self.ec2_backend.tags.get(instance.id, {}))
 
                     if not tags or not tag_filter(
                         tags
@@ -204,7 +257,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
             or "ec2:network-interface" in resource_type_filters
         ):
             for eni in self.ec2_backend.enis.values():
-                tags = get_ec2_tags(eni.id)
+                tags = format_tags(self.ec2_backend.tags.get(eni.id, {}))
 
                 if not tags or not tag_filter(
                     tags
@@ -225,7 +278,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
         ):
             for vpc in self.ec2_backend.groups.values():
                 for sg in vpc.values():
-                    tags = get_ec2_tags(sg.id)
+                    tags = format_tags(self.ec2_backend.tags.get(sg.id, {}))
 
                     if not tags or not tag_filter(
                         tags
@@ -243,7 +296,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
             or "ec2:snapshot" in resource_type_filters
         ):
             for snapshot in self.ec2_backend.snapshots.values():
-                tags = get_ec2_tags(snapshot.id)
+                tags = format_tags(self.ec2_backend.tags.get(snapshot.id, {}))
 
                 if not tags or not tag_filter(
                     tags
@@ -263,7 +316,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
             or "ec2:volume" in resource_type_filters
         ):
             for volume in self.ec2_backend.volumes.values():
-                tags = get_ec2_tags(volume.id)
+                tags = format_tags(self.ec2_backend.tags.get(volume.id, {}))
 
                 if not tags or not tag_filter(
                     tags
@@ -312,15 +365,12 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
         # Kinesis
 
         # KMS
-        def get_kms_tags(kms_key_id):
-            result = []
-            for tag in self.kms_backend.list_resource_tags(kms_key_id).get("Tags", []):
-                result.append({"Key": tag["TagKey"], "Value": tag["TagValue"]})
-            return result
-
         if not resource_type_filters or "kms" in resource_type_filters:
             for kms_key in self.kms_backend.list_keys():
-                tags = get_kms_tags(kms_key.id)
+                tags = format_tag_keys(
+                    self.kms_backend.list_resource_tags(kms_key.id).get("Tags", []),
+                    ["TagKey", "TagValue"],
+                )
                 if not tag_filter(tags):  # Skip if no tags, or invalid filter
                     continue
 
@@ -408,7 +458,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
             or "ec2:vpc" in resource_type_filters
         ):
             for vpc in self.ec2_backend.vpcs.values():
-                tags = get_ec2_tags(vpc.id)
+                tags = format_tags(self.ec2_backend.tags.get(vpc.id, {}))
                 if not tags or not tag_filter(
                     tags
                 ):  # Skip if no tags, or invalid filter
@@ -427,15 +477,9 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
         # VPC VPN Connection
 
         # Lambda Instance
-        def transform_lambda_tags(dictTags):
-            result = []
-            for key, value in dictTags.items():
-                result.append({"Key": key, "Value": value})
-            return result
-
         if not resource_type_filters or "lambda" in resource_type_filters:
             for f in self.lambda_backend.list_functions():
-                tags = transform_lambda_tags(f.tags)
+                tags = format_tags(f.tags)
                 if not tags or not tag_filter(tags):
                     continue
                 yield {
@@ -447,7 +491,7 @@ class ResourceGroupsTaggingAPIBackend(BaseBackend):
         # Look at
         # https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
 
-        # Do S3, resource type s3
+        # S3
         for bucket in self.s3_backend.buckets.values():
             tags = self.s3_backend.tagger.get_tag_dict_for_resource(bucket.arn)
             for key, _ in tags.items():
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
index 5285cd975..f10bd6500 100644
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -1319,7 +1319,10 @@ class S3Response(BaseResponse):
             response_headers["x-amz-version-id"] = key.version_id
 
         if key.storage_class in ARCHIVE_STORAGE_CLASSES:
-            raise InvalidObjectState(storage_class=key.storage_class)
+            if 'ongoing-request="false"' not in key.response_dict.get(
+                "x-amz-restore", ""
+            ):
+                raise InvalidObjectState(storage_class=key.storage_class)
         if if_unmodified_since:
             if_unmodified_since = str_to_rfc_1123_datetime(if_unmodified_since)
             if key.last_modified.replace(microsecond=0) > if_unmodified_since:
diff --git a/moto/secretsmanager/models.py b/moto/secretsmanager/models.py
index 0ae23bec5..e6cefd5a9 100644
--- a/moto/secretsmanager/models.py
+++ b/moto/secretsmanager/models.py
@@ -87,6 +87,7 @@ class FakeSecret:
         self.rotation_lambda_arn = ""
         self.auto_rotate_after_days = 0
         self.deleted_date = None
+        self.policy = None
 
     def update(
         self, description=None, tags=None, kms_key_id=None, last_changed_date=None
@@ -825,29 +826,37 @@ class SecretsManagerBackend(BaseBackend):
 
         return secret_id
 
-    @staticmethod
-    def get_resource_policy(secret_id):
-        resource_policy = {
-            "Version": "2012-10-17",
-            "Statement": {
-                "Effect": "Allow",
-                "Principal": {
-                    "AWS": [
-                        "arn:aws:iam::111122223333:root",
-                        "arn:aws:iam::444455556666:root",
-                    ]
-                },
-                "Action": ["secretsmanager:GetSecretValue"],
-                "Resource": "*",
-            },
+    def put_resource_policy(self, secret_id: str, policy: str):
+        """
+        The BlockPublicPolicy-parameter is not yet implemented
+        """
+        if not self._is_valid_identifier(secret_id):
+            raise SecretNotFoundException()
+
+        secret = self.secrets[secret_id]
+        secret.policy = policy
+        return secret.arn, secret.name
+
+    def get_resource_policy(self, secret_id):
+        if not self._is_valid_identifier(secret_id):
+            raise SecretNotFoundException()
+
+        secret = self.secrets[secret_id]
+        resp = {
+            "ARN": secret.arn,
+            "Name": secret.name,
         }
-        return json.dumps(
-            {
-                "ARN": secret_id,
-                "Name": secret_id,
-                "ResourcePolicy": json.dumps(resource_policy),
-            }
-        )
+        if secret.policy is not None:
+            resp["ResourcePolicy"] = secret.policy
+        return json.dumps(resp)
+
+    def delete_resource_policy(self, secret_id):
+        if not self._is_valid_identifier(secret_id):
+            raise SecretNotFoundException()
+
+        secret = self.secrets[secret_id]
+        secret.policy = None
+        return secret.arn, secret.name
 
 
 secretsmanager_backends = BackendDict(SecretsManagerBackend, "secretsmanager")
diff --git a/moto/secretsmanager/responses.py b/moto/secretsmanager/responses.py
index 87902d683..917579c41 100644
--- a/moto/secretsmanager/responses.py
+++ b/moto/secretsmanager/responses.py
@@ -174,6 +174,17 @@ class SecretsManagerResponse(BaseResponse):
         secret_id = self._get_param("SecretId")
         return self.backend.get_resource_policy(secret_id=secret_id)
 
+    def put_resource_policy(self):
+        secret_id = self._get_param("SecretId")
+        policy = self._get_param("ResourcePolicy")
+        arn, name = self.backend.put_resource_policy(secret_id, policy)
+        return json.dumps(dict(ARN=arn, Name=name))
+
+    def delete_resource_policy(self):
+        secret_id = self._get_param("SecretId")
+        arn, name = self.backend.delete_resource_policy(secret_id)
+        return json.dumps(dict(ARN=arn, Name=name))
+
     def tag_resource(self):
         secret_id = self._get_param("SecretId")
         tags = self._get_param("Tags", if_none=[])


Test patch:
diff --git a/tests/terraformtests/terraform-tests.success.txt b/tests/terraformtests/terraform-tests.success.txt
index 10a5a3029..0e4e3f71d 100644
--- a/tests/terraformtests/terraform-tests.success.txt
+++ b/tests/terraformtests/terraform-tests.success.txt
@@ -265,6 +265,22 @@ events:
   - TestAccEventsConnection
   - TestAccEventsConnectionDataSource
   - TestAccEventsPermission
+firehose:
+  - TestAccFirehoseDeliveryStreamDataSource_basic
+  - TestAccFirehoseDeliveryStream_basic
+  - TestAccFirehoseDeliveryStream_missingProcessing
+  - TestAccFirehoseDeliveryStream_HTTPEndpoint_retryDuration
+  - TestAccFirehoseDeliveryStream_ExtendedS3_
+  - TestAccFirehoseDeliveryStream_extendedS3DynamicPartitioningUpdate
+  - TestAccFirehoseDeliveryStream_extendedS3DynamicPartitioning
+  - TestAccFirehoseDeliveryStream_extendedS3KMSKeyARN
+  - TestAccFirehoseDeliveryStream_ExtendedS3Processing_empty
+  - TestAccFirehoseDeliveryStream_extendedS3Updates
+  - TestAccFirehoseDeliveryStream_s3
+  - TestAccFirehoseDeliveryStream_HTTP
+  - TestAccFirehoseDeliveryStream_http
+  - TestAccFirehoseDeliveryStream_splunk
+  - TestAccFirehoseDeliveryStream_Splunk
 glue:
   - TestAccGlueSchema_
 guardduty:
@@ -436,6 +452,14 @@ s3:
   - TestAccS3ObjectsDataSource_fetchOwner
 sagemaker:
   - TestAccSageMakerPrebuiltECRImageDataSource
+secretsmanager:
+  - TestAccSecretsManagerSecretDataSource_basic
+  - TestAccSecretsManagerSecretPolicy_
+  - TestAccSecretsManagerSecret_RecoveryWindowInDays_recreate
+  - TestAccSecretsManagerSecret_tags
+  - TestAccSecretsManagerSecret_kmsKeyID
+  - TestAccSecretsManagerSecret_withNamePrefix
+  - TestAccSecretsManagerSecret_rotationRules
 servicediscovery:
   - TestAccServiceDiscoveryDNSNamespaceDataSource
   - TestAccServiceDiscoveryHTTPNamespace
diff --git a/tests/test_cloudformation/test_import_value.py b/tests/test_cloudformation/test_import_value.py
index 82538cba3..6675373a2 100644
--- a/tests/test_cloudformation/test_import_value.py
+++ b/tests/test_cloudformation/test_import_value.py
@@ -1,5 +1,3 @@
-from __future__ import absolute_import, division
-
 # Standard library modules
 import unittest
 
diff --git a/tests/test_dax/test_dax.py b/tests/test_dax/test_dax.py
index 4722f8ae3..32af80020 100644
--- a/tests/test_dax/test_dax.py
+++ b/tests/test_dax/test_dax.py
@@ -1,12 +1,12 @@
 """Unit tests for dax-supported APIs."""
 import boto3
 import pytest
-import sure  # noqa # pylint: disable=unused-import
-
 from botocore.exceptions import ClientError
+
 from moto import mock_dax
 from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
 
+
 # See our Development Tips on writing tests for hints on how to write good tests:
 # http://docs.getmoto.org/en/latest/docs/contributing/development_tips/tests.html
 
@@ -21,31 +21,28 @@ def test_create_cluster_minimal():
         ReplicationFactor=3,
         IamRoleArn=iam_role_arn,
     )["Cluster"]
-
     described_cluster = client.describe_clusters(ClusterNames=["daxcluster"])[
         "Clusters"
     ][0]
 
     for cluster in [created_cluster, described_cluster]:
-        cluster["ClusterName"].should.equal("daxcluster")
-        cluster["ClusterArn"].should.equal(
-            f"arn:aws:dax:us-east-2:{ACCOUNT_ID}:cache/daxcluster"
+        assert cluster["ClusterName"] == "daxcluster"
+        assert (
+            cluster["ClusterArn"]
+            == f"arn:aws:dax:us-east-2:{ACCOUNT_ID}:cache/daxcluster"
         )
-        cluster["TotalNodes"].should.equal(3)
-        cluster["ActiveNodes"].should.equal(0)
-        cluster["NodeType"].should.equal("dax.t3.small")
-        cluster["Status"].should.equal("creating")
-        cluster["ClusterDiscoveryEndpoint"].should.equal({"Port": 8111})
-        cluster["PreferredMaintenanceWindow"].should.equal("thu:23:30-fri:00:30")
-        cluster["SubnetGroup"].should.equal("default")
-        cluster["SecurityGroups"].should.have.length_of(1)
-        cluster["IamRoleArn"].should.equal(iam_role_arn)
-        cluster.should.have.key("ParameterGroup")
-        cluster["ParameterGroup"].should.have.key("ParameterGroupName").equals(
-            "default.dax1.0"
-        )
-        cluster["SSEDescription"].should.equal({"Status": "DISABLED"})
-        cluster.should.have.key("ClusterEndpointEncryptionType").equals("NONE")
+        assert cluster["TotalNodes"] == 3
+        assert cluster["ActiveNodes"] == 0
+        assert cluster["NodeType"] == "dax.t3.small"
+        assert cluster["Status"] == "creating"
+        assert cluster["ClusterDiscoveryEndpoint"] == {"Port": 8111}
+        assert cluster["PreferredMaintenanceWindow"] == "thu:23:30-fri:00:30"
+        assert cluster["SubnetGroup"] == "default"
+        assert len(cluster["SecurityGroups"]) == 1
+        assert cluster["IamRoleArn"] == iam_role_arn
+        assert cluster["ParameterGroup"]["ParameterGroupName"] == "default.dax1.0"
+        assert cluster["SSEDescription"] == {"Status": "DISABLED"}
+        assert cluster["ClusterEndpointEncryptionType"] == "NONE"
 
 
 @mock_dax
@@ -59,14 +56,13 @@ def test_create_cluster_description():
         ReplicationFactor=3,
         IamRoleArn=iam_role_arn,
     )["Cluster"]
-
     described_cluster = client.describe_clusters(ClusterNames=["daxcluster"])[
         "Clusters"
     ][0]
 
     for cluster in [created_cluster, described_cluster]:
-        cluster["ClusterName"].should.equal("daxcluster")
-        cluster["Description"].should.equal("my cluster")
+        assert cluster["ClusterName"] == "daxcluster"
+        assert cluster["Description"] == "my cluster"
 
 
 @mock_dax
@@ -81,94 +77,46 @@ def test_create_cluster_with_sse_enabled():
         SSESpecification={"Enabled": True},
         ClusterEndpointEncryptionType="TLS",
     )["Cluster"]
-
     described_cluster = client.describe_clusters(ClusterNames=["daxcluster"])[
         "Clusters"
     ][0]
 
     for cluster in [created_cluster, described_cluster]:
-        cluster["ClusterName"].should.equal("daxcluster")
-        cluster["SSEDescription"].should.equal({"Status": "ENABLED"})
-        cluster["ClusterEndpointEncryptionType"].should.equal("TLS")
-
-
-@mock_dax
-def test_create_cluster_invalid_arn():
-    client = boto3.client("dax", region_name="eu-west-1")
-    with pytest.raises(ClientError) as exc:
-        client.create_cluster(
-            ClusterName="1invalid",
-            NodeType="dax.t3.small",
-            ReplicationFactor=3,
-            IamRoleArn="n/a",
-        )
-    err = exc.value.response["Error"]
-    err["Code"].should.equal("InvalidParameterValueException")
-    err["Message"].should.equal("ARNs must start with 'arn:': n/a")
-
-
-@mock_dax
-def test_create_cluster_invalid_arn_no_partition():
-    client = boto3.client("dax", region_name="eu-west-1")
-    with pytest.raises(ClientError) as exc:
-        client.create_cluster(
-            ClusterName="1invalid",
-            NodeType="dax.t3.small",
-            ReplicationFactor=3,
-            IamRoleArn="arn:sth",
-        )
-    err = exc.value.response["Error"]
-    err["Code"].should.equal("InvalidParameterValueException")
-    err["Message"].should.equal("Second colon partition not found: arn:sth")
+        assert cluster["ClusterName"] == "daxcluster"
+        assert cluster["SSEDescription"] == {"Status": "ENABLED"}
+        assert cluster["ClusterEndpointEncryptionType"] == "TLS"
 
 
 @mock_dax
-def test_create_cluster_invalid_arn_no_vendor():
+@pytest.mark.parametrize(
+    "iam_role,expected",
+    (
+        ("n/a", "ARNs must start with 'arn:': n/a"),
+        ("arn:sth", "Second colon partition not found: arn:sth"),
+        ("arn:sth:aws", "Third colon vendor not found: arn:sth:aws"),
+        (
+            "arn:sth:aws:else",
+            "Fourth colon (region/namespace delimiter) not found: arn:sth:aws:else",
+        ),
+        (
+            "arn:sth:aws:else:eu-west-1",
+            "Fifth colon (namespace/relative-id delimiter) not found: arn:sth:aws:else:eu-west-1",
+        ),
+    ),
+)
+def test_create_cluster_invalid_arn(iam_role: str, expected: str):
     client = boto3.client("dax", region_name="eu-west-1")
     with pytest.raises(ClientError) as exc:
         client.create_cluster(
             ClusterName="1invalid",
             NodeType="dax.t3.small",
             ReplicationFactor=3,
-            IamRoleArn="arn:sth:aws",
+            IamRoleArn=iam_role,
         )
     err = exc.value.response["Error"]
-    err["Code"].should.equal("InvalidParameterValueException")
-    err["Message"].should.equal("Third colon vendor not found: arn:sth:aws")
 
-
-@mock_dax
-def test_create_cluster_invalid_arn_no_region():
-    client = boto3.client("dax", region_name="eu-west-1")
-    with pytest.raises(ClientError) as exc:
-        client.create_cluster(
-            ClusterName="1invalid",
-            NodeType="dax.t3.small",
-            ReplicationFactor=3,
-            IamRoleArn="arn:sth:aws:else",
-        )
-    err = exc.value.response["Error"]
-    err["Code"].should.equal("InvalidParameterValueException")
-    err["Message"].should.equal(
-        "Fourth colon (region/namespace delimiter) not found: arn:sth:aws:else"
-    )
-
-
-@mock_dax
-def test_create_cluster_invalid_arn_no_namespace():
-    client = boto3.client("dax", region_name="eu-west-1")
-    with pytest.raises(ClientError) as exc:
-        client.create_cluster(
-            ClusterName="1invalid",
-            NodeType="dax.t3.small",
-            ReplicationFactor=3,
-            IamRoleArn="arn:sth:aws:else:eu-west-1",
-        )
-    err = exc.value.response["Error"]
-    err["Code"].should.equal("InvalidParameterValueException")
-    err["Message"].should.equal(
-        "Fifth colon (namespace/relative-id delimiter) not found: arn:sth:aws:else:eu-west-1"
-    )
+    assert err["Code"] == "InvalidParameterValueException"
+    assert err["Message"] == expected
 
 
 @mock_dax
@@ -177,7 +125,6 @@ def test_create_cluster_invalid_arn_no_namespace():
 )
 def test_create_cluster_invalid_name(name):
     client = boto3.client("dax", region_name="eu-west-1")
-
     with pytest.raises(ClientError) as exc:
         client.create_cluster(
             ClusterName=name,
@@ -186,8 +133,9 @@ def test_create_cluster_invalid_name(name):
             IamRoleArn="arn:aws:iam::486285699788:role/apigatewayrole",
         )
     err = exc.value.response["Error"]
-    err["Code"].should.equal("InvalidParameterValueException")
-    err["Message"].should.equal(
+
+    assert err["Code"] == "InvalidParameterValueException"
+    assert err["Message"] == (
         "Cluster ID specified 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."
     )
 
@@ -198,7 +146,6 @@ def test_create_cluster_invalid_name(name):
 )
 def test_describe_clusters_invalid_name(name):
     client = boto3.client("dax", region_name="eu-west-1")
-
     with pytest.raises(ClientError) as exc:
         client.describe_clusters(ClusterNames=[name])
     err = exc.value.response["Error"]
@@ -211,19 +158,17 @@ def test_describe_clusters_invalid_name(name):
 @mock_dax
 def test_delete_cluster_unknown():
     client = boto3.client("dax", region_name="eu-west-1")
-
     with pytest.raises(ClientError) as exc:
         client.delete_cluster(ClusterName="unknown")
-
     err = exc.value.response["Error"]
-    err["Code"].should.equals("ClusterNotFoundFault")
-    err["Message"].should.equal("Cluster not found.")
+
+    assert err["Code"] == "ClusterNotFoundFault"
+    assert err["Message"] == "Cluster not found."
 
 
 @mock_dax
 def test_delete_cluster():
     client = boto3.client("dax", region_name="eu-west-1")
-
     iam_role_arn = f"arn:aws:iam::{ACCOUNT_ID}:role/aws-service-role/dax.amazonaws.com/AWSServiceRoleForDAX"
     client.create_cluster(
         ClusterName="daxcluster",
@@ -231,32 +176,33 @@ def test_delete_cluster():
         ReplicationFactor=2,
         IamRoleArn=iam_role_arn,
     )
-
     client.delete_cluster(ClusterName="daxcluster")
 
     for _ in range(0, 3):
         # Cluster takes a while to delete...
         cluster = client.describe_clusters(ClusterNames=["daxcluster"])["Clusters"][0]
-        cluster["Status"].should.equal("deleting")
-        cluster["TotalNodes"].should.equal(2)
-        cluster["ActiveNodes"].should.equal(0)
-        cluster.shouldnt.have.key("Nodes")
+
+        assert cluster["Status"] == "deleting"
+        assert cluster["TotalNodes"] == 2
+        assert cluster["ActiveNodes"] == 0
+        assert "Nodes" not in cluster
 
     with pytest.raises(ClientError) as exc:
         client.describe_clusters(ClusterNames=["daxcluster"])
     err = exc.value.response["Error"]
-    err["Code"].should.equal("ClusterNotFoundFault")
+
+    assert err["Code"] == "ClusterNotFoundFault"
 
 
 @mock_dax
 def test_describe_cluster_unknown():
     client = boto3.client("dax", region_name="eu-west-1")
-
     with pytest.raises(ClientError) as exc:
         client.describe_clusters(ClusterNames=["unknown"])
     err = exc.value.response["Error"]
-    err["Code"].should.equal("ClusterNotFoundFault")
-    err["Message"].should.equal("Cluster unknown not found.")
+
+    assert err["Code"] == "ClusterNotFoundFault"
+    assert err["Message"] == "Cluster unknown not found."
 
 
 @mock_dax
@@ -271,8 +217,7 @@ def test_describe_clusters_returns_all():
             IamRoleArn=iam_role_arn,
         )
 
-    clusters = client.describe_clusters()["Clusters"]
-    clusters.should.have.length_of(50)
+    assert len(client.describe_clusters()["Clusters"]) == 50
 
 
 @mock_dax
@@ -288,99 +233,81 @@ def test_describe_clusters_paginates():
         )
 
     resp = client.describe_clusters(MaxResults=10)
-    resp["Clusters"].should.have.length_of(10)
-    resp.should.have.key("NextToken")
+    assert len(resp["Clusters"]) == 10
+    assert "NextToken" in resp
 
     resp = client.describe_clusters(MaxResults=10, NextToken=resp["NextToken"])
-    resp["Clusters"].should.have.length_of(10)
-    resp.should.have.key("NextToken")
+    assert len(resp["Clusters"]) == 10
+    assert "NextToken" in resp
 
     resp = client.describe_clusters(NextToken=resp["NextToken"])
-    resp["Clusters"].should.have.length_of(30)
-    resp.shouldnt.have.key("NextToken")
+    assert len(resp["Clusters"]) == 30
+    assert "NextToken" not in resp
 
 
 @mock_dax
 def test_describe_clusters_returns_nodes_after_some_time():
     client = boto3.client("dax", region_name="us-east-2")
+    iam_role_arn = f"arn:aws:iam::{ACCOUNT_ID}:role/aws-service-role/dax.amazonaws.com/AWSServiceRoleForDAX"
     client.create_cluster(
         ClusterName="daxcluster",
         NodeType="dax.t3.small",
         ReplicationFactor=3,
-        IamRoleArn=f"arn:aws:iam::{ACCOUNT_ID}:role/aws-service-role/dax.amazonaws.com/AWSServiceRoleForDAX",
-    )["Cluster"]
+        IamRoleArn=iam_role_arn,
+    )
 
     for _ in range(0, 3):
         # Cluster takes a while to load...
         cluster = client.describe_clusters(ClusterNames=["daxcluster"])["Clusters"][0]
-        cluster["Status"].should.equal("creating")
-        cluster.shouldnt.have.key("Nodes")
+        assert cluster["Status"] == "creating"
+        assert "Nodes" not in cluster
 
     # Finished loading by now
     cluster = client.describe_clusters(ClusterNames=["daxcluster"])["Clusters"][0]
 
-    cluster["ClusterName"].should.equal("daxcluster")
-    cluster["ClusterArn"].should.equal(
-        f"arn:aws:dax:us-east-2:{ACCOUNT_ID}:cache/daxcluster"
-    )
-    cluster["TotalNodes"].should.equal(3)
-    cluster["ActiveNodes"].should.equal(0)
-    cluster["NodeType"].should.equal("dax.t3.small")
-    cluster["Status"].should.equal("available")
+    assert cluster["TotalNodes"].should.equal(3)
+    assert cluster["ActiveNodes"].should.equal(0)
+    assert cluster["Status"].should.equal("available")
 
     # Address Info is only available when the cluster is ready
-    cluster.should.have.key("ClusterDiscoveryEndpoint")
     endpoint = cluster["ClusterDiscoveryEndpoint"]
-    endpoint.should.have.key("Address")
     address = endpoint["Address"]
     cluster_id = address.split(".")[1]
-    address.should.equal(
-        f"daxcluster.{cluster_id}.dax-clusters.us-east-2.amazonaws.com"
-    )
-    endpoint.should.have.key("Port").equal(8111)
-    endpoint.should.have.key("URL").equal(f"dax://{address}")
+
+    assert address == f"daxcluster.{cluster_id}.dax-clusters.us-east-2.amazonaws.com"
+    assert endpoint["Port"] == 8111
+    assert endpoint["URL"] == f"dax://{address}"
 
     # Nodes are only shown when the cluster is ready
-    cluster.should.have.key("Nodes").length_of(3)
+    assert len(cluster["Nodes"]) == 3
+
     for idx, a in enumerate(["a", "b", "c"]):
         node = cluster["Nodes"][idx]
-        node.should.have.key("NodeId").equals(f"daxcluster-{a}")
-        node.should.have.key("Endpoint")
-        node_address = (
+        expected_node_address = (
             f"daxcluster-{a}.{cluster_id}.nodes.dax-clusters.us-east-2.amazonaws.com"
         )
-        node["Endpoint"].should.have.key("Address").equals(node_address)
-        node["Endpoint"].should.have.key("Port").equals(8111)
-        node.should.contain("AvailabilityZone")
-        node.should.have.key("NodeStatus").equals("available")
-        node.should.have.key("ParameterGroupStatus").equals("in-sync")
-
-    cluster["PreferredMaintenanceWindow"].should.equal("thu:23:30-fri:00:30")
-    cluster["SubnetGroup"].should.equal("default")
-    cluster["SecurityGroups"].should.have.length_of(1)
-    cluster.should.have.key("ParameterGroup")
-    cluster["ParameterGroup"].should.have.key("ParameterGroupName").equals(
-        "default.dax1.0"
-    )
-    cluster["SSEDescription"].should.equal({"Status": "DISABLED"})
-    cluster.should.have.key("ClusterEndpointEncryptionType").equals("NONE")
+
+        assert node["NodeId"] == f"daxcluster-{a}"
+        assert node["Endpoint"]["Address"] == expected_node_address
+        assert node["Endpoint"]["Port"] == 8111
+        assert "AvailabilityZone" in node
+        assert node["NodeStatus"] == "available"
+        assert node["ParameterGroupStatus"] == "in-sync"
 
 
 @mock_dax
 def test_list_tags_unknown():
     client = boto3.client("dax", region_name="ap-southeast-1")
-
     with pytest.raises(ClientError) as exc:
         client.list_tags(ResourceName="unknown")
-
     err = exc.value.response["Error"]
-    err["Code"].should.equal("ClusterNotFoundFault")
+
+    assert err["Code"] == "ClusterNotFoundFault"
 
 
 @mock_dax
 def test_list_tags():
     client = boto3.client("dax", region_name="ap-southeast-1")
-
     cluster = client.create_cluster(
         ClusterName="daxcluster",
         NodeType="dax.t3.small",
@@ -396,8 +323,8 @@ def test_list_tags():
     for name in ["daxcluster", cluster["ClusterArn"]]:
         resp = client.list_tags(ResourceName=name)
 
-        resp.shouldnt.have.key("NextToken")
-        resp.should.have.key("Tags").equals(
+        assert "NextToken" not in resp
+        assert resp["Tags"] == (
             [
                 {"Key": "tag1", "Value": "value1"},
                 {"Key": "tag2", "Value": "value2"},
@@ -409,20 +336,18 @@ def test_list_tags():
 @mock_dax
 def test_increase_replication_factor_unknown():
     client = boto3.client("dax", region_name="ap-southeast-1")
-
     with pytest.raises(ClientError) as exc:
         client.increase_replication_factor(
             ClusterName="unknown", NewReplicationFactor=2
         )
-
     err = exc.value.response["Error"]
-    err["Code"].should.equal("ClusterNotFoundFault")
+
+    assert err["Code"] == "ClusterNotFoundFault"
 
 
 @mock_dax
 def test_increase_replication_factor():
     client = boto3.client("dax", region_name="ap-southeast-1")
-
     name = "daxcluster"
     cluster = client.create_cluster(
         ClusterName=name,
@@ -435,23 +360,25 @@ def test_increase_replication_factor():
             {"Key": "tag3", "Value": "value3"},
         ],
     )["Cluster"]
-    cluster["TotalNodes"].should.equal(2)
 
-    new_cluster = client.increase_replication_factor(
+    assert cluster["TotalNodes"] == 2
+
+    adjusted_cluster = client.increase_replication_factor(
         ClusterName=name, NewReplicationFactor=5
     )["Cluster"]
-    new_cluster["TotalNodes"].should.equal(5)
+    described_cluster = client.describe_clusters(ClusterNames=[name])["Clusters"][0]
 
-    new_cluster = client.describe_clusters(ClusterNames=[name])["Clusters"][0]
-    new_cluster["TotalNodes"].should.equal(5)
+    assert adjusted_cluster["TotalNodes"] == 5
+    assert described_cluster["TotalNodes"] == 5
 
     # Progress cluster until it's available
-    client.describe_clusters(ClusterNames=[name])["Clusters"][0]
-    client.describe_clusters(ClusterNames=[name])["Clusters"][0]
+    for _ in range(0, 3):
+        client.describe_clusters(ClusterNames=[name])
+
+    described_cluster = client.describe_clusters(ClusterNames=[name])["Clusters"][0]
+    node_ids = set([node["NodeId"] for node in described_cluster["Nodes"]])
 
-    cluster = client.describe_clusters(ClusterNames=[name])["Clusters"][0]
-    node_ids = set([n["NodeId"] for n in cluster["Nodes"]])
-    node_ids.should.equal(
+    assert node_ids == (
         {f"{name}-a", f"{name}-b", f"{name}-c", f"{name}-d", f"{name}-e"}
     )
 
@@ -459,81 +386,70 @@ def test_increase_replication_factor():
 @mock_dax
 def test_decrease_replication_factor_unknown():
     client = boto3.client("dax", region_name="ap-southeast-1")
-
     with pytest.raises(ClientError) as exc:
         client.decrease_replication_factor(
             ClusterName="unknown", NewReplicationFactor=2
         )
-
     err = exc.value.response["Error"]
-    err["Code"].should.equal("ClusterNotFoundFault")
+
+    assert err["Code"] == "ClusterNotFoundFault"
 
 
 @mock_dax
 def test_decrease_replication_factor():
     client = boto3.client("dax", region_name="eu-west-1")
-
     name = "daxcluster"
     client.create_cluster(
         ClusterName=name,
         NodeType="dax.t3.small",
         ReplicationFactor=5,
         IamRoleArn=f"arn:aws:iam::{ACCOUNT_ID}:role/aws-service-role/dax.amazonaws.com/AWSServiceRoleForDAX",
-        Tags=[
-            {"Key": "tag1", "Value": "value1"},
-            {"Key": "tag2", "Value": "value2"},
-            {"Key": "tag3", "Value": "value3"},
-        ],
     )
 
-    new_cluster = client.decrease_replication_factor(
+    adjusted_cluster = client.decrease_replication_factor(
         ClusterName=name, NewReplicationFactor=3
     )["Cluster"]
-    new_cluster["TotalNodes"].should.equal(3)
+    described_cluster = client.describe_clusters(ClusterNames=[name])["Clusters"][0]
 
-    new_cluster = client.describe_clusters(ClusterNames=[name])["Clusters"][0]
-    new_cluster["TotalNodes"].should.equal(3)
+    assert adjusted_cluster["TotalNodes"] == 3
+    assert described_cluster["TotalNodes"] == 3
 
     # Progress cluster until it's available
-    client.describe_clusters(ClusterNames=[name])["Clusters"][0]
-    client.describe_clusters(ClusterNames=[name])["Clusters"][0]
+    for _ in range(0, 3):
+        client.describe_clusters(ClusterNames=[name])
 
-    cluster = client.describe_clusters(ClusterNames=[name])["Clusters"][0]
-    node_ids = set([n["NodeId"] for n in cluster["Nodes"]])
-    node_ids.should.equal({f"{name}-a", f"{name}-b", f"{name}-c"})
+    described_cluster = client.describe_clusters(ClusterNames=[name])["Clusters"][0]
+    node_ids = set([node["NodeId"] for node in described_cluster["Nodes"]])
+
+    assert node_ids == ({f"{name}-a", f"{name}-b", f"{name}-c"})
 
 
 @mock_dax
 def test_decrease_replication_factor_specific_nodeids():
     client = boto3.client("dax", region_name="ap-southeast-1")
-
     name = "daxcluster"
     client.create_cluster(
         ClusterName=name,
         NodeType="dax.t3.small",
         ReplicationFactor=5,
         IamRoleArn=f"arn:aws:iam::{ACCOUNT_ID}:role/aws-service-role/dax.amazonaws.com/AWSServiceRoleForDAX",
-        Tags=[
-            {"Key": "tag1", "Value": "value1"},
-            {"Key": "tag2", "Value": "value2"},
-            {"Key": "tag3", "Value": "value3"},
-        ],
     )
 
-    new_cluster = client.decrease_replication_factor(
+    adjusted_cluster = client.decrease_replication_factor(
         ClusterName=name,
         NewReplicationFactor=3,
         NodeIdsToRemove=["daxcluster-b", "daxcluster-c"],
     )["Cluster"]
-    new_cluster["TotalNodes"].should.equal(3)
+    described_cluster = client.describe_clusters(ClusterNames=[name])["Clusters"][0]
 
-    new_cluster = client.describe_clusters(ClusterNames=[name])["Clusters"][0]
-    new_cluster["TotalNodes"].should.equal(3)
+    assert adjusted_cluster["TotalNodes"] == 3
+    assert described_cluster["TotalNodes"] == 3
 
     # Progress cluster until it's available
-    client.describe_clusters(ClusterNames=[name])["Clusters"][0]
-    client.describe_clusters(ClusterNames=[name])["Clusters"][0]
+    for _ in range(0, 3):
+        client.describe_clusters(ClusterNames=[name])
+
+    described_cluster = client.describe_clusters(ClusterNames=[name])["Clusters"][0]
+    node_ids = set([node["NodeId"] for node in described_cluster["Nodes"]])
 
-    cluster = client.describe_clusters(ClusterNames=[name])["Clusters"][0]
-    node_ids = set([n["NodeId"] for n in cluster["Nodes"]])
-    node_ids.should.equal({f"{name}-a", f"{name}-d", f"{name}-e"})
+    assert node_ids == ({f"{name}-a", f"{name}-d", f"{name}-e"})
diff --git a/tests/test_dax/test_server.py b/tests/test_dax/test_server.py
index f0fc9aefa..3f2d2453f 100644
--- a/tests/test_dax/test_server.py
+++ b/tests/test_dax/test_server.py
@@ -1,5 +1,4 @@
 import json
-import sure  # noqa # pylint: disable=unused-import
 
 import moto.server as server
 
@@ -7,9 +6,9 @@ import moto.server as server
 def test_dax_list():
     backend = server.create_backend_app("dax")
     test_client = backend.test_client()
-
     resp = test_client.post(
         "/", headers={"X-Amz-Target": "AmazonDAXV3.DescribeClusters"}, data="{}"
     )
-    resp.status_code.should.equal(200)
-    json.loads(resp.data).should.equal({"Clusters": [], "NextToken": None})
+
+    assert resp.status_code == 200
+    assert json.loads(resp.data) == {"Clusters": [], "NextToken": None}
diff --git a/tests/test_dynamodb/exceptions/test_key_length_exceptions.py b/tests/test_dynamodb/exceptions/test_key_length_exceptions.py
index 32332988d..de7e1b1a7 100644
--- a/tests/test_dynamodb/exceptions/test_key_length_exceptions.py
+++ b/tests/test_dynamodb/exceptions/test_key_length_exceptions.py
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 import boto3
 import sure  # noqa # pylint: disable=unused-import
 import pytest
diff --git a/tests/test_dynamodbstreams/test_dynamodbstreams.py b/tests/test_dynamodbstreams/test_dynamodbstreams.py
index 2016d6424..00494ea73 100644
--- a/tests/test_dynamodbstreams/test_dynamodbstreams.py
+++ b/tests/test_dynamodbstreams/test_dynamodbstreams.py
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 import pytest
 
 import boto3
diff --git a/tests/test_firehose/test_firehose.py b/tests/test_firehose/test_firehose.py
index 223d9baeb..9ce7ce7d1 100644
--- a/tests/test_firehose/test_firehose.py
+++ b/tests/test_firehose/test_firehose.py
@@ -32,18 +32,6 @@ def test_warnings():
     client = boto3.client("firehose", region_name=TEST_REGION)
     s3_dest_config = sample_s3_dest_config()
 
-    # DeliveryStreamEncryption is not supported.
-    stream_name = f"test_warning_{mock_random.get_random_hex(6)}"
-    with warnings.catch_warnings(record=True) as warn_msg:
-        client.create_delivery_stream(
-            DeliveryStreamName=stream_name,
-            ExtendedS3DestinationConfiguration=s3_dest_config,
-            DeliveryStreamEncryptionConfigurationInput={"KeyType": "AWS_OWNED_CMK"},
-        )
-    assert "server-side encryption enabled is not yet implemented" in str(
-        warn_msg[-1].message
-    )
-
     # Can't create a delivery stream for Splunk as it's unimplemented.
     stream_name = f"test_splunk_destination_{mock_random.get_random_hex(6)}"
     with warnings.catch_warnings(record=True) as warn_msg:
diff --git a/tests/test_firehose/test_firehose_destination_types.py b/tests/test_firehose/test_firehose_destination_types.py
index 8390db197..5f135e32c 100644
--- a/tests/test_firehose/test_firehose_destination_types.py
+++ b/tests/test_firehose/test_firehose_destination_types.py
@@ -317,52 +317,3 @@ def test_create_s3_delivery_stream():
             "HasMoreDestinations": False,
         }
     )
-
-
-@mock_firehose
-def test_create_http_stream():
-    """Verify fields of a HTTP delivery stream."""
-    client = boto3.client("firehose", region_name=TEST_REGION)
-
-    stream_name = f"stream_{mock_random.get_random_hex(6)}"
-    response = create_http_delivery_stream(client, stream_name)
-    stream_arn = response["DeliveryStreamARN"]
-
-    response = client.describe_delivery_stream(DeliveryStreamName=stream_name)
-    stream_description = response["DeliveryStreamDescription"]
-
-    # Sure and Freezegun don't play nicely together
-    _ = stream_description.pop("CreateTimestamp")
-    _ = stream_description.pop("LastUpdateTimestamp")
-
-    stream_description.should.equal(
-        {
-            "DeliveryStreamName": stream_name,
-            "DeliveryStreamARN": stream_arn,
-            "DeliveryStreamStatus": "ACTIVE",
-            "DeliveryStreamType": "DirectPut",
-            "VersionId": "1",
-            "Destinations": [
-                {
-                    "DestinationId": "destinationId-000000000001",
-                    "HttpEndpointDestinationDescription": {
-                        "EndpointConfiguration": {"Url": "google.com"},
-                        "RetryOptions": {"DurationInSeconds": 100},
-                        "BufferingHints": {"SizeInMBs": 123, "IntervalInSeconds": 124},
-                        "CloudWatchLoggingOptions": {"Enabled": False},
-                        "S3DestinationDescription": {
-                            "RoleARN": f"arn:aws:iam::{ACCOUNT_ID}:role/firehose_delivery_role",
-                            "BucketARN": "arn:aws:s3:::firehose-test",
-                            "Prefix": "myFolder/",
-                            "BufferingHints": {
-                                "SizeInMBs": 123,
-                                "IntervalInSeconds": 124,
-                            },
-                            "CompressionFormat": "UNCOMPRESSED",
-                        },
-                    },
-                }
-            ],
-            "HasMoreDestinations": False,
-        }
-    )
diff --git a/tests/test_firehose/test_firehose_encryption.py b/tests/test_firehose/test_firehose_encryption.py
new file mode 100644
index 000000000..a7510e691
--- /dev/null
+++ b/tests/test_firehose/test_firehose_encryption.py
@@ -0,0 +1,89 @@
+import boto3
+import pytest
+
+from botocore.exceptions import ClientError
+from moto import mock_firehose
+from uuid import uuid4
+from .test_firehose import sample_s3_dest_config
+
+
+@mock_firehose
+def test_firehose_without_encryption():
+    client = boto3.client("firehose", region_name="us-east-2")
+    name = str(uuid4())[0:6]
+    client.create_delivery_stream(
+        DeliveryStreamName=name,
+        ExtendedS3DestinationConfiguration=sample_s3_dest_config(),
+    )
+
+    resp = client.describe_delivery_stream(DeliveryStreamName=name)[
+        "DeliveryStreamDescription"
+    ]
+    resp.shouldnt.have.key("DeliveryStreamEncryptionConfiguration")
+
+    client.start_delivery_stream_encryption(
+        DeliveryStreamName=name,
+        DeliveryStreamEncryptionConfigurationInput={"KeyType": "AWS_OWNED_CMK"},
+    )
+
+    stream = client.describe_delivery_stream(DeliveryStreamName=name)[
+        "DeliveryStreamDescription"
+    ]
+    stream.should.have.key("DeliveryStreamEncryptionConfiguration").equals(
+        {
+            "KeyType": "AWS_OWNED_CMK",
+            "Status": "ENABLED",
+        }
+    )
+
+
+@mock_firehose
+def test_firehose_with_encryption():
+    client = boto3.client("firehose", region_name="us-east-2")
+    name = str(uuid4())[0:6]
+    client.create_delivery_stream(
+        DeliveryStreamName=name,
+        ExtendedS3DestinationConfiguration=sample_s3_dest_config(),
+        DeliveryStreamEncryptionConfigurationInput={"KeyType": "AWS_OWNED_CMK"},
+    )
+
+    stream = client.describe_delivery_stream(DeliveryStreamName=name)[
+        "DeliveryStreamDescription"
+    ]
+    stream.should.have.key("DeliveryStreamEncryptionConfiguration").equals(
+        {"KeyType": "AWS_OWNED_CMK"}
+    )
+
+    client.stop_delivery_stream_encryption(DeliveryStreamName=name)
+
+    stream = client.describe_delivery_stream(DeliveryStreamName=name)[
+        "DeliveryStreamDescription"
+    ]
+    stream.should.have.key("DeliveryStreamEncryptionConfiguration").should.have.key(
+        "Status"
+    ).equals("DISABLED")
+
+
+@mock_firehose
+def test_start_encryption_on_unknown_stream():
+    client = boto3.client("firehose", region_name="us-east-2")
+
+    with pytest.raises(ClientError) as exc:
+        client.start_delivery_stream_encryption(
+            DeliveryStreamName="?",
+            DeliveryStreamEncryptionConfigurationInput={"KeyType": "AWS_OWNED_CMK"},
+        )
+    err = exc.value.response["Error"]
+    err["Code"].should.equal("ResourceNotFoundException")
+    err["Message"].should.equal("Firehose ? under account 123456789012 not found.")
+
+
+@mock_firehose
+def test_stop_encryption_on_unknown_stream():
+    client = boto3.client("firehose", region_name="us-east-2")
+
+    with pytest.raises(ClientError) as exc:
+        client.stop_delivery_stream_encryption(DeliveryStreamName="?")
+    err = exc.value.response["Error"]
+    err["Code"].should.equal("ResourceNotFoundException")
+    err["Message"].should.equal("Firehose ? under account 123456789012 not found.")
diff --git a/tests/test_firehose/test_http_destinations.py b/tests/test_firehose/test_http_destinations.py
new file mode 100644
index 000000000..1fd5077be
--- /dev/null
+++ b/tests/test_firehose/test_http_destinations.py
@@ -0,0 +1,85 @@
+import boto3
+import sure  # noqa # pylint: disable=unused-import
+
+from moto import mock_firehose
+from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
+from moto.moto_api._internal import mock_random
+from .test_firehose_destination_types import create_http_delivery_stream
+
+TEST_REGION = "us-west-1"
+
+
+@mock_firehose
+def test_create_http_stream():
+    """Verify fields of a HTTP delivery stream."""
+    client = boto3.client("firehose", region_name=TEST_REGION)
+
+    stream_name = f"stream_{mock_random.get_random_hex(6)}"
+    response = create_http_delivery_stream(client, stream_name)
+    stream_arn = response["DeliveryStreamARN"]
+
+    response = client.describe_delivery_stream(DeliveryStreamName=stream_name)
+    stream_description = response["DeliveryStreamDescription"]
+
+    # Sure and Freezegun don't play nicely together
+    _ = stream_description.pop("CreateTimestamp")
+    _ = stream_description.pop("LastUpdateTimestamp")
+
+    stream_description.should.equal(
+        {
+            "DeliveryStreamName": stream_name,
+            "DeliveryStreamARN": stream_arn,
+            "DeliveryStreamStatus": "ACTIVE",
+            "DeliveryStreamType": "DirectPut",
+            "VersionId": "1",
+            "Destinations": [
+                {
+                    "DestinationId": "destinationId-000000000001",
+                    "HttpEndpointDestinationDescription": {
+                        "EndpointConfiguration": {"Url": "google.com"},
+                        "RetryOptions": {"DurationInSeconds": 100},
+                        "BufferingHints": {"SizeInMBs": 123, "IntervalInSeconds": 124},
+                        "CloudWatchLoggingOptions": {"Enabled": False},
+                        "S3DestinationDescription": {
+                            "RoleARN": f"arn:aws:iam::{ACCOUNT_ID}:role/firehose_delivery_role",
+                            "BucketARN": "arn:aws:s3:::firehose-test",
+                            "Prefix": "myFolder/",
+                            "BufferingHints": {
+                                "SizeInMBs": 123,
+                                "IntervalInSeconds": 124,
+                            },
+                            "CompressionFormat": "UNCOMPRESSED",
+                        },
+                    },
+                }
+            ],
+            "HasMoreDestinations": False,
+        }
+    )
+
+
+@mock_firehose
+def test_update_s3_for_http_stream():
+    """Verify fields of a HTTP delivery stream."""
+    client = boto3.client("firehose", region_name=TEST_REGION)
+
+    stream_name = f"stream_{mock_random.get_random_hex(6)}"
+    create_http_delivery_stream(client, stream_name)
+
+    stream = client.describe_delivery_stream(DeliveryStreamName=stream_name)
+    version_id = stream["DeliveryStreamDescription"]["VersionId"]
+
+    client.update_destination(
+        DeliveryStreamName=stream_name,
+        CurrentDeliveryStreamVersionId=version_id,
+        DestinationId="destinationId-000000000001",
+        HttpEndpointDestinationUpdate={"S3Update": {"ErrorOutputPrefix": "prefix2"}},
+    )
+
+    desc = client.describe_delivery_stream(DeliveryStreamName=stream_name)[
+        "DeliveryStreamDescription"
+    ]
+    s3_desc = desc["Destinations"][0]["HttpEndpointDestinationDescription"][
+        "S3DestinationDescription"
+    ]
+    s3_desc.should.have.key("ErrorOutputPrefix").equals("prefix2")
diff --git a/tests/test_glue/test_glue.py b/tests/test_glue/test_glue.py
index 820bbc14e..03d7a54e8 100644
--- a/tests/test_glue/test_glue.py
+++ b/tests/test_glue/test_glue.py
@@ -83,30 +83,36 @@ def test_get_job_exists():
         "SecurityConfiguration": "test_config",
         "NotificationProperty": {"NotifyDelayAfter": 123},
         "GlueVersion": "string",
+        "CodeGenConfigurationNodes": {},
+        "ExecutionClass": "string",
+        "SourceControlDetails": {},
     }
     job_name = create_test_job_w_all_attributes(client, **job_attributes)
     job = client.get_job(JobName=job_name)["Job"]
-    job.should.have.key("Name").equals(job_name)
-    job.should.have.key("Description")
-    job.should.have.key("LogUri")
-    job.should.have.key("Role")
-    job.should.have.key("ExecutionProperty").equals({"MaxConcurrentRuns": 123})
-    job.should.have.key("CreatedOn")
-    job.should.have.key("LastModifiedOn")
-    job.should.have.key("ExecutionProperty")
-    job.should.have.key("Command")
-    job.should.have.key("DefaultArguments")
-    job.should.have.key("NonOverridableArguments")
-    job.should.have.key("Connections")
-    job.should.have.key("MaxRetries")
-    job.should.have.key("AllocatedCapacity")
-    job.should.have.key("Timeout")
-    job.should.have.key("MaxCapacity")
-    job.should.have.key("WorkerType")
-    job.should.have.key("NumberOfWorkers")
-    job.should.have.key("SecurityConfiguration")
-    job.should.have.key("NotificationProperty")
-    job.should.have.key("GlueVersion")
+    assert job["Name"] == job_name
+    assert "Description" in job
+    assert "LogUri" in job
+    assert "Role" in job
+    assert job["ExecutionProperty"] == {"MaxConcurrentRuns": 123}
+    assert "CreatedOn" in job
+    assert "LastModifiedOn" in job
+    assert "ExecutionProperty" in job
+    assert "Command" in job
+    assert "DefaultArguments" in job
+    assert "NonOverridableArguments" in job
+    assert "Connections" in job
+    assert "MaxRetries" in job
+    assert "AllocatedCapacity" in job
+    assert "Timeout" in job
+    assert "MaxCapacity" in job
+    assert "WorkerType" in job
+    assert "NumberOfWorkers" in job
+    assert "SecurityConfiguration" in job
+    assert "NotificationProperty" in job
+    assert "GlueVersion" in job
+    assert "CodeGenConfigurationNodes" in job
+    assert "ExecutionClass" in job
+    assert "SourceControlDetails" in job
 
 
 @mock_glue
diff --git a/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py b/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py
index 072092ad3..cba164cfb 100644
--- a/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py
+++ b/tests/test_resourcegroupstaggingapi/test_resourcegroupstaggingapi.py
@@ -1,4 +1,5 @@
 import boto3
+import json
 import sure  # noqa # pylint: disable=unused-import
 from moto import mock_ec2
 from moto import mock_elbv2
@@ -8,10 +9,219 @@ from moto import mock_resourcegroupstaggingapi
 from moto import mock_s3
 from moto import mock_lambda
 from moto import mock_iam
+from moto import mock_cloudformation
+from moto import mock_ecs
 from botocore.client import ClientError
 from tests import EXAMPLE_AMI_ID, EXAMPLE_AMI_ID2
 
 
+@mock_kms
+@mock_cloudformation
+@mock_resourcegroupstaggingapi
+def test_get_resources_cloudformation():
+
+    template = {
+        "AWSTemplateFormatVersion": "2010-09-09",
+        "Resources": {"test": {"Type": "AWS::S3::Bucket"}},
+    }
+    template_json = json.dumps(template)
+
+    cf_client = boto3.client("cloudformation", region_name="us-east-1")
+
+    stack_one = cf_client.create_stack(
+        StackName="stack-1",
+        TemplateBody=template_json,
+        Tags=[{"Key": "tag", "Value": "one"}],
+    ).get("StackId")
+    stack_two = cf_client.create_stack(
+        StackName="stack-2",
+        TemplateBody=template_json,
+        Tags=[{"Key": "tag", "Value": "two"}],
+    ).get("StackId")
+    stack_three = cf_client.create_stack(
+        StackName="stack-3",
+        TemplateBody=template_json,
+        Tags=[{"Key": "tag", "Value": "three"}],
+    ).get("StackId")
+
+    rgta_client = boto3.client("resourcegroupstaggingapi", region_name="us-east-1")
+
+    resp = rgta_client.get_resources(TagFilters=[{"Key": "tag", "Values": ["one"]}])
+    resp["ResourceTagMappingList"].should.have.length_of(1)
+    resp["ResourceTagMappingList"][0]["ResourceARN"].should.contain(stack_one)
+
+    resp = rgta_client.get_resources(
+        TagFilters=[{"Key": "tag", "Values": ["one", "three"]}]
+    )
+    resp["ResourceTagMappingList"].should.have.length_of(2)
+    resp["ResourceTagMappingList"][0]["ResourceARN"].should.contain(stack_one)
+    resp["ResourceTagMappingList"][1]["ResourceARN"].should.contain(stack_three)
+
+    kms_client = boto3.client("kms", region_name="us-east-1")
+    kms_client.create_key(
+        KeyUsage="ENCRYPT_DECRYPT", Tags=[{"TagKey": "tag", "TagValue": "two"}]
+    )
+
+    resp = rgta_client.get_resources(TagFilters=[{"Key": "tag", "Values": ["two"]}])
+    resp["ResourceTagMappingList"].should.have.length_of(2)
+    resp["ResourceTagMappingList"][0]["ResourceARN"].should.contain(stack_two)
+    resp["ResourceTagMappingList"][1]["ResourceARN"].should.contain("kms")
+
+    resp = rgta_client.get_resources(
+        ResourceTypeFilters=["cloudformation:stack"],
+        TagFilters=[{"Key": "tag", "Values": ["two"]}],
+    )
+    resp["ResourceTagMappingList"].should.have.length_of(1)
+    resp["ResourceTagMappingList"][0]["ResourceARN"].should.contain(stack_two)
+
+
+@mock_ecs
+@mock_ec2
+@mock_resourcegroupstaggingapi
+def test_get_resources_ecs():
+
+    # ecs:cluster
+    client = boto3.client("ecs", region_name="us-east-1")
+    cluster_one = (
+        client.create_cluster(
+            clusterName="cluster-a", tags=[{"key": "tag", "value": "a tag"}]
+        )
+        .get("cluster")
+        .get("clusterArn")
+    )
+    cluster_two = (
+        client.create_cluster(
+            clusterName="cluster-b", tags=[{"key": "tag", "value": "b tag"}]
+        )
+        .get("cluster")
+        .get("clusterArn")
+    )
+
+    rgta_client = boto3.client("resourcegroupstaggingapi", region_name="us-east-1")
+    resp = rgta_client.get_resources(TagFilters=[{"Key": "tag", "Values": ["a tag"]}])
+    resp["ResourceTagMappingList"].should.have.length_of(1)
+    resp["ResourceTagMappingList"][0]["ResourceARN"].should.contain(cluster_one)
+
+    # ecs:service
+    service_one = (
+        client.create_service(
+            cluster=cluster_one,
+            serviceName="service-a",
+            tags=[{"key": "tag", "value": "a tag"}],
+        )
+        .get("service")
+        .get("serviceArn")
+    )
+
+    service_two = (
+        client.create_service(
+            cluster=cluster_two,
+            serviceName="service-b",
+            tags=[{"key": "tag", "value": "b tag"}],
+        )
+        .get("service")
+        .get("serviceArn")
+    )
+
+    resp = rgta_client.get_resources(TagFilters=[{"Key": "tag", "Values": ["a tag"]}])
+    resp["ResourceTagMappingList"].should.have.length_of(2)
+    resp["ResourceTagMappingList"][0]["ResourceARN"].should.contain(service_one)
+    resp["ResourceTagMappingList"][1]["ResourceARN"].should.contain(cluster_one)
+
+    resp = rgta_client.get_resources(
+        ResourceTypeFilters=["ecs:cluster"],
+        TagFilters=[{"Key": "tag", "Values": ["b tag"]}],
+    )
+    resp["ResourceTagMappingList"].should.have.length_of(1)
+    resp["ResourceTagMappingList"][0]["ResourceARN"].should_not.contain(service_two)
+    resp["ResourceTagMappingList"][0]["ResourceARN"].should.contain(cluster_two)
+
+    resp = rgta_client.get_resources(
+        ResourceTypeFilters=["ecs:service"],
+        TagFilters=[{"Key": "tag", "Values": ["b tag"]}],
+    )
+    resp["ResourceTagMappingList"].should.have.length_of(1)
+    resp["ResourceTagMappingList"][0]["ResourceARN"].should.contain(service_two)
+    resp["ResourceTagMappingList"][0]["ResourceARN"].should_not.contain(cluster_two)
+
+    # ecs:task
+    resp = client.register_task_definition(
+        family="test_ecs_task",
+        containerDefinitions=[
+            {
+                "name": "hello_world",
+                "image": "docker/hello-world:latest",
+                "cpu": 1024,
+                "memory": 400,
+                "essential": True,
+                "environment": [
+                    {"name": "AWS_ACCESS_KEY_ID", "value": "SOME_ACCESS_KEY"}
+                ],
+                "logConfiguration": {"logDriver": "json-file"},
+            }
+        ],
+    )
+
+    ec2_client = boto3.client("ec2", region_name="us-east-1")
+    vpc = ec2_client.create_vpc(CidrBlock="10.0.0.0/16").get("Vpc").get("VpcId")
+    subnet = (
+        ec2_client.create_subnet(VpcId=vpc, CidrBlock="10.0.0.0/18")
+        .get("Subnet")
+        .get("SubnetId")
+    )
+    sg = ec2_client.create_security_group(
+        VpcId=vpc, GroupName="test-ecs", Description="moto ecs"
+    ).get("GroupId")
+
+    task_one = (
+        client.run_task(
+            cluster="cluster-a",
+            taskDefinition="test_ecs_task",
+            launchType="FARGATE",
+            networkConfiguration={
+                "awsvpcConfiguration": {
+                    "subnets": [subnet],
+                    "securityGroups": [sg],
+                }
+            },
+            tags=[{"key": "tag", "value": "a tag"}],
+        )
+        .get("tasks")[0]
+        .get("taskArn")
+    )
+
+    task_two = (
+        client.run_task(
+            cluster="cluster-b",
+            taskDefinition="test_ecs_task",
+            launchType="FARGATE",
+            networkConfiguration={
+                "awsvpcConfiguration": {
+                    "subnets": [subnet],
+                    "securityGroups": [sg],
+                }
+            },
+            tags=[{"key": "tag", "value": "b tag"}],
+        )
+        .get("tasks")[0]
+        .get("taskArn")
+    )
+
+    resp = rgta_client.get_resources(TagFilters=[{"Key": "tag", "Values": ["b tag"]}])
+    resp["ResourceTagMappingList"].should.have.length_of(3)
+    resp["ResourceTagMappingList"][0]["ResourceARN"].should.contain(service_two)
+    resp["ResourceTagMappingList"][1]["ResourceARN"].should.contain(cluster_two)
+    resp["ResourceTagMappingList"][2]["ResourceARN"].should.contain(task_two)
+
+    resp = rgta_client.get_resources(
+        ResourceTypeFilters=["ecs:task"],
+        TagFilters=[{"Key": "tag", "Values": ["a tag"]}],
+    )
+    resp["ResourceTagMappingList"].should.have.length_of(1)
+    resp["ResourceTagMappingList"][0]["ResourceARN"].should.contain(task_one)
+    resp["ResourceTagMappingList"][0]["ResourceARN"].should_not.contain(task_two)
+
+
 @mock_rds
 @mock_ec2
 @mock_resourcegroupstaggingapi
@@ -422,50 +632,6 @@ def test_get_resources_rds():
     assert_response(resp, 2)
 
 
-@mock_rds
-@mock_resourcegroupstaggingapi
-def test_get_resources_rds_clusters():
-    client = boto3.client("rds", region_name="us-west-2")
-    resources_tagged = []
-    resources_untagged = []
-    for i in range(3):
-        cluster = client.create_db_cluster(
-            DBClusterIdentifier=f"db-cluster-{i}",
-            Engine="postgres",
-            MasterUsername="admin",
-            MasterUserPassword="P@ssw0rd!",
-            CopyTagsToSnapshot=True if i else False,
-            Tags=[{"Key": "test", "Value": f"value-{i}"}] if i else [],
-        ).get("DBCluster")
-        snapshot = client.create_db_cluster_snapshot(
-            DBClusterIdentifier=cluster["DBClusterIdentifier"],
-            DBClusterSnapshotIdentifier=f"snapshot-{i}",
-        ).get("DBClusterSnapshot")
-        group = resources_tagged if i else resources_untagged
-        group.append(cluster["DBClusterArn"])
-        group.append(snapshot["DBClusterSnapshotArn"])
-
-    def assert_response(response, expected_count, resource_type=None):
-        results = response.get("ResourceTagMappingList", [])
-        results.should.have.length_of(expected_count)
-        for item in results:
-            arn = item["ResourceARN"]
-            arn.should.be.within(resources_tagged)
-            arn.should_not.be.within(resources_untagged)
-            if resource_type:
-                sure.this(f":{resource_type}:").should.be.within(arn)
-
-    rtapi = boto3.client("resourcegroupstaggingapi", region_name="us-west-2")
-    resp = rtapi.get_resources(ResourceTypeFilters=["rds"])
-    assert_response(resp, 4)
-    resp = rtapi.get_resources(ResourceTypeFilters=["rds:cluster"])
-    assert_response(resp, 2, resource_type="cluster")
-    resp = rtapi.get_resources(ResourceTypeFilters=["rds:cluster-snapshot"])
-    assert_response(resp, 2, resource_type="cluster-snapshot")
-    resp = rtapi.get_resources(TagFilters=[{"Key": "test", "Values": ["value-1"]}])
-    assert_response(resp, 2)
-
-
 @mock_lambda
 @mock_resourcegroupstaggingapi
 @mock_iam
diff --git a/tests/test_s3/test_s3_storageclass.py b/tests/test_s3/test_s3_storageclass.py
index 4c71a6b33..c0fa60cd6 100644
--- a/tests/test_s3/test_s3_storageclass.py
+++ b/tests/test_s3/test_s3_storageclass.py
@@ -231,8 +231,18 @@ def test_s3_copy_object_for_deep_archive_storage_class_restored():
         Bucket="Bucket", Key="First_Object", Body="Body", StorageClass="DEEP_ARCHIVE"
     )
 
+    with pytest.raises(ClientError) as exc:
+        s3.get_object(Bucket="Bucket", Key="First_Object")
+    err = exc.value.response["Error"]
+    err["Code"].should.equal("InvalidObjectState")
+    err["Message"].should.equal(
+        "The operation is not valid for the object's storage class"
+    )
+    err["StorageClass"].should.equal("DEEP_ARCHIVE")
+
     s3.create_bucket(Bucket="Bucket2")
     s3.restore_object(Bucket="Bucket", Key="First_Object", RestoreRequest={"Days": 123})
+    s3.get_object(Bucket="Bucket", Key="First_Object")
 
     s3.copy_object(
         CopySource={"Bucket": "Bucket", "Key": "First_Object"},
diff --git a/tests/test_secretsmanager/test_policy.py b/tests/test_secretsmanager/test_policy.py
new file mode 100644
index 000000000..865f62d8e
--- /dev/null
+++ b/tests/test_secretsmanager/test_policy.py
@@ -0,0 +1,77 @@
+import boto3
+import json
+import pytest
+
+from botocore.exceptions import ClientError
+from moto import mock_secretsmanager
+
+
+@mock_secretsmanager
+def test_get_initial_policy():
+    client = boto3.client("secretsmanager", region_name="us-west-2")
+    client.create_secret(Name="test-secret")
+
+    resp = client.get_resource_policy(SecretId="test-secret")
+    assert resp.get("Name") == "test-secret"
+    assert "ARN" in resp
+    assert "ResourcePolicy" not in resp
+
+
+@mock_secretsmanager
+def test_put_resource_policy():
+    client = boto3.client("secretsmanager", region_name="us-west-2")
+    client.create_secret(Name="test-secret")
+
+    policy = {
+        "Statement": [
+            {
+                "Action": "secretsmanager:GetSecretValue",
+                "Effect": "Allow",
+                "Principal": {
+                    "AWS": "arn:aws:iam::123456789012:role/tf-acc-test-655046176950657276"
+                },
+                "Resource": "*",
+                "Sid": "EnableAllPermissions",
+            }
+        ],
+        "Version": "2012-10-17",
+    }
+    resp = client.put_resource_policy(
+        SecretId="test-secret", ResourcePolicy=json.dumps(policy)
+    )
+    assert "ARN" in resp
+    assert "Name" in resp
+
+    resp = client.get_resource_policy(SecretId="test-secret")
+    assert "ResourcePolicy" in resp
+    assert json.loads(resp["ResourcePolicy"]) == policy
+
+
+@mock_secretsmanager
+def test_delete_resource_policy():
+    client = boto3.client("secretsmanager", region_name="us-west-2")
+    client.create_secret(Name="test-secret")
+
+    client.put_resource_policy(SecretId="test-secret", ResourcePolicy="some policy")
+
+    client.delete_resource_policy(SecretId="test-secret")
+
+    resp = client.get_resource_policy(SecretId="test-secret")
+    assert "ResourcePolicy" not in resp
+
+
+@mock_secretsmanager
+def test_policies_for_unknown_secrets():
+    client = boto3.client("secretsmanager", region_name="us-west-2")
+
+    with pytest.raises(ClientError) as exc:
+        client.put_resource_policy(SecretId="unknown secret", ResourcePolicy="p")
+    assert exc.value.response["Error"]["Code"] == "ResourceNotFoundException"
+
+    with pytest.raises(ClientError) as exc:
+        client.get_resource_policy(SecretId="unknown secret")
+    assert exc.value.response["Error"]["Code"] == "ResourceNotFoundException"
+
+    with pytest.raises(ClientError) as exc:
+        client.delete_resource_policy(SecretId="unknown secret")
+    assert exc.value.response["Error"]["Code"] == "ResourceNotFoundException"