Repository: getmoto/moto
Pull request #6955 (2023-10-27 09:41:24)

Align organizations PolicyNotFoundException with boto3 response
At the moment, the PolicyNotFoundException raised by the mock_organizations is not returning the same `Error` responses as the boto3 client.

The mocked response returns a `Code` of `"400"` and an XML string in the `Message` block.

The expected behaviour based on the boto3 client would be to return a `Code` of `"PolicyNotFoundException"` and a message specific to the call, for example, `"We can't find a policy with the PolicyId that you specified."` for [DescribePolicy](https://docs.aws.amazon.com/organizations/latest/APIReference/API_DescribePolicy.html#API_DescribePolicy_Errors).

Steps to reproduce:

```python
import boto3
import moto

client = boto3.client("organizations")

try:
    client.describe_policy(PolicyId="p-definitelydoesnotexist")
except Exception as e:

    print(type(e).__name__)
    # PolicyNotFoundException

    print(e.response["Error"])
    # {'Message': "You specified a policy that doesn't exist.", 'Code': 'PolicyNotFoundException'}


with moto.mock_organizations():
    try:
        client.describe_policy(PolicyId="p-definitelydoesnotexist")
    except Exception as e:

        print(type(e).__name__)
        # ClientError

        print(e.response["Error"])
        # {'Message': '<?xml version="1.0" encoding="UTF-8"?>\n  <ErrorResponse>\n    <Errors>\n      <Error>\n        <Code>PolicyNotFoundException</Code>\n        <Message><![CDATA[You specified a policy that doesn\'t exist.]]></Message>\n        \n      </Error>\n    </Errors>\n  <RequestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</RequestId>\n</ErrorResponse>', 'Code': '400'}
```


Patch:
diff --git a/moto/organizations/exceptions.py b/moto/organizations/exceptions.py
index e3a21761d..4a08438f9 100644
--- a/moto/organizations/exceptions.py
+++ b/moto/organizations/exceptions.py
@@ -109,3 +109,10 @@ class TargetNotFoundException(JsonRESTError):
         super().__init__(
             "TargetNotFoundException", "You specified a target that doesn't exist."
         )
+
+
+class PolicyNotFoundException(JsonRESTError):
+    code = 400
+
+    def __init__(self, message: str) -> None:
+        super().__init__("PolicyNotFoundException", message)
diff --git a/moto/organizations/models.py b/moto/organizations/models.py
index 21c09f5ae..92bfd290e 100644
--- a/moto/organizations/models.py
+++ b/moto/organizations/models.py
@@ -16,6 +16,7 @@ from moto.organizations.exceptions import (
     AWSOrganizationsNotInUseException,
     AccountNotRegisteredException,
     RootNotFoundException,
+    PolicyNotFoundException,
     PolicyTypeAlreadyEnabledException,
     PolicyTypeNotEnabledException,
     TargetNotFoundException,
@@ -599,8 +600,7 @@ class OrganizationsBackend(BaseBackend):
                 (p for p in self.policies if p.id == kwargs["PolicyId"]), None
             )
             if policy is None:
-                raise RESTError(
-                    "PolicyNotFoundException",
+                raise PolicyNotFoundException(
                     "You specified a policy that doesn't exist.",
                 )
         else:
@@ -612,8 +612,7 @@ class OrganizationsBackend(BaseBackend):
             (policy for policy in self.policies if policy.id == policy_id), None
         )
         if policy is None:
-            raise RESTError(
-                "PolicyNotFoundException",
+            raise PolicyNotFoundException(
                 "We can't find a policy with the PolicyId that you specified.",
             )
         return policy
@@ -668,8 +667,7 @@ class OrganizationsBackend(BaseBackend):
                     )
                 del self.policies[idx]
                 return
-        raise RESTError(
-            "PolicyNotFoundException",
+        raise PolicyNotFoundException(
             "We can't find a policy with the PolicyId that you specified.",
         )
 
@@ -735,8 +733,7 @@ class OrganizationsBackend(BaseBackend):
                 (p for p in self.policies if p.id == kwargs["PolicyId"]), None
             )
             if policy is None:
-                raise RESTError(
-                    "PolicyNotFoundException",
+                raise PolicyNotFoundException(
                     "You specified a policy that doesn't exist.",
                 )
         else:


Test patch:
diff --git a/tests/test_organizations/test_organizations_boto3.py b/tests/test_organizations/test_organizations_boto3.py
index c10dafc39..78bfa05ac 100644
--- a/tests/test_organizations/test_organizations_boto3.py
+++ b/tests/test_organizations/test_organizations_boto3.py
@@ -683,8 +683,10 @@ def test_describe_policy_exception():
         client.describe_policy(PolicyId=policy_id)
     ex = e.value
     assert ex.operation_name == "DescribePolicy"
-    assert ex.response["Error"]["Code"] == "400"
-    assert "PolicyNotFoundException" in ex.response["Error"]["Message"]
+    assert ex.response["Error"]["Code"] == "PolicyNotFoundException"
+    assert (
+        ex.response["Error"]["Message"] == "You specified a policy that doesn't exist."
+    )
     with pytest.raises(ClientError) as e:
         client.describe_policy(PolicyId="meaninglessstring")
     ex = e.value
@@ -896,8 +898,11 @@ def test_delete_policy_exception():
         client.delete_policy(PolicyId=non_existent_policy_id)
     ex = e.value
     assert ex.operation_name == "DeletePolicy"
-    assert ex.response["Error"]["Code"] == "400"
-    assert "PolicyNotFoundException" in ex.response["Error"]["Message"]
+    assert ex.response["Error"]["Code"] == "PolicyNotFoundException"
+    assert (
+        ex.response["Error"]["Message"]
+        == "We can't find a policy with the PolicyId that you specified."
+    )
 
     # Attempt to delete an attached policy
     policy_id = client.create_policy(
@@ -993,8 +998,11 @@ def test_update_policy_exception():
         client.update_policy(PolicyId=non_existent_policy_id)
     ex = e.value
     assert ex.operation_name == "UpdatePolicy"
-    assert ex.response["Error"]["Code"] == "400"
-    assert "PolicyNotFoundException" in ex.response["Error"]["Message"]
+    assert ex.response["Error"]["Code"] == "PolicyNotFoundException"
+    assert (
+        ex.response["Error"]["Message"]
+        == "We can't find a policy with the PolicyId that you specified."
+    )
 
 
 @mock_organizations
@@ -1145,8 +1153,10 @@ def test_list_targets_for_policy_exception():
         client.list_targets_for_policy(PolicyId=policy_id)
     ex = e.value
     assert ex.operation_name == "ListTargetsForPolicy"
-    assert ex.response["Error"]["Code"] == "400"
-    assert "PolicyNotFoundException" in ex.response["Error"]["Message"]
+    assert ex.response["Error"]["Code"] == "PolicyNotFoundException"
+    assert (
+        ex.response["Error"]["Message"] == "You specified a policy that doesn't exist."
+    )
     with pytest.raises(ClientError) as e:
         client.list_targets_for_policy(PolicyId="meaninglessstring")
     ex = e.value