test_project_event_details.py 16 KB

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