test_group_events.py 16 KB

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