def test_issue_alert_issue_owners(self, mock_func): """Test that issue alerts are sent to issue owners in Slack.""" event = self.store_event( data={"message": "Hello world", "level": "error"}, project_id=self.project.id ) action_data = { "id": "sentry.mail.actions.NotifyEmailAction", "targetType": "IssueOwners", "targetIdentifier": "", } rule = Rule.objects.create( project=self.project, label="ja rule", data={ "match": "all", "actions": [action_data], }, ) notification = AlertRuleNotification( Notification(event=event, rule=rule), ActionTargetType.ISSUE_OWNERS, self.user.id ) with self.tasks(): notification.send() attachment, text = get_attachment() assert attachment["title"] == "Hello world" assert ( attachment["footer"] == f"{self.project.slug} | <http://testserver/settings/account/notifications/alerts/?referrer=AlertRuleSlackUser|Notification Settings>" )
def test_simple_notification(self): responses.add("POST", "https://api.pushover.net/1/messages.json", body=SUCCESS) self.plugin.set_option("userkey", "abcdef", self.project) self.plugin.set_option("apikey", "ghijkl", self.project) group = self.create_group(message="Hello world", culprit="foo.bar") event = self.create_event(group=group, message="Hello world", tags={"level": "warning"}) rule = Rule.objects.create(project=self.project, label="my rule") notification = Notification(event=event, rule=rule) with self.options({"system.url-prefix": "http://example.com"}): self.plugin.notify(notification) request = responses.calls[0].request payload = parse_qs(request.body) assert payload == { "message": ["{}\n\nTags: level=warning".format(event.title)], "title": ["Bar: Hello world"], "url": [ "http://example.com/organizations/baz/issues/{}/?referrer=pushover_plugin" .format(group.id) ], "url_title": ["Issue Details"], "priority": ["0"], "user": ["abcdef"], "token": ["ghijkl"], "expire": ["90"], "retry": ["30"], }
def test_simple_notification(self): responses.add( "POST", "https://api.twilio.com/2010-04-01/Accounts/abcdef/Messages.json") self.plugin.set_option("account_sid", "abcdef", self.project) self.plugin.set_option("auth_token", "abcd", self.project) self.plugin.set_option("sms_from", "4158675309", self.project) self.plugin.set_option("sms_to", "4154444444", self.project) event = self.store_event( data={ "message": "Hello world", "level": "warning", "platform": "python", "culprit": "foo.bar", }, project_id=self.project.id, ) rule = Rule.objects.create(project=self.project, label="my rule") notification = Notification(event=event, rule=rule) with self.options({"system.url-prefix": "http://example.com"}): self.plugin.notify(notification) request = responses.calls[0].request payload = parse_qs(request.body) assert payload == { "To": ["+14154444444"], "From": ["+14158675309"], "Body": ["Sentry [%s] WARNING: Hello world" % self.project.slug.title()], }
def test_no_error_on_ignorable_slack_errors(self): responses.add("POST", "http://example.com/slack", status=403, body="action_prohibited") self.plugin.set_option("webhook", "http://example.com/slack", self.project) event = self.store_event( data={ "message": "Hello world", "level": "warning", "culprit": "foo.bar" }, project_id=self.project.id, ) rule = Rule.objects.create(project=self.project, label="my rule") notification = Notification(event=event, rule=rule) # No exception since certain errors are supposed to be ignored with self.options({"system.url-prefix": "http://example.com"}): self.plugin.notify(notification) responses.replace("POST", "http://example.com/slack", status=403, body="some_other_error") # Other exceptions should not be ignored with self.options({"system.url-prefix": "http://example.com"}): with pytest.raises(ApiError): self.plugin.notify(notification)
def test_emergency_notification(self): responses.add("POST", "https://api.pushover.net/1/messages.json", body=SUCCESS) self.plugin.set_option("userkey", "abcdef", self.project) self.plugin.set_option("apikey", "ghijkl", self.project) self.plugin.set_option("priority", "2", self.project) self.plugin.set_option("expire", 90, self.project) self.plugin.set_option("retry", 30, self.project) event = self.store_event( data={"message": "Hello world", "level": "warning"}, project_id=self.project.id ) group = event.group rule = Rule.objects.create(project=self.project, label="my rule") notification = Notification(event=event, rule=rule) with self.options({"system.url-prefix": "http://example.com"}): self.plugin.notify(notification) request = responses.calls[0].request payload = parse_qs(request.body) assert payload == { "message": [f"{event.title}\n\nTags: level=warning"], "title": ["Bar: Hello world"], "url": [ f"http://example.com/organizations/baz/issues/{group.id}/?referrer=pushover_plugin" ], "url_title": ["Issue Details"], "priority": ["2"], "user": ["abcdef"], "token": ["ghijkl"], "expire": ["90"], "retry": ["30"], }
def test_notify_users_with_their_timezones(self): """ Test that ensures that datetime in issue alert email is in the user's timezone """ from django.template.defaultfilters import date timestamp = datetime.now(tz=pytz.utc) local_timestamp = timezone.localtime(timestamp, pytz.timezone("Europe/Vienna")) local_timestamp = date(local_timestamp, "N j, Y, g:i:s a e") UserOption.objects.create(user=self.user, key="timezone", value="Europe/Vienna") event = self.store_event( data={ "message": "foobar", "level": "error", "timestamp": iso_format(timestamp) }, project_id=self.project.id, ) notification = Notification(event=event) with self.options({"system.url-prefix": "http://example.com"}), self.tasks(): self.adapter.notify(notification, ActionTargetType.ISSUE_OWNERS) assert len(mail.outbox) == 1 msg = mail.outbox[0] assert local_timestamp in str(msg.alternatives)
def test_notify_users_renders_interfaces_with_utf8(self, _send_mail, _to_email_html, _get_title): group = self.create_group(first_seen=timezone.now(), last_seen=timezone.now(), project=self.project) _to_email_html.return_value = u"רונית מגן" _get_title.return_value = "Stacktrace" event = Event( group_id=group.id, project_id=self.project.id, message="Soubor ji\xc5\xbe existuje", # Create interface so get_title will be called on it. data={"stacktrace": { "frames": [{}] }}, ) notification = Notification(event=event) with self.options({"system.url-prefix": "http://example.com"}): self.plugin.notify(notification) _get_title.assert_called_once_with() _to_email_html.assert_called_once_with(event)
def test_notification_without_project(self): responses.add("POST", "http://example.com/slack") self.plugin.set_option("webhook", "http://example.com/slack", self.project) self.plugin.set_option("exclude_project", True, self.project) event = self.store_event( data={"message": "Hello world", "level": "warning", "culprit": "foo.bar"}, project_id=self.project.id, ) group = event.group rule = Rule.objects.create(project=self.project, label="my rule") notification = Notification(event=event, rule=rule) with self.options({"system.url-prefix": "http://example.com"}): self.plugin.notify(notification) request = responses.calls[0].request payload = json.loads(parse_qs(request.body)["payload"][0]) assert payload == { "username": "******", "attachments": [ { "color": "#f18500", "fields": [{"short": False, "value": "foo.bar", "title": "Culprit"}], "fallback": "[bar] Hello world", "title": "Hello world", "title_link": "http://example.com/organizations/baz/issues/%s/?referrer=slack" % group.id, } ], }
def test_simple_notification(self): responses.add( "POST", "https://alert.victorops.com/integrations/generic/20131114/alert/secret-api-key/everyone", body=SUCCESS, ) self.plugin.set_option("api_key", "secret-api-key", self.project) self.plugin.set_option("routing_key", "everyone", self.project) group = self.create_group(message="Hello world", culprit="foo.bar") event = self.create_event(group=group, message="Hello world", tags={"level": "warning"}) rule = Rule.objects.create(project=self.project, label="my rule") notification = Notification(event=event, rule=rule) with self.options({"system.url-prefix": "http://example.com"}): self.plugin.notify(notification) request = responses.calls[0].request payload = json.loads(request.body) assert { "message_type": "WARNING", "entity_id": group.id, "entity_display_name": "Hello world", "monitoring_tool": "sentry", "state_message": 'Stacktrace\n-----------\n\nStacktrace (most recent call last):\n\n File "sentry/models/foo.py", line 29, in build_msg\n string_max_length=self.string_max_length)\n\nMessage\n-----------\n\nHello world', "timestamp": int(event.datetime.strftime("%s")), "issue_url": "http://example.com/organizations/baz/issues/%s/" % group.id, "issue_id": group.id, "project_id": group.project.id, } == payload
def test_issue_alert_team(self, mock_func): """Test that issue alerts are sent to members of a Sentry team in Slack.""" user2 = self.create_user(is_superuser=False) self.create_member(teams=[self.team], user=user2, organization=self.organization) ExternalActor.objects.create( actor=user2.actor, organization=self.organization, integration=self.integration, provider=ExternalProviders.SLACK.value, external_name="goma", external_id="UXXXXXXX2", ) NotificationSetting.objects.update_settings( ExternalProviders.SLACK, NotificationSettingTypes.ISSUE_ALERTS, NotificationSettingOptionValues.ALWAYS, user=user2, ) event = self.store_event( data={"message": "Hello world", "level": "error"}, project_id=self.project.id ) action_data = { "id": "sentry.mail.actions.NotifyEmailAction", "targetType": "Team", "targetIdentifier": str(self.team.id), } rule = Rule.objects.create( project=self.project, label="ja rule", data={ "match": "all", "actions": [action_data], }, ) notification = Notification(event=event, rule=rule) with self.options({"system.url-prefix": "http://example.com"}), self.tasks(): self.adapter.notify(notification, ActionTargetType.TEAM, self.team.id) assert len(responses.calls) == 2 # check that self.user got a notification data = parse_qs(responses.calls[0].request.body) assert "attachments" in data attachments = json.loads(data["attachments"][0]) assert len(attachments) == 1 assert attachments[0]["title"] == "Hello world" assert attachments[0]["text"] == "" assert attachments[0]["footer"] == event.group.qualified_short_id # check that user2 got a notification as well data2 = parse_qs(responses.calls[1].request.body) assert "attachments" in data2 attachments = json.loads(data2["attachments"][0]) assert len(attachments) == 1 assert attachments[0]["title"] == "Hello world" assert attachments[0]["text"] == "" assert attachments[0]["footer"] == event.group.qualified_short_id
def test_disabled_org_integration_for_user(self, mock_func): OrganizationIntegration.objects.filter( integration=self.integration).update(status=ObjectStatus.DISABLED) event = self.store_event(data={ "message": "Hello world", "level": "error" }, project_id=self.project.id) action_data = { "id": "sentry.mail.actions.NotifyEmailAction", "targetType": "Member", "targetIdentifier": str(self.user.id), } rule = Rule.objects.create( project=self.project, label="ja rule", data={ "match": "all", "actions": [action_data], }, ) notification = AlertRuleNotification( Notification(event=event, rule=rule), ActionTargetType.MEMBER, self.user.id) with self.tasks(): notification.send() assert len(responses.calls) == 0
def test_notify_users_does_email(self, mock_func): event_manager = EventManager({ "message": "hello world", "level": "error" }) event_manager.normalize() event_data = event_manager.get_data() event_type = get_event_type(event_data) event_data["type"] = event_type.key event_data["metadata"] = event_type.get_metadata(event_data) event = event_manager.save(self.project.id) group = event.group with self.tasks(): AlertRuleNotification(Notification(event=event), ActionTargetType.ISSUE_OWNERS).send() assert mock_func.call_count == 1 args, kwargs = mock_func.call_args notification = args[1] self.assertEquals(notification.project, self.project) self.assertEquals(notification.get_reference(), group) assert notification.get_subject() == "BAR-1 - hello world"
def test_issue_alert_user(self, mock_func): """Test that issue alerts are sent to a Slack user.""" event = self.store_event(data={ "message": "Hello world", "level": "error" }, project_id=self.project.id) action_data = { "id": "sentry.mail.actions.NotifyEmailAction", "targetType": "Member", "targetIdentifier": str(self.user.id), } rule = Rule.objects.create( project=self.project, label="ja rule", data={ "match": "all", "actions": [action_data], }, ) notification = Notification(event=event, rule=rule) with self.options({"system.url-prefix": "http://example.com"}), self.tasks(): self.adapter.notify(notification, ActionTargetType.MEMBER, self.user.id) attachment = get_attachment() assert attachment["title"] == "Hello world" assert attachment["text"] == "" assert attachment["footer"] == event.group.qualified_short_id
def test_notify_users_does_email(self, mock_func): UserOption.objects.create(user=self.user, key="timezone", value="Europe/Vienna") event_manager = EventManager({ "message": "hello world", "level": "error" }) event_manager.normalize() event_data = event_manager.get_data() event_type = get_event_type(event_data) event_data["type"] = event_type.key event_data["metadata"] = event_type.get_metadata(event_data) event = event_manager.save(self.project.id) group = event.group with self.tasks(): AlertRuleNotification(Notification(event=event), ActionTargetType.ISSUE_OWNERS).send() assert mock_func.call_count == 1 args, kwargs = mock_func.call_args notification = args[1] assert notification.get_user_context( self.user, {})["timezone"] == pytz.timezone("Europe/Vienna") self.assertEquals(notification.project, self.project) self.assertEquals(notification.get_reference(), group) assert notification.get_subject() == "BAR-1 - hello world"
def test_notification_without_project(self): responses.add("POST", "http://example.com/slack") self.plugin.set_option("webhook", "http://example.com/slack", self.project) self.plugin.set_option("exclude_project", True, self.project) event = self.store_event( data={ "message": "Hello world", "level": "warning", "culprit": "foo.bar" }, project_id=self.project.id, ) group = event.group rule = Rule.objects.create(project=self.project, label="my rule") notification = Notification(event=event, rule=rule) with self.options({"system.url-prefix": "http://example.com"}): self.plugin.notify(notification) request = responses.calls[0].request payload = json.loads(parse_qs(request.body)["payload"][0])
def assert_notify(self, event, emails_sent_to): mail.outbox = [] with self.options({"system.url-prefix": "http://example.com"}), self.tasks(): self.plugin.notify(Notification(event=event)) assert len(mail.outbox) == len(emails_sent_to) assert sorted(email.to[0] for email in mail.outbox) == sorted(emails_sent_to)
def test_email_notification_is_not_sent_to_deleted_email(self, mock_func): """ Test that ensures if we still have some stale emails in UserOption, then upon attempting to send an email notification to those emails, these stale `UserOption` instances are deleted """ # Initial Creation user = self.create_user(email="*****@*****.**", is_active=True) self.create_member(user=user, organization=self.organization, teams=[self.team]) UserOption.objects.create(user=user, key="mail:email", value="*****@*****.**", project=self.project) # New secondary email is created useremail = UserEmail.objects.create(user=user, email="*****@*****.**", is_verified=True) # Set secondary email to be primary user.email = useremail.email user.save() # Delete first email old_useremail = UserEmail.objects.get(email="*****@*****.**") old_useremail.delete() event_manager = EventManager({ "message": "hello world", "level": "error" }) event_manager.normalize() event_data = event_manager.get_data() event_type = get_event_type(event_data) event_data["type"] = event_type.key event_data["metadata"] = event_type.get_metadata(event_data) event = event_manager.save(self.project.id) with self.tasks(): AlertRuleNotification(Notification(event=event), ActionTargetType.ISSUE_OWNERS).send() assert mock_func.call_count == 1 args, kwargs = mock_func.call_args notification = args[1] user_ids = [] for user in list(notification.get_participants().values())[0]: user_ids.append(user.id) assert "*****@*****.**" in get_email_addresses(user_ids, self.project).values() assert not len( UserOption.objects.filter(key="mail:email", value="*****@*****.**"))
def test_notify_with_suspect_commits(self): repo = Repository.objects.create( organization_id=self.organization.id, name=self.organization.id ) release = self.create_release(project=self.project, version="v12") release.set_commits( [ { "id": "a" * 40, "repository": repo.name, "author_email": "*****@*****.**", "author_name": "Bob", "message": "i fixed a bug", "patch_set": [{"path": "src/sentry/models/release.py", "type": "M"}], } ] ) event = self.store_event( data={ "message": "Kaboom!", "platform": "python", "timestamp": iso_format(before_now(seconds=1)), "stacktrace": { "frames": [ { "function": "handle_set_commits", "abs_path": "/usr/src/sentry/src/sentry/tasks.py", "module": "sentry.tasks", "in_app": True, "lineno": 30, "filename": "sentry/tasks.py", }, { "function": "set_commits", "abs_path": "/usr/src/sentry/src/sentry/models/release.py", "module": "sentry.models.release", "in_app": True, "lineno": 39, "filename": "sentry/models/release.py", }, ] }, "tags": {"sentry:release": release.version}, }, project_id=self.project.id, ) with self.tasks(): notification = Notification(event=event) self.adapter.notify(notification, ActionTargetType.ISSUE_OWNERS) assert len(mail.outbox) >= 1 msg = mail.outbox[-1] assert "Suspect Commits" in msg.body
def setUp(self): self.event = self.store_event(data={ "message": "Hello world", "level": "warning" }, project_id=self.project.id) rule = Rule.objects.create(project=self.project, label="my rule") self.notification = Notification(event=self.event, rule=rule) self.project.update_option("webhooks:urls", "http://example.com")
def test_simple_notification(self): responses.add("POST", "https://api.opsgenie.com/v2/alerts") self.plugin.set_option("api_key", "abcdef", self.project) self.plugin.set_option("alert_url", "https://api.opsgenie.com/v2/alerts", self.project) self.plugin.set_option("recipients", "me", self.project) event = self.store_event( data={ "message": "Hello world", "level": "warning", "platform": "python", "culprit": "foo.bar", }, project_id=self.project.id, ) group = event.group rule = Rule.objects.create(project=self.project, label="my rule") notification = Notification(event=event, rule=rule) with self.options({"system.url-prefix": "http://example.com"}): self.plugin.notify(notification) request = responses.calls[0].request payload = json.loads(request.body) group_id = six.text_type(group.id) assert payload == { "recipients": "me", "tags": ["level:warning"], "entity": "foo.bar", "alias": "sentry: %s" % group_id, "details": { "Project Name": self.project.name, "Triggering Rules": '["my rule"]', "Sentry Group": "Hello world", "Sentry ID": group_id, "Logger": "", "Level": "warning", "Project ID": "bar", "URL": "http://example.com/organizations/baz/issues/%s/" % group_id, }, "message": "Hello world", "source": "Sentry", }
def assert_notify( self, event, emails_sent_to, target_type=ActionTargetType.ISSUE_OWNERS, target_identifier=None, ): mail.outbox = [] with self.options({"system.url-prefix": "http://example.com"}), self.tasks(): self.adapter.notify(Notification(event=event), target_type, target_identifier) assert sorted(email.to[0] for email in mail.outbox) == sorted(emails_sent_to)
def test_notify_users_with_utf8_subject(self): event = self.store_event( data={"message": "רונית מגן", "level": "error"}, project_id=self.project.id ) notification = Notification(event=event) with self.options({"system.url-prefix": "http://example.com"}), self.tasks(): self.adapter.notify(notification, ActionTargetType.ISSUE_OWNERS) assert len(mail.outbox) == 1 msg = mail.outbox[0] assert msg.subject == "[Sentry] BAR-1 - רונית מגן"
def test_simple_notification(self): event = self.store_event( data={"message": "Hello world", "level": "error"}, project_id=self.project.id ) rule = Rule.objects.create(project=self.project, label="my rule") notification = Notification(event=event, rule=rule) with self.options({"system.url-prefix": "http://example.com"}), self.tasks(): self.adapter.notify(notification, ActionTargetType.ISSUE_OWNERS) msg = mail.outbox[0] assert msg.subject == "[Sentry] BAR-1 - Hello world" assert "my rule" in msg.alternatives[0][0]
def test_notify_users_with_utf8_subject(self): group = self.create_group(message="Hello world") event = self.create_event(group=group, message=u"רונית מגן", tags={"level": "error"}) notification = Notification(event=event) with self.options({"system.url-prefix": "http://example.com"}), self.tasks(): self.plugin.notify(notification) assert len(mail.outbox) == 1 msg = mail.outbox[0] assert msg.subject == u"[Sentry] BAR-1 - רונית מגן"
def send_as_alert_notification( context: Mapping[str, Any], target_type: ActionTargetType, target_identifier: Optional[int] = None, ) -> None: """If there is more than one record for a group, just choose the most recent one.""" from sentry.mail import mail_adapter record = max( itertools.chain.from_iterable( groups.get(context["group"], []) for groups in context["digest"].values() ), key=get_timestamp, ) notification = Notification(record.value.event, rules=record.value.rules) mail_adapter.notify(notification, target_type, target_identifier)
def test_slack_link(self): project = self.project organization = project.organization event = self.store_event(data=self.make_event_data("foo.jx"), project_id=project.id) with self.tasks(): notification = Notification(event=event) self.adapter.notify(notification, ActionTargetType.ISSUE_OWNERS) assert len(mail.outbox) >= 1 msg = mail.outbox[-1] assert ( f"/settings/{organization.slug}/integrations/slack/?referrer=alert_email" in msg.alternatives[0][0])
def test_notify_users_renders_interfaces_with_utf8(self, _to_email_html, _get_title): _to_email_html.return_value = "רונית מגן" _get_title.return_value = "Stacktrace" event = self.store_event( data={"message": "Soubor ji\xc5\xbe existuje", "stacktrace": {"frames": [{}]}}, project_id=self.project.id, ) notification = Notification(event=event) with self.options({"system.url-prefix": "http://example.com"}): self.adapter.notify(notification, ActionTargetType.ISSUE_OWNERS) _get_title.assert_called_once_with() _to_email_html.assert_called_once_with(event)
def test_not_issue_alert_team_removed_project(self, mock_func): """Test that issue alerts are not sent to a team in Slack when the team has removed the project the issue belongs to""" # create the team's notification settings ExternalActor.objects.create( actor=self.team.actor, organization=self.organization, integration=self.integration, provider=ExternalProviders.SLACK.value, external_name="goma", external_id="CXXXXXXX2", ) NotificationSetting.objects.update_settings( ExternalProviders.SLACK, NotificationSettingTypes.ISSUE_ALERTS, NotificationSettingOptionValues.ALWAYS, team=self.team, ) # remove the project from the team self.project.remove_team(self.team) event = self.store_event(data={ "message": "Hello world", "level": "error" }, project_id=self.project.id) action_data = { "id": "sentry.mail.actions.NotifyEmailAction", "targetType": "Team", "targetIdentifier": str(self.team.id), } rule = Rule.objects.create( project=self.project, label="ja rule", data={ "match": "all", "actions": [action_data], }, ) notification = Notification(event=event, rule=rule) with self.options({"system.url-prefix": "http://example.com"}), self.tasks(): self.adapter.notify(notification, ActionTargetType.TEAM, self.team.id) assert len(responses.calls) == 0
def test_simple_notification(self): group = self.create_group(message="Hello world") event = self.create_event(group=group, message="Hello world", tags={"level": "error"}) rule = Rule.objects.create(project=self.project, label="my rule") notification = Notification(event=event, rule=rule) with self.options({"system.url-prefix": "http://example.com"}), self.tasks(): self.plugin.notify(notification) msg = mail.outbox[0] assert msg.subject == "[Sentry] BAR-1 - Hello world" assert "my rule" in msg.alternatives[0][0]
def test_multiline_error(self, _send_mail): event_manager = EventManager({"message": "hello world\nfoo bar", "level": "error"}) event_manager.normalize() event_data = event_manager.get_data() event_type = get_event_type(event_data) event_data["type"] = event_type.key event_data["metadata"] = event_type.get_metadata(event_data) event = event_manager.save(self.project.id) notification = Notification(event=event) with self.options({"system.url-prefix": "http://example.com"}): self.adapter.notify(notification, ActionTargetType.ISSUE_OWNERS) assert _send_mail.call_count == 1 args, kwargs = _send_mail.call_args assert kwargs.get("subject") == "BAR-1 - hello world"