test_group_events.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. from datetime import timedelta
  2. from django.utils import timezone
  3. from freezegun import freeze_time
  4. from sentry.testutils import APITestCase, SnubaTestCase
  5. from sentry.testutils.helpers.datetime import before_now, iso_format
  6. from sentry.testutils.silo import region_silo_test
  7. from sentry.types.issues import GroupType
  8. from sentry.utils.samples import load_data
  9. @region_silo_test
  10. class GroupEventsTest(APITestCase, SnubaTestCase):
  11. def setUp(self):
  12. super().setUp()
  13. self.min_ago = before_now(minutes=1)
  14. def test_simple(self):
  15. self.login_as(user=self.user)
  16. event_1 = self.store_event(
  17. data={
  18. "event_id": "a" * 32,
  19. "fingerprint": ["1"],
  20. "timestamp": iso_format(self.min_ago),
  21. },
  22. project_id=self.project.id,
  23. )
  24. event_2 = self.store_event(
  25. data={
  26. "event_id": "b" * 32,
  27. "fingerprint": ["1"],
  28. "timestamp": iso_format(self.min_ago),
  29. },
  30. project_id=self.project.id,
  31. )
  32. url = f"/api/0/issues/{event_1.group.id}/events/"
  33. response = self.client.get(url, format="json")
  34. assert response.status_code == 200, response.content
  35. assert len(response.data) == 2
  36. assert sorted(map(lambda x: x["eventID"], response.data)) == sorted(
  37. [str(event_1.event_id), str(event_2.event_id)]
  38. )
  39. def test_tags(self):
  40. self.login_as(user=self.user)
  41. event_1 = self.store_event(
  42. data={
  43. "event_id": "a" * 32,
  44. "fingerprint": ["1"],
  45. "tags": {"foo": "baz", "bar": "buz"},
  46. "timestamp": iso_format(self.min_ago),
  47. },
  48. project_id=self.project.id,
  49. )
  50. event_2 = self.store_event(
  51. data={
  52. "event_id": "b" * 32,
  53. "fingerprint": ["1"],
  54. "tags": {"bar": "biz"},
  55. "timestamp": iso_format(before_now(seconds=61)),
  56. },
  57. project_id=self.project.id,
  58. )
  59. url = f"/api/0/issues/{event_1.group.id}/events/"
  60. response = self.client.get(url + "?query=foo:baz", format="json")
  61. assert response.status_code == 200, response.content
  62. assert len(response.data) == 1
  63. assert response.data[0]["eventID"] == str(event_1.event_id)
  64. response = self.client.get(url + "?query=!foo:baz", format="json")
  65. assert response.status_code == 200, response.content
  66. assert len(response.data) == 1
  67. assert response.data[0]["eventID"] == str(event_2.event_id)
  68. response = self.client.get(url + "?query=bar:biz", format="json")
  69. assert response.status_code == 200, response.content
  70. assert len(response.data) == 1
  71. assert response.data[0]["eventID"] == str(event_2.event_id)
  72. response = self.client.get(url + "?query=bar:biz%20foo:baz", format="json")
  73. assert response.status_code == 200, response.content
  74. assert len(response.data) == 0
  75. response = self.client.get(url + "?query=bar:buz%20foo:baz", format="json")
  76. assert response.status_code == 200, response.content
  77. assert len(response.data) == 1
  78. assert response.data[0]["eventID"] == str(event_1.event_id)
  79. response = self.client.get(url + "?query=bar:baz", format="json")
  80. assert response.status_code == 200, response.content
  81. assert len(response.data) == 0
  82. response = self.client.get(url + "?query=a:b", format="json")
  83. assert response.status_code == 200, response.content
  84. assert len(response.data) == 0
  85. response = self.client.get(url + "?query=bar:b", format="json")
  86. assert response.status_code == 200, response.content
  87. assert len(response.data) == 0
  88. response = self.client.get(url + "?query=bar:baz", format="json")
  89. assert response.status_code == 200, response.content
  90. assert len(response.data) == 0
  91. response = self.client.get(url + "?query=!bar:baz", format="json")
  92. assert response.status_code == 200, response.content
  93. assert len(response.data) == 2
  94. assert {e["eventID"] for e in response.data} == {event_1.event_id, event_2.event_id}
  95. def test_search_event_by_id(self):
  96. self.login_as(user=self.user)
  97. event_1 = self.store_event(
  98. data={
  99. "event_id": "a" * 32,
  100. "fingerprint": ["group-1"],
  101. "timestamp": iso_format(self.min_ago),
  102. },
  103. project_id=self.project.id,
  104. )
  105. self.store_event(
  106. data={
  107. "event_id": "b" * 32,
  108. "fingerprint": ["group-1"],
  109. "timestamp": iso_format(self.min_ago),
  110. },
  111. project_id=self.project.id,
  112. )
  113. url = f"/api/0/issues/{event_1.group.id}/events/?query={event_1.event_id}"
  114. response = self.client.get(url, format="json")
  115. assert response.status_code == 200, response.content
  116. assert len(response.data) == 1
  117. assert response.data[0]["eventID"] == event_1.event_id
  118. def test_search_event_by_message(self):
  119. self.login_as(user=self.user)
  120. event_1 = self.store_event(
  121. data={
  122. "event_id": "a" * 32,
  123. "fingerprint": ["group-1"],
  124. "message": "foo bar hello world",
  125. "timestamp": iso_format(self.min_ago),
  126. },
  127. project_id=self.project.id,
  128. )
  129. group = event_1.group
  130. event_2 = self.store_event(
  131. data={
  132. "event_id": "b" * 32,
  133. "fingerprint": ["group-1"],
  134. "message": "this bar hello world",
  135. "timestamp": iso_format(self.min_ago),
  136. },
  137. project_id=self.project.id,
  138. )
  139. assert group == event_2.group
  140. query_1 = "foo"
  141. query_2 = "hello+world"
  142. # Single Word Query
  143. url = f"/api/0/issues/{group.id}/events/?query={query_1}"
  144. response = self.client.get(url, format="json")
  145. assert response.status_code == 200, response.content
  146. assert len(response.data) == 1
  147. assert response.data[0]["eventID"] == event_1.event_id
  148. # Multiple Word Query
  149. url = f"/api/0/issues/{group.id}/events/?query={query_2}"
  150. response = self.client.get(url, format="json")
  151. assert response.status_code == 200, response.content
  152. assert len(response.data) == 2
  153. assert sorted(map(lambda x: x["eventID"], response.data)) == sorted(
  154. [str(event_1.event_id), str(event_2.event_id)]
  155. )
  156. def test_search_by_release(self):
  157. self.login_as(user=self.user)
  158. self.create_release(self.project, version="first-release")
  159. event_1 = self.store_event(
  160. data={
  161. "event_id": "a" * 32,
  162. "fingerprint": ["group-1"],
  163. "timestamp": iso_format(self.min_ago),
  164. "release": "first-release",
  165. },
  166. project_id=self.project.id,
  167. )
  168. url = f"/api/0/issues/{event_1.group.id}/events/?query=release:latest"
  169. response = self.client.get(url, format="json")
  170. assert response.status_code == 200, response.content
  171. assert len(response.data) == 1
  172. assert response.data[0]["eventID"] == event_1.event_id
  173. def test_environment(self):
  174. self.login_as(user=self.user)
  175. events = {}
  176. for name in ["production", "development"]:
  177. events[name] = self.store_event(
  178. data={
  179. "fingerprint": ["put-me-in-group1"],
  180. "timestamp": iso_format(self.min_ago),
  181. "environment": name,
  182. },
  183. project_id=self.project.id,
  184. )
  185. # Asserts that all are in the same group
  186. (group_id,) = {e.group.id for e in events.values()}
  187. url = f"/api/0/issues/{group_id}/events/"
  188. response = self.client.get(url + "?environment=production", format="json")
  189. assert response.status_code == 200, response.content
  190. assert set(map(lambda x: x["eventID"], response.data)) == {
  191. str(events["production"].event_id)
  192. }
  193. response = self.client.get(
  194. url, data={"environment": ["production", "development"]}, format="json"
  195. )
  196. assert response.status_code == 200, response.content
  197. assert set(map(lambda x: x["eventID"], response.data)) == {
  198. str(event.event_id) for event in events.values()
  199. }
  200. response = self.client.get(url + "?environment=invalid", format="json")
  201. assert response.status_code == 200, response.content
  202. assert response.data == []
  203. response = self.client.get(
  204. url + "?environment=production&query=environment:development", format="json"
  205. )
  206. assert response.status_code == 200, response.content
  207. assert response.data == []
  208. def test_filters_based_on_retention(self):
  209. self.login_as(user=self.user)
  210. self.store_event(
  211. data={"fingerprint": ["group_1"], "timestamp": iso_format(before_now(days=2))},
  212. project_id=self.project.id,
  213. )
  214. event_2 = self.store_event(
  215. data={"fingerprint": ["group_1"], "timestamp": iso_format(self.min_ago)},
  216. project_id=self.project.id,
  217. )
  218. group = event_2.group
  219. with self.options({"system.event-retention-days": 1}):
  220. response = self.client.get(f"/api/0/issues/{group.id}/events/")
  221. assert response.status_code == 200, response.content
  222. assert len(response.data) == 1
  223. assert sorted(map(lambda x: x["eventID"], response.data)) == sorted([str(event_2.event_id)])
  224. def test_search_event_has_tags(self):
  225. self.login_as(user=self.user)
  226. event = self.store_event(
  227. data={
  228. "timestamp": iso_format(self.min_ago),
  229. "message": "foo",
  230. "tags": {"logger": "python"},
  231. },
  232. project_id=self.project.id,
  233. )
  234. response = self.client.get(f"/api/0/issues/{event.group.id}/events/")
  235. assert response.status_code == 200, response.content
  236. assert len(response.data) == 1
  237. assert {"key": "logger", "value": "python"} in response.data[0]["tags"]
  238. @freeze_time()
  239. def test_date_filters(self):
  240. self.login_as(user=self.user)
  241. event_1 = self.store_event(
  242. data={"timestamp": iso_format(before_now(days=5)), "fingerprint": ["group-1"]},
  243. project_id=self.project.id,
  244. )
  245. event_2 = self.store_event(
  246. data={"timestamp": iso_format(before_now(days=1)), "fingerprint": ["group-1"]},
  247. project_id=self.project.id,
  248. )
  249. group = event_1.group
  250. assert group == event_2.group
  251. response = self.client.get(f"/api/0/issues/{group.id}/events/", data={"statsPeriod": "6d"})
  252. assert response.status_code == 200, response.content
  253. assert len(response.data) == 2
  254. assert sorted(map(lambda x: x["eventID"], response.data)) == sorted(
  255. [str(event_1.event_id), str(event_2.event_id)]
  256. )
  257. response = self.client.get(f"/api/0/issues/{group.id}/events/", data={"statsPeriod": "2d"})
  258. assert response.status_code == 200, response.content
  259. assert len(response.data) == 1
  260. assert response.data[0]["eventID"] == str(event_2.event_id)
  261. def test_invalid_period(self):
  262. self.login_as(user=self.user)
  263. first_seen = timezone.now() - timedelta(days=5)
  264. group = self.create_group(first_seen=first_seen)
  265. response = self.client.get(f"/api/0/issues/{group.id}/events/", data={"statsPeriod": "lol"})
  266. assert response.status_code == 400
  267. def test_invalid_query(self):
  268. self.login_as(user=self.user)
  269. first_seen = timezone.now() - timedelta(days=5)
  270. group = self.create_group(first_seen=first_seen)
  271. response = self.client.get(
  272. f"/api/0/issues/{group.id}/events/",
  273. data={"statsPeriod": "7d", "query": "foo(bar"},
  274. )
  275. assert response.status_code == 400
  276. def test_multiple_group(self):
  277. self.login_as(user=self.user)
  278. event_1 = self.store_event(
  279. data={
  280. "fingerprint": ["group_1"],
  281. "event_id": "a" * 32,
  282. "message": "foo",
  283. "timestamp": iso_format(self.min_ago),
  284. },
  285. project_id=self.project.id,
  286. )
  287. event_2 = self.store_event(
  288. data={
  289. "fingerprint": ["group_2"],
  290. "event_id": "b" * 32,
  291. "message": "group2",
  292. "timestamp": iso_format(self.min_ago),
  293. },
  294. project_id=self.project.id,
  295. )
  296. for event in (event_1, event_2):
  297. url = f"/api/0/issues/{event.group.id}/events/"
  298. response = self.client.get(url, format="json")
  299. assert response.status_code == 200, response.content
  300. assert len(response.data) == 1, response.data
  301. assert list(map(lambda x: x["eventID"], response.data)) == [str(event.event_id)]
  302. def test_perf_issue(self):
  303. event_data = load_data(
  304. "transaction",
  305. fingerprint=[f"{GroupType.PERFORMANCE_N_PLUS_ONE_DB_QUERIES.value}-group1"],
  306. )
  307. event_1 = self.store_event(data=event_data, project_id=self.project.id)
  308. event_2 = self.store_event(data=event_data, project_id=self.project.id)
  309. self.login_as(user=self.user)
  310. url = f"/api/0/issues/{event_1.groups[0].id}/events/"
  311. response = self.client.get(url, format="json")
  312. assert response.status_code == 200, response.content
  313. assert sorted(map(lambda x: x["eventID"], response.data)) == sorted(
  314. [str(event_1.event_id), str(event_2.event_id)]
  315. )