Repository: getmoto/moto (python)

Issue:
Error with autoscaling group
Hello, 

I use moto_server with command `moto_server -p4566`

When trying to create an autoscaling group with Terraform, I get the following trace error :
```
127.0.0.1 - - [08/Mar/2023 08:24:52] "POST / HTTP/1.1" 500 -
Error on request:
Traceback (most recent call last):
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\werkzeug\serving.py", line 333, in run_wsgi
    execute(self.server.app)
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\werkzeug\serving.py", line 320, in execute
    application_iter = app(environ, start_response)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\moto\moto_server\werkzeug_app.py", line 239, in __call__
    return backend_app(environ, start_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 2551, in __call__
    return self.wsgi_app(environ, start_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 2531, in wsgi_app
    response = self.handle_exception(e)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask_cors\extension.py", line 165, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
                                                ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 2528, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 1825, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask_cors\extension.py", line 165, in wrapped_function
    return cors_after_request(app.make_response(f(*args, **kwargs)))
                                                ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 1823, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\flask\app.py", line 1799, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\moto\core\utils.py", line 106, in __call__
    result = self.callback(request, request.url, dict(request.headers))
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\moto\core\responses.py", line 229, in dispatch
    return cls()._dispatch(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\moto\core\responses.py", line 370, in _dispatch
    return self.call_action()
           ^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\moto\core\responses.py", line 459, in call_action
    response = method()
               ^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\moto\autoscaling\responses.py", line 79, in create_auto_scaling_group
    params = self._get_params()
             ^^^^^^^^^^^^^^^^^^
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\moto\core\responses.py", line 691, in _get_params
    self._parse_param(k, v[0], params)
  File "C:\Users\Kevin Valleau\AppData\Local\Programs\Python\Python311\Lib\site-packages\moto\core\responses.py", line 722, in _parse_param
    obj = obj[index]
          ^^^^^^^^^^
IndexError: list index out of range
```

Here the Terraform resource :

```hcl
resource "aws_autoscaling_group" "test" {
  name_prefix         = "asg-instances-"
  min_size            = var.min
  max_size            = var.max
  desired_capacity    = var.desired
  vpc_zone_identifier = var.subnets
  target_group_arns   = [aws_lb_target_group.test.arn]

  health_check_grace_period = var.health_check_grace_period
  health_check_type         = var.health_check_type

  enabled_metrics = [
    "GroupDesiredCapacity",
    "GroupInServiceCapacity",
    "GroupPendingCapacity",
    "GroupMinSize", "GroupMaxSize",
    "GroupInServiceInstances",
    "GroupPendingInstances",
    "GroupStandbyInstances",
    "GroupStandbyCapacity",
    "GroupTerminatingCapacity",
    "GroupTerminatingInstances",
    "GroupTotalCapacity",
    "GroupTotalInstances"
  ]
  metrics_granularity = "1Minute"

  launch_template {
    id      = aws_launch_template.test.id
    version = "$Latest" #TODO: Variable
  }

  instance_refresh {
    strategy = "Rolling"
    preferences {
      min_healthy_percentage = 60
    }
  }


  tag {
    key                 = "Name"
    value               = var.autoscaling_tag
    propagate_at_launch = true
  }

  dynamic "tag" {
    for_each = var.tags

    content {
      key                 = tag.key
      value               = tag.value
      propagate_at_launch = true
    }
  }
}
```

Pull request:
fix:params not sorted properly when tags are present
Attempt to fix #6033 
This is my first time trying to contribute, so please be indulgent :)



Patch:
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/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/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/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:


Test patch:
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_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_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"},