Repository: getmoto/moto
Pull request #8059 (2024-08-30 11:42:53)

EventBridge: Add Name param to ListConnections response
This PR adds the missing 'Name' parameter to EventBridge [ListConnections](https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_ListConnections.html#API_ListConnections_ResponseSyntax) operation.

This patch is from the LocalStack fork.

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/getmoto/moto/pull/8059?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 94.43%. Comparing base [(`191ebcc`)](https://app.codecov.io/gh/getmoto/moto/commit/191ebcc02f7d63db3efdc7ea40f37c10a86bd7c7?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) to head [(`ef3b8ae`)](https://app.codecov.io/gh/getmoto/moto/commit/ef3b8aef33dfa8c45d59ae0df7bffa86d362a604?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).
> Report is 5 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master    #8059   +/-   ##
=======================================
  Coverage   94.43%   94.43%           
=======================================
  Files        1139     1139           
  Lines       97355    97448   +93     
=======================================
+ Hits        91935    92026   +91     
- Misses       5420     5422    +2     
```

| [Flag](https://app.codecov.io/gh/getmoto/moto/pull/8059/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | Coverage Δ | |
|---|---|---|
| [servertests](https://app.codecov.io/gh/getmoto/moto/pull/8059/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `28.91% <ø> (-0.01%)` | :arrow_down: |
| [unittests](https://app.codecov.io/gh/getmoto/moto/pull/8059/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto) | `94.40% <ø> (+<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=getmoto#carryforward-flags-in-the-pull-request-comment) to find out more.

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/getmoto/moto/pull/8059?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=getmoto).


Patch:
diff --git a/moto/acmpca/exceptions.py b/moto/acmpca/exceptions.py
index b17c9b245..674d2d89e 100644
--- a/moto/acmpca/exceptions.py
+++ b/moto/acmpca/exceptions.py
@@ -26,3 +26,13 @@ class InvalidStateException(JsonRESTError):
             "InvalidStateException",
             f"The certificate authority {arn} is not in the correct state to have a certificate signing request.",
         )
+
+
+class MalformedCertificateAuthorityException(JsonRESTError):
+    code = 400
+
+    def __init__(self) -> None:
+        super().__init__(
+            "MalformedCertificateAuthorityException",
+            "Malformed certificate.",
+        )
diff --git a/moto/acmpca/models.py b/moto/acmpca/models.py
index 38d91f9f8..bcff2ffa6 100644
--- a/moto/acmpca/models.py
+++ b/moto/acmpca/models.py
@@ -2,7 +2,7 @@
 
 import base64
 import datetime
-from typing import Any, Dict, List, Optional, Tuple
+from typing import Any, Dict, List, Optional, Tuple, cast
 
 from cryptography import x509
 from cryptography.hazmat.backends import default_backend
@@ -19,6 +19,7 @@ from moto.utilities.utils import get_partition
 from .exceptions import (
     InvalidS3ObjectAclInCrlConfiguration,
     InvalidStateException,
+    MalformedCertificateAuthorityException,
     ResourceNotFoundException,
 )
 
@@ -49,14 +50,16 @@ class CertificateAuthority(BaseModel):
         self.usage_mode = "SHORT_LIVED_CERTIFICATE"
         self.security_standard = security_standard or "FIPS_140_2_LEVEL_3_OR_HIGHER"
 
-        self.key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
         self.password = str(mock_random.uuid4()).encode("utf-8")
-        self.private_bytes = self.key.private_bytes(
+
+        private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
+        self.private_bytes = private_key.private_bytes(
             encoding=serialization.Encoding.PEM,
             format=serialization.PrivateFormat.TraditionalOpenSSL,
             encryption_algorithm=serialization.BestAvailableEncryption(self.password),
         )
-        self.certificate: Optional[x509.Certificate] = None
+
+        self.certificate_bytes: bytes = b""
         self.certificate_chain: Optional[bytes] = None
         self.issued_certificates: Dict[str, bytes] = dict()
 
@@ -85,6 +88,20 @@ class CertificateAuthority(BaseModel):
 
         return cert.public_bytes(serialization.Encoding.PEM)
 
+    @property
+    def key(self) -> rsa.RSAPrivateKey:
+        private_key = serialization.load_pem_private_key(
+            self.private_bytes,
+            password=self.password,
+        )
+        return cast(rsa.RSAPrivateKey, private_key)
+
+    @property
+    def certificate(self) -> Optional[x509.Certificate]:
+        if self.certificate_bytes:
+            return x509.load_pem_x509_certificate(self.certificate_bytes)
+        return None
+
     @property
     def issuer(self) -> x509.Name:
         name_attributes = []
@@ -260,12 +277,6 @@ class CertificateAuthority(BaseModel):
                     if acl not in ["PUBLIC_READ", "BUCKET_OWNER_FULL_CONTROL"]:
                         raise InvalidS3ObjectAclInCrlConfiguration(acl)
 
-    @property
-    def certificate_bytes(self) -> bytes:
-        if self.certificate:
-            return self.certificate.public_bytes(serialization.Encoding.PEM)
-        return b""
-
     @property
     def not_valid_after(self) -> Optional[float]:
         if self.certificate is None:
@@ -287,7 +298,12 @@ class CertificateAuthority(BaseModel):
     def import_certificate_authority_certificate(
         self, certificate: bytes, certificate_chain: Optional[bytes]
     ) -> None:
-        self.certificate = x509.load_pem_x509_certificate(certificate)
+        try:
+            x509.load_pem_x509_certificate(certificate)
+        except ValueError:
+            raise MalformedCertificateAuthorityException()
+
+        self.certificate_bytes = certificate
         self.certificate_chain = certificate_chain
         self.status = "ACTIVE"
         self.updated_at = unix_time()
diff --git a/moto/apigateway/responses.py b/moto/apigateway/responses.py
index 8434722ae..127128419 100644
--- a/moto/apigateway/responses.py
+++ b/moto/apigateway/responses.py
@@ -1,5 +1,5 @@
 import json
-from typing import Any, Dict, List
+from typing import Any, Dict, List, Optional
 from urllib.parse import unquote
 
 from moto.core.responses import TYPE_RESPONSE, BaseResponse
@@ -28,7 +28,7 @@ class APIGatewayResponse(BaseResponse):
     def backend(self) -> APIGatewayBackend:
         return apigateway_backends[self.current_account][self.region]
 
-    def __validate_api_key_source(self, api_key_source: str) -> TYPE_RESPONSE:  # type: ignore[return]
+    def __validate_api_key_source(self, api_key_source: str) -> Optional[TYPE_RESPONSE]:
         if api_key_source and api_key_source not in API_KEY_SOURCES:
             return self.error(
                 "ValidationException",
@@ -39,10 +39,11 @@ class APIGatewayResponse(BaseResponse):
                     "[AUTHORIZER, HEADER]"
                 ).format(api_key_source=api_key_source),
             )
+        return None
 
-    def __validate_endpoint_configuration(  # type: ignore[return]
+    def __validate_endpoint_configuration(
         self, endpoint_configuration: Dict[str, str]
-    ) -> TYPE_RESPONSE:
+    ) -> Optional[TYPE_RESPONSE]:
         if endpoint_configuration and "types" in endpoint_configuration:
             invalid_types = list(
                 set(endpoint_configuration["types"]) - set(ENDPOINT_CONFIGURATION_TYPES)
@@ -57,6 +58,7 @@ class APIGatewayResponse(BaseResponse):
                         "[PRIVATE, EDGE, REGIONAL]"
                     ).format(endpoint_type=invalid_types[0]),
                 )
