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"