From 9322245cf5b7eda8a3c54c61fcdce7deb68c2172 Mon Sep 17 00:00:00 2001 From: Snigdha Sharma Date: Wed, 14 May 2025 11:01:26 -0500 Subject: [PATCH 1/3] Convert slack notification title to rich text --- .../slack/message_builder/base/block.py | 14 +++++++ .../slack/message_builder/issues.py | 18 ++++---- .../slack/message_builder/types.py | 26 ++++++------ .../slack/test_message_builder.py | 42 +++++++++++++------ 4 files changed, 64 insertions(+), 36 deletions(-) diff --git a/src/sentry/integrations/slack/message_builder/base/block.py b/src/sentry/integrations/slack/message_builder/base/block.py index bf23a9ffeeee45..9d7d11b71645d1 100644 --- a/src/sentry/integrations/slack/message_builder/base/block.py +++ b/src/sentry/integrations/slack/message_builder/base/block.py @@ -39,6 +39,20 @@ def get_markdown_block(text: str, emoji: str | None = None) -> SlackBlock: "text": {"type": "mrkdwn", "text": text}, } + @staticmethod + def get_rich_text_link(emojis: list[str], text: str, link: str) -> SlackBlock: + elements = [] + for emoji in emojis: + elements.append({"type": "emoji", "name": emoji}) + elements.append({"type": "text", "text": " "}) + + elements.append({"type": "link", "url": link, "text": text, "style": {"bold": True}}) + + return { + "type": "rich_text", + "elements": [{"type": "rich_text_section", "elements": elements}], + } + @staticmethod def get_markdown_quote_block(text: str, max_block_text_length: int) -> SlackBlock: if len(text) > max_block_text_length: diff --git a/src/sentry/integrations/slack/message_builder/issues.py b/src/sentry/integrations/slack/message_builder/issues.py index 777a225062a06d..106d3aabacbb15 100644 --- a/src/sentry/integrations/slack/message_builder/issues.py +++ b/src/sentry/integrations/slack/message_builder/issues.py @@ -458,30 +458,28 @@ def get_title_block( ) -> SlackBlock: summary_headline = self.get_issue_summary_headline(event_or_group) title = summary_headline or build_attachment_title(event_or_group) - title_emoji = self.get_title_emoji(has_action) + title_emojis = self.get_title_emoji(has_action) - title_text = f"{title_emoji}<{title_link}|*{escape_slack_text(title)}*>" - return self.get_markdown_block(title_text) + return self.get_rich_text_link(title_emojis, title, title_link) - def get_title_emoji(self, has_action: bool) -> str | None: + def get_title_emoji(self, has_action: bool) -> list[str]: is_error_issue = self.group.issue_category == GroupCategory.ERROR - title_emoji = None + title_emojis = [] if has_action: # if issue is resolved, archived, or assigned, replace circle emojis with white circle - title_emoji = ( + title_emojis = ( ACTION_EMOJI if is_error_issue else ACTIONED_CATEGORY_TO_EMOJI.get(self.group.issue_category) ) elif is_error_issue: level_text = LOG_LEVELS[self.group.level] - title_emoji = LEVEL_TO_EMOJI.get(level_text) + title_emojis = LEVEL_TO_EMOJI.get(level_text) else: - title_emoji = CATEGORY_TO_EMOJI.get(self.group.issue_category) + title_emojis = CATEGORY_TO_EMOJI.get(self.group.issue_category) - title_emoji = title_emoji + " " if title_emoji else "" - return title_emoji + return title_emojis def get_issue_summary_headline(self, event_or_group: Event | GroupEvent | Group) -> str | None: if self.issue_summary is None: diff --git a/src/sentry/integrations/slack/message_builder/types.py b/src/sentry/integrations/slack/message_builder/types.py index d243ceeea0d11c..6b76504909fd5b 100644 --- a/src/sentry/integrations/slack/message_builder/types.py +++ b/src/sentry/integrations/slack/message_builder/types.py @@ -16,24 +16,24 @@ SLACK_URL_FORMAT = "<{url}|{text}>" LEVEL_TO_EMOJI = { - "_incident_resolved": ":green_circle:", - "debug": ":bug:", - "error": ":red_circle:", - "fatal": ":red_circle:", - "info": ":large_blue_circle:", - "warning": ":large_yellow_circle:", + "_incident_resolved": ["green_circle"], + "debug": ["bug"], + "error": ["red_circle"], + "fatal": ["red_circle"], + "info": ["large_blue_circle"], + "warning": ["large_yellow_circle"], } -ACTION_EMOJI = ":white_circle:" +ACTION_EMOJI = ["white_circle"] CATEGORY_TO_EMOJI = { - GroupCategory.PERFORMANCE: ":large_blue_circle: :chart_with_upwards_trend:", - GroupCategory.FEEDBACK: ":large_blue_circle: :busts_in_silhouette:", - GroupCategory.CRON: ":large_yellow_circle: :spiral_calendar_pad:", + GroupCategory.PERFORMANCE: ["large_blue_circle", "chart_with_upwards_trend"], + GroupCategory.FEEDBACK: ["large_blue_circle", "busts_in_silhouette"], + GroupCategory.CRON: ["large_yellow_circle", "spiral_calendar_pad"], } ACTIONED_CATEGORY_TO_EMOJI = { - GroupCategory.PERFORMANCE: ACTION_EMOJI + " :chart_with_upwards_trend:", - GroupCategory.FEEDBACK: ACTION_EMOJI + " :busts_in_silhouette:", - GroupCategory.CRON: ACTION_EMOJI + " :spiral_calendar_pad:", + GroupCategory.PERFORMANCE: [ACTION_EMOJI, "chart_with_upwards_trend"], + GroupCategory.FEEDBACK: [ACTION_EMOJI, "busts_in_silhouette"], + GroupCategory.CRON: [ACTION_EMOJI, "spiral_calendar_pad"], } diff --git a/tests/sentry/integrations/slack/test_message_builder.py b/tests/sentry/integrations/slack/test_message_builder.py index 94e609c29de74c..7ed89fcfd7452c 100644 --- a/tests/sentry/integrations/slack/test_message_builder.py +++ b/tests/sentry/integrations/slack/test_message_builder.py @@ -103,8 +103,6 @@ def build_test_message_blocks( else: title_link += f"&alert_rule_id={rule.id}&alert_type=issue" - title_text = f":red_circle: <{title_link}|*{formatted_title}*>" - if rule: if legacy_rule_id: block_id = f'{{"issue":{group.id},"rule":{legacy_rule_id}}}' @@ -115,8 +113,22 @@ def build_test_message_blocks( blocks: list[dict[str, Any]] = [ { - "type": "section", - "text": {"type": "mrkdwn", "text": title_text}, + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + {"type": "emoji", "name": "red_circle"}, + {"type": "text", "text": " "}, + { + "type": "link", + "url": title_link, + "text": formatted_title, + "style": {"bold": True}, + }, + ], + } + ], "block_id": block_id, }, ] @@ -848,7 +860,8 @@ def test_build_performance_issue(self): with self.feature("organizations:performance-issues"): blocks = SlackIssuesMessageBuilder(event.group, event).build() assert isinstance(blocks, dict) - assert "N+1 Query" in blocks["blocks"][0]["text"]["text"] + title_text = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"] + assert "N+1 Query" in title_text assert ( "db - SELECT `books_author`.`id`, `books_author`.`name` FROM `books_author` WHERE `books_author`.`id` = %s LIMIT 21" in blocks["blocks"][2]["text"]["text"] @@ -996,8 +1009,9 @@ def test_build_group_block_with_ai_summary_with_feature_flag( mock_get_summary.assert_called_once_with(group, source="alert") # Verify that the original title is \\ present - assert "IntegrationError" in blocks["blocks"][0]["text"]["text"] - assert "Identity not found" in blocks["blocks"][0]["text"]["text"] + title_text = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"] + assert "IntegrationError" in title_text + assert "Identity not found" in title_text # Verify that the AI content is used in the context block content_block = blocks["blocks"][1]["elements"][0]["text"] @@ -1042,7 +1056,8 @@ def test_build_group_block_with_ai_summary_without_feature_flag( with patch(patch_path) as mock_get_summary: mock_get_summary.assert_not_called() blocks = SlackIssuesMessageBuilder(group).build() - assert "IntegrationError" in blocks["blocks"][0]["text"]["text"] + title_text = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"] + assert "IntegrationError" in title_text @override_options({"alerts.issue_summary_timeout": 5}) @with_feature({"organizations:gen-ai-features", "projects:trigger-issue-summary-on-alerts"}) @@ -1126,7 +1141,7 @@ def test_build_group_block_with_ai_summary_text_truncation( ): mock_get_summary.return_value = (mock_summary, 200) blocks = SlackIssuesMessageBuilder(group1, event1.for_group(group1)).build() - title_text = blocks["blocks"][0]["text"]["text"] + title_text = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"] assert "First line of text..." in title_text assert "Second line" not in title_text @@ -1138,7 +1153,7 @@ def test_build_group_block_with_ai_summary_text_truncation( ): mock_get_summary.return_value = (mock_summary, 200) blocks = SlackIssuesMessageBuilder(group2, event2.for_group(group2)).build() - title_text = blocks["blocks"][0]["text"]["text"] + title_text = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"] expected_truncated = long_text[:MAX_SUMMARY_HEADLINE_LENGTH] + "..." assert expected_truncated in title_text @@ -1181,8 +1196,8 @@ def test_build_group_block_with_ai_summary_text_truncation( ): mock_get_summary.return_value = (mock_summary, 200) blocks = SlackIssuesMessageBuilder(group_lb, event_lb.for_group(group_lb)).build() - title_block = blocks["blocks"][0]["text"]["text"] - assert f": {expected_headline_part}*>" in title_block, f"Failed for {name}" + title_block = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"] + assert f": {expected_headline_part}" in title_block, f"Failed for {name}" @override_options({"alerts.issue_summary_timeout": 5}) @patch( @@ -1214,7 +1229,8 @@ def test_build_group_block_with_ai_summary_without_org_acknowledgement( mock_get_issue_summary.assert_not_called() blocks = SlackIssuesMessageBuilder(group).build() - assert "IntegrationError" in blocks["blocks"][0]["text"]["text"] + title_text = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"] + assert "IntegrationError" in title_text class BuildGroupAttachmentReplaysTest(TestCase): From 4fad183b6c5774e44f4e9bfd70e09324ecd96085 Mon Sep 17 00:00:00 2001 From: Snigdha Sharma Date: Fri, 16 May 2025 14:20:08 -0700 Subject: [PATCH 2/3] Fix tests and types --- .../slack/message_builder/base/block.py | 17 ++- .../slack/message_builder/issues.py | 8 +- .../slack/message_builder/types.py | 2 +- src/sentry/testutils/cases.py | 27 +++-- .../test_slack_notify_service_action.py | 93 +++++++++------ .../slack/notifications/test_assigned.py | 14 ++- .../slack/notifications/test_issue_alert.py | 110 ++++++++++++------ .../slack/notifications/test_regression.py | 25 ++-- .../slack/notifications/test_resolved.py | 14 ++- .../test_resolved_in_pull_request.py | 22 ++-- .../notifications/test_resolved_in_release.py | 17 ++- .../slack/notifications/test_unassigned.py | 11 +- .../integrations/slack/test_notify_action.py | 4 +- .../slack/webhooks/actions/test_status.py | 13 ++- .../notifications/test_assigned.py | 4 +- .../notifications/test_digests.py | 27 ++++- .../notifications/test_notifications.py | 23 ++-- .../sentry/rules/processing/test_processor.py | 4 +- 18 files changed, 285 insertions(+), 150 deletions(-) diff --git a/src/sentry/integrations/slack/message_builder/base/block.py b/src/sentry/integrations/slack/message_builder/base/block.py index 9d7d11b71645d1..36bae2a149c4fb 100644 --- a/src/sentry/integrations/slack/message_builder/base/block.py +++ b/src/sentry/integrations/slack/message_builder/base/block.py @@ -40,13 +40,24 @@ def get_markdown_block(text: str, emoji: str | None = None) -> SlackBlock: } @staticmethod - def get_rich_text_link(emojis: list[str], text: str, link: str) -> SlackBlock: - elements = [] + def get_rich_text_link(emojis: list[str], text: str, link: str | None = None) -> SlackBlock: + elements: list[dict[str, Any]] = [] for emoji in emojis: elements.append({"type": "emoji", "name": emoji}) elements.append({"type": "text", "text": " "}) - elements.append({"type": "link", "url": link, "text": text, "style": {"bold": True}}) + url: dict[str, Any] = {} + if link: + url = { + "type": "link", + "url": link, + "text": text, + "style": {"bold": True}, + } + else: + url = {"type": "text", "text": text} + + elements.append(url) return { "type": "rich_text", diff --git a/src/sentry/integrations/slack/message_builder/issues.py b/src/sentry/integrations/slack/message_builder/issues.py index 106d3aabacbb15..d6712a12462aaa 100644 --- a/src/sentry/integrations/slack/message_builder/issues.py +++ b/src/sentry/integrations/slack/message_builder/issues.py @@ -465,19 +465,19 @@ def get_title_block( def get_title_emoji(self, has_action: bool) -> list[str]: is_error_issue = self.group.issue_category == GroupCategory.ERROR - title_emojis = [] + title_emojis: list[str] = [] if has_action: # if issue is resolved, archived, or assigned, replace circle emojis with white circle title_emojis = ( ACTION_EMOJI if is_error_issue - else ACTIONED_CATEGORY_TO_EMOJI.get(self.group.issue_category) + else ACTIONED_CATEGORY_TO_EMOJI.get(self.group.issue_category, []) ) elif is_error_issue: level_text = LOG_LEVELS[self.group.level] - title_emojis = LEVEL_TO_EMOJI.get(level_text) + title_emojis = LEVEL_TO_EMOJI.get(level_text, []) else: - title_emojis = CATEGORY_TO_EMOJI.get(self.group.issue_category) + title_emojis = CATEGORY_TO_EMOJI.get(self.group.issue_category, []) return title_emojis diff --git a/src/sentry/integrations/slack/message_builder/types.py b/src/sentry/integrations/slack/message_builder/types.py index 6b76504909fd5b..b759258241f7a1 100644 --- a/src/sentry/integrations/slack/message_builder/types.py +++ b/src/sentry/integrations/slack/message_builder/types.py @@ -32,7 +32,7 @@ GroupCategory.CRON: ["large_yellow_circle", "spiral_calendar_pad"], } -ACTIONED_CATEGORY_TO_EMOJI = { +ACTIONED_CATEGORY_TO_EMOJI: dict[GroupCategory, list[str]] = { GroupCategory.PERFORMANCE: [ACTION_EMOJI, "chart_with_upwards_trend"], GroupCategory.FEEDBACK: [ACTION_EMOJI, "busts_in_silhouette"], GroupCategory.CRON: [ACTION_EMOJI, "spiral_calendar_pad"], diff --git a/src/sentry/testutils/cases.py b/src/sentry/testutils/cases.py index 87564050b10f86..709d6e5804f9a9 100644 --- a/src/sentry/testutils/cases.py +++ b/src/sentry/testutils/cases.py @@ -2835,14 +2835,17 @@ def assert_performance_issue_blocks_with_culprit_blocks( alert_type: FineTuningAPIKey = FineTuningAPIKey.WORKFLOW, issue_link_extra_params=None, ): - notification_uuid = self.get_notification_uuid(blocks[1]["text"]["text"]) + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) issue_link = f"http://testserver/organizations/{org.slug}/issues/{group.id}/?referrer={referrer}¬ification_uuid={notification_uuid}" if issue_link_extra_params is not None: issue_link += issue_link_extra_params - assert ( - blocks[1]["text"]["text"] - == f":large_blue_circle: :chart_with_upwards_trend: <{issue_link}|*N+1 Query*>" - ) + emoji = "large_blue_circle" + text = "N+1 Query" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == issue_link + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert blocks[2]["elements"][0]["text"] == "/books/" assert ( blocks[3]["text"]["text"] @@ -2866,14 +2869,18 @@ def assert_generic_issue_blocks( issue_link_extra_params=None, with_culprit=False, ): - notification_uuid = self.get_notification_uuid(blocks[1]["text"]["text"]) + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) issue_link = f"http://testserver/organizations/{org.slug}/issues/{group.id}/?referrer={referrer}¬ification_uuid={notification_uuid}" if issue_link_extra_params is not None: issue_link += issue_link_extra_params - assert ( - blocks[1]["text"]["text"] - == f":red_circle: <{issue_link}|*{TEST_ISSUE_OCCURRENCE.issue_title}*>" - ) + emoji = "red_circle" + text = f"{TEST_ISSUE_OCCURRENCE.issue_title}" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == issue_link + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text + if with_culprit: assert blocks[2]["elements"][0]["text"] == "raven.tasks.run_a_test" evidence_index = 3 diff --git a/tests/sentry/integrations/slack/actions/notification/test_slack_notify_service_action.py b/tests/sentry/integrations/slack/actions/notification/test_slack_notify_service_action.py index c656336ed0c1e8..d5a23b459ed3e9 100644 --- a/tests/sentry/integrations/slack/actions/notification/test_slack_notify_service_action.py +++ b/tests/sentry/integrations/slack/actions/notification/test_slack_notify_service_action.py @@ -99,10 +99,12 @@ def test_after(self, mock_api_call, mock_post, mock_record): blocks = mock_post.call_args.kwargs["blocks"] blocks = orjson.loads(blocks) - assert ( - blocks[0]["text"]["text"] - == f":large_yellow_circle: " - ) + emoji = "large_yellow_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.event.group.id}/?referrer=slack" + text = "Hello world" + assert blocks[0]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[0]["elements"][0]["elements"][-1]["url"] == url + assert blocks[0]["elements"][0]["elements"][-1]["text"] == text assert NotificationMessage.objects.all().count() == 0 @@ -139,10 +141,12 @@ def test_after_slo_halt(self, mock_post, mock_record): blocks = mock_post.call_args.kwargs["blocks"] blocks = orjson.loads(blocks) - assert ( - blocks[0]["text"]["text"] - == f":large_yellow_circle: " - ) + emoji = "large_yellow_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.event.group.id}/?referrer=slack" + text = "Hello world" + assert blocks[0]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[0]["elements"][0]["elements"][-1]["url"] == url + assert blocks[0]["elements"][0]["elements"][-1]["text"] == text assert NotificationMessage.objects.all().count() == 0 @@ -200,10 +204,12 @@ def test_after_with_threads(self, mock_api_call, mock_post, mock_record): blocks = mock_post.call_args.kwargs["blocks"] blocks = orjson.loads(blocks) - assert ( - blocks[0]["text"]["text"] - == f":large_yellow_circle: " - ) + emoji = "large_yellow_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.event.group.id}/?referrer=slack&alert_rule_id={self.rule.id}&alert_type=issue" + text = "Hello world" + assert blocks[0]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[0]["elements"][0]["elements"][-1]["url"] == url + assert blocks[0]["elements"][0]["elements"][-1]["text"] == text assert NotificationMessage.objects.all().count() == 1 @@ -257,10 +263,12 @@ def test_after_reply_in_thread(self, mock_api_call, mock_post, mock_record): blocks = mock_post.call_args.kwargs["blocks"] blocks = orjson.loads(blocks) - assert ( - blocks[0]["text"]["text"] - == f":large_yellow_circle: " - ) + emoji = "large_yellow_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.event.group.id}/?referrer=slack&alert_rule_id={self.rule.id}&alert_type=issue" + text = "Hello world" + assert blocks[0]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[0]["elements"][0]["elements"][-1]["url"] == url + assert blocks[0]["elements"][0]["elements"][-1]["text"] == text assert NotificationMessage.objects.all().count() == 2 assert ( @@ -302,10 +310,13 @@ def test_after_noa(self, mock_api_call, mock_post, mock_record): blocks = mock_post.call_args.kwargs["blocks"] blocks = orjson.loads(blocks) - assert ( - blocks[0]["text"]["text"] - == f":large_yellow_circle: " - ) + emoji = "large_yellow_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.event.group.id}/?referrer=slack&alert_rule_id={action_data['legacy_rule_id']}&alert_type=issue" + text = "Hello world" + + assert blocks[0]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[0]["elements"][0]["elements"][-1]["url"] == url + assert blocks[0]["elements"][0]["elements"][-1]["text"] == text assert NotificationMessage.objects.all().count() == 1 @@ -345,10 +356,13 @@ def test_after_noa_test_action(self, mock_api_call, mock_post, mock_record): blocks = mock_post.call_args.kwargs["blocks"] blocks = orjson.loads(blocks) - assert ( - blocks[0]["text"]["text"] - == f":large_yellow_circle: " - ) + emoji = "large_yellow_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.event.group.id}/?referrer=slack&alert_rule_id={action_data['legacy_rule_id']}&alert_type=issue" + text = "Hello world" + + assert blocks[0]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[0]["elements"][0]["elements"][-1]["url"] == url + assert blocks[0]["elements"][0]["elements"][-1]["text"] == text # Test action should not create a notification message assert NotificationMessage.objects.all().count() == 0 @@ -386,10 +400,13 @@ def test_after_noa_new_ui(self, mock_api_call, mock_post, mock_record): blocks = mock_post.call_args.kwargs["blocks"] blocks = orjson.loads(blocks) - assert ( - blocks[0]["text"]["text"] - == f":large_yellow_circle: " - ) + emoji = "large_yellow_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.event.group.id}/?referrer=slack&workflow_id={action_data['workflow_id']}&alert_type=issue" + text = "Hello world" + + assert blocks[0]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[0]["elements"][0]["elements"][-1]["url"] == url + assert blocks[0]["elements"][0]["elements"][-1]["text"] == text assert NotificationMessage.objects.all().count() == 1 @@ -429,10 +446,12 @@ def test_after_with_threads_noa(self, mock_api_call, mock_post, mock_record): blocks = mock_post.call_args.kwargs["blocks"] blocks = orjson.loads(blocks) - assert ( - blocks[0]["text"]["text"] - == f":large_yellow_circle: " - ) + emoji = "large_yellow_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.event.group.id}/?referrer=slack&alert_rule_id={action_data['legacy_rule_id']}&alert_type=issue" + text = "Hello world" + assert blocks[0]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[0]["elements"][0]["elements"][-1]["url"] == url + assert blocks[0]["elements"][0]["elements"][-1]["text"] == text assert NotificationMessage.objects.all().count() == 1 @@ -488,10 +507,12 @@ def test_after_reply_in_thread_noa(self, mock_api_call, mock_post, mock_record): blocks = mock_post.call_args.kwargs["blocks"] blocks = orjson.loads(blocks) - assert ( - blocks[0]["text"]["text"] - == f":large_yellow_circle: " - ) + emoji = "large_yellow_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.event.group.id}/?referrer=slack&alert_rule_id={action_data['legacy_rule_id']}&alert_type=issue" + text = "Hello world" + assert blocks[0]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[0]["elements"][0]["elements"][-1]["url"] == url + assert blocks[0]["elements"][0]["elements"][-1]["text"] == text assert NotificationMessage.objects.all().count() == 2 assert ( diff --git a/tests/sentry/integrations/slack/notifications/test_assigned.py b/tests/sentry/integrations/slack/notifications/test_assigned.py index 7cda79de1b2d52..fd8bc251a31856 100644 --- a/tests/sentry/integrations/slack/notifications/test_assigned.py +++ b/tests/sentry/integrations/slack/notifications/test_assigned.py @@ -98,11 +98,17 @@ def test_assignment_block(self): fallback_text = self.mock_post.call_args.kwargs["text"] assert fallback_text == f"Issue assigned to {self.name} by themselves" assert blocks[0]["text"]["text"] == fallback_text - notification_uuid = self.get_notification_uuid(blocks[1]["text"]["text"]) - assert ( - blocks[1]["text"]["text"] - == f":red_circle: " + + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] ) + emoji = "red_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.group.id}/?referrer=assigned_activity-slack¬ification_uuid={notification_uuid}" + text = f"{self.group.title}" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text + assert ( blocks[3]["elements"][0]["text"] == f"{self.project.slug} | " diff --git a/tests/sentry/integrations/slack/notifications/test_issue_alert.py b/tests/sentry/integrations/slack/notifications/test_issue_alert.py index 316cafb059f146..8722d8bccd3de3 100644 --- a/tests/sentry/integrations/slack/notifications/test_issue_alert.py +++ b/tests/sentry/integrations/slack/notifications/test_issue_alert.py @@ -89,10 +89,12 @@ def test_issue_alert_user_block(self): ) assert blocks[0]["text"]["text"] == fallback_text assert event.group - assert ( - blocks[1]["text"]["text"] - == f":red_circle: " - ) + emoji = "red_circle" + url = f"http://testserver/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&alert_rule_id={self.rule.id}&alert_type=issue" + text = "Hello world" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert ( blocks[4]["elements"][0]["text"] == f"{event.project.slug} | " @@ -374,10 +376,12 @@ def test_issue_alert_issue_owners_block(self): ) assert blocks[0]["text"]["text"] == fallback_text assert event.group - assert ( - blocks[1]["text"]["text"] - == f":red_circle: " - ) + emoji = "red_circle" + url = f"http://testserver/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue" + text = "Hello world" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert ( blocks[4]["elements"][0]["text"] == f"{event.project.slug} | " @@ -434,10 +438,12 @@ def test_issue_alert_issue_owners_environment_block(self): ) assert blocks[0]["text"]["text"] == fallback_text assert event.group - assert ( - blocks[1]["text"]["text"] - == f":red_circle: " - ) + emoji = "red_circle" + url = f"http://testserver/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&environment={environment.name}&alert_rule_id={rule.id}&alert_type=issue" + text = "Hello world" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert ( blocks[4]["elements"][0]["text"] == f"{event.project.slug} | {environment.name} | " @@ -533,10 +539,12 @@ def test_issue_alert_team_issue_owners_block(self): ) assert blocks[0]["text"]["text"] == fallback_text assert event.group - assert ( - blocks[1]["text"]["text"] - == f":red_circle: " - ) + emoji = "red_circle" + url = f"http://testserver/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue" + text = "Hello world" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert blocks[6]["elements"][0]["text"] == f"Suggested Assignees: #{self.team.slug}" assert ( blocks[7]["elements"][0]["text"] @@ -689,9 +697,15 @@ def test_issue_alert_team_issue_owners_user_settings_off_digests(self, digests): assert self.mock_post.call_args.kwargs["channel"] == "CXXXXXXX2" blocks = orjson.loads(self.mock_post.call_args.kwargs["blocks"]) - assert "Hello world" in blocks[1]["text"]["text"] - title_link = blocks[1]["text"]["text"][13:][1:-1] - notification_uuid = self.get_notification_uuid(title_link) + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) + emoji = "red_circle" + url = f"http://testserver/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue" + text = "Hello world" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert ( blocks[-2]["elements"][0]["text"] == f"{self.project.slug} | " @@ -768,7 +782,9 @@ def test_issue_alert_team_block(self): blocks = orjson.loads(self.mock_post.call_args.kwargs["blocks"]) fallback_text = self.mock_post.call_args.kwargs["text"] - notification_uuid = self.get_notification_uuid(blocks[1]["text"]["text"]) + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) assert ( fallback_text @@ -776,10 +792,12 @@ def test_issue_alert_team_block(self): ) assert blocks[0]["text"]["text"] == fallback_text assert event.group - assert ( - blocks[1]["text"]["text"] - == f":red_circle: " - ) + emoji = "red_circle" + url = f"http://example.com/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue" + text = "Hello world" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert ( blocks[5]["elements"][0]["text"] == f"{event.project.slug} | " @@ -853,9 +871,15 @@ def test_issue_alert_team_new_project(self): assert self.mock_post.call_args.kwargs["channel"] == "CXXXXXXX2" blocks = orjson.loads(self.mock_post.call_args.kwargs["blocks"]) - assert "Hello world" in blocks[1]["text"]["text"] - title_link = blocks[1]["text"]["text"][13:][1:-1] - notification_uuid = self.get_notification_uuid(title_link) + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) + emoji = "red_circle" + url = f"http://example.com/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue" + text = "Hello world" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert ( blocks[-2]["elements"][0]["text"] == f"{project2.slug} | " @@ -955,9 +979,15 @@ def test_issue_alert_team_fallback(self): assert data["channel"] == "UXXXXXXX1" blocks = orjson.loads(data["blocks"]) - assert "Hello world" in blocks[1]["text"]["text"] - title_link = blocks[1]["text"]["text"] - notification_uuid = self.get_notification_uuid(title_link) + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) + emoji = "red_circle" + url = f"http://example.com/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue" + text = "Hello world" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert ( blocks[-2]["elements"][0]["text"] == f"{self.project.slug} | " @@ -967,7 +997,15 @@ def test_issue_alert_team_fallback(self): assert data2["channel"] == "UXXXXXXX2" assert "blocks" in data2 blocks = orjson.loads(data2["blocks"]) - assert "Hello world" in blocks[1]["text"]["text"] + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) + emoji = "red_circle" + url = f"http://example.com/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue" + text = "Hello world" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert ( blocks[-2]["elements"][0]["text"] == f"{self.project.slug} | " @@ -999,17 +1037,21 @@ def test_digest_enabled_block(self, digests): blocks = orjson.loads(self.mock_post.call_args.kwargs["blocks"]) fallback_text = self.mock_post.call_args.kwargs["text"] - notification_uuid = self.get_notification_uuid(blocks[1]["text"]["text"]) + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) assert ( fallback_text == f"Alert triggered " ) assert blocks[0]["text"]["text"] == fallback_text assert event.group + assert blocks[1]["elements"][0]["elements"][-1]["text"] == "Hello world" assert ( - blocks[1]["text"]["text"] - == f":red_circle: " + blocks[1]["elements"][0]["elements"][-1]["url"] + == f"http://testserver/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue" ) + assert blocks[1]["elements"][0]["elements"][0]["name"] == "red_circle" assert ( blocks[4]["elements"][0]["text"] == f"{event.project.slug} | " diff --git a/tests/sentry/integrations/slack/notifications/test_regression.py b/tests/sentry/integrations/slack/notifications/test_regression.py index bc9d740aeb3f9f..4430c638141efc 100644 --- a/tests/sentry/integrations/slack/notifications/test_regression.py +++ b/tests/sentry/integrations/slack/notifications/test_regression.py @@ -36,10 +36,16 @@ def test_regression_block(self): fallback_text = self.mock_post.call_args.kwargs["text"] assert fallback_text == "Issue marked as regression" assert blocks[0]["text"]["text"] == fallback_text - notification_uuid = self.get_notification_uuid(blocks[1]["text"]["text"]) - assert blocks[1]["text"]["text"] == ( - f":red_circle: " + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] ) + emoji = "red_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.group.id}/?referrer=regression_activity-slack¬ification_uuid={notification_uuid}" + text = f"{self.group.title}" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text + assert blocks[3]["elements"][0]["text"] == ( f"{self.project.slug} | " ) @@ -54,15 +60,20 @@ def test_regression_with_release_block(self): blocks = orjson.loads(self.mock_post.call_args.kwargs["blocks"]) fallback_text = self.mock_post.call_args.kwargs["text"] - notification_uuid = self.get_notification_uuid(blocks[1]["text"]["text"]) + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) assert ( fallback_text == f"Issue marked as regression in release " ) assert blocks[0]["text"]["text"] == fallback_text - assert blocks[1]["text"]["text"] == ( - f":red_circle: " - ) + emoji = "red_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.group.id}/?referrer=regression_activity-slack¬ification_uuid={notification_uuid}" + text = f"{self.group.title}" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert blocks[3]["elements"][0]["text"] == ( f"{self.project.slug} | " ) diff --git a/tests/sentry/integrations/slack/notifications/test_resolved.py b/tests/sentry/integrations/slack/notifications/test_resolved.py index 22563b4bf14547..d9d12b91d18817 100644 --- a/tests/sentry/integrations/slack/notifications/test_resolved.py +++ b/tests/sentry/integrations/slack/notifications/test_resolved.py @@ -34,7 +34,9 @@ def test_resolved_block(self): blocks = orjson.loads(self.mock_post.call_args.kwargs["blocks"]) fallback_text = self.mock_post.call_args.kwargs["text"] - notification_uuid = self.get_notification_uuid(fallback_text) + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) issue_link = ( f"http://testserver/organizations/{self.organization.slug}/issues/{self.group.id}" ) @@ -43,10 +45,12 @@ def test_resolved_block(self): == f"{self.name} marked <{issue_link}/?referrer=activity_notification¬ification_uuid={notification_uuid}|{self.short_id}> as resolved" ) assert blocks[0]["text"]["text"] == fallback_text - assert ( - blocks[1]["text"]["text"] - == f":red_circle: <{issue_link}/?referrer=resolved_activity-slack¬ification_uuid={notification_uuid}|*{self.group.title}*>" - ) + emoji = "red_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.group.id}/?referrer=resolved_activity-slack¬ification_uuid={notification_uuid}" + text = f"{self.group.title}" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert ( blocks[3]["elements"][0]["text"] == f"{self.project.slug} | " diff --git a/tests/sentry/integrations/slack/notifications/test_resolved_in_pull_request.py b/tests/sentry/integrations/slack/notifications/test_resolved_in_pull_request.py index 07fffdb8c23424..88b57d6c1470af 100644 --- a/tests/sentry/integrations/slack/notifications/test_resolved_in_pull_request.py +++ b/tests/sentry/integrations/slack/notifications/test_resolved_in_pull_request.py @@ -39,17 +39,21 @@ def test_resolved_in_pull_request_block(self): blocks = orjson.loads(self.mock_post.call_args.kwargs["blocks"]) fallback_text = self.mock_post.call_args.kwargs["text"] - notification_uuid = self.get_notification_uuid(blocks[1]["text"]["text"]) + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) assert ( fallback_text == f"{self.name} made a <{self.pull_request_url}| pull request> that will resolve " ) assert blocks[0]["text"]["text"] == fallback_text - assert ( - blocks[1]["text"]["text"] - == f":red_circle: " - ) + emoji = "red_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.group.id}/?referrer=resolved_in_pull_request_activity-slack¬ification_uuid={notification_uuid}" + text = f"{self.group.title}" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert ( blocks[3]["elements"][0]["text"] == f"{self.project.slug} | " @@ -72,7 +76,9 @@ def test_resolved_in_pull_request_performance_issue_block_with_culprit_blocks(se blocks = orjson.loads(self.mock_post.call_args.kwargs["blocks"]) fallback_text = self.mock_post.call_args.kwargs["text"] - notification_uuid = self.get_notification_uuid(blocks[1]["text"]["text"]) + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) assert ( fallback_text @@ -107,7 +113,9 @@ def test_resolved_in_pull_request_generic_issue_block(self, occurrence): blocks = orjson.loads(self.mock_post.call_args.kwargs["blocks"]) fallback_text = self.mock_post.call_args.kwargs["text"] - notification_uuid = self.get_notification_uuid(blocks[1]["text"]["text"]) + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) assert ( fallback_text diff --git a/tests/sentry/integrations/slack/notifications/test_resolved_in_release.py b/tests/sentry/integrations/slack/notifications/test_resolved_in_release.py index a2f305d02a6107..e65475d7b16d45 100644 --- a/tests/sentry/integrations/slack/notifications/test_resolved_in_release.py +++ b/tests/sentry/integrations/slack/notifications/test_resolved_in_release.py @@ -39,11 +39,16 @@ def test_resolved_in_release_block(self): release_name = notification.activity.data["version"] assert fallback_text == f"Issue marked as resolved in {release_name} by {self.name}" assert blocks[0]["text"]["text"] == fallback_text - notification_uuid = self.get_notification_uuid(blocks[1]["text"]["text"]) - assert ( - blocks[1]["text"]["text"] - == f":red_circle: " + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] ) + emoji = "red_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.group.id}/?referrer=resolved_in_release_activity-slack¬ification_uuid={notification_uuid}" + text = f"{self.group.title}" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text + assert ( blocks[3]["elements"][0]["text"] == f"{self.project.slug} | " @@ -123,7 +128,9 @@ def test_resolved_in_release_parsed_version_block(self): assert fallback_text == f"Issue marked as resolved in 1.0.0 by {self.name}" assert blocks[0]["text"]["text"] == fallback_text - notification_uuid = self.get_notification_uuid(blocks[1]["text"]["text"]) + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] + ) assert ( blocks[3]["elements"][0]["text"] == f"{self.project.slug} | " diff --git a/tests/sentry/integrations/slack/notifications/test_unassigned.py b/tests/sentry/integrations/slack/notifications/test_unassigned.py index 3b2702fee11bde..7f9f9c086abd38 100644 --- a/tests/sentry/integrations/slack/notifications/test_unassigned.py +++ b/tests/sentry/integrations/slack/notifications/test_unassigned.py @@ -38,10 +38,15 @@ def test_unassignment_block(self): assert fallback_text == f"Issue unassigned by {self.name}" assert blocks[0]["text"]["text"] == fallback_text - notification_uuid = self.get_notification_uuid(blocks[1]["text"]["text"]) - assert blocks[1]["text"]["text"] == ( - f":red_circle: " + notification_uuid = self.get_notification_uuid( + blocks[1]["elements"][0]["elements"][-1]["url"] ) + emoji = "red_circle" + url = f"http://testserver/organizations/{self.organization.slug}/issues/{self.group.id}/?referrer=unassigned_activity-slack¬ification_uuid={notification_uuid}" + text = f"{self.group.title}" + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[1]["elements"][0]["elements"][-1]["url"] == url + assert blocks[1]["elements"][0]["elements"][-1]["text"] == text assert ( blocks[3]["elements"][0]["text"] == f"{self.project.slug} | " diff --git a/tests/sentry/integrations/slack/test_notify_action.py b/tests/sentry/integrations/slack/test_notify_action.py index 96d235cf32642d..21201e628f70cd 100644 --- a/tests/sentry/integrations/slack/test_notify_action.py +++ b/tests/sentry/integrations/slack/test_notify_action.py @@ -93,7 +93,7 @@ def test_no_upgrade_notice_bot_app(self, mock_api_call, mock_post): blocks = mock_post.call_args.kwargs["blocks"] blocks = orjson.loads(blocks) - assert event.title in blocks[0]["text"]["text"] + assert event.title in blocks[0]["elements"][0]["elements"][-1]["text"] def test_render_label_with_notes(self): rule = self.get_rule( @@ -364,7 +364,7 @@ def test_additional_attachment(self, mock_api_call, mock_post, mock_record): blocks = mock_post.call_args.kwargs["blocks"] blocks = orjson.loads(blocks) - assert event.title in blocks[0]["text"]["text"] + assert event.title in blocks[0]["elements"][0]["elements"][-1]["text"] assert blocks[5]["text"]["text"] == self.organization.slug assert blocks[6]["text"]["text"] == self.integration.id mock_record.assert_called_with( diff --git a/tests/sentry/integrations/slack/webhooks/actions/test_status.py b/tests/sentry/integrations/slack/webhooks/actions/test_status.py index 8af706fe631147..9d92fa6b253184 100644 --- a/tests/sentry/integrations/slack/webhooks/actions/test_status.py +++ b/tests/sentry/integrations/slack/webhooks/actions/test_status.py @@ -262,7 +262,7 @@ def test_archive_issue_until_escalating(self, mock_tags, mock_record): assert self.notification_text in blocks[1]["text"]["text"] assert blocks[2]["text"]["text"].endswith(expect_status) assert "via" not in blocks[4]["elements"][0]["text"] - assert ":white_circle:" in blocks[0]["text"]["text"] + assert "white_circle" in blocks[0]["elements"][0]["elements"][0]["name"] assert len(mock_record.mock_calls) == 4 start_1, success_1, start_2, success_2 = mock_record.mock_calls @@ -487,7 +487,7 @@ def test_assign_issue(self, mock_tags): expect_status = f"*Issue assigned to {user2.get_display_name()} by <@{self.external_id}>*" assert self.notification_text in blocks[1]["text"]["text"] assert blocks[2]["text"]["text"].endswith(expect_status), text - assert ":white_circle:" in blocks[0]["text"]["text"] + assert "white_circle" in blocks[0]["elements"][0]["elements"][0]["name"] # Assign to team self.assign_issue(original_message, self.team) @@ -499,7 +499,7 @@ def test_assign_issue(self, mock_tags): expect_status = f"*Issue assigned to #{self.team.slug} by <@{self.external_id}>*" assert self.notification_text in blocks[1]["text"]["text"] assert blocks[2]["text"]["text"].endswith(expect_status), text - assert ":white_circle:" in blocks[0]["text"]["text"] + assert "white_circle" in blocks[0]["elements"][0]["elements"][0]["name"] # Assert group assignment activity recorded group_activity = list(Activity.objects.filter(group=self.group)) @@ -542,7 +542,7 @@ def test_assign_issue_error(self, mock_logger): expect_status = f"*Issue assigned to {user2.get_display_name()} by <@{self.external_id}>*" assert self.notification_text in resp.data["blocks"][1]["text"]["text"] assert resp.data["blocks"][2]["text"]["text"].endswith(expect_status), resp.data["text"] - assert ":white_circle:" in resp.data["blocks"][0]["text"]["text"] + assert "white_circle" in resp.data["blocks"][0]["elements"][0]["elements"][0]["name"] # Assert group assignment activity recorded group_activity = list(Activity.objects.filter(group=self.group)) @@ -738,7 +738,7 @@ def test_resolve_issue(self, mock_tags, mock_record): expect_status = f"*Issue resolved by <@{self.external_id}>*" assert self.notification_text in blocks[1]["text"]["text"] assert blocks[2]["text"]["text"] == expect_status - assert ":white_circle:" in blocks[0]["text"]["text"] + assert "white_circle" in blocks[0]["elements"][0]["elements"][0]["name"] @patch("sentry.integrations.utils.metrics.EventLifecycle.record_event") @patch("sentry.integrations.slack.message_builder.issues.get_tags", return_value=[]) @@ -772,7 +772,8 @@ def test_resolve_perf_issue(self, mock_tags, mock_record): in blocks[2]["text"]["text"] ) assert blocks[3]["text"]["text"] == expect_status - assert ":white_circle: :chart_with_upwards_trend:" in blocks[0]["text"]["text"] + assert "white_circle" in blocks[0]["elements"][0]["elements"][0]["name"] + assert "chart_with_upwards_trend" in blocks[0]["elements"][0]["elements"][2]["name"] @patch("sentry.integrations.slack.message_builder.issues.get_tags", return_value=[]) def test_resolve_issue_through_unfurl(self, mock_tags): diff --git a/tests/sentry/notifications/notifications/test_assigned.py b/tests/sentry/notifications/notifications/test_assigned.py index 820f604d193926..5635d0adb47678 100644 --- a/tests/sentry/notifications/notifications/test_assigned.py +++ b/tests/sentry/notifications/notifications/test_assigned.py @@ -41,7 +41,7 @@ def validate_slack_message(self, msg, group, project, user_id, mock_post, index= fallback_text = mock_post.call_args_list[index].kwargs["text"] assert fallback_text == msg - assert group.title in blocks[1]["text"]["text"] + assert blocks[1]["elements"][0]["elements"][-1]["text"] == group.title assert project.slug in blocks[-2]["elements"][0]["text"] channel = mock_post.call_args_list[index].kwargs["channel"] assert channel == str(user_id) @@ -99,7 +99,7 @@ def test_sends_assignment_notification(self, mock_post): fallback_text = mock_post.call_args.kwargs["text"] assert fallback_text == f"Issue assigned to {user.get_display_name()} by themselves" - assert self.group.title in blocks[1]["text"]["text"] + assert self.group.title in blocks[1]["elements"][0]["elements"][-1]["text"] assert self.project.slug in blocks[-2]["elements"][0]["text"] def test_sends_reassignment_notification_user(self, mock_post): diff --git a/tests/sentry/notifications/notifications/test_digests.py b/tests/sentry/notifications/notifications/test_digests.py index 5052cc1fee88c8..ccc64b9f4c45d6 100644 --- a/tests/sentry/notifications/notifications/test_digests.py +++ b/tests/sentry/notifications/notifications/test_digests.py @@ -216,18 +216,33 @@ def test_slack_digest_notification_block(self, digests): assert blocks[0]["text"]["text"] == fallback_text assert event1.group - event1_alert_title = f":red_circle: " + emoji = "red_circle" + event1_alert_title = { + "url": f"http://testserver/organizations/{self.organization.slug}/issues/{event1.group.id}/?referrer=digest-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue", + "text": f"{event1.group.title}", + } assert event2.group - event2_alert_title = f":red_circle: " + event2_alert_title = { + "url": f"http://testserver/organizations/{self.organization.slug}/issues/{event2.group.id}/?referrer=digest-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue", + "text": f"{event2.group.title}", + } # digest order not definitive try: - assert blocks[1]["text"]["text"] == event1_alert_title - assert blocks[5]["text"]["text"] == event2_alert_title + assert blocks[1]["elements"][0]["elements"][-1]["text"] == event1_alert_title["text"] + assert blocks[1]["elements"][0]["elements"][-1]["url"] == event1_alert_title["url"] + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[5]["elements"][0]["elements"][-1]["text"] == event2_alert_title["text"] + assert blocks[5]["elements"][0]["elements"][-1]["url"] == event2_alert_title["url"] + assert blocks[5]["elements"][0]["elements"][0]["name"] == emoji except AssertionError: - assert blocks[1]["text"]["text"] == event2_alert_title - assert blocks[5]["text"]["text"] == event1_alert_title + assert blocks[1]["elements"][0]["elements"][-1]["text"] == event2_alert_title["text"] + assert blocks[1]["elements"][0]["elements"][-1]["url"] == event2_alert_title["url"] + assert blocks[1]["elements"][0]["elements"][0]["name"] == emoji + assert blocks[5]["elements"][0]["elements"][-1]["text"] == event1_alert_title["text"] + assert blocks[5]["elements"][0]["elements"][-1]["url"] == event1_alert_title["url"] + assert blocks[5]["elements"][0]["elements"][0]["name"] == emoji assert ( blocks[3]["elements"][0]["text"] diff --git a/tests/sentry/notifications/test_notifications.py b/tests/sentry/notifications/test_notifications.py index 9c53c02de08d65..5fe5567702b9fb 100644 --- a/tests/sentry/notifications/test_notifications.py +++ b/tests/sentry/notifications/test_notifications.py @@ -187,13 +187,12 @@ def test_sends_unassignment_notification(self, mock_post): assert f"{self.user.username} unassigned" in msg.alternatives[0][0] blocks = orjson.loads(mock_post.call_args.kwargs["blocks"]) - block = blocks[1]["text"]["text"] footer = blocks[3]["elements"][0]["text"] text = mock_post.call_args.kwargs["text"] assert text == f"Issue unassigned by {self.name}" - assert self.group.title in block - title_link = block[13:][1:-1] # removes emoji and <> + assert self.group.title in blocks[1]["elements"][0]["elements"][-1]["text"] + title_link = blocks[1]["elements"][0]["elements"][-1]["url"] notification_uuid = get_notification_uuid(title_link) assert ( footer @@ -248,12 +247,11 @@ def test_sends_resolution_notification(self, record_analytics, mock_post): assert f"{self.short_id} as resolved

