|
@@ -5,10 +5,15 @@ from typing import Any
|
|
|
from unittest import mock
|
|
|
from unittest.mock import MagicMock
|
|
|
|
|
|
+import pytest
|
|
|
+
|
|
|
+from sentry.event_manager import _get_updated_group_title
|
|
|
+from sentry.eventtypes.base import DefaultEvent
|
|
|
from sentry.grouping.result import CalculatedHashes
|
|
|
from sentry.models.group import Group
|
|
|
from sentry.testutils.cases import TestCase
|
|
|
from sentry.testutils.helpers.eventprocessing import save_new_event
|
|
|
+from sentry.testutils.pytest.fixtures import django_db_all
|
|
|
from sentry.testutils.skips import requires_snuba
|
|
|
|
|
|
pytestmark = [requires_snuba]
|
|
@@ -113,6 +118,202 @@ class EventManagerGroupingTest(TestCase):
|
|
|
assert group.message == event2.message
|
|
|
|
|
|
|
|
|
+class PlaceholderTitleTest(TestCase):
|
|
|
+ """
|
|
|
+ Tests for a bug where error events were interpreted as default-type events and therefore all
|
|
|
+ came out with a placeholder title.
|
|
|
+ """
|
|
|
+
|
|
|
+ def test_fixes_broken_title_data(self):
|
|
|
+ # An event before the bug was introduced
|
|
|
+ event1 = save_new_event(
|
|
|
+ {
|
|
|
+ "exception": {
|
|
|
+ "values": [{"type": "DogsAreNeverAnError", "value": "Dogs are great!"}],
|
|
|
+ },
|
|
|
+ # Use a fingerprint to guarantee all events end up in the same group
|
|
|
+ "fingerprint": ["adopt don't shop"],
|
|
|
+ },
|
|
|
+ self.project,
|
|
|
+ )
|
|
|
+
|
|
|
+ group = Group.objects.get(id=event1.group_id)
|
|
|
+
|
|
|
+ assert group.title == event1.title == "DogsAreNeverAnError: Dogs are great!"
|
|
|
+ assert group.data["title"] == event1.data["title"] == "DogsAreNeverAnError: Dogs are great!"
|
|
|
+ assert group.data["metadata"].get("title") is event1.data["metadata"].get("title") is None
|
|
|
+ assert group.message == "Dogs are great! DogsAreNeverAnError"
|
|
|
+
|
|
|
+ # Simulate the bug
|
|
|
+ with mock.patch(
|
|
|
+ "sentry.event_manager.get_event_type",
|
|
|
+ return_value=DefaultEvent(),
|
|
|
+ ):
|
|
|
+ # Neutralize the data fixes by making them unable to recognize a bad title and by
|
|
|
+ # unconditionally using the incoming title
|
|
|
+ with (
|
|
|
+ mock.patch(
|
|
|
+ "sentry.event_manager._is_placeholder_title",
|
|
|
+ return_value=False,
|
|
|
+ ),
|
|
|
+ mock.patch(
|
|
|
+ "sentry.event_manager._get_updated_group_title",
|
|
|
+ new=lambda existing_container, incoming_container: incoming_container.get(
|
|
|
+ "title"
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ):
|
|
|
+ event2 = save_new_event(
|
|
|
+ {
|
|
|
+ "exception": {
|
|
|
+ "values": [{"type": "DogsAreNeverAnError", "value": "Maisey is silly"}],
|
|
|
+ },
|
|
|
+ "fingerprint": ["adopt don't shop"],
|
|
|
+ },
|
|
|
+ self.project,
|
|
|
+ )
|
|
|
+
|
|
|
+ assert event2.group_id == event1.group_id
|
|
|
+
|
|
|
+ # Pull the group again to get updated data
|
|
|
+ group = Group.objects.get(id=event2.group_id)
|
|
|
+
|
|
|
+ # As expected, without the fixes, the bug screws up both the event and group data. (Compare
|
|
|
+ # this to the next test, where the fixes are left in place, and the group remains untouched.)
|
|
|
+ assert group.title == event2.title == "<unlabeled event>"
|
|
|
+ assert group.data["title"] == event2.data["title"] == "<unlabeled event>"
|
|
|
+ assert (
|
|
|
+ group.data["metadata"].get("title")
|
|
|
+ == event2.data["metadata"].get("title")
|
|
|
+ == "<unlabeled event>"
|
|
|
+ )
|
|
|
+ assert group.message == "<unlabeled event>"
|
|
|
+
|
|
|
+ # Now that we have a group with bad data, return to the current world - where the bug has
|
|
|
+ # been fixed and the data fix is also in place - and we can see that the group's data
|
|
|
+ # returns to what it should be
|
|
|
+ event3 = save_new_event(
|
|
|
+ {
|
|
|
+ "exception": {
|
|
|
+ "values": [{"type": "DogsAreNeverAnError", "value": "Charlie is goofy"}],
|
|
|
+ },
|
|
|
+ "fingerprint": ["adopt don't shop"],
|
|
|
+ },
|
|
|
+ self.project,
|
|
|
+ )
|
|
|
+
|
|
|
+ assert event3.group_id == event2.group_id == event1.group_id
|
|
|
+
|
|
|
+ # Pull the group again to get updated data
|
|
|
+ group = Group.objects.get(id=event3.group_id)
|
|
|
+
|
|
|
+ # Title data is updated with values from newest event, and is back to the structure it was
|
|
|
+ # before the bug
|
|
|
+ assert group.title == event3.title == "DogsAreNeverAnError: Charlie is goofy"
|
|
|
+ assert (
|
|
|
+ group.data["title"] == event3.data["title"] == "DogsAreNeverAnError: Charlie is goofy"
|
|
|
+ )
|
|
|
+ assert group.data["metadata"].get("title") is event3.data["metadata"].get("title") is None
|
|
|
+ assert group.message == "Charlie is goofy DogsAreNeverAnError"
|
|
|
+
|
|
|
+ # This is the same as the data-fixing test above, except that the fix is left in place when
|
|
|
+ # the bug happens, and so the bad titles never get saved on the group
|
|
|
+ def test_bug_regression_no_longer_breaks_titles(self):
|
|
|
+ # An event before the bug was introduced
|
|
|
+ event1 = save_new_event(
|
|
|
+ {
|
|
|
+ "exception": {
|
|
|
+ "values": [{"type": "DogsAreNeverAnError", "value": "Dogs are great!"}],
|
|
|
+ },
|
|
|
+ # Use a fingerprint to guarantee all events end up in the same group
|
|
|
+ "fingerprint": ["adopt don't shop"],
|
|
|
+ },
|
|
|
+ self.project,
|
|
|
+ )
|
|
|
+
|
|
|
+ group = Group.objects.get(id=event1.group_id)
|
|
|
+
|
|
|
+ assert group.title == event1.title == "DogsAreNeverAnError: Dogs are great!"
|
|
|
+ assert group.data["title"] == event1.data["title"] == "DogsAreNeverAnError: Dogs are great!"
|
|
|
+ assert group.data["metadata"].get("title") is event1.data["metadata"].get("title") is None
|
|
|
+ assert group.message == "Dogs are great! DogsAreNeverAnError"
|
|
|
+
|
|
|
+ # Simulate the bug, but with the fix in place
|
|
|
+ with mock.patch(
|
|
|
+ "sentry.event_manager.get_event_type",
|
|
|
+ return_value=DefaultEvent(),
|
|
|
+ ):
|
|
|
+ event2 = save_new_event(
|
|
|
+ {
|
|
|
+ "exception": {
|
|
|
+ "values": [{"type": "DogsAreNeverAnError", "value": "Maisey is silly"}],
|
|
|
+ },
|
|
|
+ "fingerprint": ["adopt don't shop"],
|
|
|
+ },
|
|
|
+ self.project,
|
|
|
+ )
|
|
|
+
|
|
|
+ assert event2.group_id == event1.group_id
|
|
|
+
|
|
|
+ # Pull the group again to get updated data
|
|
|
+ group = Group.objects.get(id=event2.group_id)
|
|
|
+
|
|
|
+ # The event may be messed up, but it didn't mess up the group
|
|
|
+ assert event2.title == "<unlabeled event>"
|
|
|
+ assert group.title == "DogsAreNeverAnError: Dogs are great!"
|
|
|
+ assert event2.data["title"] == "<unlabeled event>"
|
|
|
+ assert group.data["title"] == "DogsAreNeverAnError: Dogs are great!"
|
|
|
+ assert group.data["metadata"].get("title") is None
|
|
|
+ assert event2.data["metadata"].get("title") == "<unlabeled event>"
|
|
|
+ assert group.message == "Dogs are great! DogsAreNeverAnError"
|
|
|
+
|
|
|
+ # An event after the bug was fixed
|
|
|
+ event3 = save_new_event(
|
|
|
+ {
|
|
|
+ "exception": {
|
|
|
+ "values": [{"type": "DogsAreNeverAnError", "value": "Charlie is goofy"}],
|
|
|
+ },
|
|
|
+ "fingerprint": ["adopt don't shop"],
|
|
|
+ },
|
|
|
+ self.project,
|
|
|
+ )
|
|
|
+
|
|
|
+ assert event3.group_id == event2.group_id == event1.group_id
|
|
|
+
|
|
|
+ # Pull the group again to get updated data
|
|
|
+ group = Group.objects.get(id=event3.group_id)
|
|
|
+
|
|
|
+ # Title data is updated with values from newest event
|
|
|
+ assert group.title == event3.title == "DogsAreNeverAnError: Charlie is goofy"
|
|
|
+ assert (
|
|
|
+ group.data["title"] == event3.data["title"] == "DogsAreNeverAnError: Charlie is goofy"
|
|
|
+ )
|
|
|
+ assert group.data["metadata"].get("title") is event3.data["metadata"].get("title") is None
|
|
|
+ assert group.message == "Charlie is goofy DogsAreNeverAnError"
|
|
|
+
|
|
|
+
|
|
|
+@django_db_all
|
|
|
+@pytest.mark.parametrize(
|
|
|
+ ["existing_title", "incoming_title", "expected_title"],
|
|
|
+ [
|
|
|
+ ("Dogs are great!", "Adopt don't shop", "Adopt don't shop"),
|
|
|
+ ("Dogs are great!", "<untitled>", "Dogs are great!"),
|
|
|
+ ("Dogs are great!", None, "Dogs are great!"),
|
|
|
+ ("<unlabeled event>", "Adopt don't shop", "Adopt don't shop"),
|
|
|
+ ("<unlabeled event>", "<untitled>", "<untitled>"),
|
|
|
+ ("<unlabeled event>", None, None),
|
|
|
+ (None, "Adopt don't shop", "Adopt don't shop"),
|
|
|
+ (None, "<untitled>", None),
|
|
|
+ (None, None, None),
|
|
|
+ ],
|
|
|
+)
|
|
|
+def test_get_updated_group_title(existing_title, incoming_title, expected_title):
|
|
|
+ existing_data = {"title": existing_title} if existing_title is not None else {}
|
|
|
+ incoming_data = {"title": incoming_title} if incoming_title is not None else {}
|
|
|
+
|
|
|
+ assert _get_updated_group_title(existing_data, incoming_data) == expected_title
|
|
|
+
|
|
|
+
|
|
|
class EventManagerGroupingMetricsTest(TestCase):
|
|
|
@mock.patch("sentry.event_manager.metrics.incr")
|
|
|
def test_records_avg_calculations_per_event_metrics(self, mock_metrics_incr: MagicMock):
|