Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not send response to CloudFormation when testing with sam local #54

Open
ycourtois opened this issue Dec 26, 2021 · 4 comments
Open

Comments

@ycourtois
Copy link

When testing my custom resource with sam local, the resource helper tries to send a response to CloudFormation.

It generates an error since I am using fake information in my request payload.

Example:

{
  "RequestType": "Create",
  "ResponseURL": "http://pre-signed-S3-url-for-response",
  "StackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/MyStack/guid",
  "RequestId": "unique id for this create request",
  "ResourceType": "Custom::TestResource",
  "LogicalResourceId": "MyTestResource",
...

It would be nice to disable response sending to cfn when testing with sam local.

crhelper version: 2.0.10

@iainelder
Copy link

You can do this by mocking the _send method of the CfnResource class.

The normal implementation builds a response body. This behavior you want to keep so you can test the response.

_send passes the response body to a function parameter called send_response which normally writes the response body to the ResponseURL. This writing behavior you want to remove.

So if you replace _send with a wrapped version that sets send_response to a Mock instance, and you keep a a handle on the mock in your test, you should be able to get the response body from the called_args of the mock and assert its contents.

def _send(self, status=None, reason="", send_response=_send_response):
if len(str(str(self.Reason))) > 256:
self.Reason = "ERROR: (truncated) " + str(self.Reason)[len(str(self.Reason)) - 240:]
if len(str(reason)) > 256:
reason = "ERROR: (truncated) " + str(reason)[len(str(reason)) - 240:]
response_body = {
'Status': self.Status,
'PhysicalResourceId': str(self.PhysicalResourceId),
'StackId': self.StackId,
'RequestId': self.RequestId,
'LogicalResourceId': self.LogicalResourceId,
'Reason': str(self.Reason),
'Data': self.Data,
'NoEcho': self.NoEcho,
}
if status:
response_body.update({'Status': status, 'Reason': reason})
send_response(self._response_url, response_body, self._ssl_verify)

I'd love to see a built-in way of supporting such testing without so much monkey patching :-)

@verbitan
Copy link

@iainelder please forgive my ignorance, but are you able to explain why this doesn't work?

    @patch('crhelper.utils._send_response')
    @patch('crhelper.resource_helper.CfnResource._set_timeout', Mock())
    def test_create(self, mock_send):
        event = {
            "RequestType": "Create",
            "RequestId": "test-event-id",
            "StackId": "arn/test-stack-id/guid",
            "LogicalResourceId": "TestResourceId",
            "ResponseURL": "response_url",
            "ResourceProperties": {
                "MyData": "SomeProperty"
            }
        }

        lambda_function.handler(event, "")

        # Do some asserting on mock_send.call_args_list

@iainelder
Copy link

@verbitan , it's not a complete example and you haven't shown me what you expect or what you get instead. So it's hard to say why it doesn't work.

It's been a while since I've used crhelper, but I did develop a working solution for this on a previous project.

I've extracted from it a minimal example and I've published it to a new GitHub repo: https://github.com/iainelder/custom-resource-helper-mock

You should be able to set up the environment and run the example in PyTest. The mocked_send_response function is the core of the solution.

Does that help? Can you adapt it for your use case?

@verbitan
Copy link

That's perfect @iainelder, thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants