Repository: getmoto/moto (python)

Issue:
Error creating secret with moto_server
Hello, 

I'm using moto_server with command : `moto_server -p4566`.

When I try to create a simple aws secrets with Terraform, I get the following error, with no trace in moto :

```
│ Error: reading Secrets Manager Secret (arn:aws:secretsmanager:<region>:123456789012:secret:test-tls-20230308133703611100000001-RDLGSh) policy: parsing policy: unexpected result: (<nil>) "<nil>"
│
│   with aws_secretsmanager_secret.tls,
│   on secret.tf line 32, in resource "aws_secretsmanager_secret" "tls":
│   32: resource "aws_secretsmanager_secret" "tls" {
```

Here is the Terraform resource :

```hcl
resource "aws_secretsmanager_secret" "tls" {
  name_prefix = "test-tls-"
}

resource "aws_secretsmanager_secret_version" "tls" {
  secret_id     = aws_secretsmanager_secret.tls.id
  secret_string = local.secret
}
``` 

I try to add `policy=""` or `policy="{}"`, the result is the same.
And if I try to add a valid policy, I get an error because the `put_resource_policy` action is not supported.

Pull request:
SecretsManager - put/delete_resource_policy
Fixes #6034 

We no longer return a policy if there hasn't been one created, in-line with what AWS does. Only when it is created, do we return it.

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 ce2a77809..51718c2fe 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -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/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/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/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 7439dbbd7..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))
 
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 dfea7c1e4..0e4e3f71d 100644
--- a/tests/terraformtests/terraform-tests.success.txt
+++ b/tests/terraformtests/terraform-tests.success.txt
@@ -452,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_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_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_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"