test_project_event_details.py 15 KB

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