From 513e4d6ec1ffdc5209fe397f8c1f4c062345f131 Mon Sep 17 00:00:00 2001 From: maxhoheiser Date: Fri, 27 Dec 2024 17:37:49 +0100 Subject: [PATCH 01/12] feat: add test with multiple rules and multiple targets --- tests/aws/services/events/test_events.py | 66 ++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 588c8c6f645aa..5043b4a6521ce 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -1394,6 +1394,72 @@ def test_update_rule_with_targets( response = aws_client.events.list_targets_by_rule(Rule=rule_name) snapshot.match("list-targets-after-update", response) + @markers.aws.validated + def test_process_to_multiple_matching_rules( + self, + events_create_event_bus, + sqs_create_queue, + sqs_get_queue_arn, + events_put_rule, + aws_client, + snapshot, + ): + """two rules with each two sqs targets, all 4 ques should receive the event""" + + custom_bus_name = f"test-eventbus-{short_uid()}" + events_create_event_bus(Name=custom_bus_name) + + # create sqs queues targets + targets = {} + for i in range(4): + queue_url = sqs_create_queue() + queue_arn = sqs_get_queue_arn(queue_url) + targets[f"sqs_target_{i}"] = {"queue_url": queue_url, "queue_arn": queue_arn} + + # create rules + rules = {} + for i in range(2): + rule_name = f"test-rule-{i}-{short_uid()}" + rule = events_put_rule( + Name=rule_name, + EventBusName=custom_bus_name, + EventPattern=json.dumps(TEST_EVENT_PATTERN), + State="ENABLED", + ) + rule_arn = rule["RuleArn"] + rules[f"rule_{i}"] = {"rule_name": rule_name, "rule_arn": rule_arn} + + # attach targets to rule + combinations = [("0", ["0", "1"]), ("1", ["2", "3"])] + for rule_idx, targets_idxs in combinations: + rule_arn = rules[f"rule_{rule_idx}"]["rule_arn"] + for target_idx in targets_idxs: + queue_url = targets[f"sqs_target_{target_idx}"]["queue_url"] + queue_arn = targets[f"sqs_target_{target_idx}"]["queue_arn"] + allow_event_rule_to_sqs_queue( + aws_client=aws_client, + sqs_queue_url=queue_url, + sqs_queue_arn=queue_arn, + event_rule_arn=rule_arn, + ) + + aws_client.events.put_targets( + Rule=rules[f"rule_{rule}"]["rule_name"], + EventBusName=custom_bus_name, + Targets=[ + {"Id": f"test-target-{target_idx}-{short_uid()}", "Arn": queue_arn}, + ], + ) + + # put event + aws_client.events.put_events( + Entries={ + "Source": TEST_EVENT_PATTERN["source"][0], + "DetailType": TEST_EVENT_PATTERN["detail-type"][0], + "Detail": TEST_EVENT_PATTERN["detail"], + } + ) + class TestEventPattern: @markers.aws.validated From 2dc4c3b70f0c74e5cd523468c5406b57c6d69053 Mon Sep 17 00:00:00 2001 From: maxhoheiser Date: Fri, 27 Dec 2024 20:12:47 +0100 Subject: [PATCH 02/12] feat: update default test event detail name --- .../events/test_archive_and_replay.py | 6 +-- tests/aws/services/events/test_events.py | 53 +++++++++++++------ .../services/events/test_events.snapshot.json | 4 ++ .../events/test_events.validation.json | 8 +-- .../test_events_cross_account_region.py | 8 +-- .../aws/services/events/test_events_inputs.py | 16 +++--- .../services/events/test_events_targets.py | 16 +++--- 7 files changed, 66 insertions(+), 45 deletions(-) diff --git a/tests/aws/services/events/test_archive_and_replay.py b/tests/aws/services/events/test_archive_and_replay.py index ff8c468c05f2a..9fb0ce8a7535d 100644 --- a/tests/aws/services/events/test_archive_and_replay.py +++ b/tests/aws/services/events/test_archive_and_replay.py @@ -13,7 +13,7 @@ wait_for_replay_in_state, ) from tests.aws.services.events.test_events import ( - EVENT_DETAIL, + TEST_EVENT_DETAIL, TEST_EVENT_PATTERN, TEST_EVENT_PATTERN_NO_DETAIL, ) @@ -219,7 +219,7 @@ def test_list_archive_with_events( entry = { "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } entries.append(entry) @@ -412,7 +412,7 @@ def test_start_list_describe_canceled_replay( entry = { "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), "EventBusName": event_bus_name, } entries.append(entry) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 5043b4a6521ce..1201e69ec23ac 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -29,6 +29,7 @@ ) EVENT_DETAIL = {"command": "update-account", "payload": {"acc_id": "0a787ecb-4015", "sf_id": "baz"}} + SPECIAL_EVENT_DETAIL = { "command": "update-account", "payload": {"acc_id": "0a787ecb-4015", "sf_id": "baz"}, @@ -36,6 +37,11 @@ "listmulti": ["ACTIVE", "INACTIVE"], } +TEST_EVENT_DETAIL = { + "command": "update-account", + "payload": {"acc_id": "0a787ecb-4015", "sf_id": "baz"}, +} + TEST_EVENT_PATTERN = { "source": ["core.update-account-command"], "detail-type": ["core.update-account-command"], @@ -73,7 +79,7 @@ def test_put_events_without_source(self, snapshot, aws_client): entries = [ { "DetailType": TEST_EVENT_PATTERN_NO_SOURCE["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), }, ] response = aws_client.events.put_events(Entries=entries) @@ -121,7 +127,7 @@ def test_put_event_without_detail_type(self, snapshot, aws_client): entries = [ { "Source": "some.source", - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), "DetailType": "", }, ] @@ -212,7 +218,7 @@ def test_put_events_exceed_limit_ten_entries( { "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), "EventBusName": bus_name, } ) @@ -328,7 +334,7 @@ def check_rule_active(): test_event = { "Source": TEST_EVENT_PATTERN_NO_DETAIL["source"][0], "DetailType": TEST_EVENT_PATTERN_NO_DETAIL["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } event_response = aws_client.events.put_events(Entries=[test_event]) @@ -352,7 +358,7 @@ def verify_message_content(message, original_event_id): detail = body["detail"] # detail is already parsed as dict assert isinstance(detail, dict), f"Detail should be a dict, got {type(detail)}" - assert detail == EVENT_DETAIL, f"Unexpected detail content: {detail}" + assert detail == TEST_EVENT_DETAIL, f"Unexpected detail content: {detail}" assert ( body["id"] == original_event_id @@ -419,7 +425,7 @@ def test_put_events_with_target_delivery_failure( test_event = { "Source": TEST_EVENT_PATTERN_NO_DETAIL["source"][0], "DetailType": TEST_EVENT_PATTERN_NO_DETAIL["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } response = aws_client.events.put_events(Entries=[test_event]) @@ -962,7 +968,7 @@ def test_put_events_bus_to_bus( "EventBusName": bus_name_one, "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } ] ) @@ -1402,11 +1408,10 @@ def test_process_to_multiple_matching_rules( sqs_get_queue_arn, events_put_rule, aws_client, - snapshot, ): """two rules with each two sqs targets, all 4 ques should receive the event""" - custom_bus_name = f"test-eventbus-{short_uid()}" + custom_bus_name = f"test-bus-{short_uid()}" events_create_event_bus(Name=custom_bus_name) # create sqs queues targets @@ -1423,7 +1428,7 @@ def test_process_to_multiple_matching_rules( rule = events_put_rule( Name=rule_name, EventBusName=custom_bus_name, - EventPattern=json.dumps(TEST_EVENT_PATTERN), + EventPattern=json.dumps(TEST_EVENT_PATTERN_NO_DETAIL), State="ENABLED", ) rule_arn = rule["RuleArn"] @@ -1444,7 +1449,7 @@ def test_process_to_multiple_matching_rules( ) aws_client.events.put_targets( - Rule=rules[f"rule_{rule}"]["rule_name"], + Rule=rules[f"rule_{rule_idx}"]["rule_name"], EventBusName=custom_bus_name, Targets=[ {"Id": f"test-target-{target_idx}-{short_uid()}", "Arn": queue_arn}, @@ -1453,11 +1458,27 @@ def test_process_to_multiple_matching_rules( # put event aws_client.events.put_events( - Entries={ - "Source": TEST_EVENT_PATTERN["source"][0], - "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": TEST_EVENT_PATTERN["detail"], - } + Entries=[ + { + "EventBusName": custom_bus_name, + "Source": TEST_EVENT_PATTERN_NO_DETAIL["source"][0], + "DetailType": TEST_EVENT_PATTERN_NO_DETAIL["detail-type"][0], + "Detail": json.dumps(TEST_EVENT_DETAIL), + } + ], + ) + + sqs_collect_messages( + aws_client, targets["sqs_target_0"]["queue_url"], expected_events_count=1 + ) + sqs_collect_messages( + aws_client, targets["sqs_target_1"]["queue_url"], expected_events_count=1 + ) + sqs_collect_messages( + aws_client, targets["sqs_target_2"]["queue_url"], expected_events_count=1 + ) + sqs_collect_messages( + aws_client, targets["sqs_target_3"]["queue_url"], expected_events_count=1 ) diff --git a/tests/aws/services/events/test_events.snapshot.json b/tests/aws/services/events/test_events.snapshot.json index 33ae5bc1c260f..3ecc4ff46c5ae 100644 --- a/tests/aws/services/events/test_events.snapshot.json +++ b/tests/aws/services/events/test_events.snapshot.json @@ -1982,5 +1982,9 @@ } } } + }, + "tests/aws/services/events/test_events.py::TestEventRule::test_process_to_multiple_matching_rules": { + "recorded-date": "27-12-2024, 19:02:05", + "recorded-content": {} } } diff --git a/tests/aws/services/events/test_events.validation.json b/tests/aws/services/events/test_events.validation.json index c7a4c7efa0a3b..8f2362f1a75a6 100644 --- a/tests/aws/services/events/test_events.validation.json +++ b/tests/aws/services/events/test_events.validation.json @@ -1,11 +1,7 @@ { - "tests/aws/services/events/test_events.py::TestEvents::test_create_connection_validations": { - "last_validated_date": "2024-12-13T10:54:30+00:00" + "tests/aws/services/events/test_events.py::TestEventRule::test_process_to_multiple_matching_rules": { + "last_validated_date": "2024-12-27T19:07:55+00:00" } -} - } -} - } " } diff --git a/tests/aws/services/events/test_events_cross_account_region.py b/tests/aws/services/events/test_events_cross_account_region.py index 572f52780a8f4..5a7e8adb1837a 100644 --- a/tests/aws/services/events/test_events_cross_account_region.py +++ b/tests/aws/services/events/test_events_cross_account_region.py @@ -11,7 +11,7 @@ sqs_collect_messages, ) from tests.aws.services.events.test_events import ( - EVENT_DETAIL, + TEST_EVENT_DETAIL, TEST_EVENT_PATTERN_NO_SOURCE, ) @@ -231,7 +231,7 @@ def test_event_bus_to_event_bus_cross_account_region( { "Source": SOURCE_PRIMARY, "DetailType": TEST_EVENT_PATTERN_NO_SOURCE["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), "EventBusName": event_bus_name_primary, } ], @@ -265,7 +265,7 @@ def test_event_bus_to_event_bus_cross_account_region( { "Source": SOURCE_SECONDARY, "DetailType": TEST_EVENT_PATTERN_NO_SOURCE["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), "EventBusName": event_bus_name_secondary, } ], @@ -424,7 +424,7 @@ def test_put_events( { "Source": SOURCE_PRIMARY, "DetailType": TEST_EVENT_PATTERN_NO_SOURCE["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), "EventBusName": event_bus_arn, # using arn for cross region / cross account } ], diff --git a/tests/aws/services/events/test_events_inputs.py b/tests/aws/services/events/test_events_inputs.py index bf531a2a95413..30efe56e432fe 100644 --- a/tests/aws/services/events/test_events_inputs.py +++ b/tests/aws/services/events/test_events_inputs.py @@ -12,8 +12,8 @@ sqs_collect_messages, ) from tests.aws.services.events.test_events import ( - EVENT_DETAIL, SPECIAL_EVENT_DETAIL, + TEST_EVENT_DETAIL, TEST_EVENT_PATTERN, ) @@ -82,7 +82,7 @@ def test_put_events_with_input_path(self, put_events_with_filter_to_sqs, snapsho { "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } ] entries_asserts = [(entries1, True)] @@ -101,7 +101,7 @@ def test_put_events_with_input_path(self, put_events_with_filter_to_sqs, snapsho snapshot.match("message", messages) @markers.aws.validated - @pytest.mark.parametrize("event_detail", [EVENT_DETAIL, EVENT_DETAIL_DUPLICATED_KEY]) + @pytest.mark.parametrize("event_detail", [TEST_EVENT_DETAIL, EVENT_DETAIL_DUPLICATED_KEY]) def test_put_events_with_input_path_nested( self, event_detail, put_events_with_filter_to_sqs, snapshot ): @@ -135,7 +135,7 @@ def test_put_events_with_input_path_max_level_depth( { "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } ] entries_asserts = [(entries1, True)] @@ -193,7 +193,7 @@ def test_put_events_with_input_path_multiple_targets( "EventBusName": bus_name, "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } ] ) @@ -235,7 +235,7 @@ def test_put_events_with_input_transformer_input_template_string( { "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } ] entries_asserts = [(entries, True)] @@ -294,7 +294,7 @@ def test_put_events_with_input_transformer_input_template_json( { "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } ] entries_asserts = [(entries, True)] @@ -455,7 +455,7 @@ def test_input_transformer_predefined_variables( "EventBusName": bus_name, "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } ] ) diff --git a/tests/aws/services/events/test_events_targets.py b/tests/aws/services/events/test_events_targets.py index bfa433591fb33..a0ebdc0285d54 100644 --- a/tests/aws/services/events/test_events_targets.py +++ b/tests/aws/services/events/test_events_targets.py @@ -22,7 +22,7 @@ from tests.aws.scenario.kinesis_firehose.conftest import get_all_expected_messages_from_s3 from tests.aws.services.events.helper_functions import is_old_provider, sqs_collect_messages from tests.aws.services.events.test_api_destinations_and_connection import API_DESTINATION_AUTHS -from tests.aws.services.events.test_events import EVENT_DETAIL, TEST_EVENT_PATTERN +from tests.aws.services.events.test_events import TEST_EVENT_DETAIL, TEST_EVENT_PATTERN from tests.aws.services.firehose.helper_functions import get_firehose_iam_documents from tests.aws.services.kinesis.helper_functions import get_shard_iterator from tests.aws.services.lambda_.test_lambda import ( @@ -533,7 +533,7 @@ def test_put_events_with_target_cloudwatch_logs( "EventBusName": event_bus_name, "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } put_events_response = aws_client.events.put_events(Entries=[event_entry]) snapshot.match("put_events_response", put_events_response) @@ -657,7 +657,7 @@ def test_put_events_with_target_events( { "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), "EventBusName": event_bus_name_source, } ], @@ -782,7 +782,7 @@ def test_put_events_with_target_firehose( "EventBusName": event_bus_name, "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } ] ) @@ -895,7 +895,7 @@ def test_put_events_with_target_kinesis( "EventBusName": event_bus_name, "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } ] ) @@ -959,7 +959,7 @@ def test_put_events_with_target_lambda( "EventBusName": bus_name, "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } ] ) @@ -1247,7 +1247,7 @@ def test_put_events_with_target_sns( "EventBusName": event_bus_name, "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } ] ) @@ -1277,7 +1277,7 @@ def test_put_events_with_target_sqs(self, put_events_with_filter_to_sqs, snapsho { "Source": TEST_EVENT_PATTERN["source"][0], "DetailType": TEST_EVENT_PATTERN["detail-type"][0], - "Detail": json.dumps(EVENT_DETAIL), + "Detail": json.dumps(TEST_EVENT_DETAIL), } ] message = put_events_with_filter_to_sqs( From 44e84e01b66cc942357d9419cceb0c2964893383 Mon Sep 17 00:00:00 2001 From: maxhoheiser Date: Thu, 2 Jan 2025 12:40:15 +0100 Subject: [PATCH 03/12] feat: add test multiple rules single target --- tests/aws/services/events/test_events.py | 80 ++++++++++++++++++- .../services/events/test_events.snapshot.json | 43 +++++++++- .../events/test_events.validation.json | 7 +- 3 files changed, 126 insertions(+), 4 deletions(-) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 1201e69ec23ac..17f989e244d94 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -14,6 +14,7 @@ from localstack_snapshot.snapshots.transformer import SortingTransformer from localstack import config +from localstack.aws.api.lambda_ import Runtime from localstack.services.events.v1.provider import _get_events_tmp_dir from localstack.testing.aws.eventbus_utils import allow_event_rule_to_sqs_queue from localstack.testing.aws.util import is_aws_cloud @@ -21,12 +22,16 @@ from localstack.utils.files import load_file from localstack.utils.strings import long_uid, short_uid from localstack.utils.sync import retry +from localstack.utils.testutil import check_expected_lambda_log_events_length from tests.aws.services.events.helper_functions import ( assert_valid_event, is_old_provider, is_v2_provider, sqs_collect_messages, ) +from tests.aws.services.lambda_.test_lambda import ( + TEST_LAMBDA_PYTHON_ECHO, +) EVENT_DETAIL = {"command": "update-account", "payload": {"acc_id": "0a787ecb-4015", "sf_id": "baz"}} @@ -1401,7 +1406,7 @@ def test_update_rule_with_targets( snapshot.match("list-targets-after-update", response) @markers.aws.validated - def test_process_to_multiple_matching_rules( + def test_process_to_multiple_matching_rules_different_targets( self, events_create_event_bus, sqs_create_queue, @@ -1481,6 +1486,79 @@ def test_process_to_multiple_matching_rules( aws_client, targets["sqs_target_3"]["queue_url"], expected_events_count=1 ) + @markers.aws.validated + def test_process_to_multiple_matching_rules_single_target( + self, + create_lambda_function, + events_create_event_bus, + events_put_rule, + aws_client, + snapshot, + ): + """two rules with both the same lambda target, the lambda target should be invoked twice. + This will only work for certain targets, since e.g. sqs has message deduplication""" + + bus_name = f"test-bus-{short_uid()}" + events_create_event_bus(Name=bus_name) + + # create lambda target + function_name = f"lambda-func-{short_uid()}" + create_lambda_response = create_lambda_function( + handler_file=TEST_LAMBDA_PYTHON_ECHO, + func_name=function_name, + runtime=Runtime.python3_12, + ) + lambda_function_arn = create_lambda_response["CreateFunctionResponse"]["FunctionArn"] + + # create rules + for i in range(2): + rule_name = f"test-rule-{i}-{short_uid()}" + rule = events_put_rule( + Name=rule_name, + EventBusName=bus_name, + EventPattern=json.dumps(TEST_EVENT_PATTERN_NO_DETAIL), + State="ENABLED", + ) + rule_arn = rule["RuleArn"] + + aws_client.lambda_.add_permission( + FunctionName=function_name, + StatementId=f"{rule_name}-Event", + Action="lambda:InvokeFunction", + Principal="events.amazonaws.com", + SourceArn=rule_arn, + ) + + target_id = f"test-target-{i}-{short_uid()}" + aws_client.events.put_targets( + Rule=rule_name, + EventBusName=bus_name, + Targets=[{"Id": target_id, "Arn": lambda_function_arn}], + ) + + # put event + aws_client.events.put_events( + Entries=[ + { + "EventBusName": bus_name, + "Source": TEST_EVENT_PATTERN_NO_DETAIL["source"][0], + "DetailType": TEST_EVENT_PATTERN_NO_DETAIL["detail-type"][0], + "Detail": json.dumps(TEST_EVENT_DETAIL), + } + ], + ) + + # check lambda invocation + events = retry( + check_expected_lambda_log_events_length, + retries=3, + sleep=1, + function_name=function_name, + expected_length=2, + logs_client=aws_client.logs, + ) + snapshot.match("events", events) + class TestEventPattern: @markers.aws.validated diff --git a/tests/aws/services/events/test_events.snapshot.json b/tests/aws/services/events/test_events.snapshot.json index 3ecc4ff46c5ae..4ddcbfa512517 100644 --- a/tests/aws/services/events/test_events.snapshot.json +++ b/tests/aws/services/events/test_events.snapshot.json @@ -1983,8 +1983,49 @@ } } }, - "tests/aws/services/events/test_events.py::TestEventRule::test_process_to_multiple_matching_rules": { + "tests/aws/services/events/test_events.py::TestEventRule::test_process_to_multiple_matching_rules_different_targets": { "recorded-date": "27-12-2024, 19:02:05", "recorded-content": {} + }, + "tests/aws/services/events/test_events.py::TestEventRule::test_process_to_multiple_matching_rules_single_target": { + "recorded-date": "02-01-2025, 11:37:47", + "recorded-content": { + "events": [ + { + "version": "0", + "id": "", + "detail-type": "core.update-account-command", + "source": "core.update-account-command", + "account": "111111111111", + "time": "date", + "region": "", + "resources": [], + "detail": { + "command": "update-account", + "payload": { + "acc_id": "0a787ecb-4015", + "sf_id": "baz" + } + } + }, + { + "version": "0", + "id": "", + "detail-type": "core.update-account-command", + "source": "core.update-account-command", + "account": "111111111111", + "time": "date", + "region": "", + "resources": [], + "detail": { + "command": "update-account", + "payload": { + "acc_id": "0a787ecb-4015", + "sf_id": "baz" + } + } + } + ] + } } } diff --git a/tests/aws/services/events/test_events.validation.json b/tests/aws/services/events/test_events.validation.json index 8f2362f1a75a6..7eeb9b161c696 100644 --- a/tests/aws/services/events/test_events.validation.json +++ b/tests/aws/services/events/test_events.validation.json @@ -1,8 +1,11 @@ { - "tests/aws/services/events/test_events.py::TestEventRule::test_process_to_multiple_matching_rules": { - "last_validated_date": "2024-12-27T19:07:55+00:00" + "tests/aws/services/events/test_events.py::TestEventRule::test_process_to_multiple_matching_rules_single_target": { + "last_validated_date": "2025-01-02T11:37:47+00:00" } } + + +} " } } From 671acadb53e6494ce618452c6712b20c0cda57f4 Mon Sep 17 00:00:00 2001 From: maxhoheiser Date: Thu, 2 Jan 2025 13:11:46 +0100 Subject: [PATCH 04/12] feat: add test same target multiple rules different conditions --- tests/aws/services/events/test_events.py | 75 ++++++++++++ .../services/events/test_events.snapshot.json | 113 ++++++++++++++++++ .../events/test_events.validation.json | 5 +- 3 files changed, 191 insertions(+), 2 deletions(-) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 17f989e244d94..fda893b5775dd 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -1559,6 +1559,81 @@ def test_process_to_multiple_matching_rules_single_target( ) snapshot.match("events", events) + @markers.aws.validated + def test_process_to_single_matching_rules_single_target( + self, + create_lambda_function, + events_create_event_bus, + events_put_rule, + aws_client, + snapshot, + ): + """Three rules with all the same lambda target, but different patterns as condition. + The lambda should onl be invoked by the rule matching the event pattern.""" + + bus_name = f"test-bus-{short_uid()}" + events_create_event_bus(Name=bus_name) + + # create lambda target + function_name = f"lambda-func-{short_uid()}" + create_lambda_response = create_lambda_function( + handler_file=TEST_LAMBDA_PYTHON_ECHO, + func_name=function_name, + runtime=Runtime.python3_12, + ) + lambda_function_arn = create_lambda_response["CreateFunctionResponse"]["FunctionArn"] + + # create rules + sources = ["source-one", "source-two", "source-three"] + for i, source in zip(range(3), sources): + rule_name = f"test-rule-{i}-{short_uid()}" + rule = events_put_rule( + Name=rule_name, + EventBusName=bus_name, + EventPattern=json.dumps({"source": [source]}), + State="ENABLED", + ) + rule_arn = rule["RuleArn"] + + aws_client.lambda_.add_permission( + FunctionName=function_name, + StatementId=f"{rule_name}-Event", + Action="lambda:InvokeFunction", + Principal="events.amazonaws.com", + SourceArn=rule_arn, + ) + + target_id = f"test-target-{i}-{short_uid()}" + aws_client.events.put_targets( + Rule=rule_name, + EventBusName=bus_name, + Targets=[{"Id": target_id, "Arn": lambda_function_arn}], + ) + + for i, source in zip(range(3), sources): + num_events = i + 1 + aws_client.events.put_events( + Entries=[ + { + "EventBusName": bus_name, + "Source": source, + "DetailType": TEST_EVENT_PATTERN_NO_DETAIL["detail-type"][0], + "Detail": json.dumps(TEST_EVENT_DETAIL), + } + ], + ) + + # check lambda invocation + events = retry( + check_expected_lambda_log_events_length, + retries=3, + sleep=1, + function_name=function_name, + expected_length=num_events, + logs_client=aws_client.logs, + ) + snapshot.match(f"events-{source}", events) + class TestEventPattern: @markers.aws.validated diff --git a/tests/aws/services/events/test_events.snapshot.json b/tests/aws/services/events/test_events.snapshot.json index 4ddcbfa512517..85fe61e95a982 100644 --- a/tests/aws/services/events/test_events.snapshot.json +++ b/tests/aws/services/events/test_events.snapshot.json @@ -2027,5 +2027,118 @@ } ] } + }, + "tests/aws/services/events/test_events.py::TestEventRule::test_process_to_single_matching_rules_single_target": { + "recorded-date": "02-01-2025, 12:09:21", + "recorded-content": { + "events-source-one": [ + { + "version": "0", + "id": "", + "detail-type": "core.update-account-command", + "source": "source-one", + "account": "111111111111", + "time": "date", + "region": "", + "resources": [], + "detail": { + "command": "update-account", + "payload": { + "acc_id": "0a787ecb-4015", + "sf_id": "baz" + } + } + } + ], + "events-source-two": [ + { + "version": "0", + "id": "", + "detail-type": "core.update-account-command", + "source": "source-one", + "account": "111111111111", + "time": "date", + "region": "", + "resources": [], + "detail": { + "command": "update-account", + "payload": { + "acc_id": "0a787ecb-4015", + "sf_id": "baz" + } + } + }, + { + "version": "0", + "id": "", + "detail-type": "core.update-account-command", + "source": "source-two", + "account": "111111111111", + "time": "date", + "region": "", + "resources": [], + "detail": { + "command": "update-account", + "payload": { + "acc_id": "0a787ecb-4015", + "sf_id": "baz" + } + } + } + ], + "events-source-three": [ + { + "version": "0", + "id": "", + "detail-type": "core.update-account-command", + "source": "source-one", + "account": "111111111111", + "time": "date", + "region": "", + "resources": [], + "detail": { + "command": "update-account", + "payload": { + "acc_id": "0a787ecb-4015", + "sf_id": "baz" + } + } + }, + { + "version": "0", + "id": "", + "detail-type": "core.update-account-command", + "source": "source-two", + "account": "111111111111", + "time": "date", + "region": "", + "resources": [], + "detail": { + "command": "update-account", + "payload": { + "acc_id": "0a787ecb-4015", + "sf_id": "baz" + } + } + }, + { + "version": "0", + "id": "", + "detail-type": "core.update-account-command", + "source": "source-three", + "account": "111111111111", + "time": "date", + "region": "", + "resources": [], + "detail": { + "command": "update-account", + "payload": { + "acc_id": "0a787ecb-4015", + "sf_id": "baz" + } + } + } + ] + } } } diff --git a/tests/aws/services/events/test_events.validation.json b/tests/aws/services/events/test_events.validation.json index 7eeb9b161c696..675dfca2a31fb 100644 --- a/tests/aws/services/events/test_events.validation.json +++ b/tests/aws/services/events/test_events.validation.json @@ -1,8 +1,9 @@ { - "tests/aws/services/events/test_events.py::TestEventRule::test_process_to_multiple_matching_rules_single_target": { - "last_validated_date": "2025-01-02T11:37:47+00:00" + "tests/aws/services/events/test_events.py::TestEventRule::test_process_to_single_matching_rules_single_target": { + "last_validated_date": "2025-01-02T12:09:21+00:00" } } +} } From 718f429297bd880122bfaa91c782db48c5a3552c Mon Sep 17 00:00:00 2001 From: maxhoheiser Date: Thu, 2 Jan 2025 13:48:19 +0100 Subject: [PATCH 05/12] feat: add test multiple rules multiple patterns same target --- tests/aws/services/events/test_events.py | 99 +++++++++++++++++++ .../services/events/test_events.snapshot.json | 28 ++++++ .../events/test_events.validation.json | 9 +- 3 files changed, 129 insertions(+), 7 deletions(-) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index fda893b5775dd..3eb2184157cc9 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -1634,6 +1634,105 @@ def test_process_to_single_matching_rules_single_target( ) snapshot.match(f"events-{source}", events) + @markers.aws.validated + def test_process_pattern_to_single_matching_rules_single_target( + self, + create_lambda_function, + events_create_event_bus, + events_put_rule, + aws_client, + snapshot, + ): + """Three rules with all the same lambda target, but different patterns as condition. + The lambda should onl be invoked by the rule matching the event pattern.""" + + bus_name = f"test-bus-{short_uid()}" + events_create_event_bus(Name=bus_name) + + # create lambda target + function_name = f"lambda-func-{short_uid()}" + create_lambda_response = create_lambda_function( + handler_file=TEST_LAMBDA_PYTHON_ECHO, + func_name=function_name, + runtime=Runtime.python3_12, + ) + lambda_function_arn = create_lambda_response["CreateFunctionResponse"]["FunctionArn"] + + # create rules + input_path_map = {"detail": "$.detail"} + patterns = [ + {"detail": {"payload": {"id": [{"exists": True}]}}}, + {"detail": {"id": [{"exists": True}]}}, + ] + input_transformers = [ + { + "InputPathsMap": input_path_map, + "InputTemplate": '{"detail-payload-with-id": }', + }, + { + "InputPathsMap": input_path_map, + "InputTemplate": '{"detail-with-id": }', + }, + ] + for i, pattern, input_transformer in zip(range(3), patterns, input_transformers): + rule_name = f"test-rule-{i}-{short_uid()}" + rule = events_put_rule( + Name=rule_name, + EventBusName=bus_name, + EventPattern=json.dumps(pattern), + State="ENABLED", + ) + rule_arn = rule["RuleArn"] + + aws_client.lambda_.add_permission( + FunctionName=function_name, + StatementId=f"{rule_name}-Event", + Action="lambda:InvokeFunction", + Principal="events.amazonaws.com", + SourceArn=rule_arn, + ) + + target_id = f"test-target-{i}-{short_uid()}" + aws_client.events.put_targets( + Rule=rule_name, + EventBusName=bus_name, + Targets=[ + { + "Id": target_id, + "Arn": lambda_function_arn, + "InputTransformer": input_transformer, + } + ], + ) + + details = [ + {"payload": {"id": "123"}}, + {"id": "123"}, + ] + for i, detail in zip(range(3), details): + num_events = i + 1 + aws_client.events.put_events( + Entries=[ + { + "EventBusName": bus_name, + "Source": TEST_EVENT_PATTERN_NO_DETAIL["source"][0], + "DetailType": TEST_EVENT_PATTERN_NO_DETAIL["detail-type"][0], + "Detail": json.dumps(detail), + } + ], + ) + + # check lambda invocation + events = retry( + check_expected_lambda_log_events_length, + retries=3, + sleep=1, + function_name=function_name, + expected_length=num_events, + logs_client=aws_client.logs, + ) + snapshot.match(f"events-{num_events}", events) + class TestEventPattern: @markers.aws.validated diff --git a/tests/aws/services/events/test_events.snapshot.json b/tests/aws/services/events/test_events.snapshot.json index 85fe61e95a982..9afd753b70f1d 100644 --- a/tests/aws/services/events/test_events.snapshot.json +++ b/tests/aws/services/events/test_events.snapshot.json @@ -2140,5 +2140,33 @@ } ] } + }, + "tests/aws/services/events/test_events.py::TestEventRule::test_process_pattern_to_single_matching_rules_single_target": { + "recorded-date": "02-01-2025, 12:47:30", + "recorded-content": { + "events-1": [ + { + "detail-payload-with-id": { + "payload": { + "id": "123" + } + } + } + ], + "events-2": [ + { + "detail-payload-with-id": { + "payload": { + "id": "123" + } + } + }, + { + "detail-with-id": { + "id": "123" + } + } + ] + } } } diff --git a/tests/aws/services/events/test_events.validation.json b/tests/aws/services/events/test_events.validation.json index 675dfca2a31fb..123162886f6b9 100644 --- a/tests/aws/services/events/test_events.validation.json +++ b/tests/aws/services/events/test_events.validation.json @@ -1,13 +1,8 @@ { - "tests/aws/services/events/test_events.py::TestEventRule::test_process_to_single_matching_rules_single_target": { - "last_validated_date": "2025-01-02T12:09:21+00:00" + "tests/aws/services/events/test_events.py::TestEventRule::test_process_pattern_to_single_matching_rules_single_target": { + "last_validated_date": "2025-01-02T12:47:30+00:00" } } -} - - -} -" } } " From f364d37332ffcb478feb685cb2b3484db59597f1 Mon Sep 17 00:00:00 2001 From: maxhoheiser Date: Thu, 2 Jan 2025 14:09:04 +0100 Subject: [PATCH 06/12] fix: skip test for v1 provider --- tests/aws/services/events/test_events.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 3eb2184157cc9..57424bac77c4b 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -1635,6 +1635,10 @@ def test_process_to_single_matching_rules_single_target( snapshot.match(f"events-{source}", events) @markers.aws.validated + @pytest.mark.skipif( + is_old_provider(), + reason="V1 provider does not support this feature", + ) def test_process_pattern_to_single_matching_rules_single_target( self, create_lambda_function, From 8c430dc7c99a22f113b13d74b2b3857ef6f221f4 Mon Sep 17 00:00:00 2001 From: maxhoheiser Date: Fri, 3 Jan 2025 13:08:06 +0100 Subject: [PATCH 07/12] feat: add test target ids --- tests/aws/services/events/test_events.py | 82 ++++++++++++++++++- .../services/events/test_events.snapshot.json | 49 +++++++++++ .../events/test_events.validation.json | 5 +- 3 files changed, 131 insertions(+), 5 deletions(-) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 57424bac77c4b..6d1865c2ea60f 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -1678,7 +1678,7 @@ def test_process_pattern_to_single_matching_rules_single_target( "InputTemplate": '{"detail-with-id": }', }, ] - for i, pattern, input_transformer in zip(range(3), patterns, input_transformers): + for i, pattern, input_transformer in zip(range(2), patterns, input_transformers): rule_name = f"test-rule-{i}-{short_uid()}" rule = events_put_rule( Name=rule_name, @@ -1713,7 +1713,7 @@ def test_process_pattern_to_single_matching_rules_single_target( {"payload": {"id": "123"}}, {"id": "123"}, ] - for i, detail in zip(range(3), details): + for i, detail in zip(range(2), details): num_events = i + 1 aws_client.events.put_events( Entries=[ @@ -1969,3 +1969,81 @@ def test_put_target_id_validation( {"Id": target_id, "Arn": queue_arn, "InputPath": "$.detail"}, ], ) + + @markers.aws.validated + def test_put_multiple_targets_with_same_id_single_rule( + self, sqs_create_queue, sqs_get_queue_arn, events_put_rule, snapshot, aws_client + ): + """Targets attached to a rule must have unique IDs, but there is no validation for this. + The last target with the same ID will overwrite the previous one.""" + rule_name = f"rule-{short_uid()}" + queue_url = sqs_create_queue() + queue_arn = sqs_get_queue_arn(queue_url) + + events_put_rule( + Name=rule_name, EventPattern=json.dumps(TEST_EVENT_PATTERN), State="ENABLED" + ) + + target_id = f"test-With_valid.Characters-{short_uid()}" + aws_client.events.put_targets( + Rule=rule_name, + Targets=[ + {"Id": target_id, "Arn": queue_arn, "InputPath": "$.detail"}, + ], + ) + + aws_client.events.put_targets( + Rule=rule_name, + Targets=[ + { + "Id": target_id, + "Arn": queue_arn, + "InputPath": "$.notexisting", + }, + ], + ) + + response = aws_client.events.list_targets_by_rule(Rule=rule_name) + snapshot.match("list-targets", response) + + @markers.aws.validated + def test_put_multiple_targets_with_same_id_across_different_rules( + self, sqs_create_queue, sqs_get_queue_arn, events_put_rule, snapshot, aws_client + ): + """Targets attached to different rules can have the same ID""" + rule_one_name = f"test-rule-one-{short_uid()}" + rule_two_name = f"test-rule-two-{short_uid()}" + queue_url = sqs_create_queue() + queue_arn = sqs_get_queue_arn(queue_url) + + events_put_rule( + Name=rule_one_name, EventPattern=json.dumps(TEST_EVENT_PATTERN), State="ENABLED" + ) + events_put_rule( + Name=rule_two_name, EventPattern=json.dumps(TEST_EVENT_PATTERN), State="ENABLED" + ) + + target_id = f"test-With_valid.Characters-{short_uid()}" + aws_client.events.put_targets( + Rule=rule_one_name, + Targets=[ + {"Id": target_id, "Arn": queue_arn, "InputPath": "$.detail"}, + ], + ) + + aws_client.events.put_targets( + Rule=rule_two_name, + Targets=[ + { + "Id": target_id, + "Arn": queue_arn, + "InputPath": "$.notexisting", + }, + ], + ) + + response = aws_client.events.list_targets_by_rule(Rule=rule_one_name) + snapshot.match("list-targets-rule-one", response) + + response = aws_client.events.list_targets_by_rule(Rule=rule_two_name) + snapshot.match("list-targets-rule-two", response) diff --git a/tests/aws/services/events/test_events.snapshot.json b/tests/aws/services/events/test_events.snapshot.json index 9afd753b70f1d..c026e2fb86240 100644 --- a/tests/aws/services/events/test_events.snapshot.json +++ b/tests/aws/services/events/test_events.snapshot.json @@ -2168,5 +2168,54 @@ } ] } + }, + "tests/aws/services/events/test_events.py::TestEventTarget::test_put_multiple_targets_with_same_id_single_rule": { + "recorded-date": "03-01-2025, 12:04:01", + "recorded-content": { + "list-targets": { + "Targets": [ + { + "Arn": "arn::sqs::111111111111:test-queue-4930c31e", + "Id": "test-With_valid.Characters-316ebb69", + "InputPath": "$.notexisting" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/events/test_events.py::TestEventTarget::test_put_multiple_targets_with_same_id_across_different_rules": { + "recorded-date": "03-01-2025, 12:04:32", + "recorded-content": { + "list-targets-rule-one": { + "Targets": [ + { + "Arn": "arn::sqs::111111111111:test-queue-82897537", + "Id": "test-With_valid.Characters-bc4021a0", + "InputPath": "$.detail" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list-targets-rule-two": { + "Targets": [ + { + "Arn": "arn::sqs::111111111111:test-queue-82897537", + "Id": "test-With_valid.Characters-bc4021a0", + "InputPath": "$.notexisting" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } } } diff --git a/tests/aws/services/events/test_events.validation.json b/tests/aws/services/events/test_events.validation.json index 123162886f6b9..dfcd4fda4c2f5 100644 --- a/tests/aws/services/events/test_events.validation.json +++ b/tests/aws/services/events/test_events.validation.json @@ -1,9 +1,8 @@ { - "tests/aws/services/events/test_events.py::TestEventRule::test_process_pattern_to_single_matching_rules_single_target": { - "last_validated_date": "2025-01-02T12:47:30+00:00" + "tests/aws/services/events/test_events.py::TestEventTarget::test_put_multiple_targets_with_same_id_across_different_rules": { + "last_validated_date": "2025-01-03T12:04:32+00:00" } } - } } " } From a9602875056dd07a5cf9b6ed491dce878e4610c4 Mon Sep 17 00:00:00 2001 From: maxhoheiser Date: Fri, 3 Jan 2025 13:12:00 +0100 Subject: [PATCH 08/12] feat: test targets with same arn --- tests/aws/services/events/test_events.py | 64 +++++++++++++++++++ .../services/events/test_events.snapshot.json | 54 ++++++++++++++++ .../events/test_events.validation.json | 6 +- 3 files changed, 121 insertions(+), 3 deletions(-) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 6d1865c2ea60f..c561d148b7487 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -2047,3 +2047,67 @@ def test_put_multiple_targets_with_same_id_across_different_rules( response = aws_client.events.list_targets_by_rule(Rule=rule_two_name) snapshot.match("list-targets-rule-two", response) + + @markers.aws.validated + def test_put_multiple_targets_with_same_arn_single_rule( + self, sqs_create_queue, sqs_get_queue_arn, events_put_rule, snapshot, aws_client + ): + """Targets attached to a rule can have the same ARN, but different IDs""" + rule_name = f"rule-{short_uid()}" + queue_url = sqs_create_queue() + queue_arn = sqs_get_queue_arn(queue_url) + + events_put_rule( + Name=rule_name, EventPattern=json.dumps(TEST_EVENT_PATTERN), State="ENABLED" + ) + + target_id_one = f"test-With_valid.Characters-{short_uid()}" + target_id_two = f"test-With_valid.Characters-{short_uid()}" + aws_client.events.put_targets( + Rule=rule_name, + Targets=[ + {"Id": target_id_one, "Arn": queue_arn, "InputPath": "$.detail"}, + {"Id": target_id_two, "Arn": queue_arn, "InputPath": "$.doesnotexist"}, + ], + ) + + response = aws_client.events.list_targets_by_rule(Rule=rule_name) + snapshot.match("list-targets", response) + + @markers.aws.validated + def test_put_multiple_targets_with_same_arn_across_different_rules( + self, sqs_create_queue, sqs_get_queue_arn, events_put_rule, snapshot, aws_client + ): + """Targets attached to different rules can have the same ARN""" + rule_one_name = f"test-rule-one-{short_uid()}" + rule_two_name = f"test-rule-two-{short_uid()}" + queue_url = sqs_create_queue() + queue_arn = sqs_get_queue_arn(queue_url) + + events_put_rule( + Name=rule_one_name, EventPattern=json.dumps(TEST_EVENT_PATTERN), State="ENABLED" + ) + events_put_rule( + Name=rule_two_name, EventPattern=json.dumps(TEST_EVENT_PATTERN), State="ENABLED" + ) + + target_id = f"test-With_valid.Characters-{short_uid()}" + aws_client.events.put_targets( + Rule=rule_one_name, + Targets=[ + {"Id": target_id, "Arn": queue_arn, "InputPath": "$.detail"}, + ], + ) + + aws_client.events.put_targets( + Rule=rule_two_name, + Targets=[ + {"Id": target_id, "Arn": queue_arn, "InputPath": "$.doesnotexist"}, + ], + ) + + response = aws_client.events.list_targets_by_rule(Rule=rule_one_name) + snapshot.match("list-targets-rule-one", response) + + response = aws_client.events.list_targets_by_rule(Rule=rule_two_name) + snapshot.match("list-targets-rule-two", response) diff --git a/tests/aws/services/events/test_events.snapshot.json b/tests/aws/services/events/test_events.snapshot.json index c026e2fb86240..e70a595c13e92 100644 --- a/tests/aws/services/events/test_events.snapshot.json +++ b/tests/aws/services/events/test_events.snapshot.json @@ -2217,5 +2217,59 @@ } } } + }, + "tests/aws/services/events/test_events.py::TestEventTarget::test_put_multiple_targets_with_same_arn_single_rule": { + "recorded-date": "03-01-2025, 12:09:50", + "recorded-content": { + "list-targets": { + "Targets": [ + { + "Arn": "arn::sqs::111111111111:test-queue-77ae0162", + "Id": "test-With_valid.Characters-195a2962", + "InputPath": "$.detail" + }, + { + "Arn": "arn::sqs::111111111111:test-queue-77ae0162", + "Id": "test-With_valid.Characters-7e963a3c", + "InputPath": "$.doesnotexist" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } + }, + "tests/aws/services/events/test_events.py::TestEventTarget::test_put_multiple_targets_with_same_arn_across_different_rules": { + "recorded-date": "03-01-2025, 12:10:07", + "recorded-content": { + "list-targets-rule-one": { + "Targets": [ + { + "Arn": "arn::sqs::111111111111:test-queue-7ff20252", + "Id": "test-With_valid.Characters-21b5dd7f", + "InputPath": "$.detail" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + }, + "list-targets-rule-two": { + "Targets": [ + { + "Arn": "arn::sqs::111111111111:test-queue-7ff20252", + "Id": "test-With_valid.Characters-21b5dd7f", + "InputPath": "$.doesnotexist" + } + ], + "ResponseMetadata": { + "HTTPHeaders": {}, + "HTTPStatusCode": 200 + } + } + } } } diff --git a/tests/aws/services/events/test_events.validation.json b/tests/aws/services/events/test_events.validation.json index dfcd4fda4c2f5..c67e4e7de6fca 100644 --- a/tests/aws/services/events/test_events.validation.json +++ b/tests/aws/services/events/test_events.validation.json @@ -1,9 +1,9 @@ { - "tests/aws/services/events/test_events.py::TestEventTarget::test_put_multiple_targets_with_same_id_across_different_rules": { - "last_validated_date": "2025-01-03T12:04:32+00:00" + "tests/aws/services/events/test_events.py::TestEventTarget::test_put_multiple_targets_with_same_arn_across_different_rules": { + "last_validated_date": "2025-01-03T12:10:07+00:00" } } -} + " } } From 3aad0af7cc933998c95d6c62604969f3d63379ed Mon Sep 17 00:00:00 2001 From: maxhoheiser Date: Fri, 3 Jan 2025 13:34:36 +0100 Subject: [PATCH 09/12] fix: add input transformers --- tests/aws/services/events/test_events.py | 28 +++++++++++++++ .../services/events/test_events.snapshot.json | 36 +++++++++---------- .../events/test_events.validation.json | 2 +- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index c561d148b7487..28fbc8b8a36fb 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -2003,6 +2003,12 @@ def test_put_multiple_targets_with_same_id_single_rule( ], ) + snapshot.add_transformers_list( + [ + snapshot.transform.regex(target_id, "target-id"), + snapshot.transform.regex(queue_arn, "target-arn"), + ] + ) response = aws_client.events.list_targets_by_rule(Rule=rule_name) snapshot.match("list-targets", response) @@ -2042,6 +2048,13 @@ def test_put_multiple_targets_with_same_id_across_different_rules( ], ) + snapshot.add_transformers_list( + [ + snapshot.transform.regex(target_id, "target-id"), + snapshot.transform.regex(queue_arn, "target-arn"), + ] + ) + response = aws_client.events.list_targets_by_rule(Rule=rule_one_name) snapshot.match("list-targets-rule-one", response) @@ -2071,6 +2084,14 @@ def test_put_multiple_targets_with_same_arn_single_rule( ], ) + snapshot.add_transformers_list( + [ + snapshot.transform.regex(target_id_one, "target-id-one"), + snapshot.transform.regex(target_id_two, "target-id-two"), + snapshot.transform.regex(queue_arn, "target-arn"), + ] + ) + response = aws_client.events.list_targets_by_rule(Rule=rule_name) snapshot.match("list-targets", response) @@ -2106,6 +2127,13 @@ def test_put_multiple_targets_with_same_arn_across_different_rules( ], ) + snapshot.add_transformers_list( + [ + snapshot.transform.regex(target_id, "target-id"), + snapshot.transform.regex(queue_arn, "target-arn"), + ] + ) + response = aws_client.events.list_targets_by_rule(Rule=rule_one_name) snapshot.match("list-targets-rule-one", response) diff --git a/tests/aws/services/events/test_events.snapshot.json b/tests/aws/services/events/test_events.snapshot.json index e70a595c13e92..8766362af3472 100644 --- a/tests/aws/services/events/test_events.snapshot.json +++ b/tests/aws/services/events/test_events.snapshot.json @@ -2170,13 +2170,13 @@ } }, "tests/aws/services/events/test_events.py::TestEventTarget::test_put_multiple_targets_with_same_id_single_rule": { - "recorded-date": "03-01-2025, 12:04:01", + "recorded-date": "03-01-2025, 12:29:52", "recorded-content": { "list-targets": { "Targets": [ { - "Arn": "arn::sqs::111111111111:test-queue-4930c31e", - "Id": "test-With_valid.Characters-316ebb69", + "Arn": "target-arn", + "Id": "target-id", "InputPath": "$.notexisting" } ], @@ -2188,13 +2188,13 @@ } }, "tests/aws/services/events/test_events.py::TestEventTarget::test_put_multiple_targets_with_same_id_across_different_rules": { - "recorded-date": "03-01-2025, 12:04:32", + "recorded-date": "03-01-2025, 12:30:57", "recorded-content": { "list-targets-rule-one": { "Targets": [ { - "Arn": "arn::sqs::111111111111:test-queue-82897537", - "Id": "test-With_valid.Characters-bc4021a0", + "Arn": "target-arn", + "Id": "target-id", "InputPath": "$.detail" } ], @@ -2206,8 +2206,8 @@ "list-targets-rule-two": { "Targets": [ { - "Arn": "arn::sqs::111111111111:test-queue-82897537", - "Id": "test-With_valid.Characters-bc4021a0", + "Arn": "target-arn", + "Id": "target-id", "InputPath": "$.notexisting" } ], @@ -2219,18 +2219,18 @@ } }, "tests/aws/services/events/test_events.py::TestEventTarget::test_put_multiple_targets_with_same_arn_single_rule": { - "recorded-date": "03-01-2025, 12:09:50", + "recorded-date": "03-01-2025, 12:32:45", "recorded-content": { "list-targets": { "Targets": [ { - "Arn": "arn::sqs::111111111111:test-queue-77ae0162", - "Id": "test-With_valid.Characters-195a2962", + "Arn": "target-arn", + "Id": "target-id-one", "InputPath": "$.detail" }, { - "Arn": "arn::sqs::111111111111:test-queue-77ae0162", - "Id": "test-With_valid.Characters-7e963a3c", + "Arn": "target-arn", + "Id": "target-id-two", "InputPath": "$.doesnotexist" } ], @@ -2242,13 +2242,13 @@ } }, "tests/aws/services/events/test_events.py::TestEventTarget::test_put_multiple_targets_with_same_arn_across_different_rules": { - "recorded-date": "03-01-2025, 12:10:07", + "recorded-date": "03-01-2025, 12:33:23", "recorded-content": { "list-targets-rule-one": { "Targets": [ { - "Arn": "arn::sqs::111111111111:test-queue-7ff20252", - "Id": "test-With_valid.Characters-21b5dd7f", + "Arn": "target-arn", + "Id": "target-id", "InputPath": "$.detail" } ], @@ -2260,8 +2260,8 @@ "list-targets-rule-two": { "Targets": [ { - "Arn": "arn::sqs::111111111111:test-queue-7ff20252", - "Id": "test-With_valid.Characters-21b5dd7f", + "Arn": "target-arn", + "Id": "target-id", "InputPath": "$.doesnotexist" } ], diff --git a/tests/aws/services/events/test_events.validation.json b/tests/aws/services/events/test_events.validation.json index c67e4e7de6fca..335927aba3e7e 100644 --- a/tests/aws/services/events/test_events.validation.json +++ b/tests/aws/services/events/test_events.validation.json @@ -1,6 +1,6 @@ { "tests/aws/services/events/test_events.py::TestEventTarget::test_put_multiple_targets_with_same_arn_across_different_rules": { - "last_validated_date": "2025-01-03T12:10:07+00:00" + "last_validated_date": "2025-01-03T12:33:23+00:00" } } From cddfad5020dbac111ac9bd99b867b13370ad992c Mon Sep 17 00:00:00 2001 From: maxhoheiser Date: Fri, 3 Jan 2025 13:35:43 +0100 Subject: [PATCH 10/12] feat: switch to unique_id for target factory store --- .../localstack/services/events/provider.py | 23 ++++++++++--------- .../localstack/services/events/target.py | 14 ++++++++++- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/localstack-core/localstack/services/events/provider.py b/localstack-core/localstack/services/events/provider.py index c38f3b3eb85f0..1319fe9cb6c09 100644 --- a/localstack-core/localstack/services/events/provider.py +++ b/localstack-core/localstack/services/events/provider.py @@ -1772,7 +1772,7 @@ def create_target_sender( target_sender = TargetSenderFactory( target, rule_arn, rule_name, region, account_id ).get_target_sender() - self._target_sender_store[target_sender.arn] = target_sender + self._target_sender_store[target_sender.unique_id] = target_sender return target_sender def create_archive_service( @@ -1835,11 +1835,11 @@ def _delete_rule_services(self, rules: RuleDict | Rule) -> None: def _delete_target_sender(self, ids: TargetIdList, rule) -> None: for target_id in ids: if target := rule.targets.get(target_id): - target_arn = target["Arn"] + target_unique_id = f"{rule.arn}-{target_id}" try: - del self._target_sender_store[target_arn] + del self._target_sender_store[target_unique_id] except KeyError: - LOG.error("Error deleting target service %s.", target_arn) + LOG.error("Error deleting target service %s.", target["Arn"]) def _get_limited_dict_and_next_token( self, input_dict: dict, next_token: NextToken | None, limit: LimitMax100 | None @@ -1889,8 +1889,8 @@ def func(*args, **kwargs): "resources": [rule.arn], "detail": {}, } - - target_sender = self._target_sender_store[target["Arn"]] + target_unique_id = f"{rule.arn}-{target['Id']}" + target_sender = self._target_sender_store[target_unique_id] try: target_sender.process_event(event.copy()) except Exception as e: @@ -2178,16 +2178,17 @@ def _process_rules( return for target in rule.targets.values(): - target_arn = target["Arn"] - if is_archive_arn(target_arn): + target_id = target["Id"] + if is_archive_arn(target["Arn"]): self._put_to_archive( region, account_id, - archive_target_id=target["Id"], + archive_target_id=target_id, event=event_formatted, ) else: - target_sender = self._target_sender_store[target_arn] + target_unique_id = f"{rule.arn}-{target_id}" + target_sender = self._target_sender_store[target_unique_id] try: target_sender.process_event(event_formatted.copy()) rule_invocation.record(target_sender.service) @@ -2198,7 +2199,7 @@ def _process_rules( json.dumps( { "ErrorCode": "TargetDeliveryFailure", - "ErrorMessage": f"Failed to deliver to target {target['Id']}: {str(error)}", + "ErrorMessage": f"Failed to deliver to target {target_id}: {str(error)}", } ) ) diff --git a/localstack-core/localstack/services/events/target.py b/localstack-core/localstack/services/events/target.py index d82a774be8e37..d92066f586690 100644 --- a/localstack-core/localstack/services/events/target.py +++ b/localstack-core/localstack/services/events/target.py @@ -160,6 +160,18 @@ def __init__( def arn(self): return self.target["Arn"] + @property + def target_id(self): + return self.target["Id"] + + @property + def unique_id(self): + """Necessary to distinguish between targets with the same ARN but for different rules. + The unique_id is a combination of the ARN and the rule ARN. + This is necessary nice input path and input transformer can be different for the same target ARN, + attached to different rules.""" + return f"{self.rule_arn}-{self.target_id}" + @property def client(self): """Lazy initialization of internal botoclient factory.""" @@ -263,7 +275,7 @@ def _get_predefined_template_replacements(self, event: FormattedEvent) -> dict[s return predefined_template_replacements -TargetSenderDict = dict[Arn, TargetSender] +TargetSenderDict = dict[str, TargetSender] # rule_arn-target_id as global unique id # Target Senders are ordered alphabetically by service name From 50980657bfa7a201e1bb73ce5c9644855b26e638 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 7 Jan 2025 11:50:06 +0100 Subject: [PATCH 11/12] Update localstack-core/localstack/services/events/target.py Co-authored-by: Ben Simon Hartung <42031100+bentsku@users.noreply.github.com> --- localstack-core/localstack/services/events/target.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/localstack-core/localstack/services/events/target.py b/localstack-core/localstack/services/events/target.py index d92066f586690..a0a214549553e 100644 --- a/localstack-core/localstack/services/events/target.py +++ b/localstack-core/localstack/services/events/target.py @@ -167,8 +167,8 @@ def target_id(self): @property def unique_id(self): """Necessary to distinguish between targets with the same ARN but for different rules. - The unique_id is a combination of the ARN and the rule ARN. - This is necessary nice input path and input transformer can be different for the same target ARN, + The unique_id is a combination of the rule ARN and the Target Id. + This is necessary since input path and input transformer can be different for the same target ARN, attached to different rules.""" return f"{self.rule_arn}-{self.target_id}" From a91d8fcb17407c282c302b2eb87ee6a6c79407bf Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 7 Jan 2025 11:50:18 +0100 Subject: [PATCH 12/12] Update tests/aws/services/events/test_events.py Co-authored-by: Ben Simon Hartung <42031100+bentsku@users.noreply.github.com> --- tests/aws/services/events/test_events.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/aws/services/events/test_events.py b/tests/aws/services/events/test_events.py index 28fbc8b8a36fb..d63ec6c41d2ea 100644 --- a/tests/aws/services/events/test_events.py +++ b/tests/aws/services/events/test_events.py @@ -1414,7 +1414,7 @@ def test_process_to_multiple_matching_rules_different_targets( events_put_rule, aws_client, ): - """two rules with each two sqs targets, all 4 ques should receive the event""" + """two rules with each two sqs targets, all 4 queues should receive the event""" custom_bus_name = f"test-bus-{short_uid()}" events_create_event_bus(Name=custom_bus_name)