2121from aws_lambda_powertools .utilities .idempotency .idempotency import idempotent , idempotent_function
2222from aws_lambda_powertools .utilities .idempotency .persistence .base import BasePersistenceLayer , DataRecord
2323from aws_lambda_powertools .utilities .validation import envelopes , validator
24+ from tests .functional .idempotency .conftest import serialize
2425from tests .functional .utils import load_event
2526
2627TABLE_NAME = "TEST_TABLE"
@@ -741,7 +742,7 @@ def test_default_no_raise_on_missing_idempotency_key(
741742 hashed_key = persistence_store ._get_hashed_idempotency_key ({})
742743
743744 # THEN return the hash of None
744- expected_value = "test-func#" + md5 (json . dumps (None ).encode ()).hexdigest ()
745+ expected_value = "test-func#" + md5 (serialize (None ).encode ()).hexdigest ()
745746 assert expected_value == hashed_key
746747
747748
@@ -785,7 +786,7 @@ def test_jmespath_with_powertools_json(
785786 expected_value = [sub_attr_value , key_attr_value ]
786787 api_gateway_proxy_event = {
787788 "requestContext" : {"authorizer" : {"claims" : {"sub" : sub_attr_value }}},
788- "body" : json . dumps ({"id" : key_attr_value }),
789+ "body" : serialize ({"id" : key_attr_value }),
789790 }
790791
791792 # WHEN calling _get_hashed_idempotency_key
@@ -869,7 +870,7 @@ def _delete_record(self, data_record: DataRecord) -> None:
869870def test_idempotent_lambda_event_source (lambda_context ):
870871 # Scenario to validate that we can use the event_source decorator before or after the idempotent decorator
871872 mock_event = load_event ("apiGatewayProxyV2Event.json" )
872- persistence_layer = MockPersistenceLayer ("test-func#" + hashlib .md5 (json . dumps (mock_event ).encode ()).hexdigest ())
873+ persistence_layer = MockPersistenceLayer ("test-func#" + hashlib .md5 (serialize (mock_event ).encode ()).hexdigest ())
873874 expected_result = {"message" : "Foo" }
874875
875876 # GIVEN an event_source decorator
@@ -889,7 +890,7 @@ def lambda_handler(event, _):
889890def test_idempotent_function ():
890891 # Scenario to validate we can use idempotent_function with any function
891892 mock_event = {"data" : "value" }
892- persistence_layer = MockPersistenceLayer ("test-func#" + hashlib .md5 (json . dumps (mock_event ).encode ()).hexdigest ())
893+ persistence_layer = MockPersistenceLayer ("test-func#" + hashlib .md5 (serialize (mock_event ).encode ()).hexdigest ())
893894 expected_result = {"message" : "Foo" }
894895
895896 @idempotent_function (persistence_store = persistence_layer , data_keyword_argument = "record" )
@@ -906,7 +907,7 @@ def test_idempotent_function_arbitrary_args_kwargs():
906907 # Scenario to validate we can use idempotent_function with a function
907908 # with an arbitrary number of args and kwargs
908909 mock_event = {"data" : "value" }
909- persistence_layer = MockPersistenceLayer ("test-func#" + hashlib .md5 (json . dumps (mock_event ).encode ()).hexdigest ())
910+ persistence_layer = MockPersistenceLayer ("test-func#" + hashlib .md5 (serialize (mock_event ).encode ()).hexdigest ())
910911 expected_result = {"message" : "Foo" }
911912
912913 @idempotent_function (persistence_store = persistence_layer , data_keyword_argument = "record" )
@@ -921,7 +922,7 @@ def record_handler(arg_one, arg_two, record, is_record):
921922
922923def test_idempotent_function_invalid_data_kwarg ():
923924 mock_event = {"data" : "value" }
924- persistence_layer = MockPersistenceLayer ("test-func#" + hashlib .md5 (json . dumps (mock_event ).encode ()).hexdigest ())
925+ persistence_layer = MockPersistenceLayer ("test-func#" + hashlib .md5 (serialize (mock_event ).encode ()).hexdigest ())
925926 expected_result = {"message" : "Foo" }
926927 keyword_argument = "payload"
927928
@@ -938,7 +939,7 @@ def record_handler(record):
938939
939940def test_idempotent_function_arg_instead_of_kwarg ():
940941 mock_event = {"data" : "value" }
941- persistence_layer = MockPersistenceLayer ("test-func#" + hashlib .md5 (json . dumps (mock_event ).encode ()).hexdigest ())
942+ persistence_layer = MockPersistenceLayer ("test-func#" + hashlib .md5 (serialize (mock_event ).encode ()).hexdigest ())
942943 expected_result = {"message" : "Foo" }
943944 keyword_argument = "record"
944945
@@ -956,7 +957,7 @@ def record_handler(record):
956957def test_idempotent_function_and_lambda_handler (lambda_context ):
957958 # Scenario to validate we can use both idempotent_function and idempotent decorators
958959 mock_event = {"data" : "value" }
959- persistence_layer = MockPersistenceLayer ("test-func#" + hashlib .md5 (json . dumps (mock_event ).encode ()).hexdigest ())
960+ persistence_layer = MockPersistenceLayer ("test-func#" + hashlib .md5 (serialize (mock_event ).encode ()).hexdigest ())
960961 expected_result = {"message" : "Foo" }
961962
962963 @idempotent_function (persistence_store = persistence_layer , data_keyword_argument = "record" )
@@ -976,3 +977,20 @@ def lambda_handler(event, _):
976977 # THEN we expect the function and lambda handler to execute successfully
977978 assert fn_result == expected_result
978979 assert handler_result == expected_result
980+
981+
982+ def test_idempotent_data_sorting ():
983+ # Scenario to validate same data in different order hashes to the same idempotency key
984+ data_one = {"data" : "test message 1" , "more_data" : "more data 1" }
985+ data_two = {"more_data" : "more data 1" , "data" : "test message 1" }
986+
987+ # Assertion will happen in MockPersistenceLayer
988+ persistence_layer = MockPersistenceLayer ("test-func#" + hashlib .md5 (json .dumps (data_one ).encode ()).hexdigest ())
989+
990+ # GIVEN
991+ @idempotent_function (data_keyword_argument = "payload" , persistence_store = persistence_layer )
992+ def dummy (payload ):
993+ return {"message" : "hello" }
994+
995+ # WHEN
996+ dummy (payload = data_two )
0 commit comments