test_group.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. from __future__ import annotations
  2. import uuid
  3. from collections.abc import Sequence
  4. from unittest.mock import patch
  5. from sentry.eventstore.models import GroupEvent
  6. from sentry.issues.grouptype import PerformanceNPlusOneGroupType, ProfileFileIOGroupType
  7. from sentry.models.group import Group
  8. from sentry.testutils.cases import PerformanceIssueTestCase, SnubaTestCase, TestCase
  9. from sentry.testutils.helpers.datetime import before_now, iso_format
  10. from sentry.utils.samples import load_data
  11. from tests.sentry.issues.test_utils import OccurrenceTestMixin
  12. def _get_recommended_non_null(g: Group) -> GroupEvent:
  13. ret = g.get_recommended_event_for_environments()
  14. assert ret is not None
  15. return ret
  16. def _get_latest_non_null(g: Group, environments: Sequence[str] = ()) -> GroupEvent:
  17. ret = g.get_latest_event_for_environments(environments)
  18. assert ret is not None
  19. return ret
  20. def _get_oldest_non_null(g: Group, environments: Sequence[str] = ()) -> GroupEvent:
  21. ret = g.get_oldest_event_for_environments(environments)
  22. assert ret is not None
  23. return ret
  24. class GroupTestSnuba(TestCase, SnubaTestCase, PerformanceIssueTestCase, OccurrenceTestMixin):
  25. def test_get_oldest_latest_for_environments(self):
  26. project = self.create_project()
  27. min_ago = iso_format(before_now(minutes=1))
  28. self.store_event(
  29. data={
  30. "event_id": "a" * 32,
  31. "environment": "production",
  32. "timestamp": min_ago,
  33. "fingerprint": ["group-1"],
  34. },
  35. project_id=project.id,
  36. )
  37. self.store_event(
  38. data={
  39. "event_id": "b" * 32,
  40. "environment": "production",
  41. "timestamp": min_ago,
  42. "fingerprint": ["group-1"],
  43. },
  44. project_id=project.id,
  45. )
  46. self.store_event(
  47. data={"event_id": "c" * 32, "timestamp": min_ago, "fingerprint": ["group-1"]},
  48. project_id=project.id,
  49. )
  50. group = Group.objects.get()
  51. assert _get_latest_non_null(group).event_id == "c" * 32
  52. assert group.get_latest_event_for_environments(["staging"]) is None
  53. assert _get_latest_non_null(group, ["production"]).event_id == "b" * 32
  54. assert _get_oldest_non_null(group).event_id == "a" * 32
  55. assert _get_oldest_non_null(group, ["staging", "production"]).event_id == "a" * 32
  56. assert group.get_oldest_event_for_environments(["staging"]) is None
  57. def test_perf_issue(self):
  58. group_fingerprint = f"{PerformanceNPlusOneGroupType.type_id}-group1"
  59. event_data_1 = load_data("transaction-n-plus-one", fingerprint=[group_fingerprint])
  60. event_data_1["timestamp"] = iso_format(before_now(seconds=10))
  61. event_data_1["start_timestamp"] = iso_format(before_now(seconds=11))
  62. event_data_1["event_id"] = "d" * 32
  63. event_data_2 = load_data("transaction-n-plus-one", fingerprint=[group_fingerprint])
  64. event_data_2["timestamp"] = iso_format(before_now(seconds=20))
  65. event_data_2["start_timestamp"] = iso_format(before_now(seconds=21))
  66. event_data_2["event_id"] = "f" * 32
  67. event_data_3 = load_data("transaction-n-plus-one", fingerprint=[group_fingerprint])
  68. event_data_3["timestamp"] = iso_format(before_now(seconds=30))
  69. event_data_3["start_timestamp"] = iso_format(before_now(seconds=31))
  70. event_data_3["event_id"] = "f" * 32
  71. transaction_event_1 = self.create_performance_issue(
  72. event_data=event_data_1, fingerprint=group_fingerprint
  73. )
  74. self.create_performance_issue(event_data=event_data_2, fingerprint=group_fingerprint)
  75. self.create_performance_issue(event_data=event_data_3, fingerprint=group_fingerprint)
  76. perf_group = transaction_event_1.group
  77. assert _get_latest_non_null(perf_group).event_id == "d" * 32
  78. assert _get_oldest_non_null(perf_group).event_id == "f" * 32
  79. def test_error_issue_get_helpful_for_environments(self):
  80. project = self.create_project()
  81. min_ago = iso_format(before_now(minutes=1))
  82. replay_id = uuid.uuid4().hex
  83. event_all_helpful_params = self.store_event(
  84. data={
  85. "event_id": "a" * 32,
  86. "timestamp": min_ago,
  87. "fingerprint": ["group-1"],
  88. "contexts": {
  89. "replay": {"replay_id": replay_id},
  90. "trace": {
  91. "sampled": True,
  92. "span_id": "babaae0d4b7512d9",
  93. "trace_id": "a7d67cf796774551a95be6543cacd459",
  94. },
  95. },
  96. "errors": [],
  97. },
  98. project_id=project.id,
  99. assert_no_errors=False,
  100. )
  101. self.store_event(
  102. data={
  103. "event_id": "b" * 32,
  104. "timestamp": min_ago,
  105. "fingerprint": ["group-1"],
  106. "contexts": {
  107. "replay": {"replay_id": replay_id},
  108. },
  109. "errors": [{"type": "one"}, {"type": "two"}],
  110. },
  111. project_id=project.id,
  112. assert_no_errors=False,
  113. )
  114. event_none_helpful_params = self.store_event(
  115. data={"event_id": "c" * 32, "timestamp": min_ago, "fingerprint": ["group-1"]},
  116. project_id=project.id,
  117. )
  118. group = Group.objects.get()
  119. assert _get_recommended_non_null(group).event_id == event_all_helpful_params.event_id
  120. assert _get_latest_non_null(group).event_id == event_none_helpful_params.event_id
  121. assert _get_oldest_non_null(group).event_id == event_all_helpful_params.event_id
  122. def test_perf_issue_helpful(self):
  123. group_fingerprint = f"{PerformanceNPlusOneGroupType.type_id}-group1"
  124. transaction_event_data_with_all_helpful = load_data(
  125. "transaction-n-plus-one", fingerprint=[group_fingerprint]
  126. )
  127. transaction_event_data_with_all_helpful["timestamp"] = iso_format(before_now(seconds=10))
  128. transaction_event_data_with_all_helpful["start_timestamp"] = iso_format(
  129. before_now(seconds=11)
  130. )
  131. transaction_event_data_with_all_helpful["event_id"] = "d" * 32
  132. transaction_event_data_with_all_helpful["contexts"].update(
  133. {"profile": {"profile_id": uuid.uuid4().hex}}
  134. )
  135. transaction_event_data_with_all_helpful["contexts"].update(
  136. {"replay": {"replay_id": uuid.uuid4().hex}}
  137. )
  138. transaction_event_data_with_all_helpful["contexts"]["trace"]["sampled"] = True
  139. transaction_event_data_with_all_helpful["errors"] = []
  140. transaction_event_data_with_none_helpful = load_data(
  141. "transaction-n-plus-one", fingerprint=[group_fingerprint]
  142. )
  143. transaction_event_data_with_none_helpful["timestamp"] = iso_format(before_now(seconds=20))
  144. transaction_event_data_with_none_helpful["start_timestamp"] = iso_format(
  145. before_now(seconds=21)
  146. )
  147. transaction_event_data_with_none_helpful["event_id"] = "f" * 32
  148. transaction_event_1 = self.create_performance_issue(
  149. event_data=transaction_event_data_with_all_helpful, fingerprint=group_fingerprint
  150. )
  151. transaction_event_2 = self.create_performance_issue(
  152. event_data=transaction_event_data_with_none_helpful, fingerprint=group_fingerprint
  153. )
  154. perf_group = transaction_event_1.group
  155. assert _get_recommended_non_null(perf_group).event_id == transaction_event_1.event_id
  156. assert _get_latest_non_null(perf_group).event_id == transaction_event_1.event_id
  157. assert _get_oldest_non_null(perf_group).event_id == transaction_event_2.event_id
  158. def test_profile_issue_helpful(self):
  159. event_id_1 = uuid.uuid4().hex
  160. occurrence, _ = self.process_occurrence(
  161. event_id=event_id_1,
  162. project_id=self.project.id,
  163. event_data={
  164. "fingerprint": ["group-1"],
  165. "timestamp": before_now(minutes=2).isoformat(),
  166. "contexts": {
  167. "profile": {"profile_id": uuid.uuid4().hex},
  168. "replay": {"replay_id": uuid.uuid4().hex},
  169. "trace": {
  170. "sampled": True,
  171. "span_id": "babaae0d4b7512d9",
  172. "trace_id": "a7d67cf796774551a95be6543cacd459",
  173. },
  174. },
  175. "errors": [],
  176. },
  177. )
  178. event_id_2 = uuid.uuid4().hex
  179. occurrence_2, _ = self.process_occurrence(
  180. event_id=event_id_2,
  181. project_id=self.project.id,
  182. event_data={
  183. "fingerprint": ["group-1"],
  184. "timestamp": before_now(minutes=1).isoformat(),
  185. },
  186. )
  187. group = Group.objects.get()
  188. group.update(type=ProfileFileIOGroupType.type_id)
  189. group_event = _get_recommended_non_null(group)
  190. assert group_event.event_id == occurrence.event_id
  191. self.assert_occurrences_identical(group_event.occurrence, occurrence)
  192. assert _get_latest_non_null(group).event_id == occurrence_2.event_id
  193. assert _get_oldest_non_null(group).event_id == occurrence.event_id
  194. @patch("sentry.quotas.backend.get_event_retention")
  195. def test_get_recommended_event_for_environments_retention_limit(self, mock_get_event_retention):
  196. """
  197. If last_seen is outside of the retention limit, falls back to the latest event behavior.
  198. """
  199. mock_get_event_retention.return_value = 90
  200. project = self.create_project()
  201. outside_retention_date = before_now(days=91)
  202. event = self.store_event(
  203. data={
  204. "event_id": "a" * 32,
  205. "timestamp": iso_format(outside_retention_date),
  206. "fingerprint": ["group-1"],
  207. "contexts": {},
  208. "errors": [],
  209. },
  210. project_id=project.id,
  211. assert_no_errors=False,
  212. )
  213. group = Group.objects.get()
  214. group.last_seen = before_now(days=91)
  215. assert _get_recommended_non_null(group).event_id == event.event_id