test_organization_event_details.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. from datetime import timedelta
  2. from django.urls import NoReverseMatch, reverse
  3. from sentry.models import Group
  4. from sentry.testutils import APITestCase, SnubaTestCase
  5. from sentry.testutils.helpers.datetime import before_now, iso_format
  6. from sentry.utils.samples import load_data
  7. def format_project_event(project_slug, event_id):
  8. return f"{project_slug}:{event_id}"
  9. class OrganizationEventDetailsEndpointTest(APITestCase, SnubaTestCase):
  10. def setUp(self):
  11. super().setUp()
  12. min_ago = iso_format(before_now(minutes=1))
  13. two_min_ago = iso_format(before_now(minutes=2))
  14. three_min_ago = iso_format(before_now(minutes=3))
  15. self.login_as(user=self.user)
  16. self.project = self.create_project()
  17. self.project_2 = self.create_project()
  18. self.store_event(
  19. data={
  20. "event_id": "a" * 32,
  21. "message": "oh no",
  22. "timestamp": three_min_ago,
  23. "fingerprint": ["group-1"],
  24. },
  25. project_id=self.project.id,
  26. )
  27. self.store_event(
  28. data={
  29. "event_id": "b" * 32,
  30. "message": "very bad",
  31. "timestamp": two_min_ago,
  32. "fingerprint": ["group-1"],
  33. },
  34. project_id=self.project.id,
  35. )
  36. self.store_event(
  37. data={
  38. "event_id": "c" * 32,
  39. "message": "very bad",
  40. "timestamp": min_ago,
  41. "fingerprint": ["group-2"],
  42. },
  43. project_id=self.project.id,
  44. )
  45. self.groups = list(Group.objects.all().order_by("id"))
  46. def test_performance_flag(self):
  47. url = reverse(
  48. "sentry-api-0-organization-event-details",
  49. kwargs={
  50. "organization_slug": self.project.organization.slug,
  51. "project_slug": self.project.slug,
  52. "event_id": "a" * 32,
  53. },
  54. )
  55. with self.feature(
  56. {"organizations:discover-basic": False, "organizations:performance-view": True}
  57. ):
  58. response = self.client.get(url, format="json")
  59. assert response.status_code == 200, response.content
  60. assert response.data["id"] == "a" * 32
  61. assert response.data["projectSlug"] == self.project.slug
  62. def test_simple(self):
  63. url = reverse(
  64. "sentry-api-0-organization-event-details",
  65. kwargs={
  66. "organization_slug": self.project.organization.slug,
  67. "project_slug": self.project.slug,
  68. "event_id": "a" * 32,
  69. },
  70. )
  71. with self.feature("organizations:discover-basic"):
  72. response = self.client.get(url, format="json")
  73. assert response.status_code == 200, response.content
  74. assert response.data["id"] == "a" * 32
  75. assert response.data["projectSlug"] == self.project.slug
  76. def test_simple_transaction(self):
  77. min_ago = iso_format(before_now(minutes=1))
  78. event = self.store_event(
  79. data={
  80. "event_id": "d" * 32,
  81. "type": "transaction",
  82. "transaction": "api.issue.delete",
  83. "spans": [],
  84. "contexts": {"trace": {"op": "foobar", "trace_id": "a" * 32, "span_id": "a" * 16}},
  85. "start_timestamp": iso_format(before_now(minutes=1, seconds=5)),
  86. "timestamp": min_ago,
  87. },
  88. project_id=self.project.id,
  89. )
  90. url = reverse(
  91. "sentry-api-0-organization-event-details",
  92. kwargs={
  93. "organization_slug": self.project.organization.slug,
  94. "project_slug": self.project.slug,
  95. "event_id": event.event_id,
  96. },
  97. )
  98. with self.feature("organizations:discover-basic"):
  99. response = self.client.get(url, format="json")
  100. assert response.status_code == 200
  101. assert response.data["id"] == "d" * 32
  102. assert response.data["type"] == "transaction"
  103. def test_no_access_missing_feature(self):
  104. with self.feature({"organizations:discover-basic": False}):
  105. url = reverse(
  106. "sentry-api-0-organization-event-details",
  107. kwargs={
  108. "organization_slug": self.project.organization.slug,
  109. "project_slug": self.project.slug,
  110. "event_id": "a" * 32,
  111. },
  112. )
  113. response = self.client.get(url, format="json")
  114. assert response.status_code == 404, response.content
  115. def test_access_non_member_project(self):
  116. # Add a new user to a project and then access events on project they are not part of.
  117. member_user = self.create_user()
  118. team = self.create_team(members=[member_user])
  119. self.create_project(organization=self.organization, teams=[team])
  120. # Enable open membership
  121. self.organization.flags.allow_joinleave = True
  122. self.organization.save()
  123. self.login_as(member_user)
  124. url = reverse(
  125. "sentry-api-0-organization-event-details",
  126. kwargs={
  127. "organization_slug": self.organization.slug,
  128. "project_slug": self.project.slug,
  129. "event_id": "a" * 32,
  130. },
  131. )
  132. with self.feature("organizations:discover-basic"):
  133. response = self.client.get(url, format="json")
  134. assert response.status_code == 200, response.content
  135. # When open membership is off, access should be denied to non owner users
  136. self.organization.flags.allow_joinleave = False
  137. self.organization.save()
  138. with self.feature("organizations:discover-basic"):
  139. response = self.client.get(url, format="json")
  140. assert response.status_code == 404, response.content
  141. def test_no_event(self):
  142. url = reverse(
  143. "sentry-api-0-organization-event-details",
  144. kwargs={
  145. "organization_slug": self.project.organization.slug,
  146. "project_slug": self.project.slug,
  147. "event_id": "d" * 32,
  148. },
  149. )
  150. with self.feature("organizations:discover-basic"):
  151. response = self.client.get(url, format="json")
  152. assert response.status_code == 404, response.content
  153. def test_invalid_event_id(self):
  154. with self.assertRaises(NoReverseMatch):
  155. reverse(
  156. "sentry-api-0-organization-event-details",
  157. kwargs={
  158. "organization_slug": self.project.organization.slug,
  159. "project_slug": self.project.slug,
  160. "event_id": "not-an-event",
  161. },
  162. )
  163. def test_long_trace_description(self):
  164. data = load_data("transaction")
  165. data["event_id"] = "d" * 32
  166. data["timestamp"] = iso_format(before_now(minutes=1))
  167. data["start_timestamp"] = iso_format(before_now(minutes=1) - timedelta(seconds=5))
  168. data["contexts"]["trace"]["description"] = "b" * 512
  169. self.store_event(data=data, project_id=self.project.id)
  170. url = reverse(
  171. "sentry-api-0-organization-event-details",
  172. kwargs={
  173. "organization_slug": self.project.organization.slug,
  174. "project_slug": self.project.slug,
  175. "event_id": "d" * 32,
  176. },
  177. )
  178. with self.feature("organizations:discover-basic"):
  179. response = self.client.get(url, format="json")
  180. assert response.status_code == 200, response.content
  181. trace = response.data["contexts"]["trace"]
  182. original_trace = data["contexts"]["trace"]
  183. assert trace["trace_id"] == original_trace["trace_id"]
  184. assert trace["span_id"] == original_trace["span_id"]
  185. assert trace["parent_span_id"] == original_trace["parent_span_id"]
  186. assert trace["description"][:-3] in original_trace["description"]
  187. def test_blank_fields(self):
  188. url = reverse(
  189. "sentry-api-0-organization-event-details",
  190. kwargs={
  191. "organization_slug": self.project.organization.slug,
  192. "project_slug": self.project.slug,
  193. "event_id": "a" * 32,
  194. },
  195. )
  196. with self.feature("organizations:discover-basic"):
  197. response = self.client.get(
  198. url,
  199. data={"field": ["", " "], "statsPeriod": "24h"},
  200. format="json",
  201. )
  202. assert response.status_code == 200, response.content
  203. assert response.data["id"] == "a" * 32
  204. assert response.data["projectSlug"] == self.project.slug
  205. def test_out_of_retention(self):
  206. self.store_event(
  207. data={
  208. "event_id": "d" * 32,
  209. "message": "oh no",
  210. "timestamp": iso_format(before_now(days=2)),
  211. "fingerprint": ["group-1"],
  212. },
  213. project_id=self.project.id,
  214. )
  215. url = reverse(
  216. "sentry-api-0-organization-event-details",
  217. kwargs={
  218. "organization_slug": self.project.organization.slug,
  219. "project_slug": self.project.slug,
  220. "event_id": "d" * 32,
  221. },
  222. )
  223. with self.options({"system.event-retention-days": 1}):
  224. response = self.client.get(
  225. url,
  226. format="json",
  227. )
  228. assert response.status_code == 400, response.content