Repository navigation
enable local and cloudwatch handler re-invocation while IN_PROGRESS #63
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -149,12 +149,16 @@ def test__parse_request_valid_request(): | |
|
|
||
| with patch( | ||
| "cloudformation_cli_python_lib.resource._get_boto_session" | ||
| ) as mock_session: | ||
| ) as mock_caller_session, patch( | ||
| "cloudformation_cli_python_lib.resource.boto3.Session" | ||
| ) as mock_platform_session: | ||
| ret = resource._parse_request(ENTRYPOINT_PAYLOAD) | ||
| session, request, action, callback_context = ret | ||
| caller_sess, platform_sess, request, action, callback_context, _event = ret | ||
|
|
||
| mock_session.assert_called_once() | ||
| assert session is mock_session.return_value | ||
| mock_caller_session.assert_called_once() | ||
| assert caller_sess is mock_caller_session.return_value | ||
| mock_platform_session.assert_called_once() | ||
| assert platform_sess is mock_platform_session.return_value | ||
|
|
||
| mock_model._deserialize.assert_has_calls( | ||
| [call(sentinel.state_in1), call(sentinel.state_in2)] | ||
|
|
@@ -218,17 +222,29 @@ def test__invoke_handler_not_found(resource): | |
|
|
||
|
|
||
| def test__invoke_handler_was_found(resource): | ||
| mock_handler = resource.handler(Action.CREATE)(Mock(return_value=sentinel.response)) | ||
| progress_event = ProgressEvent(status=OperationStatus.IN_PROGRESS) | ||
| mock_handler = resource.handler(Action.CREATE)(Mock(return_value=progress_event)) | ||
|
|
||
| resp = resource._invoke_handler( | ||
| sentinel.session, sentinel.request, Action.CREATE, sentinel.context | ||
| ) | ||
| assert resp is sentinel.response | ||
| assert resp is progress_event | ||
| mock_handler.assert_called_once_with( | ||
| sentinel.session, sentinel.request, sentinel.context | ||
| ) | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("action", [Action.LIST, Action.READ]) | ||
| def test__invoke_handler_non_mutating_must_be_synchronous(resource, action): | ||
| progress_event = ProgressEvent(status=OperationStatus.IN_PROGRESS) | ||
| resource.handler(action)(Mock(return_value=progress_event)) | ||
| with pytest.raises(Exception) as excinfo: | ||
| resource._invoke_handler( | ||
| sentinel.session, sentinel.request, action, sentinel.context | ||
| ) | ||
| assert excinfo.value.args[0] == "READ and LIST handlers must return synchronously." | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("event,messages", [({}, ("missing", "credentials"))]) | ||
| def test__parse_test_request_invalid_request(resource, event, messages): | ||
| with pytest.raises(InvalidRequest) as excinfo: | ||
|
|
@@ -301,7 +317,8 @@ def test_test_entrypoint_success(): | |
| mock_model._deserialize.side_effect = [None, None] | ||
|
|
||
| resource = Resource(mock_model) | ||
| mock_handler = resource.handler(Action.CREATE)(Mock(return_value=sentinel.response)) | ||
| progress_event = ProgressEvent(status=OperationStatus.SUCCESS) | ||
| mock_handler = resource.handler(Action.CREATE)(Mock(return_value=progress_event)) | ||
|
|
||
| payload = { | ||
| "credentials": {"accessKeyId": "", "secretAccessKey": "", "sessionToken": ""}, | ||
|
|
@@ -317,7 +334,79 @@ def test_test_entrypoint_success(): | |
| event = resource.test_entrypoint.__wrapped__( # pylint: disable=no-member | ||
| resource, payload, None | ||
| ) | ||
| assert event is sentinel.response | ||
| assert event is progress_event | ||
|
|
||
| mock_model._deserialize.assert_has_calls([call(None), call(None)]) | ||
| mock_handler.assert_called_once() | ||
|
|
||
|
|
||
| def test_schedule_reinvocation_not_in_progress(): | ||
| progress = ProgressEvent(status=OperationStatus.SUCCESS) | ||
| with patch( | ||
| "cloudformation_cli_python_lib.resource.boto3.Session", autospec=True | ||
| ) as mock_session, patch( | ||
| "cloudformation_cli_python_lib.resource.CloudWatchScheduler", autospec=True | ||
| ) as mock_scheduler: | ||
| reinvoke = Resource.schedule_reinvocation( | ||
| sentinel.request, progress, sentinel.context, sentinel.session | ||
| ) | ||
| assert reinvoke is False | ||
| mock_session.assert_not_called() | ||
| mock_scheduler.assert_not_called() | ||
|
|
||
|
|
||
| def test_schedule_reinvocation_local_callback(): | ||
| progress = ProgressEvent(status=OperationStatus.IN_PROGRESS, callbackDelaySeconds=5) | ||
| mock_request = Mock( | ||
| "cloudformation_cli_python_lib.interface.HandlerRequest", autospec=True | ||
| )() | ||
| mock_request.requestContext = {} | ||
| mock_context = Mock( | ||
| "cloudformation_cli_python_lib.interface.LambdaContext", autospec=True | ||
| )() | ||
| mock_context.get_remaining_time_in_millis.return_value = 600000 | ||
| with patch( | ||
| "cloudformation_cli_python_lib.resource.sleep", autospec=True | ||
| ) as mock_sleep: | ||
| reinvoke = Resource.schedule_reinvocation( | ||
| mock_request, progress, mock_context, sentinel.session | ||
| ) | ||
| assert reinvoke is True | ||
| mock_sleep.assert_called_once_with(5) | ||
| assert mock_request.requestContext.get("invocation") == 1 | ||
|
|
||
|
|
||
| def test_schedule_reinvocation_cloudwatch_callback(): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. there are a lot of mocks here. i vastly prefer some decoupling, such as creating the platform session somewhere else
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. reduced the mock count somewhat, session is created elsewhere, still quite a few mocks left though |
||
| progress = ProgressEvent( | ||
| status=OperationStatus.IN_PROGRESS, callbackDelaySeconds=60 | ||
| ) | ||
| mock_request = Mock( | ||
| "cloudformation_cli_python_lib.interface.HandlerRequest", autospec=True | ||
| )() | ||
| mock_request.requestContext = {} | ||
| mock_context = Mock( | ||
| "cloudformation_cli_python_lib.interface.LambdaContext", autospec=True | ||
| )() | ||
| mock_context.get_remaining_time_in_millis.return_value = 6000 | ||
| mock_context.invoked_function_arn = "arn:aaa:bbb:ccc" | ||
| with patch( | ||
| "cloudformation_cli_python_lib.resource.CloudWatchScheduler", autospec=True | ||
| ) as mock_scheduler, patch( | ||
| "cloudformation_cli_python_lib.resource.sleep", autospec=True | ||
| ) as mock_sleep: | ||
| reinvoke = Resource.schedule_reinvocation( | ||
| mock_request, progress, mock_context, Mock() | ||
| ) | ||
| assert reinvoke is False | ||
| mock_scheduler.assert_called_once() | ||
| assert mock_scheduler.method_calls[0] == ( | ||
| "().reschedule_after_minutes", | ||
| (), | ||
| { | ||
| "function_arn": "arn:aaa:bbb:ccc", | ||
| "minutes_from_now": 1, | ||
| "handler_request": mock_request, | ||
| }, | ||
| ) | ||
| mock_sleep.assert_not_called() | ||
| assert mock_request.requestContext.get("invocation") == 1 | ||
Uh oh!
There was an error while loading. Please reload this page.