test_process_issue_event.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import uuid
  2. from django.urls import reverse
  3. from apps.issue_events.constants import EventStatus
  4. from apps.issue_events.models import Issue, IssueEvent, IssueHash
  5. from ..process_event import process_issue_events
  6. from ..schema import ErrorIssueEventSchema, InterchangeIssueEvent, IssueEventSchema
  7. from .utils import EventIngestTestCase
  8. COMPAT_TEST_DATA_DIR = "events/test_data"
  9. class IssueEventIngestTestCase(EventIngestTestCase):
  10. """
  11. These tests bypass the API and celery. They test the event ingest logic itself.
  12. This file should be large are test the following use cases
  13. - Multiple event saved at the same time
  14. - Sentry API compatibility
  15. - Default, Error, and CSP types
  16. - Graceful failure such as duplicate event ids or invalid data
  17. """
  18. def test_two_events(self):
  19. events = []
  20. for _ in range(2):
  21. payload = IssueEventSchema()
  22. events.append(
  23. InterchangeIssueEvent(project_id=self.project.id, payload=payload)
  24. )
  25. with self.assertNumQueries(12):
  26. process_issue_events(events)
  27. self.assertEqual(Issue.objects.count(), 1)
  28. self.assertEqual(IssueHash.objects.count(), 1)
  29. self.assertEqual(IssueEvent.objects.count(), 2)
  30. def test_reopen_resolved_issue(self):
  31. event = InterchangeIssueEvent(
  32. project_id=self.project.id, payload=IssueEventSchema()
  33. )
  34. process_issue_events([event])
  35. issue = Issue.objects.first()
  36. issue.status = EventStatus.RESOLVED
  37. issue.save()
  38. event.event_id = uuid.uuid4().hex
  39. process_issue_events([event])
  40. issue.refresh_from_db()
  41. self.assertEqual(issue.status, EventStatus.UNRESOLVED)
  42. class SentryCompatTestCase(IssueEventIngestTestCase):
  43. """
  44. These tests specifically test former open source sentry compatibility
  45. But otherwise are part of issue event ingest testing
  46. """
  47. def setUp(self):
  48. super().setUp()
  49. self.create_logged_in_user()
  50. def get_json_test_data(self, name: str):
  51. """Get incoming event, sentry json, sentry api event"""
  52. event = self.get_json_data(
  53. f"{COMPAT_TEST_DATA_DIR}/incoming_events/{name}.json"
  54. )
  55. sentry_json = self.get_json_data(
  56. f"{COMPAT_TEST_DATA_DIR}/oss_sentry_json/{name}.json"
  57. )
  58. # Force captured test data to match test generated data
  59. sentry_json["project"] = self.project.id
  60. api_sentry_event = self.get_json_data(
  61. f"{COMPAT_TEST_DATA_DIR}/oss_sentry_events/{name}.json"
  62. )
  63. return event, sentry_json, api_sentry_event
  64. # Upgrade functions handle intentional differences between GlitchTip and Sentry OSS
  65. def upgrade_title(self, value: str):
  66. """Sentry OSS uses ... while GlitchTip uses unicode …"""
  67. if value[-1] == "…":
  68. return value[:-4]
  69. return value.strip("...")
  70. def upgrade_metadata(self, value: dict):
  71. value["title"] = self.upgrade_title(value["title"])
  72. return value
  73. def assertCompareData(self, data1: dict, data2: dict, fields: list[str]):
  74. """Compare data of two dict objects. Compare only provided fields list"""
  75. for field in fields:
  76. field_value1 = data1.get(field)
  77. field_value2 = data2.get(field)
  78. if field == "title" and isinstance(field_value1, str):
  79. field_value1 = self.upgrade_title(field_value1)
  80. if field_value2:
  81. field_value2 = self.upgrade_title(field_value2)
  82. if (
  83. field == "metadata"
  84. and isinstance(field_value1, dict)
  85. and field_value1.get("title")
  86. ):
  87. field_value1 = self.upgrade_metadata(field_value1)
  88. if field_value2:
  89. field_value2 = self.upgrade_metadata(field_value2)
  90. self.assertEqual(
  91. field_value1,
  92. field_value2,
  93. f"Failed for field '{field}'",
  94. )
  95. def get_project_events_detail(self, event_id: str):
  96. return reverse(
  97. "api:project_issue_event_retrieve",
  98. kwargs={
  99. "organization_slug": self.project.organization.slug,
  100. "project_slug": self.project.slug,
  101. "event_id": event_id,
  102. },
  103. )
  104. def test_template_error(self):
  105. sdk_error, sentry_json, sentry_data = self.get_json_test_data(
  106. "django_template_error"
  107. )
  108. event = InterchangeIssueEvent(
  109. event_id=sdk_error["event_id"],
  110. project_id=self.project.id,
  111. payload=ErrorIssueEventSchema(**sdk_error),
  112. )
  113. process_issue_events([event])
  114. event = IssueEvent.objects.get(pk=event.event_id)
  115. url = self.get_project_events_detail(event.id.hex)
  116. res = self.client.get(url)
  117. self.assertEqual(res.status_code, 200)
  118. # self.assertCompareData(res.json(), sentry_data, ["culprit", "title", "metadata"])
  119. # res_frames = res.data["entries"][0]["data"]["values"][0]["stacktrace"]["frames"]
  120. # frames = sentry_data["entries"][0]["data"]["values"][0]["stacktrace"]["frames"]
  121. # for i in range(6):
  122. # # absPath don't always match - needs fixed
  123. # self.assertCompareData(res_frames[i], frames[i], ["absPath"])
  124. # for res_frame, frame in zip(res_frames, frames):
  125. # self.assertCompareData(
  126. # res_frame,
  127. # frame,
  128. # ["lineNo", "function", "filename", "module", "context"],
  129. # )
  130. # if frame.get("vars"):
  131. # self.assertCompareData(
  132. # res_frame["vars"], frame["vars"], ["exc", "request"]
  133. # )
  134. # if frame["vars"].get("get_response"):
  135. # # Memory address is different, truncate it
  136. # self.assertEqual(
  137. # res_frame["vars"]["get_response"][:-16],
  138. # frame["vars"]["get_response"][:-16],
  139. # )
  140. # self.assertCompareData(
  141. # res.data["entries"][0]["data"],
  142. # sentry_data["entries"][0]["data"],
  143. # ["env", "headers", "url", "method", "inferredContentType"],
  144. # )
  145. # url = reverse("issue-detail", kwargs={"pk": event.issue.pk})
  146. # res = self.client.get(url)
  147. # self.assertEqual(res.status_code, 200)
  148. # data = self.get_json_data("events/test_data/django_template_error_issue.json")
  149. # self.assertCompareData(res.data, data, ["title", "metadata"])