21
21
from aws_lambda_powertools .utilities .idempotency .idempotency import idempotent , idempotent_function
22
22
from aws_lambda_powertools .utilities .idempotency .persistence .base import BasePersistenceLayer , DataRecord
23
23
from aws_lambda_powertools .utilities .validation import envelopes , validator
24
+ from tests .functional .idempotency .conftest import serialize
24
25
from tests .functional .utils import load_event
25
26
26
27
TABLE_NAME = "TEST_TABLE"
@@ -741,7 +742,7 @@ def test_default_no_raise_on_missing_idempotency_key(
741
742
hashed_key = persistence_store ._get_hashed_idempotency_key ({})
742
743
743
744
# 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 ()
745
746
assert expected_value == hashed_key
746
747
747
748
@@ -785,7 +786,7 @@ def test_jmespath_with_powertools_json(
785
786
expected_value = [sub_attr_value , key_attr_value ]
786
787
api_gateway_proxy_event = {
787
788
"requestContext" : {"authorizer" : {"claims" : {"sub" : sub_attr_value }}},
788
- "body" : json . dumps ({"id" : key_attr_value }),
789
+ "body" : serialize ({"id" : key_attr_value }),
789
790
}
790
791
791
792
# WHEN calling _get_hashed_idempotency_key
@@ -869,7 +870,7 @@ def _delete_record(self, data_record: DataRecord) -> None:
869
870
def test_idempotent_lambda_event_source (lambda_context ):
870
871
# Scenario to validate that we can use the event_source decorator before or after the idempotent decorator
871
872
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 ())
873
874
expected_result = {"message" : "Foo" }
874
875
875
876
# GIVEN an event_source decorator
@@ -889,7 +890,7 @@ def lambda_handler(event, _):
889
890
def test_idempotent_function ():
890
891
# Scenario to validate we can use idempotent_function with any function
891
892
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 ())
893
894
expected_result = {"message" : "Foo" }
894
895
895
896
@idempotent_function (persistence_store = persistence_layer , data_keyword_argument = "record" )
@@ -906,7 +907,7 @@ def test_idempotent_function_arbitrary_args_kwargs():
906
907
# Scenario to validate we can use idempotent_function with a function
907
908
# with an arbitrary number of args and kwargs
908
909
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 ())
910
911
expected_result = {"message" : "Foo" }
911
912
912
913
@idempotent_function (persistence_store = persistence_layer , data_keyword_argument = "record" )
@@ -921,7 +922,7 @@ def record_handler(arg_one, arg_two, record, is_record):
921
922
922
923
def test_idempotent_function_invalid_data_kwarg ():
923
924
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 ())
925
926
expected_result = {"message" : "Foo" }
926
927
keyword_argument = "payload"
927
928
@@ -938,7 +939,7 @@ def record_handler(record):
938
939
939
940
def test_idempotent_function_arg_instead_of_kwarg ():
940
941
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 ())
942
943
expected_result = {"message" : "Foo" }
943
944
keyword_argument = "record"
944
945
@@ -956,7 +957,7 @@ def record_handler(record):
956
957
def test_idempotent_function_and_lambda_handler (lambda_context ):
957
958
# Scenario to validate we can use both idempotent_function and idempotent decorators
958
959
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 ())
960
961
expected_result = {"message" : "Foo" }
961
962
962
963
@idempotent_function (persistence_store = persistence_layer , data_keyword_argument = "record" )
@@ -976,3 +977,20 @@ def lambda_handler(event, _):
976
977
# THEN we expect the function and lambda handler to execute successfully
977
978
assert fn_result == expected_result
978
979
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