+        return None
 
     def create_rest_api(self) -> TYPE_RESPONSE:
         api_doc = deserialize_body(self.body)
@@ -102,14 +104,15 @@ class APIGatewayResponse(BaseResponse):
         apis = self.backend.list_apis()
         return json.dumps({"item": [api.to_dict() for api in apis]})
 
-    def __validte_rest_patch_operations(  # type: ignore[return]
+    def __validte_rest_patch_operations(
         self, patch_operations: List[Dict[str, str]]
-    ) -> TYPE_RESPONSE:
+    ) -> Optional[TYPE_RESPONSE]:
         for op in patch_operations:
             path = op["path"]
             if "apiKeySource" in path:
                 value = op["value"]
                 return self.__validate_api_key_source(value)
+        return None
 
     def delete_rest_api(self) -> TYPE_RESPONSE:
         function_id = self.path.replace("/restapis/", "", 1).split("/")[0]
@@ -121,7 +124,8 @@ class APIGatewayResponse(BaseResponse):
         rest_api = self.backend.get_rest_api(function_id)
         return 200, {}, json.dumps(rest_api.to_dict())
 
-    def get_rest_api_without_id(self, *args: Any) -> TYPE_RESPONSE:  # type: ignore[misc]
+    @staticmethod
+    def get_rest_api_without_id(*args: Any) -> TYPE_RESPONSE:  # type: ignore[misc]
         """
         AWS is returning an empty response when restApiId is an empty string. This is slightly odd and it seems an
         outlier, therefore it was decided we could have a custom handler for this particular use case instead of
diff --git a/moto/apigateway/urls.py b/moto/apigateway/urls.py
index 49d291611..9bcd317af 100644
--- a/moto/apigateway/urls.py
+++ b/moto/apigateway/urls.py
@@ -44,4 +44,4 @@ url_paths = {
 }
 
 # Also manages the APIGatewayV2
-url_paths.update(url_paths_v2)  # type: ignore[arg-type]
+url_paths.update(url_paths_v2)
diff --git a/moto/awslambda/responses.py b/moto/awslambda/responses.py
index 4f825b991..6c2328025 100644
--- a/moto/awslambda/responses.py
+++ b/moto/awslambda/responses.py
@@ -321,20 +321,14 @@ class LambdaResponse(BaseResponse):
 
     def get_function_concurrency(self) -> TYPE_RESPONSE:
         path_function_name = unquote(self.path.rsplit("/", 2)[-2])
-        function_name = self.backend.get_function(path_function_name)
-
-        if function_name is None:
-            return 404, {"status": 404}, "{}"
+        self.backend.get_function(path_function_name)
 
         resp = self.backend.get_function_concurrency(path_function_name)
         return 200, {}, json.dumps({"ReservedConcurrentExecutions": resp})
 
     def delete_function_concurrency(self) -> TYPE_RESPONSE:
         path_function_name = unquote(self.path.rsplit("/", 2)[-2])
-        function_name = self.backend.get_function(path_function_name)
-
-        if function_name is None:
-            return 404, {}, "{}"
+        self.backend.get_function(path_function_name)
 
         self.backend.delete_function_concurrency(path_function_name)
 
@@ -342,10 +336,7 @@ class LambdaResponse(BaseResponse):
 
     def put_function_concurrency(self) -> TYPE_RESPONSE:
         path_function_name = unquote(self.path.rsplit("/", 2)[-2])
-        function = self.backend.get_function(path_function_name)
-
-        if function is None:
-            return 404, {}, "{}"
+        self.backend.get_function(path_function_name)
 
         concurrency = self._get_param("ReservedConcurrentExecutions", None)
         resp = self.backend.put_function_concurrency(path_function_name, concurrency)
diff --git a/moto/cloudwatch/responses.py b/moto/cloudwatch/responses.py
index 6675d0998..40d7d3c9c 100644
--- a/moto/cloudwatch/responses.py
+++ b/moto/cloudwatch/responses.py
@@ -234,7 +234,7 @@ class CloudWatchResponse(BaseResponse):
 
     def delete_dashboards(self) -> Union[str, ERROR_RESPONSE]:
         dashboards = self._get_multi_param("DashboardNames.member")
-        if dashboards is None:
+        if not dashboards:
             return self._error("InvalidParameterValue", "Need at least 1 dashboard")
 
         error = self.cloudwatch_backend.delete_dashboards(dashboards)
diff --git a/moto/dynamodb/responses.py b/moto/dynamodb/responses.py
index b0141dfba..fb1f9d57b 100644
--- a/moto/dynamodb/responses.py
+++ b/moto/dynamodb/responses.py
@@ -53,28 +53,25 @@ def include_consumed_capacity(
 
             response = f(*args, **kwargs)
 
-            if isinstance(response, str):
-                body = json.loads(response)
+            body = json.loads(response)
 
-                if expected_capacity == "TOTAL":
-                    body["ConsumedCapacity"] = {
-                        "TableName": table_name,
-                        "CapacityUnits": val,
-                    }
-                elif expected_capacity == "INDEXES":
-                    body["ConsumedCapacity"] = {
-                        "TableName": table_name,
-                        "CapacityUnits": val,
-                        "Table": {"CapacityUnits": val},
+            if expected_capacity == "TOTAL":
+                body["ConsumedCapacity"] = {
+                    "TableName": table_name,
+                    "CapacityUnits": val,
+                }
+            elif expected_capacity == "INDEXES":
+                body["ConsumedCapacity"] = {
+                    "TableName": table_name,
+                    "CapacityUnits": val,
+                    "Table": {"CapacityUnits": val},
+                }
+                if index_name:
+                    body["ConsumedCapacity"]["LocalSecondaryIndexes"] = {
+                        index_name: {"CapacityUnits": val}
                     }
-                    if index_name:
-                        body["ConsumedCapacity"]["LocalSecondaryIndexes"] = {
-                            index_name: {"CapacityUnits": val}
-                        }
-
-                return dynamo_json_dump(body)
 
-            return response
+            return dynamo_json_dump(body)
 
         return _wrapper
 
diff --git a/moto/ec2/responses/launch_templates.py b/moto/ec2/responses/launch_templates.py
index 648c36dbf..595c02217 100644
--- a/moto/ec2/responses/launch_templates.py
+++ b/moto/ec2/responses/launch_templates.py
@@ -187,11 +187,7 @@ class LaunchTemplates(EC2BaseResponse):
         ret_versions = []
         if versions and template is not None:
             for v in versions:
-                if str(v).lower() == "$latest" or "$default":
-                    tv = template.get_version(v)
-                else:
-                    tv = template.get_version(int(v))
-                ret_versions.append(tv)
+                ret_versions.append(template.get_version(v))
         elif min_version:
             if max_version:
                 vMax = max_version
diff --git a/moto/emr/models.py b/moto/emr/models.py
index 5922d17a2..28df66ab2 100644
--- a/moto/emr/models.py
+++ b/moto/emr/models.py
@@ -555,7 +555,7 @@ class ElasticMapReduceBackend(BaseBackend):
         self.clusters: Dict[str, FakeCluster] = {}
         self.instance_groups: Dict[str, FakeInstanceGroup] = {}
         self.security_configurations: Dict[str, FakeSecurityConfiguration] = {}
-        self.block_public_access_configuration: Dict[str, Any] = {}  # type: ignore [misc]
+        self.block_public_access_configuration: Dict[str, Any] = {}
 
     @property
     def ec2_backend(self) -> Any:  # type: ignore[misc]
diff --git a/moto/events/responses.py b/moto/events/responses.py
index bca839ccb..027687e74 100644
--- a/moto/events/responses.py
+++ b/moto/events/responses.py
@@ -424,6 +424,7 @@ class EventsHandler(BaseResponse):
                     "ConnectionArn": connection.arn,
                     "ConnectionState": "AUTHORIZED",
                     "CreationTime": connection.creation_time,
+                    "Name": connection.name,
                     "LastModifiedTime": connection.creation_time,
                     "AuthorizationType": connection.authorization_type,
                 }
diff --git a/moto/fsx/models.py b/moto/fsx/models.py
index 7d6d51a09..554974ec5 100644
--- a/moto/fsx/models.py
+++ b/moto/fsx/models.py
@@ -174,14 +174,13 @@ class FSxBackend(BaseBackend):
         resource = self._get_resource_from_arn(resource_arn)
         resource.tags.extend(tags)
 
-    def _get_resource_from_arn(self, arn: str) -> Any:
+    def _get_resource_from_arn(self, arn: str) -> FileSystem:
         target_resource, target_name = arn.split(":")[-1].split("/")
         try:
-            resource = self.file_systems.get(target_name)  # type: ignore
+            return self.file_systems[target_name]
         except KeyError:
             message = f"Could not find {target_resource} with name {target_name}"
             raise ValueError(message)
-        return resource
 
     def untag_resource(self, resource_arn: str, tag_keys: List[str]) -> None:
         resource = self._get_resource_from_arn(resource_arn)
diff --git a/moto/glue/responses.py b/moto/glue/responses.py
index c1bf3997c..0ff012910 100644
--- a/moto/glue/responses.py
+++ b/moto/glue/responses.py
@@ -34,7 +34,7 @@ class GlueResponse(BaseResponse):
         self.glue_backend.create_database(
             database_name,
             database_input,  # type: ignore[arg-type]
-            self.parameters.get("Tags"),  # type: ignore[arg-type]
+            self.parameters.get("Tags"),
         )
         return ""
 
diff --git a/moto/networkmanager/models.py b/moto/networkmanager/models.py
index e8eee54a3..238b29c8a 100644
--- a/moto/networkmanager/models.py
+++ b/moto/networkmanager/models.py
@@ -266,10 +266,9 @@ class NetworkManagerBackend(BaseBackend):
                     for inner_dict in resources.values()
                     for k, v in inner_dict.items()
                 }
-            resource = resources.get(target_name)  # type: ignore
+            return resources[target_name]
         except (KeyError, ValueError, AttributeError):
             raise ResourceNotFound(arn)
-        return resource
 
     def create_global_network(
         self,
diff --git a/moto/qldb/models.py b/moto/qldb/models.py
index ef2084f93..3b46855a4 100644
--- a/moto/qldb/models.py
+++ b/moto/qldb/models.py
@@ -110,7 +110,7 @@ class QLDBBackend(BaseBackend):
         Optional[datetime],
         Optional[bool],
         Optional[Dict[str, Union[str, datetime]]],
-    ]:  # type: ignore[misc]
+    ]:
         if name not in self.ledgers:
             raise LedgerNotFoundException(name)
         if deletion_protection is not None:
@@ -131,7 +131,7 @@ class QLDBBackend(BaseBackend):
             ledger.get("encryption_description"),
         )
 
-    def _get_ledger_by_resource_arn(self, resource_arn: str) -> Dict[str, Any]:  # type: ignore[misc]
+    def _get_ledger_by_resource_arn(self, resource_arn: str) -> Dict[str, Any]:
         for ledger in self.ledgers.values():
             if ledger.get("resource_arn") == resource_arn:
                 return ledger
diff --git a/moto/qldb/responses.py b/moto/qldb/responses.py
index 47a487f51..16959646f 100644
--- a/moto/qldb/responses.py
+++ b/moto/qldb/responses.py
@@ -78,7 +78,7 @@ class QLDBResponse(BaseResponse):
             permissions_mode,
             deletion_protection,
             kms_key_arn,
-        ) = self.qldb_backend.create_ledger(  # type: ignore[misc]
+        ) = self.qldb_backend.create_ledger(
             name=name,
             tags=tags,
             permissions_mode=permissions_mode,
diff --git a/moto/route53domains/models.py b/moto/route53domains/models.py
index 3bb36cc86..623bcdbe4 100644
--- a/moto/route53domains/models.py
+++ b/moto/route53domains/models.py
@@ -151,7 +151,7 @@ class Route53DomainsBackend(BaseBackend):
 
         return self.__domains[domain_name]
 
-    @paginate(pagination_model=PAGINATION_MODEL)  # type: ignore[misc]
+    @paginate(pagination_model=PAGINATION_MODEL)
     def list_domains(
         self,
         filter_conditions: Optional[List[Dict[str, Any]]] = None,
@@ -197,7 +197,7 @@ class Route53DomainsBackend(BaseBackend):
 
         return domains_to_return
 
-    @paginate(pagination_model=PAGINATION_MODEL)  # type: ignore[misc]
+    @paginate(pagination_model=PAGINATION_MODEL)
     def list_operations(
         self,
         submitted_since_timestamp: Optional[int] = None,
diff --git a/moto/s3/responses.py b/moto/s3/responses.py
index 14ef66ae4..f8ecf20b3 100644
--- a/moto/s3/responses.py
+++ b/moto/s3/responses.py
@@ -1423,7 +1423,7 @@ class S3Response(BaseResponse):
 
         # Enforces policy when creating new objects, `if key` does not https://github.com/getmoto/moto/issues/7837
         if bucket:
-            resource = f"arn:{bucket.partition}:s3:::{bucket_name}/{key_name}"  # type: ignore[union-attr]
+            resource = f"arn:{bucket.partition}:s3:::{bucket_name}/{key_name}"
 
             # Authorization Workflow
             # https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-auth-workflow-object-operation.html
@@ -1432,7 +1432,7 @@ class S3Response(BaseResponse):
             from moto.iam.access_control import PermissionResult
 
             action = f"s3:{method.upper()[0]}{method.lower()[1:]}Object"
-            bucket_permissions = bucket.get_permission(action, resource)  # type: ignore
+            bucket_permissions = bucket.get_permission(action, resource)
             if bucket_permissions == PermissionResult.DENIED:
                 return 403, {}, ""
 
diff --git a/moto/transfer/types.py b/moto/transfer/types.py
index c02527092..efaef0997 100644
--- a/moto/transfer/types.py
+++ b/moto/transfer/types.py
@@ -37,9 +37,7 @@ class User(BaseModel):
         if self.arn == "":
             self.arn = f"arn:aws:transfer:{self.user_name}:{datetime.now().strftime('%Y%m%d%H%M%S')}"
 
-    def to_dict(
-        self,
-    ) -> Dict[str, Any]:  # type: ignore[misc]
+    def to_dict(self) -> Dict[str, Any]:
         user = {
             "HomeDirectory": self.home_directory,
             "HomeDirectoryType": self.home_directory_type,
@@ -152,7 +150,7 @@ class Server(BaseModel):
         if self.as2_service_managed_egress_ip_addresses == []:
             self.as2_service_managed_egress_ip_addresses.append("0.0.0.0/0")
 
-    def to_dict(self) -> Dict[str, Any]:  # type: ignore[misc]
+    def to_dict(self) -> Dict[str, Any]:
         on_upload = []
         on_partial_upload = []
         if self.workflow_details is not None:


Test patch:
diff --git a/tests/test_acmpca/test_acmpca.py b/tests/test_acmpca/test_acmpca.py
index a9c592cd5..56b9c0df6 100644
--- a/tests/test_acmpca/test_acmpca.py
+++ b/tests/test_acmpca/test_acmpca.py
@@ -1,4 +1,5 @@
 import datetime
+import pickle
 
 import boto3
 import cryptography.hazmat.primitives.asymmetric.rsa
@@ -388,8 +389,16 @@ def test_import_certificate_authority_certificate():
         CertificateAuthorityType="SUBORDINATE",
     )["CertificateAuthorityArn"]
 
-    cert = create_cert()
+    with pytest.raises(ClientError) as exc:
+        client.import_certificate_authority_certificate(
+            CertificateAuthorityArn=ca_arn,
+            Certificate="invalid certificate pem",
+        )
+    err = exc.value.response["Error"]
+    assert err["Code"] == "MalformedCertificateAuthorityException"
+    assert err["Message"] == "Malformed certificate."
 
+    cert = create_cert()
     client.import_certificate_authority_certificate(
         CertificateAuthorityArn=ca_arn,
         Certificate=cert,
@@ -513,6 +522,46 @@ def test_untag_certificate_authority():
     assert resp["Tags"] == [{"Key": "t2", "Value": "v2"}]
 
 
+@mock_aws
+def test_acmpca_backend_is_serialisable():
+    # This test ensures that the backend does not store any non-picklable objects (e.g. native OpenSSL objects)
+    # This is important for downstream projects like LocalStack
+
+    client = boto3.client("acm-pca", region_name="eu-west-1")
+
+    ca_arn = client.create_certificate_authority(
+        CertificateAuthorityConfiguration={
+            "KeyAlgorithm": "RSA_4096",
+            "SigningAlgorithm": "SHA512WITHRSA",
+            "Subject": {"CommonName": "yscb41lw.test"},
+        },
+        CertificateAuthorityType="SUBORDINATE",
+    )["CertificateAuthorityArn"]
+
+    cert = create_cert()
+    client.import_certificate_authority_certificate(
+        CertificateAuthorityArn=ca_arn,
+        Certificate=cert,
+    )
+    client.update_certificate_authority(
+        CertificateAuthorityArn=ca_arn,
+        Status="DISABLED",
+    )
+
+    csr = client.get_certificate_authority_csr(CertificateAuthorityArn=ca_arn)["Csr"]
+    client.issue_certificate(
+        CertificateAuthorityArn=ca_arn,
+        Csr=csr,
+        SigningAlgorithm="SHA512WITHRSA",
+        Validity={"Type": "YEARS", "Value": 10},
+    )
+    client.describe_certificate_authority(CertificateAuthorityArn=ca_arn)
+
+    from moto.acmpca import acmpca_backends
+
+    assert pickle.dumps(acmpca_backends)
+
+
 def create_csr(private_key, country, state, org, cn):
     csr = (
         cryptography.x509.CertificateSigningRequestBuilder()
diff --git a/tests/test_events/test_events.py b/tests/test_events/test_events.py
index babba5bee..17bd2960b 100644
--- a/tests/test_events/test_events.py
+++ b/tests/test_events/test_events.py
@@ -2317,6 +2317,7 @@ def test_create_and_list_connections():
         f"arn:aws:events:eu-central-1:{ACCOUNT_ID}:connection/test/"
         in response["Connections"][0]["ConnectionArn"]
     )
+    assert response["Connections"][0]["Name"] == "test"
 
 
 @mock_aws