Repository: getmoto/moto

Issue:
removing inexisting permissions on lambda does not raise exceptions
Hello,
I figured out that removing permissions that do not exist from lambdas does not raise any error in the mock environment.

Using the real environment, it raises a `botocore.errorfactory.ResourceNotFoundException`.

Is this a feature not developed yet?


```python
import boto3
from moto import mock_lambda

@mock_lambda
def test__remove_inexistant_permission():

    lambda_name = 'hoge'

    # creating here a dummy lambda
    # ...

	# removing permission that does not exist
    _ = boto3.client('lambda').remove_permission(
        FunctionName=lambda_name,
        StatementId="thisIDdoesNotExist"
    )
	
	# one more time just for sure...
	_ = boto3.client('lambda').remove_permission(
        FunctionName=lambda_name,
        StatementId="thisIDdoesNotExist00"
    )

    assert 1 == 2  # <- pytest fails here instead of higher

```

my environment:
```
>>> botocore.__version__
'1.21.24'
>>> moto.__version__
'2.0.11'
>>> boto3.__version__
'1.18.24'
>>> 
```



Discussion:
Hi @Guriido, welcome to Moto! Correct, the error handling here is not yet implemented. Will mark it as an enhancement to add this.
Thank you for your answer @bblommers ! I am not familiar yet with moto source code, but I may be interested in creating a PR for adding this. 
Regarding the development policy of moto, would it be okay to implement the error handling for this case in particular?
(that is, are contributors expected to implement exhaustive parts of boto3 simulation, like in this case, all errors regarding lambda permissions etc)
A PR would be always welcome, @Guriido! Contributors are not _expected_ to implement anything, but everything helps if it brings Moto closer to feature parity with AWS.

Some context that might be useful when implementing this:
Errors will always be simulated by raising one of Moto's custom exceptions, similar to this: https://github.com/spulec/moto/blob/master/moto/awslambda/models.py#L179
I do not know whether we can reuse any of our exceptions, or whether we have to define a new one - that will depend on the exact exception that AWS raises.

The unit test would look like this:
```
# setup with creating function..
...
# invoke remove_permission with invalid value
with pytest.raises(ClientError) as ex:
    remove_permission(..)
err = ex.value.response["Error"]
err["Code"].should.equal(..)
err["Message"].should.equal(..)
```

Let me know if there's anything else you need - happy to help out.

Patch:
diff --git a/moto/awslambda/exceptions.py b/moto/awslambda/exceptions.py
--- a/moto/awslambda/exceptions.py
+++ b/moto/awslambda/exceptions.py
@@ -1,11 +1,9 @@
-from botocore.client import ClientError
 from moto.core.exceptions import JsonRESTError
 
 
-class LambdaClientError(ClientError):
+class LambdaClientError(JsonRESTError):
     def __init__(self, error, message):
-        error_response = {"Error": {"Code": error, "Message": message}}
-        super().__init__(error_response, None)
+        super().__init__(error, message)
 
 
 class CrossAccountNotAllowed(LambdaClientError):
@@ -35,3 +33,13 @@ class PreconditionFailedException(JsonRESTError):
 
     def __init__(self, message):
         super().__init__("PreconditionFailedException", message)
+
+
+class UnknownPolicyException(LambdaClientError):
+    code = 404
+
+    def __init__(self):
+        super().__init__(
+            "ResourceNotFoundException",
+            "No policy is associated with the given resource.",
+        )
diff --git a/moto/awslambda/policy.py b/moto/awslambda/policy.py
--- a/moto/awslambda/policy.py
+++ b/moto/awslambda/policy.py
@@ -1,7 +1,10 @@
 import json
 import uuid
 
-from moto.awslambda.exceptions import PreconditionFailedException
+from moto.awslambda.exceptions import (
+    PreconditionFailedException,
+    UnknownPolicyException,
+)
 
 
 class Policy:
@@ -48,6 +51,9 @@ def del_statement(self, sid, revision=""):
         for statement in self.statements:
             if "Sid" in statement and statement["Sid"] == sid:
                 self.statements.remove(statement)
+                break
+        else:
+            raise UnknownPolicyException()
 
     # converts AddPermission request to PolicyStatement
     # https://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
diff --git a/moto/awslambda/responses.py b/moto/awslambda/responses.py
--- a/moto/awslambda/responses.py
+++ b/moto/awslambda/responses.py
@@ -6,11 +6,24 @@
 except ImportError:
     from urllib.parse import unquote
 
+from functools import wraps
 from moto.core.utils import amz_crc32, amzn_request_id, path_url
 from moto.core.responses import BaseResponse
+from .exceptions import LambdaClientError
 from .models import lambda_backends
 
 
+def error_handler(f):
+    @wraps(f)
+    def _wrapper(*args, **kwargs):
+        try:
+            return f(*args, **kwargs)
+        except LambdaClientError as e:
+            return e.code, e.get_headers(), e.get_body()
+
+    return _wrapper
+
+
 class LambdaResponse(BaseResponse):
     @property
     def json_body(self):
@@ -29,6 +42,7 @@ def lambda_backend(self):
         """
         return lambda_backends[self.region]
 
+    @error_handler
     def root(self, request, full_url, headers):
         self.setup_class(request, full_url, headers)
         if request.method == "GET":
@@ -127,6 +141,7 @@ def tag(self, request, full_url, headers):
         else:
             raise ValueError("Cannot handle {0} request".format(request.method))
 
+    @error_handler
     def policy(self, request, full_url, headers):
         self.setup_class(request, full_url, headers)
         if request.method == "GET":


Test patch:
diff --git a/tests/test_awslambda/test_lambda.py b/tests/test_awslambda/test_lambda.py
--- a/tests/test_awslambda/test_lambda.py
+++ b/tests/test_awslambda/test_lambda.py
@@ -5,6 +5,7 @@
 import sure  # noqa # pylint: disable=unused-import
 import pytest
 
+from botocore.exceptions import ClientError
 from freezegun import freeze_time
 from moto import mock_lambda, mock_s3
 from moto.core.models import ACCOUNT_ID
@@ -1193,3 +1194,24 @@ def test_remove_function_permission(key):
     policy = conn.get_policy(FunctionName=name_or_arn, Qualifier="2")["Policy"]
     policy = json.loads(policy)
     policy["Statement"].should.equal([])
+
+
+@mock_lambda
+def test_remove_unknown_permission_throws_error():
+    conn = boto3.client("lambda", _lambda_region)
+    zip_content = get_test_zip_file1()
+    function_name = str(uuid4())[0:6]
+    f = conn.create_function(
+        FunctionName=function_name,
+        Runtime="python3.7",
+        Role=(get_role_name()),
+        Handler="lambda_function.handler",
+        Code={"ZipFile": zip_content},
+    )
+    arn = f["FunctionArn"]
+
+    with pytest.raises(ClientError) as exc:
+        conn.remove_permission(FunctionName=arn, StatementId="1")
+    err = exc.value.response["Error"]
+    err["Code"].should.equal("ResourceNotFoundException")
+    err["Message"].should.equal("No policy is associated with the given resource.")