From ae6706bf45a4d90d40f23c3d65532fb408543bd4 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Fri, 28 Oct 2022 11:36:36 +0200 Subject: [PATCH 1/2] fix(idempotency): idempotent_function should support standalone falsy values --- .../utilities/idempotency/idempotency.py | 6 ++--- .../idempotency/test_idempotency.py | 25 ++++++++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/aws_lambda_powertools/utilities/idempotency/idempotency.py b/aws_lambda_powertools/utilities/idempotency/idempotency.py index abd45a86be1..14fd3bfe5af 100644 --- a/aws_lambda_powertools/utilities/idempotency/idempotency.py +++ b/aws_lambda_powertools/utilities/idempotency/idempotency.py @@ -134,14 +134,14 @@ def decorate(*args, **kwargs): if os.getenv(constants.IDEMPOTENCY_DISABLED_ENV): return function(*args, **kwargs) - payload = kwargs.get(data_keyword_argument) - - if not payload: + if data_keyword_argument not in kwargs: raise RuntimeError( f"Unable to extract '{data_keyword_argument}' from keyword arguments." f" Ensure this exists in your function's signature as well as the caller used it as a keyword argument" ) + payload = kwargs.get(data_keyword_argument) + idempotency_handler = IdempotencyHandler( function=function, function_payload=payload, diff --git a/tests/functional/idempotency/test_idempotency.py b/tests/functional/idempotency/test_idempotency.py index e5c5c777971..9f52cb4096d 100644 --- a/tests/functional/idempotency/test_idempotency.py +++ b/tests/functional/idempotency/test_idempotency.py @@ -72,7 +72,7 @@ def test_idempotent_lambda_already_completed( lambda_context, ): """ - Test idempotent decorator where event with matching event key has already been succesfully processed + Test idempotent decorator where event with matching event key has already been successfully processed """ stubber = stub.Stubber(persistence_store.table.meta.client) @@ -1247,6 +1247,29 @@ def lambda_handler(event, _): assert handler_result == expected_result +@pytest.mark.parametrize("data", [None, 0, False]) +def test_idempotent_function_falsy_values(data): + # Scenario to validate we can use idempotent_function with any function + # receiving a falsy value (`None`, `False`, `0`, etc.) + # shouldn't cause a RuntimeError + mock_event = data + idempotency_key = f"{TESTS_MODULE_PREFIX}.test_idempotent_function_falsy_values..record_handler#{hash_idempotency_key(mock_event)}" # noqa: E501 + + # 'test-func.functional.idempotency.test_idempotency.test_idempotent_function_falsy_values..record_handler#37a6259cc0c1dae299a7866489dff0bd' + + persistence_layer = MockPersistenceLayer(expected_idempotency_key=idempotency_key) + expected_result = {"message": "Foo"} + + @idempotent_function(persistence_store=persistence_layer, data_keyword_argument="record") + def record_handler(record): + return expected_result + + # WHEN calling the function + result = record_handler(record=mock_event) + # THEN we expect the function to execute successfully + assert result == expected_result + + def test_idempotent_data_sorting(): # Scenario to validate same data in different order hashes to the same idempotency key data_one = {"data": "test message 1", "more_data": "more data 1"} From f6da33f8e2de17de2f62d3a4304b594421309651 Mon Sep 17 00:00:00 2001 From: heitorlessa Date: Fri, 28 Oct 2022 11:46:46 +0200 Subject: [PATCH 2/2] chore(tests): test idempotencyKeyError is raised on falsy values when enabled --- .../idempotency/test_idempotency.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/functional/idempotency/test_idempotency.py b/tests/functional/idempotency/test_idempotency.py index 9f52cb4096d..8f39b832554 100644 --- a/tests/functional/idempotency/test_idempotency.py +++ b/tests/functional/idempotency/test_idempotency.py @@ -1255,8 +1255,6 @@ def test_idempotent_function_falsy_values(data): mock_event = data idempotency_key = f"{TESTS_MODULE_PREFIX}.test_idempotent_function_falsy_values..record_handler#{hash_idempotency_key(mock_event)}" # noqa: E501 - # 'test-func.functional.idempotency.test_idempotency.test_idempotent_function_falsy_values..record_handler#37a6259cc0c1dae299a7866489dff0bd' - persistence_layer = MockPersistenceLayer(expected_idempotency_key=idempotency_key) expected_result = {"message": "Foo"} @@ -1270,6 +1268,25 @@ def record_handler(record): assert result == expected_result +@pytest.mark.parametrize("data", [None, 0, False]) +def test_idempotent_function_falsy_values_with_raise_on_no_idempotency_key( + data, persistence_store: DynamoDBPersistenceLayer +): + # GIVEN raise_on_no_idempotency_key is True + idempotency_config = IdempotencyConfig(event_key_jmespath="idemKey", raise_on_no_idempotency_key=True) + + @idempotent_function(data_keyword_argument="record", persistence_store=persistence_store, config=idempotency_config) + def record_handler(record): + return ValueError("Should not be raised") + + # WHEN calling the function + with pytest.raises(IdempotencyKeyError) as e: + record_handler(record=data) + + # THEN we expect an idempotency key error message + assert "No data found to create a hashed idempotency_key" == e.value.args[0] + + def test_idempotent_data_sorting(): # Scenario to validate same data in different order hashes to the same idempotency key data_one = {"data": "test message 1", "more_data": "more data 1"}