|
46 | 46 | _dsm_set_checkpoint, |
47 | 47 | extract_context_from_kinesis_event, |
48 | 48 | extract_context_from_sqs_or_sns_event_or_context, |
| 49 | + extract_context_from_eventbridge_event, |
49 | 50 | ) |
50 | 51 |
|
51 | 52 | from datadog_lambda.trigger import parse_event_source |
@@ -3646,3 +3647,155 @@ def test_kinesis_data_streams_disabled(self): |
3646 | 3647 | arn = "arn:aws:kinesis:us-east-1:123456789012:stream/test-stream" |
3647 | 3648 |
|
3648 | 3649 | _dsm_set_checkpoint(context_json, event_type, arn) |
| 3650 | + |
| 3651 | + # EVENTBRIDGE -> SQS TESTS |
| 3652 | + |
| 3653 | + @staticmethod |
| 3654 | + def _eventbridge_sqs_record(queue_arn, pathway_ctx): |
| 3655 | + body = { |
| 3656 | + "detail-type": "MyDetailType", |
| 3657 | + "source": "my.event.source", |
| 3658 | + "detail": { |
| 3659 | + "_datadog": { |
| 3660 | + # Complete trace context so the extractor returns early and |
| 3661 | + # does not fall through to the regular SQS path. |
| 3662 | + "x-datadog-trace-id": "12345", |
| 3663 | + "x-datadog-parent-id": "67890", |
| 3664 | + "x-datadog-sampling-priority": "1", |
| 3665 | + "dd-pathway-ctx-base64": pathway_ctx, |
| 3666 | + } |
| 3667 | + }, |
| 3668 | + } |
| 3669 | + return { |
| 3670 | + "eventSourceARN": queue_arn, |
| 3671 | + "eventSource": "aws:sqs", |
| 3672 | + "body": json.dumps(body), |
| 3673 | + } |
| 3674 | + |
| 3675 | + def test_eventbridge_sqs_context_propagated(self): |
| 3676 | + queue_arn = "arn:aws:sqs:us-east-1:123456789012:eb-queue" |
| 3677 | + event = {"Records": [self._eventbridge_sqs_record(queue_arn, "12345")]} |
| 3678 | + |
| 3679 | + extract_context_from_sqs_or_sns_event_or_context( |
| 3680 | + event, self.lambda_context, parse_event_source(event) |
| 3681 | + ) |
| 3682 | + |
| 3683 | + # EventBridge -> SQS is consumed from the queue, so it uses SQS tags. |
| 3684 | + self.assertEqual(self.mock_checkpoint.call_count, 1) |
| 3685 | + args, _ = self.mock_checkpoint.call_args |
| 3686 | + self.assertEqual(args[0], "sqs") |
| 3687 | + self.assertEqual(args[1], queue_arn) |
| 3688 | + carrier_get = args[2] |
| 3689 | + self.assertEqual(carrier_get("dd-pathway-ctx-base64"), "12345") |
| 3690 | + |
| 3691 | + def test_eventbridge_sqs_checkpoints_all_records(self): |
| 3692 | + arn1 = "arn:aws:sqs:us-east-1:123456789012:eb-queue" |
| 3693 | + arn2 = "arn:aws:sqs:us-east-1:123456789012:eb-queue-2" |
| 3694 | + event = { |
| 3695 | + "Records": [ |
| 3696 | + self._eventbridge_sqs_record(arn1, "ctx-1"), |
| 3697 | + self._eventbridge_sqs_record(arn2, "ctx-2"), |
| 3698 | + ] |
| 3699 | + } |
| 3700 | + |
| 3701 | + extract_context_from_sqs_or_sns_event_or_context( |
| 3702 | + event, self.lambda_context, parse_event_source(event) |
| 3703 | + ) |
| 3704 | + |
| 3705 | + self.assertEqual(self.mock_checkpoint.call_count, 2) |
| 3706 | + first_args, _ = self.mock_checkpoint.call_args_list[0] |
| 3707 | + second_args, _ = self.mock_checkpoint.call_args_list[1] |
| 3708 | + self.assertEqual((first_args[0], first_args[1]), ("sqs", arn1)) |
| 3709 | + self.assertEqual(first_args[2]("dd-pathway-ctx-base64"), "ctx-1") |
| 3710 | + self.assertEqual((second_args[0], second_args[1]), ("sqs", arn2)) |
| 3711 | + self.assertEqual(second_args[2]("dd-pathway-ctx-base64"), "ctx-2") |
| 3712 | + |
| 3713 | + @patch("datadog_lambda.config.Config.data_streams_enabled", False) |
| 3714 | + def test_eventbridge_sqs_data_streams_disabled(self): |
| 3715 | + queue_arn = "arn:aws:sqs:us-east-1:123456789012:eb-queue" |
| 3716 | + event = {"Records": [self._eventbridge_sqs_record(queue_arn, "12345")]} |
| 3717 | + |
| 3718 | + extract_context_from_sqs_or_sns_event_or_context( |
| 3719 | + event, self.lambda_context, parse_event_source(event) |
| 3720 | + ) |
| 3721 | + |
| 3722 | + self.mock_checkpoint.assert_not_called() |
| 3723 | + |
| 3724 | + |
| 3725 | +class TestEventBridgeDSMLogic(unittest.TestCase): |
| 3726 | + def setUp(self): |
| 3727 | + self.lambda_context = get_mock_context() |
| 3728 | + self.mock_processor = Mock() |
| 3729 | + processor_patcher = patch( |
| 3730 | + "ddtrace.internal.datastreams.data_streams_processor", |
| 3731 | + return_value=self.mock_processor, |
| 3732 | + ) |
| 3733 | + processor_patcher.start() |
| 3734 | + self.addCleanup(processor_patcher.stop) |
| 3735 | + config_patcher = patch( |
| 3736 | + "datadog_lambda.config.Config.data_streams_enabled", True |
| 3737 | + ) |
| 3738 | + config_patcher.start() |
| 3739 | + self.addCleanup(config_patcher.stop) |
| 3740 | + |
| 3741 | + @staticmethod |
| 3742 | + def _eventbridge_event(detail_type="MyDetailType", pathway_ctx="12345"): |
| 3743 | + return { |
| 3744 | + "detail-type": detail_type, |
| 3745 | + "source": "my.event.source", |
| 3746 | + "detail": {"_datadog": {"dd-pathway-ctx-base64": pathway_ctx}}, |
| 3747 | + } |
| 3748 | + |
| 3749 | + def test_eventbridge_context_propagated(self): |
| 3750 | + event = self._eventbridge_event() |
| 3751 | + |
| 3752 | + extract_context_from_eventbridge_event(event, self.lambda_context) |
| 3753 | + |
| 3754 | + self.mock_processor.decode_pathway_b64.assert_called_once_with("12345") |
| 3755 | + self.mock_processor.set_checkpoint.assert_called_once() |
| 3756 | + (tags,), _ = self.mock_processor.set_checkpoint.call_args |
| 3757 | + self.assertIn("direction:in", tags) |
| 3758 | + self.assertIn("type:eventbridge", tags) |
| 3759 | + self.assertIn("topic:MyDetailType", tags) |
| 3760 | + self.assertFalse(any(t.startswith("exchange:") for t in tags)) |
| 3761 | + |
| 3762 | + @patch("datadog_lambda.config.Config.dsm_exchange_name", "my-event-bus") |
| 3763 | + def test_eventbridge_exchange_tag_from_env(self): |
| 3764 | + event = self._eventbridge_event() |
| 3765 | + |
| 3766 | + extract_context_from_eventbridge_event(event, self.lambda_context) |
| 3767 | + |
| 3768 | + (tags,), _ = self.mock_processor.set_checkpoint.call_args |
| 3769 | + self.assertIn("exchange:my-event-bus", tags) |
| 3770 | + self.assertIn("topic:MyDetailType", tags) |
| 3771 | + self.assertIn("type:eventbridge", tags) |
| 3772 | + |
| 3773 | + def test_eventbridge_no_detail_type_skips_checkpoint(self): |
| 3774 | + event = self._eventbridge_event(detail_type=None) |
| 3775 | + |
| 3776 | + extract_context_from_eventbridge_event(event, self.lambda_context) |
| 3777 | + |
| 3778 | + self.mock_processor.set_checkpoint.assert_not_called() |
| 3779 | + |
| 3780 | + def test_eventbridge_no_dd_context_still_checkpoints(self): |
| 3781 | + event = {"detail-type": "MyDetailType", "detail": {}} |
| 3782 | + |
| 3783 | + extract_context_from_eventbridge_event(event, self.lambda_context) |
| 3784 | + |
| 3785 | + self.mock_processor.decode_pathway_b64.assert_called_once_with(None) |
| 3786 | + self.mock_processor.set_checkpoint.assert_called_once() |
| 3787 | + |
| 3788 | + def test_eventbridge_missing_detail_still_checkpoints(self): |
| 3789 | + event = {"detail-type": "MyDetailType"} |
| 3790 | + |
| 3791 | + extract_context_from_eventbridge_event(event, self.lambda_context) |
| 3792 | + |
| 3793 | + self.mock_processor.set_checkpoint.assert_called_once() |
| 3794 | + |
| 3795 | + @patch("datadog_lambda.config.Config.data_streams_enabled", False) |
| 3796 | + def test_eventbridge_data_streams_disabled(self): |
| 3797 | + event = self._eventbridge_event() |
| 3798 | + |
| 3799 | + extract_context_from_eventbridge_event(event, self.lambda_context) |
| 3800 | + |
| 3801 | + self.mock_processor.set_checkpoint.assert_not_called() |
0 commit comments