" in msg.alternatives[0][0] blocks = orjson.loads(mock_post.call_args.kwargs["blocks"]) - block = blocks[1]["text"]["text"] footer = blocks[3]["elements"][0]["text"] text = mock_post.call_args.kwargs["text"] - assert self.group.title in block - title_link = block[13:][1:-1] # removes emoji and <> + assert self.group.title in blocks[1]["elements"][0]["elements"][-1]["text"] + title_link = blocks[1]["elements"][0]["elements"][-1]["url"] notification_uuid = get_notification_uuid(title_link) assert ( text @@ -385,12 +383,11 @@ def test_sends_regression_notification(self, record_analytics, mock_post): assert f"{group.qualified_short_id} as a regression

" in msg.alternatives[0][0] blocks = orjson.loads(mock_post.call_args.kwargs["blocks"]) - block = blocks[1]["text"]["text"] footer = blocks[3]["elements"][0]["text"] text = mock_post.call_args.kwargs["text"] assert text == "Issue marked as regression" - title_link = block[13:][1:-1] # removes emoji and <> + title_link = blocks[1]["elements"][0]["elements"][-1]["url"] notification_uuid = get_notification_uuid(title_link) assert ( footer @@ -446,13 +443,12 @@ def test_sends_resolved_in_release_notification(self, record_analytics, mock_pos ) blocks = orjson.loads(mock_post.call_args.kwargs["blocks"]) - block = blocks[1]["text"]["text"] footer = blocks[3]["elements"][0]["text"] text = mock_post.call_args.kwargs["text"] assert text == f"Issue marked as resolved in {parsed_version} by {self.name}" - assert self.group.title in block - title_link = block[13:][1:-1] # removes emoji and <> + assert self.group.title in blocks[1]["elements"][0]["elements"][-1]["text"] + title_link = blocks[1]["elements"][0]["elements"][-1]["url"] notification_uuid = get_notification_uuid(title_link) assert ( footer @@ -532,11 +528,10 @@ def test_sends_issue_notification(self, record_analytics, mock_post): assert "Hello world" in msg.alternatives[0][0] blocks = orjson.loads(mock_post.call_args_list[0].kwargs["blocks"]) - block = blocks[1]["text"]["text"] footer = blocks[4]["elements"][0]["text"] - assert "Hello world" in block - title_link = block[13:][1:-1] # removes emoji and <> + assert "Hello world" in blocks[1]["elements"][0]["elements"][-1]["text"] + title_link = blocks[1]["elements"][0]["elements"][-1]["url"] notification_uuid = get_notification_uuid(title_link) assert ( footer diff --git a/tests/sentry/rules/processing/test_processor.py b/tests/sentry/rules/processing/test_processor.py index 7b4bd2f532f71e..5c19fd09e84c0c 100644 --- a/tests/sentry/rules/processing/test_processor.py +++ b/tests/sentry/rules/processing/test_processor.py @@ -877,7 +877,9 @@ def test_slack_title_link_notification_uuid(self, mock_post): mock_post.assert_called_once() assert ( "notification_uuid" - in json.loads(mock_post.call_args.kwargs["blocks"])[0]["text"]["text"] + in json.loads(mock_post.call_args.kwargs["blocks"])[0]["elements"][0]["elements"][-1][ + "url" + ] ) @patch("sentry.shared_integrations.client.base.BaseApiClient.post") From c34400ee18ae6fc9ac6b4fc89f34c41a26efff47 Mon Sep 17 00:00:00 2001 From: Snigdha Sharma Date: Mon, 19 May 2025 10:19:34 -0700 Subject: [PATCH 3/3] More typing fixes --- src/sentry/integrations/slack/message_builder/types.py | 6 +++--- .../integrations/slack/notifications/test_issue_alert.py | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/sentry/integrations/slack/message_builder/types.py b/src/sentry/integrations/slack/message_builder/types.py index b759258241f7a1..3ac5066dfddf86 100644 --- a/src/sentry/integrations/slack/message_builder/types.py +++ b/src/sentry/integrations/slack/message_builder/types.py @@ -33,7 +33,7 @@ } ACTIONED_CATEGORY_TO_EMOJI: dict[GroupCategory, list[str]] = { - GroupCategory.PERFORMANCE: [ACTION_EMOJI, "chart_with_upwards_trend"], - GroupCategory.FEEDBACK: [ACTION_EMOJI, "busts_in_silhouette"], - GroupCategory.CRON: [ACTION_EMOJI, "spiral_calendar_pad"], + GroupCategory.PERFORMANCE: [ACTION_EMOJI[0], "chart_with_upwards_trend"], + GroupCategory.FEEDBACK: [ACTION_EMOJI[0], "busts_in_silhouette"], + GroupCategory.CRON: [ACTION_EMOJI[0], "spiral_calendar_pad"], } diff --git a/tests/sentry/integrations/slack/notifications/test_issue_alert.py b/tests/sentry/integrations/slack/notifications/test_issue_alert.py index 8722d8bccd3de3..0c3b3245a7d661 100644 --- a/tests/sentry/integrations/slack/notifications/test_issue_alert.py +++ b/tests/sentry/integrations/slack/notifications/test_issue_alert.py @@ -700,6 +700,7 @@ def test_issue_alert_team_issue_owners_user_settings_off_digests(self, digests): notification_uuid = self.get_notification_uuid( blocks[1]["elements"][0]["elements"][-1]["url"] ) + assert event.group emoji = "red_circle" url = f"http://testserver/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue" text = "Hello world" @@ -874,6 +875,7 @@ def test_issue_alert_team_new_project(self): notification_uuid = self.get_notification_uuid( blocks[1]["elements"][0]["elements"][-1]["url"] ) + assert event.group emoji = "red_circle" url = f"http://example.com/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue" text = "Hello world" @@ -982,6 +984,7 @@ def test_issue_alert_team_fallback(self): notification_uuid = self.get_notification_uuid( blocks[1]["elements"][0]["elements"][-1]["url"] ) + assert event.group emoji = "red_circle" url = f"http://example.com/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue" text = "Hello world" @@ -1000,6 +1003,7 @@ def test_issue_alert_team_fallback(self): notification_uuid = self.get_notification_uuid( blocks[1]["elements"][0]["elements"][-1]["url"] ) + assert event.group emoji = "red_circle" url = f"http://example.com/organizations/{event.organization.slug}/issues/{event.group.id}/?referrer=issue_alert-slack¬ification_uuid={notification_uuid}&alert_rule_id={rule.id}&alert_type=issue" text = "Hello world"