Browse Source

ref(digests): Add generic issues to debug view and tests (#42574)

Update the digest debug view and digest tests to include generic issues.
No code changes are needed for them to pass through, this just makes
sure they look nice and are tested.

Closes #42055

<img width="487" alt="Screen Shot 2022-12-21 at 12 32 00 PM"
src="https://user-images.githubusercontent.com/29959063/208999346-c95d6148-a004-4cb0-92b5-a7a5d7a70be3.png">
Colleen O'Rourke 2 years ago
parent
commit
7cee40b750

+ 20 - 5
fixtures/emails/digest.txt

@@ -1,21 +1,24 @@
 Notifications for example
 June 22, 2016, 4:16 p.m. UTC to July 4, 2016, 2:18 p.m. UTC
 
-Rule #2
+Rule #3
 
 * DecidingKoiError: faithful hookworm top sharp filly merely sought trivially (8193 events, 9186 users)
   http://testserver/organizations/example/issues/2/?referrer=digest_email
 
+* something bad happened (5113 events, 5954 users)
+  http://testserver/organizations/example/issues/201/?referrer=digest_email
+
 * N+1 Query (4418 events, 3851 users)
   http://testserver/organizations/example/issues/100/?referrer=digest_email
 
-* N+1 Query (2057 events, 2439 users)
-  http://testserver/organizations/example/issues/101/?referrer=digest_email
-
 * SnailError: molly engaged wahoo feline hedgehog salmon novel boxer polecat (920 events, 5856 users)
   http://testserver/organizations/example/issues/3/?referrer=digest_email
 
-Rule #3
+* something bad happened (349 events, 6979 users)
+  http://testserver/organizations/example/issues/200/?referrer=digest_email
+
+Rule #2
 
 * DecidingKoiError: faithful hookworm top sharp filly merely sought trivially (8193 events, 9186 users)
   http://testserver/organizations/example/issues/2/?referrer=digest_email
@@ -23,20 +26,32 @@ Rule #3
 * N+1 Query (4418 events, 3851 users)
   http://testserver/organizations/example/issues/100/?referrer=digest_email
 
+* N+1 Query (2057 events, 2439 users)
+  http://testserver/organizations/example/issues/101/?referrer=digest_email
+
 * SnailError: molly engaged wahoo feline hedgehog salmon novel boxer polecat (920 events, 5856 users)
   http://testserver/organizations/example/issues/3/?referrer=digest_email
 
+* something bad happened (349 events, 6979 users)
+  http://testserver/organizations/example/issues/200/?referrer=digest_email
+
 Rule #1
 
 * DecidingKoiError: faithful hookworm top sharp filly merely sought trivially (8193 events, 9186 users)
   http://testserver/organizations/example/issues/2/?referrer=digest_email
 
+* something bad happened (5113 events, 5954 users)
+  http://testserver/organizations/example/issues/201/?referrer=digest_email
+
 * N+1 Query (4418 events, 3851 users)
   http://testserver/organizations/example/issues/100/?referrer=digest_email
 
 * SnailError: molly engaged wahoo feline hedgehog salmon novel boxer polecat (920 events, 5856 users)
   http://testserver/organizations/example/issues/3/?referrer=digest_email
 
+* something bad happened (349 events, 6979 users)
+  http://testserver/organizations/example/issues/200/?referrer=digest_email
+
 
 
 Unsubscribe: javascript:alert("This is a preview page, what did you expect to happen?");

+ 2 - 31
src/sentry/web/frontend/debug/debug_generic_issue.py

@@ -1,49 +1,20 @@
-import uuid
-from datetime import datetime
-
 import pytz
 from django.utils.safestring import mark_safe
 from django.views.generic import View
 
-from sentry.issues.issue_occurrence import IssueEvidence, IssueOccurrence
 from sentry.models import Organization, Project, Rule
 from sentry.notifications.utils import get_generic_data, get_group_settings_link, get_rules
-from sentry.types.issues import GroupType
 from sentry.utils import json
-from sentry.utils.dates import ensure_aware
 
-from .mail import COMMIT_EXAMPLE, MailPreview, make_error_event
+from .mail import COMMIT_EXAMPLE, MailPreview, make_generic_event
 
 
 class DebugGenericIssueEmailView(View):
     def get(self, request):
-        platform = request.GET.get("platform", "python")
         org = Organization(id=1, slug="example", name="Example")
         project = Project(id=1, slug="example", name="Example", organization=org)
 
-        event = make_error_event(request, project, platform)
-        event = event.for_group(event.groups[0])
-
-        occurrence = IssueOccurrence(
-            uuid.uuid4().hex,
-            uuid.uuid4().hex,
-            ["some-fingerprint"],
-            "something bad happened",
-            "it was bad",
-            "1234",
-            {"Test": 123},
-            [
-                IssueEvidence("Name 1", "Value 1", True),
-                IssueEvidence("Name 2", "Value 2", False),
-                IssueEvidence("Name 3", "Value 3", False),
-            ],
-            GroupType.PROFILE_BLOCKED_THREAD,
-            ensure_aware(datetime.now()),
-        )
-        occurrence.save()
-        event.occurrence = occurrence
-        event.group.type = GroupType.PROFILE_BLOCKED_THREAD
-
+        event = make_generic_event(project)
         group = event.group
 
         rule = Rule(id=1, label="An example rule")

+ 39 - 2
src/sentry/web/frontend/debug/mail.py

@@ -27,6 +27,7 @@ from sentry.digests.notifications import Notification, build_digest
 from sentry.digests.utils import get_digest_metadata
 from sentry.event_manager import EventManager, get_event_type
 from sentry.http import get_server_hostname
+from sentry.issues.occurrence_consumer import process_event_and_issue_occurrence
 from sentry.mail.notifications import get_builder_args
 from sentry.models import (
     Activity,
@@ -45,6 +46,8 @@ from sentry.notifications.notifications.digest import DigestNotification
 from sentry.notifications.types import GroupSubscriptionReason
 from sentry.notifications.utils import get_group_settings_link, get_interface_list, get_rules
 from sentry.testutils.helpers import override_options
+from sentry.testutils.helpers.datetime import before_now
+from sentry.testutils.helpers.notifications import TEST_ISSUE_OCCURRENCE
 from sentry.types.issues import GROUP_TYPE_TO_TEXT
 from sentry.utils import json, loremipsum
 from sentry.utils.dates import to_datetime, to_timestamp
@@ -173,7 +176,6 @@ def make_error_event(request, project, platform):
         ("level", "error"),
         ("device", "Other"),
     ]
-
     event_manager = EventManager(data)
     event_manager.normalize()
     data = event_manager.get_data()
@@ -208,6 +210,19 @@ def make_performance_event(project):
     return perf_event
 
 
+def make_generic_event(project):
+    occurrence, group_info = process_event_and_issue_occurrence(
+        TEST_ISSUE_OCCURRENCE.to_dict(),
+        {
+            "event_id": uuid.uuid4().hex,
+            "project_id": project.id,
+            "timestamp": before_now(minutes=1).isoformat(),
+        },
+    )
+    generic_group = group_info.group
+    return generic_group.get_latest_event()
+
+
 def get_shared_context(rule, org, project, group, event):
     return {
         "rule": rule,
@@ -490,7 +505,6 @@ def digest(request):
             event = eventstore.create_event(
                 event_id=uuid.uuid4().hex, group_id=group.id, project_id=project.id, data=data.data
             )
-
             records.append(
                 Record(
                     event.event_id,
@@ -531,6 +545,29 @@ def digest(request):
         state["event_counts"][perf_group.id] = random.randint(10, 1e4)
         state["user_counts"][perf_group.id] = random.randint(10, 1e4)
 
+    # add in generic issues
+    for i in range(random.randint(1, 3)):
+        generic_event = make_generic_event(project)
+        generic_group = generic_event.group
+        generic_group.id = i + 200  # don't clobber other issue ids
+
+        records.append(
+            Record(
+                generic_event.event_id,
+                Notification(
+                    generic_event,
+                    random.sample(
+                        list(state["rules"].keys()), random.randint(1, len(state["rules"]))
+                    ),
+                ),
+                # this is required for acceptance tests to pass as the EventManager won't accept a timestamp in the past
+                to_timestamp(datetime(2016, 6, 22, 16, 16, 0, tzinfo=timezone.utc)),
+            )
+        )
+        state["groups"][generic_group.id] = generic_group
+        state["event_counts"][generic_group.id] = random.randint(10, 1e4)
+        state["user_counts"][generic_group.id] = random.randint(10, 1e4)
+
     digest = build_digest(project, records, state)[0]
     start, end, counts = get_digest_metadata(digest)
 

+ 33 - 25
tests/sentry/notifications/notifications/test_digests.py

@@ -1,3 +1,4 @@
+import uuid
 from unittest import mock
 from unittest.mock import patch
 from urllib.parse import parse_qs
@@ -9,40 +10,36 @@ import sentry
 from sentry.digests.backends.base import Backend
 from sentry.digests.backends.redis import RedisBackend
 from sentry.digests.notifications import event_to_record
-from sentry.event_manager import EventManager
+from sentry.issues.occurrence_consumer import process_event_and_issue_occurrence
 from sentry.models import ProjectOwnership, Rule
 from sentry.tasks.digests import deliver_digest
 from sentry.testutils import TestCase
-from sentry.testutils.cases import SlackActivityNotificationTest
-from sentry.testutils.helpers import override_options
+from sentry.testutils.cases import PerformanceIssueTestCase, SlackActivityNotificationTest
 from sentry.testutils.helpers.datetime import before_now, iso_format
 from sentry.testutils.helpers.slack import send_notification
 from sentry.utils import json
-from sentry.utils.samples import load_data
+from tests.sentry.issues.test_utils import OccurrenceTestMixin
 
 USER_COUNT = 2
 
 
-class DigestNotificationTest(TestCase):
+class DigestNotificationTest(TestCase, OccurrenceTestMixin, PerformanceIssueTestCase):
     def add_event(self, fingerprint: str, backend: Backend, event_type: str = "error") -> None:
         if event_type == "performance":
-            with override_options(
+            event = self.create_performance_issue()
+        elif event_type == "generic":
+            occurrence_data = self.build_occurrence_data()
+            event_id = uuid.uuid4().hex
+            occurrence, group_info = process_event_and_issue_occurrence(
+                occurrence_data,
                 {
-                    "performance.issues.n_plus_one_db.problem-creation": 1.0,
-                }
-            ):
-                data = dict(
-                    load_data(
-                        "transaction-n-plus-one",
-                        timestamp=before_now(days=1),
-                    )
-                )
-                event_manager = EventManager(data)
-                event_manager.normalize()
-                data = event_manager.get_data()
-                event = event_manager.save(self.project.id)
-
-            event = event.for_group(event.groups[0])
+                    "event_id": event_id,
+                    "project_id": self.project.id,
+                    "timestamp": before_now(minutes=1).isoformat(),
+                },
+            )
+            group = group_info.group
+            event = group.get_latest_event()
         else:
             event = self.store_event(
                 data={
@@ -52,12 +49,19 @@ class DigestNotificationTest(TestCase):
                 },
                 project_id=self.project.id,
             )
+
         backend.add(
             self.key, event_to_record(event, [self.rule]), increment_delay=0, maximum_delay=0
         )
 
     @patch.object(sentry, "digests")
-    def run_test(self, digests, event_count: int, performance_issues: bool = False):
+    def run_test(
+        self,
+        digests,
+        event_count: int,
+        performance_issues: bool = False,
+        generic_issues: bool = False,
+    ):
         backend = RedisBackend()
         digests.digest = backend.digest
 
@@ -65,7 +69,10 @@ class DigestNotificationTest(TestCase):
             self.add_event(f"group-{i}", backend, "error")
 
         if performance_issues:
-            self.add_event(f"group-{i}", backend, "performance")
+            self.add_event(f"group-{event_count+1}", backend, "performance")
+
+        if generic_issues:
+            self.add_event(f"group-{event_count+2}", backend, "generic")
 
         with self.tasks():
             deliver_digest(self.key)
@@ -89,10 +96,11 @@ class DigestNotificationTest(TestCase):
     def test_sends_digest_to_every_member(self):
         """Test that each member of the project the events are created in receive a digest email notification"""
         event_count = 4
-        self.run_test(event_count=event_count, performance_issues=True)
-        assert f"{event_count + 1} new alerts since" in mail.outbox[0].subject
+        self.run_test(event_count=event_count, performance_issues=True, generic_issues=True)
+        assert f"{event_count + 2} new alerts since" in mail.outbox[0].subject
         assert "N+1 Query" in mail.outbox[0].body
         assert "oh no" in mail.outbox[0].body
+        assert self.build_occurrence_data()["issue_title"] in mail.outbox[0].body
 
     def test_sends_alert_rule_notification_to_each_member(self):
         """Test that if there is only one event it is sent as a regular alert rule notification"""