test_project_event_details.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  1. from django.urls import reverse
  2. from sentry.issues.occurrence_consumer import process_event_and_issue_occurrence
  3. from sentry.testutils import APITestCase, SnubaTestCase
  4. from sentry.testutils.cases import PerformanceIssueTestCase
  5. from sentry.testutils.helpers.datetime import before_now, iso_format
  6. from sentry.testutils.silo import region_silo_test
  7. from sentry.utils.samples import load_data
  8. from tests.sentry.issues.test_utils import OccurrenceTestMixin
  9. @region_silo_test
  10. class ProjectEventDetailsTest(APITestCase, SnubaTestCase):
  11. def setUp(self):
  12. super().setUp()
  13. self.login_as(user=self.user)
  14. self.setup_data()
  15. def setup_data(self):
  16. one_min_ago = iso_format(before_now(minutes=1))
  17. two_min_ago = iso_format(before_now(minutes=2))
  18. three_min_ago = iso_format(before_now(minutes=3))
  19. four_min_ago = iso_format(before_now(minutes=4))
  20. self.prev_event = self.store_event(
  21. data={"event_id": "a" * 32, "timestamp": four_min_ago, "fingerprint": ["group-1"]},
  22. project_id=self.project.id,
  23. )
  24. self.cur_event = self.store_event(
  25. data={"event_id": "b" * 32, "timestamp": three_min_ago, "fingerprint": ["group-1"]},
  26. project_id=self.project.id,
  27. )
  28. self.next_event = self.store_event(
  29. data={
  30. "event_id": "c" * 32,
  31. "timestamp": two_min_ago,
  32. "fingerprint": ["group-1"],
  33. "environment": "production",
  34. "tags": {"environment": "production"},
  35. },
  36. project_id=self.project.id,
  37. )
  38. self.cur_group = self.next_event.group
  39. # Event in different group
  40. self.store_event(
  41. data={
  42. "event_id": "d" * 32,
  43. "timestamp": one_min_ago,
  44. "fingerprint": ["group-2"],
  45. "environment": "production",
  46. "tags": {"environment": "production"},
  47. },
  48. project_id=self.project.id,
  49. )
  50. def test_simple(self):
  51. url = reverse(
  52. "sentry-api-0-project-event-details",
  53. kwargs={
  54. "event_id": self.cur_event.event_id,
  55. "project_slug": self.project.slug,
  56. "organization_slug": self.project.organization.slug,
  57. },
  58. )
  59. response = self.client.get(url, format="json")
  60. assert response.status_code == 200, response.content
  61. assert response.data["id"] == str(self.cur_event.event_id)
  62. assert response.data["nextEventID"] == str(self.next_event.event_id)
  63. assert response.data["previousEventID"] == str(self.prev_event.event_id)
  64. assert response.data["groupID"] == str(self.cur_group.id)
  65. def test_snuba_no_prev(self):
  66. url = reverse(
  67. "sentry-api-0-project-event-details",
  68. kwargs={
  69. "event_id": self.prev_event.event_id,
  70. "project_slug": self.project.slug,
  71. "organization_slug": self.project.organization.slug,
  72. },
  73. )
  74. response = self.client.get(url, format="json")
  75. assert response.status_code == 200, response.content
  76. assert response.data["id"] == str(self.prev_event.event_id)
  77. assert response.data["previousEventID"] is None
  78. assert response.data["nextEventID"] == self.cur_event.event_id
  79. assert response.data["groupID"] == str(self.cur_group.id)
  80. def test_snuba_with_environment(self):
  81. url = reverse(
  82. "sentry-api-0-project-event-details",
  83. kwargs={
  84. "event_id": self.cur_event.event_id,
  85. "project_slug": self.project.slug,
  86. "organization_slug": self.project.organization.slug,
  87. },
  88. )
  89. response = self.client.get(
  90. url, format="json", data={"environment": ["production", "staging"]}
  91. )
  92. assert response.status_code == 200, response.content
  93. assert response.data["id"] == str(self.cur_event.event_id)
  94. assert response.data["previousEventID"] is None
  95. assert response.data["nextEventID"] == self.next_event.event_id
  96. assert response.data["groupID"] == str(self.cur_group.id)
  97. def test_ignores_different_group(self):
  98. url = reverse(
  99. "sentry-api-0-project-event-details",
  100. kwargs={
  101. "event_id": self.next_event.event_id,
  102. "project_slug": self.project.slug,
  103. "organization_slug": self.project.organization.slug,
  104. },
  105. )
  106. response = self.client.get(url, format="json")
  107. assert response.status_code == 200, response.content
  108. assert response.data["id"] == str(self.next_event.event_id)
  109. assert response.data["nextEventID"] is None
  110. @region_silo_test
  111. class ProjectEventDetailsGenericTest(OccurrenceTestMixin, ProjectEventDetailsTest):
  112. def setup_data(self):
  113. one_min_ago = iso_format(before_now(minutes=1))
  114. two_min_ago = iso_format(before_now(minutes=2))
  115. three_min_ago = iso_format(before_now(minutes=3))
  116. four_min_ago = iso_format(before_now(minutes=4))
  117. prev_event_id = "a" * 32
  118. self.prev_event, prev_group_info = process_event_and_issue_occurrence(
  119. self.build_occurrence_data(
  120. event_id=prev_event_id, project_id=self.project.id, fingerprint=["group-1"]
  121. ),
  122. {
  123. "event_id": prev_event_id,
  124. "project_id": self.project.id,
  125. "timestamp": four_min_ago,
  126. "message_timestamp": four_min_ago,
  127. },
  128. )
  129. cur_event_id = "b" * 32
  130. self.cur_event, cur_group_info = process_event_and_issue_occurrence(
  131. self.build_occurrence_data(
  132. event_id=cur_event_id, project_id=self.project.id, fingerprint=["group-1"]
  133. ),
  134. {
  135. "event_id": cur_event_id,
  136. "project_id": self.project.id,
  137. "timestamp": three_min_ago,
  138. "message_timestamp": three_min_ago,
  139. },
  140. )
  141. self.cur_group = cur_group_info.group
  142. next_event_id = "c" * 32
  143. self.next_event, next_group_info = process_event_and_issue_occurrence(
  144. self.build_occurrence_data(
  145. event_id=next_event_id, project_id=self.project.id, fingerprint=["group-1"]
  146. ),
  147. {
  148. "event_id": next_event_id,
  149. "project_id": self.project.id,
  150. "timestamp": two_min_ago,
  151. "message_timestamp": two_min_ago,
  152. "tags": {"environment": "production"},
  153. },
  154. )
  155. unrelated_event_id = "d" * 32
  156. process_event_and_issue_occurrence(
  157. self.build_occurrence_data(
  158. event_id=unrelated_event_id, project_id=self.project.id, fingerprint=["group-2"]
  159. ),
  160. {
  161. "event_id": unrelated_event_id,
  162. "project_id": self.project.id,
  163. "timestamp": one_min_ago,
  164. "message_timestamp": one_min_ago,
  165. "tags": {"environment": "production"},
  166. },
  167. )[0]
  168. def test_generic_event_with_occurrence(self):
  169. url = reverse(
  170. "sentry-api-0-project-event-details",
  171. kwargs={
  172. "event_id": self.cur_event.event_id,
  173. "project_slug": self.project.slug,
  174. "organization_slug": self.project.organization.slug,
  175. },
  176. )
  177. response = self.client.get(url, format="json", data={"group_id": self.cur_group.id})
  178. assert response.status_code == 200, response.content
  179. assert response.data["id"] == self.cur_event.event_id
  180. assert response.data["occurrence"] is not None
  181. assert response.data["occurrence"]["id"] == self.cur_event.id
  182. @region_silo_test
  183. class ProjectEventDetailsTransactionTest(APITestCase, SnubaTestCase, PerformanceIssueTestCase):
  184. def setUp(self):
  185. super().setUp()
  186. self.login_as(user=self.user)
  187. project = self.create_project()
  188. one_min_ago = before_now(minutes=1)
  189. two_min_ago = before_now(minutes=2)
  190. three_min_ago = before_now(minutes=3)
  191. four_min_ago = before_now(minutes=4)
  192. self.prev_transaction_event = self.create_performance_issue(
  193. event_data=load_data(
  194. event_id="a" * 32,
  195. platform="transaction-n-plus-one",
  196. timestamp=four_min_ago,
  197. start_timestamp=four_min_ago,
  198. ),
  199. project_id=project.id,
  200. )
  201. self.group = self.prev_transaction_event.group
  202. self.cur_transaction_event = self.create_performance_issue(
  203. event_data=load_data(
  204. event_id="b" * 32,
  205. platform="transaction-n-plus-one",
  206. timestamp=three_min_ago,
  207. start_timestamp=three_min_ago,
  208. ),
  209. project_id=project.id,
  210. )
  211. self.next_transaction_event = self.create_performance_issue(
  212. event_data=load_data(
  213. event_id="c" * 32,
  214. platform="transaction-n-plus-one",
  215. timestamp=two_min_ago,
  216. start_timestamp=two_min_ago,
  217. ),
  218. project_id=project.id,
  219. )
  220. self.create_performance_issue(
  221. event_data=load_data(
  222. event_id="d" * 32,
  223. platform="transaction-n-plus-one",
  224. timestamp=one_min_ago,
  225. start_timestamp=one_min_ago,
  226. ),
  227. fingerprint="other_group",
  228. project_id=project.id,
  229. )
  230. def test_transaction_event(self):
  231. """Test that you can look up a transaction event w/ a prev and next event"""
  232. url = reverse(
  233. "sentry-api-0-project-event-details",
  234. kwargs={
  235. "event_id": self.cur_transaction_event.event_id,
  236. "project_slug": self.cur_transaction_event.project.slug,
  237. "organization_slug": self.cur_transaction_event.project.organization.slug,
  238. },
  239. )
  240. response = self.client.get(url, format="json", data={"group_id": self.group.id})
  241. assert response.status_code == 200, response.content
  242. assert response.data["id"] == str(self.cur_transaction_event.event_id)
  243. assert response.data["nextEventID"] == str(self.next_transaction_event.event_id)
  244. assert response.data["previousEventID"] == str(self.prev_transaction_event.event_id)
  245. assert response.data["groupID"] == str(self.cur_transaction_event.group.id)
  246. def test_no_previous_event(self):
  247. """Test the case in which there is no previous event"""
  248. url = reverse(
  249. "sentry-api-0-project-event-details",
  250. kwargs={
  251. "event_id": self.prev_transaction_event.event_id,
  252. "project_slug": self.prev_transaction_event.project.slug,
  253. "organization_slug": self.prev_transaction_event.project.organization.slug,
  254. },
  255. )
  256. response = self.client.get(url, format="json", data={"group_id": self.group.id})
  257. assert response.status_code == 200, response.content
  258. assert response.data["id"] == str(self.prev_transaction_event.event_id)
  259. assert response.data["previousEventID"] is None
  260. assert response.data["nextEventID"] == self.cur_transaction_event.event_id
  261. assert response.data["groupID"] == str(self.prev_transaction_event.group.id)
  262. def test_ignores_different_group(self):
  263. """Test that a different group's events aren't attributed to the one that was passed"""
  264. url = reverse(
  265. "sentry-api-0-project-event-details",
  266. kwargs={
  267. "event_id": self.next_transaction_event.event_id,
  268. "project_slug": self.next_transaction_event.project.slug,
  269. "organization_slug": self.next_transaction_event.project.organization.slug,
  270. },
  271. )
  272. response = self.client.get(url, format="json", data={"group_id": self.group.id})
  273. assert response.status_code == 200, response.content
  274. assert response.data["id"] == str(self.next_transaction_event.event_id)
  275. assert response.data["nextEventID"] is None
  276. def test_no_group_id(self):
  277. """Test the case where a group_id was not passed"""
  278. url = reverse(
  279. "sentry-api-0-project-event-details",
  280. kwargs={
  281. "event_id": self.cur_transaction_event.event_id,
  282. "project_slug": self.cur_transaction_event.project.slug,
  283. "organization_slug": self.cur_transaction_event.project.organization.slug,
  284. },
  285. )
  286. response = self.client.get(url, format="json")
  287. assert response.status_code == 200, response.content
  288. assert response.data["id"] == str(self.cur_transaction_event.event_id)
  289. assert response.data["previousEventID"] is None
  290. assert response.data["nextEventID"] is None
  291. assert response.data["groupID"] is None
  292. @region_silo_test
  293. class ProjectEventJsonEndpointTest(APITestCase, SnubaTestCase):
  294. def setUp(self):
  295. super().setUp()
  296. self.login_as(user=self.user)
  297. self.event_id = "c" * 32
  298. self.fingerprint = ["group_2"]
  299. self.min_ago = iso_format(before_now(minutes=1))
  300. self.event = self.store_event(
  301. data={
  302. "event_id": self.event_id,
  303. "timestamp": self.min_ago,
  304. "fingerprint": self.fingerprint,
  305. "user": {"email": self.user.email},
  306. },
  307. project_id=self.project.id,
  308. )
  309. self.url = reverse(
  310. "sentry-api-0-event-json",
  311. kwargs={
  312. "organization_slug": self.organization.slug,
  313. "project_slug": self.project.slug,
  314. "event_id": self.event_id,
  315. },
  316. )
  317. def assert_event(self, data):
  318. assert data["event_id"] == self.event_id
  319. assert data["user"]["email"] == self.user.email
  320. assert data["datetime"][:19] == self.min_ago
  321. assert data["fingerprint"] == self.fingerprint
  322. def test_simple(self):
  323. response = self.client.get(self.url, format="json")
  324. assert response.status_code == 200, response.content
  325. self.assert_event(response.data)
  326. def test_event_does_not_exist(self):
  327. self.url = reverse(
  328. "sentry-api-0-event-json",
  329. kwargs={
  330. "organization_slug": self.organization.slug,
  331. "project_slug": self.project.slug,
  332. "event_id": "no" * 16,
  333. },
  334. )
  335. response = self.client.get(self.url, format="json")
  336. assert response.status_code == 404, response.content
  337. assert response.data == {"detail": "Event not found"}
  338. def test_user_unauthorized(self):
  339. user = self.create_user()
  340. self.login_as(user)
  341. response = self.client.get(self.url, format="json")
  342. assert response.status_code == 403, response.content
  343. assert response.data == {"detail": "You do not have permission to perform this action."}
  344. def test_project_not_associated_with_event(self):
  345. project2 = self.create_project(organization=self.organization)
  346. url = reverse(
  347. "sentry-api-0-event-json",
  348. kwargs={
  349. "organization_slug": self.organization.slug,
  350. "project_slug": project2.slug,
  351. "event_id": self.event_id,
  352. },
  353. )
  354. response = self.client.get(url, format="json")
  355. assert response.status_code == 404, response.content
  356. assert response.data == {"detail": "Event not found"}