test_organization_events_span_metrics.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. import pytest
  2. from django.urls import reverse
  3. from sentry.testutils import MetricsEnhancedPerformanceTestCase
  4. from sentry.testutils.helpers.datetime import before_now
  5. from sentry.testutils.silo import region_silo_test
  6. pytestmark = pytest.mark.sentry_metrics
  7. @region_silo_test
  8. class OrganizationEventsMetricsEnhancedPerformanceEndpointTest(MetricsEnhancedPerformanceTestCase):
  9. viewname = "sentry-api-0-organization-events"
  10. # Poor intentionally omitted for test_measurement_rating_that_does_not_exist
  11. METRIC_STRINGS = [
  12. "foo_transaction",
  13. "bar_transaction",
  14. ]
  15. def setUp(self):
  16. super().setUp()
  17. self.min_ago = before_now(minutes=1)
  18. self.six_min_ago = before_now(minutes=6)
  19. self.features = {
  20. "organizations:starfish-view": True,
  21. }
  22. def do_request(self, query, features=None):
  23. if features is None:
  24. features = {"organizations:discover-basic": True}
  25. features.update(self.features)
  26. self.login_as(user=self.user)
  27. url = reverse(
  28. self.viewname,
  29. kwargs={"organization_slug": self.organization.slug},
  30. )
  31. with self.feature(features):
  32. return self.client.get(url, query, format="json")
  33. def test_p50_with_no_data(self):
  34. response = self.do_request(
  35. {
  36. "field": ["p50()"],
  37. "query": "",
  38. "project": self.project.id,
  39. "dataset": "spansMetrics",
  40. }
  41. )
  42. assert response.status_code == 200, response.content
  43. data = response.data["data"]
  44. meta = response.data["meta"]
  45. assert len(data) == 1
  46. assert data[0]["p50()"] == 0
  47. assert meta["dataset"] == "spansMetrics"
  48. def test_count(self):
  49. self.store_span_metric(
  50. 1,
  51. timestamp=self.min_ago,
  52. )
  53. response = self.do_request(
  54. {
  55. "field": ["count()"],
  56. "query": "",
  57. "project": self.project.id,
  58. "dataset": "spansMetrics",
  59. }
  60. )
  61. assert response.status_code == 200, response.content
  62. data = response.data["data"]
  63. meta = response.data["meta"]
  64. assert len(data) == 1
  65. assert data[0]["count()"] == 1
  66. assert meta["dataset"] == "spansMetrics"
  67. def test_count_unique(self):
  68. self.store_span_metric(
  69. 1,
  70. "user",
  71. timestamp=self.min_ago,
  72. )
  73. self.store_span_metric(
  74. 2,
  75. "user",
  76. timestamp=self.min_ago,
  77. )
  78. response = self.do_request(
  79. {
  80. "field": ["count_unique(user)"],
  81. "query": "",
  82. "project": self.project.id,
  83. "dataset": "spansMetrics",
  84. }
  85. )
  86. assert response.status_code == 200, response.content
  87. data = response.data["data"]
  88. meta = response.data["meta"]
  89. assert len(data) == 1
  90. assert data[0]["count_unique(user)"] == 2
  91. assert meta["dataset"] == "spansMetrics"
  92. def test_sum(self):
  93. self.store_span_metric(
  94. 321,
  95. timestamp=self.min_ago,
  96. )
  97. self.store_span_metric(
  98. 99,
  99. timestamp=self.min_ago,
  100. )
  101. response = self.do_request(
  102. {
  103. "field": ["sum(span.duration)"],
  104. "query": "",
  105. "project": self.project.id,
  106. "dataset": "spansMetrics",
  107. }
  108. )
  109. assert response.status_code == 200, response.content
  110. data = response.data["data"]
  111. meta = response.data["meta"]
  112. assert len(data) == 1
  113. assert data[0]["sum(span.duration)"] == 420
  114. assert meta["dataset"] == "spansMetrics"
  115. # TODO(wmak)
  116. # test_percentile
  117. def test_p50(self):
  118. self.store_span_metric(
  119. 1,
  120. timestamp=self.min_ago,
  121. )
  122. response = self.do_request(
  123. {
  124. "field": ["p50()"],
  125. "query": "",
  126. "project": self.project.id,
  127. "dataset": "spansMetrics",
  128. }
  129. )
  130. assert response.status_code == 200, response.content
  131. data = response.data["data"]
  132. meta = response.data["meta"]
  133. assert len(data) == 1
  134. assert data[0]["p50()"] == 1
  135. assert meta["dataset"] == "spansMetrics"
  136. def test_eps(self):
  137. for _ in range(6):
  138. self.store_span_metric(
  139. 1,
  140. timestamp=self.min_ago,
  141. )
  142. response = self.do_request(
  143. {
  144. "field": ["eps()", "sps()"],
  145. "query": "",
  146. "project": self.project.id,
  147. "dataset": "spansMetrics",
  148. "statsPeriod": "10m",
  149. }
  150. )
  151. assert response.status_code == 200, response.content
  152. data = response.data["data"]
  153. meta = response.data["meta"]
  154. assert len(data) == 1
  155. assert data[0]["eps()"] == 0.01
  156. assert data[0]["sps()"] == 0.01
  157. assert meta["dataset"] == "spansMetrics"
  158. def test_epm(self):
  159. for _ in range(6):
  160. self.store_span_metric(
  161. 1,
  162. timestamp=self.min_ago,
  163. )
  164. response = self.do_request(
  165. {
  166. "field": ["epm()", "spm()"],
  167. "query": "",
  168. "project": self.project.id,
  169. "dataset": "spansMetrics",
  170. "statsPeriod": "10m",
  171. }
  172. )
  173. assert response.status_code == 200, response.content
  174. data = response.data["data"]
  175. meta = response.data["meta"]
  176. assert len(data) == 1
  177. assert data[0]["epm()"] == 0.6
  178. assert data[0]["spm()"] == 0.6
  179. assert meta["dataset"] == "spansMetrics"
  180. def test_time_spent_percentage(self):
  181. for _ in range(4):
  182. self.store_span_metric(
  183. 1,
  184. tags={"transaction": "foo_transaction"},
  185. timestamp=self.min_ago,
  186. )
  187. self.store_span_metric(
  188. 1,
  189. tags={"transaction": "bar_transaction"},
  190. timestamp=self.min_ago,
  191. )
  192. response = self.do_request(
  193. {
  194. "field": ["transaction", "time_spent_percentage()"],
  195. "query": "",
  196. "orderby": ["-time_spent_percentage()"],
  197. "project": self.project.id,
  198. "dataset": "spansMetrics",
  199. "statsPeriod": "10m",
  200. }
  201. )
  202. assert response.status_code == 200, response.content
  203. data = response.data["data"]
  204. meta = response.data["meta"]
  205. assert len(data) == 2
  206. assert data[0]["time_spent_percentage()"] == 0.8
  207. assert data[0]["transaction"] == "foo_transaction"
  208. assert data[1]["time_spent_percentage()"] == 0.2
  209. assert data[1]["transaction"] == "bar_transaction"
  210. assert meta["dataset"] == "spansMetrics"
  211. def test_http_error_rate_and_count(self):
  212. for _ in range(4):
  213. self.store_span_metric(
  214. 1,
  215. tags={"span.status_code": "500"},
  216. timestamp=self.min_ago,
  217. )
  218. self.store_span_metric(
  219. 1,
  220. tags={"span.status_code": "200"},
  221. timestamp=self.min_ago,
  222. )
  223. response = self.do_request(
  224. {
  225. "field": ["http_error_count()", "http_error_rate()"],
  226. "query": "",
  227. "orderby": ["-http_error_rate()"],
  228. "project": self.project.id,
  229. "dataset": "spansMetrics",
  230. "statsPeriod": "10m",
  231. }
  232. )
  233. assert response.status_code == 200, response.content
  234. data = response.data["data"]
  235. meta = response.data["meta"]
  236. assert len(data) == 1
  237. assert data[0]["http_error_rate()"] == 0.8
  238. assert meta["dataset"] == "spansMetrics"
  239. assert meta["fields"]["http_error_count()"] == "integer"
  240. assert meta["fields"]["http_error_rate()"] == "percentage"
  241. def percentile_percent_change(self):
  242. self.store_span_metric(
  243. 5,
  244. timestamp=self.six_min_ago,
  245. )
  246. self.store_span_metric(
  247. 10,
  248. timestamp=self.min_ago,
  249. )
  250. response = self.do_request(
  251. {
  252. "field": ["percentile_percent_change(span.duration)"],
  253. "query": "",
  254. "orderby": ["-percentile_percent_change()"],
  255. "project": self.project.id,
  256. "dataset": "spansMetrics",
  257. "statsPeriod": "10m",
  258. }
  259. )
  260. assert response.status_code == 200, response.content
  261. data = response.data["data"]
  262. meta = response.data["meta"]
  263. assert len(data) == 1
  264. assert data[0]["percentile_percent_change()"] == 1
  265. assert meta["dataset"] == "spansMetrics"
  266. assert meta["fields"]["percentile_percent_change()"] == "percentage"
  267. def test_http_error_count_percent_change(self):
  268. for _ in range(4):
  269. self.store_span_metric(
  270. 1,
  271. tags={"span.status_code": "500"},
  272. timestamp=self.six_min_ago,
  273. )
  274. self.store_span_metric(
  275. 1,
  276. tags={"span.status_code": "500"},
  277. timestamp=self.min_ago,
  278. )
  279. response = self.do_request(
  280. {
  281. "field": ["http_error_count_percent_change()"],
  282. "query": "",
  283. "orderby": ["-http_error_count_percent_change()"],
  284. "project": self.project.id,
  285. "dataset": "spansMetrics",
  286. "statsPeriod": "10m",
  287. }
  288. )
  289. assert response.status_code == 200, response.content
  290. data = response.data["data"]
  291. meta = response.data["meta"]
  292. assert len(data) == 1
  293. assert data[0]["http_error_count_percent_change()"] == -0.75
  294. assert meta["dataset"] == "spansMetrics"
  295. assert meta["fields"]["http_error_count_percent_change()"] == "percentage"
  296. def test_epm_percent_change(self):
  297. for _ in range(4):
  298. self.store_span_metric(
  299. 1,
  300. timestamp=self.six_min_ago,
  301. )
  302. self.store_span_metric(
  303. 1,
  304. timestamp=self.min_ago,
  305. )
  306. response = self.do_request(
  307. {
  308. "field": ["epm_percent_change()", "spm_percent_change()"],
  309. "query": "",
  310. "orderby": ["-epm_percent_change()"],
  311. "project": self.project.id,
  312. "dataset": "spansMetrics",
  313. "statsPeriod": "10m",
  314. }
  315. )
  316. assert response.status_code == 200, response.content
  317. data = response.data["data"]
  318. meta = response.data["meta"]
  319. assert len(data) == 1
  320. assert data[0]["epm_percent_change()"] == pytest.approx(-0.75)
  321. assert data[0]["spm_percent_change()"] == pytest.approx(-0.75)
  322. assert meta["dataset"] == "spansMetrics"
  323. assert meta["fields"]["epm_percent_change()"] == "percentage"
  324. assert meta["fields"]["spm_percent_change()"] == "percentage"
  325. def test_eps_percent_change(self):
  326. for _ in range(4):
  327. self.store_span_metric(
  328. 1,
  329. timestamp=self.min_ago,
  330. )
  331. self.store_span_metric(
  332. 1,
  333. timestamp=self.six_min_ago,
  334. )
  335. response = self.do_request(
  336. {
  337. "field": ["eps_percent_change()", "sps_percent_change()"],
  338. "query": "",
  339. "orderby": ["-eps_percent_change()"],
  340. "project": self.project.id,
  341. "dataset": "spansMetrics",
  342. "statsPeriod": "10m",
  343. }
  344. )
  345. assert response.status_code == 200, response.content
  346. data = response.data["data"]
  347. meta = response.data["meta"]
  348. assert len(data) == 1
  349. assert data[0]["eps_percent_change()"] == pytest.approx(3)
  350. assert data[0]["sps_percent_change()"] == pytest.approx(3)
  351. assert meta["dataset"] == "spansMetrics"
  352. assert meta["fields"]["eps_percent_change()"] == "percentage"
  353. assert meta["fields"]["sps_percent_change()"] == "percentage"