test_organization_events_mep.py 75 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106
  1. from unittest import mock
  2. import pytest
  3. from django.urls import reverse
  4. from snuba_sdk.conditions import InvalidConditionError
  5. from sentry.discover.models import TeamKeyTransaction
  6. from sentry.exceptions import IncompatibleMetricsQuery, InvalidSearchQuery
  7. from sentry.models import ProjectTeam
  8. from sentry.models.transaction_threshold import (
  9. ProjectTransactionThreshold,
  10. ProjectTransactionThresholdOverride,
  11. TransactionMetric,
  12. )
  13. from sentry.search.events import constants
  14. from sentry.testutils import MetricsEnhancedPerformanceTestCase
  15. from sentry.testutils.helpers.datetime import before_now, iso_format
  16. from sentry.testutils.silo import region_silo_test
  17. from sentry.utils.samples import load_data
  18. pytestmark = pytest.mark.sentry_metrics
  19. @region_silo_test
  20. class OrganizationEventsMetricsEnhancedPerformanceEndpointTest(MetricsEnhancedPerformanceTestCase):
  21. viewname = "sentry-api-0-organization-events"
  22. # Poor intentionally omitted for test_measurement_rating_that_does_not_exist
  23. METRIC_STRINGS = [
  24. "foo_transaction",
  25. "bar_transaction",
  26. "baz_transaction",
  27. "staging",
  28. "measurement_rating",
  29. "good",
  30. "meh",
  31. "d:transactions/measurements.something_custom@millisecond",
  32. "d:transactions/measurements.runtime@hour",
  33. "d:transactions/measurements.bytes_transfered@byte",
  34. "d:transactions/measurements.datacenter_memory@petabyte",
  35. "d:transactions/measurements.custom.kilobyte@kilobyte",
  36. "d:transactions/measurements.longtaskcount@none",
  37. "d:transactions/measurements.percent@ratio",
  38. "d:transactions/measurements.custom_type@somethingcustom",
  39. ]
  40. def setUp(self):
  41. super().setUp()
  42. self.min_ago = before_now(minutes=1)
  43. self.two_min_ago = before_now(minutes=2)
  44. self.transaction_data = load_data("transaction", timestamp=before_now(minutes=1))
  45. self.features = {
  46. "organizations:performance-use-metrics": True,
  47. }
  48. def do_request(self, query, features=None):
  49. if features is None:
  50. features = {"organizations:discover-basic": True}
  51. features.update(self.features)
  52. self.login_as(user=self.user)
  53. url = reverse(
  54. self.viewname,
  55. kwargs={"organization_slug": self.organization.slug},
  56. )
  57. with self.feature(features):
  58. return self.client.get(url, query, format="json")
  59. def test_no_projects(self):
  60. response = self.do_request(
  61. {
  62. "dataset": "metricsEnhanced",
  63. }
  64. )
  65. assert response.status_code == 200, response.content
  66. def test_invalid_dataset(self):
  67. response = self.do_request(
  68. {
  69. "dataset": "aFakeDataset",
  70. "project": self.project.id,
  71. }
  72. )
  73. assert response.status_code == 400, response.content
  74. assert (
  75. response.data["detail"]
  76. == "dataset must be one of: discover, metricsEnhanced, metrics, profiles"
  77. )
  78. def test_out_of_retention(self):
  79. self.create_project()
  80. with self.options({"system.event-retention-days": 10}):
  81. query = {
  82. "field": ["id", "timestamp"],
  83. "orderby": ["-timestamp", "-id"],
  84. "query": "event.type:transaction",
  85. "start": iso_format(before_now(days=20)),
  86. "end": iso_format(before_now(days=15)),
  87. "dataset": "metricsEnhanced",
  88. }
  89. response = self.do_request(query)
  90. assert response.status_code == 400, response.content
  91. assert response.data["detail"] == "Invalid date range. Please try a more recent date range."
  92. def test_invalid_search_terms(self):
  93. response = self.do_request(
  94. {
  95. "field": ["epm()"],
  96. "query": "hi \n there",
  97. "project": self.project.id,
  98. "dataset": "metricsEnhanced",
  99. }
  100. )
  101. assert response.status_code == 400, response.content
  102. assert (
  103. response.data["detail"]
  104. == "Parse error at 'hi \n ther' (column 4). This is commonly caused by unmatched parentheses. Enclose any text in double quotes."
  105. )
  106. def test_percentile_with_no_data(self):
  107. response = self.do_request(
  108. {
  109. "field": ["p50()"],
  110. "query": "",
  111. "project": self.project.id,
  112. "dataset": "metricsEnhanced",
  113. }
  114. )
  115. assert response.status_code == 200, response.content
  116. data = response.data["data"]
  117. assert len(data) == 1
  118. assert data[0]["p50()"] == 0
  119. def test_project_name(self):
  120. self.store_transaction_metric(
  121. 1,
  122. tags={"environment": "staging"},
  123. timestamp=self.min_ago,
  124. )
  125. response = self.do_request(
  126. {
  127. "field": ["project.name", "environment", "epm()"],
  128. "query": "event.type:transaction",
  129. "dataset": "metricsEnhanced",
  130. "per_page": 50,
  131. }
  132. )
  133. assert response.status_code == 200, response.content
  134. assert len(response.data["data"]) == 1
  135. data = response.data["data"]
  136. meta = response.data["meta"]
  137. field_meta = meta["fields"]
  138. assert data[0]["project.name"] == self.project.slug
  139. assert "project.id" not in data[0]
  140. assert data[0]["environment"] == "staging"
  141. assert meta["isMetricsData"]
  142. assert field_meta["project.name"] == "string"
  143. assert field_meta["environment"] == "string"
  144. assert field_meta["epm()"] == "number"
  145. def test_title_alias(self):
  146. """title is an alias to transaction name"""
  147. self.store_transaction_metric(
  148. 1,
  149. tags={"transaction": "foo_transaction"},
  150. timestamp=self.min_ago,
  151. )
  152. response = self.do_request(
  153. {
  154. "field": ["title", "p50()"],
  155. "query": "event.type:transaction",
  156. "dataset": "metricsEnhanced",
  157. "per_page": 50,
  158. }
  159. )
  160. assert response.status_code == 200, response.content
  161. assert len(response.data["data"]) == 1
  162. data = response.data["data"]
  163. meta = response.data["meta"]
  164. field_meta = meta["fields"]
  165. assert data[0]["title"] == "foo_transaction"
  166. assert data[0]["p50()"] == 1
  167. assert meta["isMetricsData"]
  168. assert field_meta["title"] == "string"
  169. assert field_meta["p50()"] == "duration"
  170. def test_having_condition(self):
  171. self.store_transaction_metric(
  172. 1,
  173. tags={"environment": "staging", "transaction": "foo_transaction"},
  174. timestamp=self.min_ago,
  175. )
  176. self.store_transaction_metric(
  177. # shouldn't show up
  178. 100,
  179. tags={"environment": "staging", "transaction": "bar_transaction"},
  180. timestamp=self.min_ago,
  181. )
  182. response = self.do_request(
  183. {
  184. "field": ["transaction", "project", "p50(transaction.duration)"],
  185. "query": "event.type:transaction p50(transaction.duration):<50",
  186. "dataset": "metricsEnhanced",
  187. "per_page": 50,
  188. }
  189. )
  190. assert response.status_code == 200, response.content
  191. assert len(response.data["data"]) == 1
  192. data = response.data["data"]
  193. meta = response.data["meta"]
  194. field_meta = meta["fields"]
  195. assert data[0]["transaction"] == "foo_transaction"
  196. assert data[0]["project"] == self.project.slug
  197. assert data[0]["p50(transaction.duration)"] == 1
  198. assert meta["isMetricsData"]
  199. assert field_meta["transaction"] == "string"
  200. assert field_meta["project"] == "string"
  201. assert field_meta["p50(transaction.duration)"] == "duration"
  202. def test_having_condition_with_preventing_aggregates(self):
  203. self.store_transaction_metric(
  204. 1,
  205. tags={"environment": "staging", "transaction": "foo_transaction"},
  206. timestamp=self.min_ago,
  207. )
  208. self.store_transaction_metric(
  209. 100,
  210. tags={"environment": "staging", "transaction": "bar_transaction"},
  211. timestamp=self.min_ago,
  212. )
  213. response = self.do_request(
  214. {
  215. "field": ["transaction", "project", "p50(transaction.duration)"],
  216. "query": "event.type:transaction p50(transaction.duration):<50",
  217. "dataset": "metricsEnhanced",
  218. "preventMetricAggregates": "1",
  219. "per_page": 50,
  220. }
  221. )
  222. assert response.status_code == 200, response.content
  223. assert len(response.data["data"]) == 0
  224. meta = response.data["meta"]
  225. field_meta = meta["fields"]
  226. assert not meta["isMetricsData"]
  227. assert field_meta["transaction"] == "string"
  228. assert field_meta["project"] == "string"
  229. assert field_meta["p50(transaction.duration)"] == "duration"
  230. def test_having_condition_with_preventing_aggregate_metrics_only(self):
  231. """same as the previous test, but with the dataset on explicit metrics
  232. which should throw a 400 error instead"""
  233. response = self.do_request(
  234. {
  235. "field": ["transaction", "project", "p50(transaction.duration)"],
  236. "query": "event.type:transaction p50(transaction.duration):<50",
  237. "dataset": "metrics",
  238. "preventMetricAggregates": "1",
  239. "per_page": 50,
  240. "project": self.project.id,
  241. }
  242. )
  243. assert response.status_code == 400, response.content
  244. def test_having_condition_not_selected(self):
  245. self.store_transaction_metric(
  246. 1,
  247. tags={"environment": "staging", "transaction": "foo_transaction"},
  248. timestamp=self.min_ago,
  249. )
  250. self.store_transaction_metric(
  251. # shouldn't show up
  252. 100,
  253. tags={"environment": "staging", "transaction": "bar_transaction"},
  254. timestamp=self.min_ago,
  255. )
  256. response = self.do_request(
  257. {
  258. "field": ["transaction", "project", "p50(transaction.duration)"],
  259. "query": "event.type:transaction p75(transaction.duration):<50",
  260. "dataset": "metricsEnhanced",
  261. "per_page": 50,
  262. }
  263. )
  264. assert response.status_code == 200, response.content
  265. assert len(response.data["data"]) == 1
  266. data = response.data["data"]
  267. meta = response.data["meta"]
  268. field_meta = meta["fields"]
  269. assert data[0]["transaction"] == "foo_transaction"
  270. assert data[0]["project"] == self.project.slug
  271. assert data[0]["p50(transaction.duration)"] == 1
  272. assert meta["isMetricsData"]
  273. assert field_meta["transaction"] == "string"
  274. assert field_meta["project"] == "string"
  275. assert field_meta["p50(transaction.duration)"] == "duration"
  276. def test_non_metrics_tag_with_implicit_format(self):
  277. self.store_transaction_metric(
  278. 1,
  279. tags={"environment": "staging", "transaction": "foo_transaction"},
  280. timestamp=self.min_ago,
  281. )
  282. response = self.do_request(
  283. {
  284. "field": ["test", "p50(transaction.duration)"],
  285. "query": "event.type:transaction",
  286. "dataset": "metricsEnhanced",
  287. "per_page": 50,
  288. }
  289. )
  290. assert response.status_code == 200, response.content
  291. assert len(response.data["data"]) == 0
  292. assert not response.data["meta"]["isMetricsData"]
  293. def test_non_metrics_tag_with_implicit_format_metrics_dataset(self):
  294. self.store_transaction_metric(
  295. 1,
  296. tags={"environment": "staging", "transaction": "foo_transaction"},
  297. timestamp=self.min_ago,
  298. )
  299. response = self.do_request(
  300. {
  301. "field": ["test", "p50(transaction.duration)"],
  302. "query": "event.type:transaction",
  303. "dataset": "metrics",
  304. "per_page": 50,
  305. }
  306. )
  307. assert response.status_code == 400, response.content
  308. def test_performance_homepage_query(self):
  309. self.store_transaction_metric(
  310. 1,
  311. tags={
  312. "transaction": "foo_transaction",
  313. constants.METRIC_SATISFACTION_TAG_KEY: constants.METRIC_SATISFIED_TAG_VALUE,
  314. },
  315. timestamp=self.min_ago,
  316. )
  317. self.store_transaction_metric(
  318. 1,
  319. "measurements.fcp",
  320. tags={"transaction": "foo_transaction"},
  321. timestamp=self.min_ago,
  322. )
  323. self.store_transaction_metric(
  324. 2,
  325. "measurements.lcp",
  326. tags={"transaction": "foo_transaction"},
  327. timestamp=self.min_ago,
  328. )
  329. self.store_transaction_metric(
  330. 3,
  331. "measurements.fid",
  332. tags={"transaction": "foo_transaction"},
  333. timestamp=self.min_ago,
  334. )
  335. self.store_transaction_metric(
  336. 4,
  337. "measurements.cls",
  338. tags={"transaction": "foo_transaction"},
  339. timestamp=self.min_ago,
  340. )
  341. self.store_transaction_metric(
  342. 1,
  343. "user",
  344. tags={
  345. "transaction": "foo_transaction",
  346. constants.METRIC_SATISFACTION_TAG_KEY: constants.METRIC_FRUSTRATED_TAG_VALUE,
  347. },
  348. timestamp=self.min_ago,
  349. )
  350. for dataset in ["metrics", "metricsEnhanced"]:
  351. response = self.do_request(
  352. {
  353. "field": [
  354. "transaction",
  355. "project",
  356. "tpm()",
  357. "p75(measurements.fcp)",
  358. "p75(measurements.lcp)",
  359. "p75(measurements.fid)",
  360. "p75(measurements.cls)",
  361. "count_unique(user)",
  362. "apdex()",
  363. "count_miserable(user)",
  364. "user_misery()",
  365. "failure_rate()",
  366. "failure_count()",
  367. ],
  368. "orderby": "tpm()",
  369. "query": "event.type:transaction",
  370. "dataset": dataset,
  371. "per_page": 50,
  372. }
  373. )
  374. assert len(response.data["data"]) == 1
  375. data = response.data["data"][0]
  376. meta = response.data["meta"]
  377. field_meta = meta["fields"]
  378. assert data["transaction"] == "foo_transaction"
  379. assert data["project"] == self.project.slug
  380. assert data["p75(measurements.fcp)"] == 1.0
  381. assert data["p75(measurements.lcp)"] == 2.0
  382. assert data["p75(measurements.fid)"] == 3.0
  383. assert data["p75(measurements.cls)"] == 4.0
  384. assert data["apdex()"] == 1.0
  385. assert data["count_miserable(user)"] == 1.0
  386. assert data["user_misery()"] == 0.058
  387. assert data["failure_rate()"] == 1
  388. assert data["failure_count()"] == 1
  389. assert meta["isMetricsData"]
  390. assert field_meta["transaction"] == "string"
  391. assert field_meta["project"] == "string"
  392. assert field_meta["p75(measurements.fcp)"] == "duration"
  393. assert field_meta["p75(measurements.lcp)"] == "duration"
  394. assert field_meta["p75(measurements.fid)"] == "duration"
  395. assert field_meta["p75(measurements.cls)"] == "number"
  396. assert field_meta["apdex()"] == "number"
  397. assert field_meta["count_miserable(user)"] == "integer"
  398. assert field_meta["user_misery()"] == "number"
  399. assert field_meta["failure_rate()"] == "percentage"
  400. assert field_meta["failure_count()"] == "integer"
  401. def test_user_misery_and_team_key_sort(self):
  402. self.store_transaction_metric(
  403. 1,
  404. tags={
  405. "transaction": "foo_transaction",
  406. constants.METRIC_SATISFACTION_TAG_KEY: constants.METRIC_SATISFIED_TAG_VALUE,
  407. },
  408. timestamp=self.min_ago,
  409. )
  410. self.store_transaction_metric(
  411. 1,
  412. "measurements.fcp",
  413. tags={"transaction": "foo_transaction"},
  414. timestamp=self.min_ago,
  415. )
  416. self.store_transaction_metric(
  417. 2,
  418. "measurements.lcp",
  419. tags={"transaction": "foo_transaction"},
  420. timestamp=self.min_ago,
  421. )
  422. self.store_transaction_metric(
  423. 3,
  424. "measurements.fid",
  425. tags={"transaction": "foo_transaction"},
  426. timestamp=self.min_ago,
  427. )
  428. self.store_transaction_metric(
  429. 4,
  430. "measurements.cls",
  431. tags={"transaction": "foo_transaction"},
  432. timestamp=self.min_ago,
  433. )
  434. self.store_transaction_metric(
  435. 1,
  436. "user",
  437. tags={
  438. "transaction": "foo_transaction",
  439. constants.METRIC_SATISFACTION_TAG_KEY: constants.METRIC_FRUSTRATED_TAG_VALUE,
  440. },
  441. timestamp=self.min_ago,
  442. )
  443. response = self.do_request(
  444. {
  445. "field": [
  446. "team_key_transaction",
  447. "transaction",
  448. "project",
  449. "tpm()",
  450. "p75(measurements.fcp)",
  451. "p75(measurements.lcp)",
  452. "p75(measurements.fid)",
  453. "p75(measurements.cls)",
  454. "count_unique(user)",
  455. "apdex()",
  456. "count_miserable(user)",
  457. "user_misery()",
  458. "failure_rate()",
  459. "failure_count()",
  460. ],
  461. "orderby": ["team_key_transaction", "user_misery()"],
  462. "query": "event.type:transaction",
  463. "dataset": "metrics",
  464. "per_page": 50,
  465. }
  466. )
  467. assert response.status_code == 200, response.content
  468. assert len(response.data["data"]) == 1
  469. data = response.data["data"][0]
  470. meta = response.data["meta"]
  471. field_meta = meta["fields"]
  472. assert data["transaction"] == "foo_transaction"
  473. assert data["project"] == self.project.slug
  474. assert data["p75(measurements.fcp)"] == 1.0
  475. assert data["p75(measurements.lcp)"] == 2.0
  476. assert data["p75(measurements.fid)"] == 3.0
  477. assert data["p75(measurements.cls)"] == 4.0
  478. assert data["apdex()"] == 1.0
  479. assert data["count_miserable(user)"] == 1.0
  480. assert data["user_misery()"] == 0.058
  481. assert data["failure_rate()"] == 1
  482. assert data["failure_count()"] == 1
  483. assert meta["isMetricsData"]
  484. assert field_meta["transaction"] == "string"
  485. assert field_meta["project"] == "string"
  486. assert field_meta["p75(measurements.fcp)"] == "duration"
  487. assert field_meta["p75(measurements.lcp)"] == "duration"
  488. assert field_meta["p75(measurements.fid)"] == "duration"
  489. assert field_meta["p75(measurements.cls)"] == "number"
  490. assert field_meta["apdex()"] == "number"
  491. assert field_meta["count_miserable(user)"] == "integer"
  492. assert field_meta["user_misery()"] == "number"
  493. assert field_meta["failure_rate()"] == "percentage"
  494. assert field_meta["failure_count()"] == "integer"
  495. def test_no_team_key_transactions(self):
  496. self.store_transaction_metric(
  497. 1, tags={"transaction": "foo_transaction"}, timestamp=self.min_ago
  498. )
  499. self.store_transaction_metric(
  500. 100, tags={"transaction": "bar_transaction"}, timestamp=self.min_ago
  501. )
  502. query = {
  503. "team": "myteams",
  504. "project": [self.project.id],
  505. # TODO sort by transaction here once that's possible for order to match the same test without metrics
  506. "orderby": "p95()",
  507. "field": [
  508. "team_key_transaction",
  509. "transaction",
  510. "transaction.status",
  511. "project",
  512. "epm()",
  513. "failure_rate()",
  514. "p95()",
  515. ],
  516. "per_page": 50,
  517. "dataset": "metricsEnhanced",
  518. }
  519. response = self.do_request(query)
  520. assert response.status_code == 200, response.content
  521. assert len(response.data["data"]) == 2
  522. data = response.data["data"]
  523. meta = response.data["meta"]
  524. field_meta = meta["fields"]
  525. assert data[0]["team_key_transaction"] == 0
  526. assert data[0]["transaction"] == "foo_transaction"
  527. assert data[1]["team_key_transaction"] == 0
  528. assert data[1]["transaction"] == "bar_transaction"
  529. assert meta["isMetricsData"]
  530. assert field_meta["team_key_transaction"] == "boolean"
  531. assert field_meta["transaction"] == "string"
  532. def test_team_key_transactions_my_teams(self):
  533. team1 = self.create_team(organization=self.organization, name="Team A")
  534. self.create_team_membership(team1, user=self.user)
  535. self.project.add_team(team1)
  536. team2 = self.create_team(organization=self.organization, name="Team B")
  537. self.project.add_team(team2)
  538. key_transactions = [
  539. (team1, "foo_transaction"),
  540. (team2, "baz_transaction"),
  541. ]
  542. # Not a key transaction
  543. self.store_transaction_metric(
  544. 100, tags={"transaction": "bar_transaction"}, timestamp=self.min_ago
  545. )
  546. for team, transaction in key_transactions:
  547. self.store_transaction_metric(
  548. 1, tags={"transaction": transaction}, timestamp=self.min_ago
  549. )
  550. TeamKeyTransaction.objects.create(
  551. organization=self.organization,
  552. transaction=transaction,
  553. project_team=ProjectTeam.objects.get(project=self.project, team=team),
  554. )
  555. query = {
  556. "team": "myteams",
  557. "project": [self.project.id],
  558. "field": [
  559. "team_key_transaction",
  560. "transaction",
  561. "transaction.status",
  562. "project",
  563. "epm()",
  564. "failure_rate()",
  565. "p95()",
  566. ],
  567. "per_page": 50,
  568. "dataset": "metricsEnhanced",
  569. }
  570. query["orderby"] = ["team_key_transaction", "p95()"]
  571. response = self.do_request(query)
  572. assert response.status_code == 200, response.content
  573. assert len(response.data["data"]) == 3
  574. data = response.data["data"]
  575. meta = response.data["meta"]
  576. field_meta = meta["fields"]
  577. assert data[0]["team_key_transaction"] == 0
  578. assert data[0]["transaction"] == "baz_transaction"
  579. assert data[1]["team_key_transaction"] == 0
  580. assert data[1]["transaction"] == "bar_transaction"
  581. assert data[2]["team_key_transaction"] == 1
  582. assert data[2]["transaction"] == "foo_transaction"
  583. assert meta["isMetricsData"]
  584. assert field_meta["team_key_transaction"] == "boolean"
  585. assert field_meta["transaction"] == "string"
  586. # not specifying any teams should use my teams
  587. query = {
  588. "project": [self.project.id],
  589. "field": [
  590. "team_key_transaction",
  591. "transaction",
  592. "transaction.status",
  593. "project",
  594. "epm()",
  595. "failure_rate()",
  596. "p95()",
  597. ],
  598. "per_page": 50,
  599. "dataset": "metricsEnhanced",
  600. }
  601. query["orderby"] = ["team_key_transaction", "p95()"]
  602. response = self.do_request(query)
  603. assert response.status_code == 200, response.content
  604. assert len(response.data["data"]) == 3
  605. data = response.data["data"]
  606. meta = response.data["meta"]
  607. field_meta = meta["fields"]
  608. assert data[0]["team_key_transaction"] == 0
  609. assert data[0]["transaction"] == "baz_transaction"
  610. assert data[1]["team_key_transaction"] == 0
  611. assert data[1]["transaction"] == "bar_transaction"
  612. assert data[2]["team_key_transaction"] == 1
  613. assert data[2]["transaction"] == "foo_transaction"
  614. assert meta["isMetricsData"]
  615. assert field_meta["team_key_transaction"] == "boolean"
  616. assert field_meta["transaction"] == "string"
  617. def test_team_key_transactions_orderby(self):
  618. team1 = self.create_team(organization=self.organization, name="Team A")
  619. team2 = self.create_team(organization=self.organization, name="Team B")
  620. key_transactions = [
  621. (team1, "foo_transaction", 1),
  622. (team2, "baz_transaction", 100),
  623. ]
  624. # Not a key transaction
  625. self.store_transaction_metric(
  626. 100, tags={"transaction": "bar_transaction"}, timestamp=self.min_ago
  627. )
  628. for team, transaction, value in key_transactions:
  629. self.store_transaction_metric(
  630. value, tags={"transaction": transaction}, timestamp=self.min_ago
  631. )
  632. self.create_team_membership(team, user=self.user)
  633. self.project.add_team(team)
  634. TeamKeyTransaction.objects.create(
  635. organization=self.organization,
  636. transaction=transaction,
  637. project_team=ProjectTeam.objects.get(project=self.project, team=team),
  638. )
  639. query = {
  640. "team": "myteams",
  641. "project": [self.project.id],
  642. "field": [
  643. "team_key_transaction",
  644. "transaction",
  645. "transaction.status",
  646. "project",
  647. "epm()",
  648. "failure_rate()",
  649. "p95()",
  650. ],
  651. "per_page": 50,
  652. "dataset": "metricsEnhanced",
  653. }
  654. # test ascending order
  655. query["orderby"] = ["team_key_transaction", "p95()"]
  656. response = self.do_request(query)
  657. assert response.status_code == 200, response.content
  658. assert len(response.data["data"]) == 3
  659. data = response.data["data"]
  660. meta = response.data["meta"]
  661. field_meta = meta["fields"]
  662. assert data[0]["team_key_transaction"] == 0
  663. assert data[0]["transaction"] == "bar_transaction"
  664. assert data[1]["team_key_transaction"] == 1
  665. assert data[1]["transaction"] == "foo_transaction"
  666. assert data[2]["team_key_transaction"] == 1
  667. assert data[2]["transaction"] == "baz_transaction"
  668. assert meta["isMetricsData"]
  669. assert field_meta["team_key_transaction"] == "boolean"
  670. assert field_meta["transaction"] == "string"
  671. # test descending order
  672. query["orderby"] = ["-team_key_transaction", "p95()"]
  673. response = self.do_request(query)
  674. assert response.status_code == 200, response.content
  675. assert len(response.data["data"]) == 3
  676. data = response.data["data"]
  677. meta = response.data["meta"]
  678. field_meta = meta["fields"]
  679. assert data[0]["team_key_transaction"] == 1
  680. assert data[0]["transaction"] == "foo_transaction"
  681. assert data[1]["team_key_transaction"] == 1
  682. assert data[1]["transaction"] == "baz_transaction"
  683. assert data[2]["team_key_transaction"] == 0
  684. assert data[2]["transaction"] == "bar_transaction"
  685. assert meta["isMetricsData"]
  686. assert field_meta["team_key_transaction"] == "boolean"
  687. assert field_meta["transaction"] == "string"
  688. def test_team_key_transactions_query(self):
  689. team1 = self.create_team(organization=self.organization, name="Team A")
  690. team2 = self.create_team(organization=self.organization, name="Team B")
  691. key_transactions = [
  692. (team1, "foo_transaction", 1),
  693. (team2, "baz_transaction", 100),
  694. ]
  695. # Not a key transaction
  696. self.store_transaction_metric(
  697. 100, tags={"transaction": "bar_transaction"}, timestamp=self.min_ago
  698. )
  699. for team, transaction, value in key_transactions:
  700. self.store_transaction_metric(
  701. value, tags={"transaction": transaction}, timestamp=self.min_ago
  702. )
  703. self.create_team_membership(team, user=self.user)
  704. self.project.add_team(team)
  705. TeamKeyTransaction.objects.create(
  706. organization=self.organization,
  707. transaction=transaction,
  708. project_team=ProjectTeam.objects.get(project=self.project, team=team),
  709. )
  710. query = {
  711. "team": "myteams",
  712. "project": [self.project.id],
  713. # use the order by to ensure the result order
  714. "orderby": "p95()",
  715. "field": [
  716. "team_key_transaction",
  717. "transaction",
  718. "transaction.status",
  719. "project",
  720. "epm()",
  721. "failure_rate()",
  722. "p95()",
  723. ],
  724. "per_page": 50,
  725. "dataset": "metricsEnhanced",
  726. }
  727. # key transactions
  728. query["query"] = "has:team_key_transaction"
  729. response = self.do_request(query)
  730. assert response.status_code == 200, response.content
  731. assert len(response.data["data"]) == 2
  732. data = response.data["data"]
  733. meta = response.data["meta"]
  734. field_meta = meta["fields"]
  735. assert data[0]["team_key_transaction"] == 1
  736. assert data[0]["transaction"] == "foo_transaction"
  737. assert data[1]["team_key_transaction"] == 1
  738. assert data[1]["transaction"] == "baz_transaction"
  739. assert meta["isMetricsData"]
  740. assert field_meta["team_key_transaction"] == "boolean"
  741. assert field_meta["transaction"] == "string"
  742. # key transactions
  743. query["query"] = "team_key_transaction:true"
  744. response = self.do_request(query)
  745. assert response.status_code == 200, response.content
  746. assert len(response.data["data"]) == 2
  747. data = response.data["data"]
  748. meta = response.data["meta"]
  749. field_meta = meta["fields"]
  750. assert data[0]["team_key_transaction"] == 1
  751. assert data[0]["transaction"] == "foo_transaction"
  752. assert data[1]["team_key_transaction"] == 1
  753. assert data[1]["transaction"] == "baz_transaction"
  754. assert meta["isMetricsData"]
  755. assert field_meta["team_key_transaction"] == "boolean"
  756. assert field_meta["transaction"] == "string"
  757. # not key transactions
  758. query["query"] = "!has:team_key_transaction"
  759. response = self.do_request(query)
  760. assert response.status_code == 200, response.content
  761. assert len(response.data["data"]) == 1
  762. data = response.data["data"]
  763. meta = response.data["meta"]
  764. field_meta = meta["fields"]
  765. assert data[0]["team_key_transaction"] == 0
  766. assert data[0]["transaction"] == "bar_transaction"
  767. assert meta["isMetricsData"]
  768. assert field_meta["team_key_transaction"] == "boolean"
  769. assert field_meta["transaction"] == "string"
  770. # not key transactions
  771. query["query"] = "team_key_transaction:false"
  772. response = self.do_request(query)
  773. assert response.status_code == 200, response.content
  774. assert len(response.data["data"]) == 1
  775. data = response.data["data"]
  776. meta = response.data["meta"]
  777. field_meta = meta["fields"]
  778. assert data[0]["team_key_transaction"] == 0
  779. assert data[0]["transaction"] == "bar_transaction"
  780. assert meta["isMetricsData"]
  781. assert field_meta["team_key_transaction"] == "boolean"
  782. assert field_meta["transaction"] == "string"
  783. def test_team_key_transaction_not_exists(self):
  784. team1 = self.create_team(organization=self.organization, name="Team A")
  785. team2 = self.create_team(organization=self.organization, name="Team B")
  786. key_transactions = [
  787. (team1, "foo_transaction", 1),
  788. (team2, "baz_transaction", 100),
  789. ]
  790. for team, transaction, value in key_transactions:
  791. self.store_transaction_metric(
  792. value, tags={"transaction": transaction}, timestamp=self.min_ago
  793. )
  794. self.create_team_membership(team, user=self.user)
  795. self.project.add_team(team)
  796. TeamKeyTransaction.objects.create(
  797. organization=self.organization,
  798. transaction=transaction,
  799. project_team=ProjectTeam.objects.get(project=self.project, team=team),
  800. )
  801. # Don't create a metric for this one
  802. TeamKeyTransaction.objects.create(
  803. organization=self.organization,
  804. transaction="not_in_metrics",
  805. project_team=ProjectTeam.objects.get(project=self.project, team=team1),
  806. )
  807. query = {
  808. "team": "myteams",
  809. "project": [self.project.id],
  810. # use the order by to ensure the result order
  811. "orderby": "p95()",
  812. "field": [
  813. "team_key_transaction",
  814. "transaction",
  815. "transaction.status",
  816. "project",
  817. "epm()",
  818. "failure_rate()",
  819. "p95()",
  820. ],
  821. "per_page": 50,
  822. "dataset": "metricsEnhanced",
  823. }
  824. # key transactions
  825. query["query"] = "has:team_key_transaction"
  826. response = self.do_request(query)
  827. assert response.status_code == 200, response.content
  828. assert len(response.data["data"]) == 2
  829. data = response.data["data"]
  830. meta = response.data["meta"]
  831. field_meta = meta["fields"]
  832. assert data[0]["team_key_transaction"] == 1
  833. assert data[0]["transaction"] == "foo_transaction"
  834. assert data[1]["team_key_transaction"] == 1
  835. assert data[1]["transaction"] == "baz_transaction"
  836. assert meta["isMetricsData"]
  837. assert field_meta["team_key_transaction"] == "boolean"
  838. assert field_meta["transaction"] == "string"
  839. # key transactions
  840. query["query"] = "team_key_transaction:true"
  841. response = self.do_request(query)
  842. assert response.status_code == 200, response.content
  843. assert len(response.data["data"]) == 2
  844. data = response.data["data"]
  845. meta = response.data["meta"]
  846. field_meta = meta["fields"]
  847. assert data[0]["team_key_transaction"] == 1
  848. assert data[0]["transaction"] == "foo_transaction"
  849. assert data[1]["team_key_transaction"] == 1
  850. assert data[1]["transaction"] == "baz_transaction"
  851. assert meta["isMetricsData"]
  852. assert field_meta["team_key_transaction"] == "boolean"
  853. assert field_meta["transaction"] == "string"
  854. # not key transactions
  855. query["query"] = "!has:team_key_transaction"
  856. response = self.do_request(query)
  857. assert response.status_code == 200, response.content
  858. assert len(response.data["data"]) == 0
  859. data = response.data["data"]
  860. meta = response.data["meta"]
  861. field_meta = meta["fields"]
  862. assert meta["isMetricsData"]
  863. assert field_meta["team_key_transaction"] == "boolean"
  864. assert field_meta["transaction"] == "string"
  865. # not key transactions
  866. query["query"] = "team_key_transaction:false"
  867. response = self.do_request(query)
  868. assert response.status_code == 200, response.content
  869. assert len(response.data["data"]) == 0
  870. data = response.data["data"]
  871. meta = response.data["meta"]
  872. field_meta = meta["fields"]
  873. assert meta["isMetricsData"]
  874. assert field_meta["team_key_transaction"] == "boolean"
  875. assert field_meta["transaction"] == "string"
  876. def test_too_many_team_key_transactions(self):
  877. MAX_QUERYABLE_TEAM_KEY_TRANSACTIONS = 1
  878. with mock.patch(
  879. "sentry.search.events.fields.MAX_QUERYABLE_TEAM_KEY_TRANSACTIONS",
  880. MAX_QUERYABLE_TEAM_KEY_TRANSACTIONS,
  881. ):
  882. team = self.create_team(organization=self.organization, name="Team A")
  883. self.create_team_membership(team, user=self.user)
  884. self.project.add_team(team)
  885. project_team = ProjectTeam.objects.get(project=self.project, team=team)
  886. transactions = ["foo_transaction", "bar_transaction", "baz_transaction"]
  887. for i in range(MAX_QUERYABLE_TEAM_KEY_TRANSACTIONS + 1):
  888. self.store_transaction_metric(
  889. 100, tags={"transaction": transactions[i]}, timestamp=self.min_ago
  890. )
  891. TeamKeyTransaction.objects.bulk_create(
  892. [
  893. TeamKeyTransaction(
  894. organization=self.organization,
  895. project_team=project_team,
  896. transaction=transactions[i],
  897. )
  898. for i in range(MAX_QUERYABLE_TEAM_KEY_TRANSACTIONS + 1)
  899. ]
  900. )
  901. query = {
  902. "team": "myteams",
  903. "project": [self.project.id],
  904. "orderby": "p95()",
  905. "field": [
  906. "team_key_transaction",
  907. "transaction",
  908. "transaction.status",
  909. "project",
  910. "epm()",
  911. "failure_rate()",
  912. "p95()",
  913. ],
  914. "dataset": "metricsEnhanced",
  915. "per_page": 50,
  916. }
  917. response = self.do_request(query)
  918. assert response.status_code == 200, response.content
  919. assert len(response.data["data"]) == 2
  920. data = response.data["data"]
  921. meta = response.data["meta"]
  922. assert (
  923. sum(row["team_key_transaction"] for row in data)
  924. == MAX_QUERYABLE_TEAM_KEY_TRANSACTIONS
  925. )
  926. assert meta["isMetricsData"]
  927. def test_measurement_rating(self):
  928. self.store_transaction_metric(
  929. 50,
  930. metric="measurements.lcp",
  931. tags={"measurement_rating": "good", "transaction": "foo_transaction"},
  932. timestamp=self.min_ago,
  933. )
  934. self.store_transaction_metric(
  935. 15,
  936. metric="measurements.fp",
  937. tags={"measurement_rating": "good", "transaction": "foo_transaction"},
  938. timestamp=self.min_ago,
  939. )
  940. self.store_transaction_metric(
  941. 1500,
  942. metric="measurements.fcp",
  943. tags={"measurement_rating": "meh", "transaction": "foo_transaction"},
  944. timestamp=self.min_ago,
  945. )
  946. self.store_transaction_metric(
  947. 125,
  948. metric="measurements.fid",
  949. tags={"measurement_rating": "meh", "transaction": "foo_transaction"},
  950. timestamp=self.min_ago,
  951. )
  952. self.store_transaction_metric(
  953. 0.15,
  954. metric="measurements.cls",
  955. tags={"measurement_rating": "good", "transaction": "foo_transaction"},
  956. timestamp=self.min_ago,
  957. )
  958. response = self.do_request(
  959. {
  960. "field": [
  961. "transaction",
  962. "count_web_vitals(measurements.lcp, good)",
  963. "count_web_vitals(measurements.fp, good)",
  964. "count_web_vitals(measurements.fcp, meh)",
  965. "count_web_vitals(measurements.fid, meh)",
  966. "count_web_vitals(measurements.cls, good)",
  967. ],
  968. "query": "event.type:transaction",
  969. "dataset": "metricsEnhanced",
  970. "per_page": 50,
  971. }
  972. )
  973. assert response.status_code == 200, response.content
  974. assert len(response.data["data"]) == 1
  975. data = response.data["data"]
  976. meta = response.data["meta"]
  977. field_meta = meta["fields"]
  978. assert data[0]["count_web_vitals(measurements.lcp, good)"] == 1
  979. assert data[0]["count_web_vitals(measurements.fp, good)"] == 1
  980. assert data[0]["count_web_vitals(measurements.fcp, meh)"] == 1
  981. assert data[0]["count_web_vitals(measurements.fid, meh)"] == 1
  982. assert data[0]["count_web_vitals(measurements.cls, good)"] == 1
  983. assert meta["isMetricsData"]
  984. assert field_meta["count_web_vitals(measurements.lcp, good)"] == "integer"
  985. assert field_meta["count_web_vitals(measurements.fp, good)"] == "integer"
  986. assert field_meta["count_web_vitals(measurements.fcp, meh)"] == "integer"
  987. assert field_meta["count_web_vitals(measurements.fid, meh)"] == "integer"
  988. assert field_meta["count_web_vitals(measurements.cls, good)"] == "integer"
  989. def test_measurement_rating_that_does_not_exist(self):
  990. self.store_transaction_metric(
  991. 1,
  992. metric="measurements.lcp",
  993. tags={"measurement_rating": "good", "transaction": "foo_transaction"},
  994. timestamp=self.min_ago,
  995. )
  996. response = self.do_request(
  997. {
  998. "field": ["transaction", "count_web_vitals(measurements.lcp, poor)"],
  999. "query": "event.type:transaction",
  1000. "dataset": "metricsEnhanced",
  1001. "per_page": 50,
  1002. }
  1003. )
  1004. assert response.status_code == 200, response.content
  1005. assert len(response.data["data"]) == 1
  1006. data = response.data["data"]
  1007. meta = response.data["meta"]
  1008. assert data[0]["count_web_vitals(measurements.lcp, poor)"] == 0
  1009. assert meta["isMetricsData"]
  1010. assert meta["fields"]["count_web_vitals(measurements.lcp, poor)"] == "integer"
  1011. def test_count_web_vitals_invalid_vital(self):
  1012. query = {
  1013. "field": [
  1014. "count_web_vitals(measurements.foo, poor)",
  1015. ],
  1016. "project": [self.project.id],
  1017. "dataset": "metricsEnhanced",
  1018. }
  1019. response = self.do_request(query)
  1020. assert response.status_code == 400, response.content
  1021. query = {
  1022. "field": [
  1023. "count_web_vitals(tags[lcp], poor)",
  1024. ],
  1025. "project": [self.project.id],
  1026. "dataset": "metricsEnhanced",
  1027. }
  1028. response = self.do_request(query)
  1029. assert response.status_code == 400, response.content
  1030. query = {
  1031. "field": [
  1032. "count_web_vitals(transaction.duration, poor)",
  1033. ],
  1034. "project": [self.project.id],
  1035. "dataset": "metricsEnhanced",
  1036. }
  1037. response = self.do_request(query)
  1038. assert response.status_code == 400, response.content
  1039. query = {
  1040. "field": [
  1041. "count_web_vitals(measurements.lcp, bad)",
  1042. ],
  1043. "project": [self.project.id],
  1044. "dataset": "metricsEnhanced",
  1045. }
  1046. response = self.do_request(query)
  1047. assert response.status_code == 400, response.content
  1048. @mock.patch("sentry.snuba.metrics_performance.MetricsQueryBuilder")
  1049. def test_failed_dry_run_does_not_error(self, mock_builder):
  1050. with self.feature("organizations:performance-dry-run-mep"):
  1051. mock_builder.side_effect = InvalidSearchQuery("Something bad")
  1052. query = {
  1053. "field": ["count()"],
  1054. "project": [self.project.id],
  1055. }
  1056. response = self.do_request(query)
  1057. assert response.status_code == 200, response.content
  1058. assert len(mock_builder.mock_calls) == 1
  1059. assert mock_builder.call_args.kwargs["dry_run"]
  1060. mock_builder.side_effect = IncompatibleMetricsQuery("Something bad")
  1061. query = {
  1062. "field": ["count()"],
  1063. "project": [self.project.id],
  1064. }
  1065. response = self.do_request(query)
  1066. assert response.status_code == 200, response.content
  1067. assert len(mock_builder.mock_calls) == 2
  1068. assert mock_builder.call_args.kwargs["dry_run"]
  1069. mock_builder.side_effect = InvalidConditionError("Something bad")
  1070. query = {
  1071. "field": ["count()"],
  1072. "project": [self.project.id],
  1073. }
  1074. response = self.do_request(query)
  1075. assert response.status_code == 200, response.content
  1076. assert len(mock_builder.mock_calls) == 3
  1077. assert mock_builder.call_args.kwargs["dry_run"]
  1078. def test_count_unique_user_returns_zero(self):
  1079. self.store_transaction_metric(
  1080. 50,
  1081. metric="user",
  1082. tags={"transaction": "foo_transaction"},
  1083. timestamp=self.min_ago,
  1084. )
  1085. self.store_transaction_metric(
  1086. 50,
  1087. tags={"transaction": "foo_transaction"},
  1088. timestamp=self.min_ago,
  1089. )
  1090. self.store_transaction_metric(
  1091. 100,
  1092. tags={"transaction": "bar_transaction"},
  1093. timestamp=self.min_ago,
  1094. )
  1095. query = {
  1096. "project": [self.project.id],
  1097. "orderby": "p50()",
  1098. "field": [
  1099. "transaction",
  1100. "count_unique(user)",
  1101. "p50()",
  1102. ],
  1103. "dataset": "metricsEnhanced",
  1104. "per_page": 50,
  1105. }
  1106. response = self.do_request(query)
  1107. assert response.status_code == 200, response.content
  1108. assert len(response.data["data"]) == 2
  1109. data = response.data["data"]
  1110. meta = response.data["meta"]
  1111. assert data[0]["transaction"] == "foo_transaction"
  1112. assert data[0]["count_unique(user)"] == 1
  1113. assert data[1]["transaction"] == "bar_transaction"
  1114. assert data[1]["count_unique(user)"] == 0
  1115. assert meta["isMetricsData"]
  1116. def test_sum_transaction_duration(self):
  1117. self.store_transaction_metric(
  1118. 50,
  1119. tags={"transaction": "foo_transaction"},
  1120. timestamp=self.min_ago,
  1121. )
  1122. self.store_transaction_metric(
  1123. 100,
  1124. tags={"transaction": "foo_transaction"},
  1125. timestamp=self.min_ago,
  1126. )
  1127. self.store_transaction_metric(
  1128. 150,
  1129. tags={"transaction": "foo_transaction"},
  1130. timestamp=self.min_ago,
  1131. )
  1132. query = {
  1133. "project": [self.project.id],
  1134. "orderby": "sum(transaction.duration)",
  1135. "field": [
  1136. "transaction",
  1137. "sum(transaction.duration)",
  1138. ],
  1139. "dataset": "metricsEnhanced",
  1140. "per_page": 50,
  1141. }
  1142. response = self.do_request(query)
  1143. assert response.status_code == 200, response.content
  1144. assert len(response.data["data"]) == 1
  1145. data = response.data["data"]
  1146. meta = response.data["meta"]
  1147. assert data[0]["transaction"] == "foo_transaction"
  1148. assert data[0]["sum(transaction.duration)"] == 300
  1149. assert meta["isMetricsData"]
  1150. def test_custom_measurements_simple(self):
  1151. self.store_transaction_metric(
  1152. 1,
  1153. metric="measurements.something_custom",
  1154. internal_metric="d:transactions/measurements.something_custom@millisecond",
  1155. entity="metrics_distributions",
  1156. tags={"transaction": "foo_transaction"},
  1157. timestamp=self.min_ago,
  1158. )
  1159. query = {
  1160. "project": [self.project.id],
  1161. "orderby": "p50(measurements.something_custom)",
  1162. "field": [
  1163. "transaction",
  1164. "p50(measurements.something_custom)",
  1165. ],
  1166. "statsPeriod": "24h",
  1167. "dataset": "metricsEnhanced",
  1168. "per_page": 50,
  1169. }
  1170. response = self.do_request(query)
  1171. assert response.status_code == 200, response.content
  1172. assert len(response.data["data"]) == 1
  1173. data = response.data["data"]
  1174. meta = response.data["meta"]
  1175. assert data[0]["transaction"] == "foo_transaction"
  1176. assert data[0]["p50(measurements.something_custom)"] == 1
  1177. assert meta["isMetricsData"]
  1178. assert meta["fields"]["p50(measurements.something_custom)"] == "duration"
  1179. assert meta["units"]["p50(measurements.something_custom)"] == "millisecond"
  1180. def test_custom_measurement_size_meta_type(self):
  1181. self.store_transaction_metric(
  1182. 100,
  1183. metric="measurements.custom_type",
  1184. internal_metric="d:transactions/measurements.custom_type@somethingcustom",
  1185. entity="metrics_distributions",
  1186. tags={"transaction": "foo_transaction"},
  1187. timestamp=self.min_ago,
  1188. )
  1189. self.store_transaction_metric(
  1190. 100,
  1191. metric="measurements.percent",
  1192. internal_metric="d:transactions/measurements.percent@ratio",
  1193. entity="metrics_distributions",
  1194. tags={"transaction": "foo_transaction"},
  1195. timestamp=self.min_ago,
  1196. )
  1197. self.store_transaction_metric(
  1198. 100,
  1199. metric="measurements.longtaskcount",
  1200. internal_metric="d:transactions/measurements.longtaskcount@none",
  1201. entity="metrics_distributions",
  1202. tags={"transaction": "foo_transaction"},
  1203. timestamp=self.min_ago,
  1204. )
  1205. query = {
  1206. "project": [self.project.id],
  1207. "orderby": "p50(measurements.longtaskcount)",
  1208. "field": [
  1209. "transaction",
  1210. "p50(measurements.longtaskcount)",
  1211. "p50(measurements.percent)",
  1212. "p50(measurements.custom_type)",
  1213. ],
  1214. "statsPeriod": "24h",
  1215. "dataset": "metricsEnhanced",
  1216. "per_page": 50,
  1217. }
  1218. response = self.do_request(query)
  1219. assert response.status_code == 200, response.content
  1220. assert len(response.data["data"]) == 1
  1221. data = response.data["data"]
  1222. meta = response.data["meta"]
  1223. assert data[0]["transaction"] == "foo_transaction"
  1224. assert data[0]["p50(measurements.longtaskcount)"] == 100
  1225. assert data[0]["p50(measurements.percent)"] == 100
  1226. assert data[0]["p50(measurements.custom_type)"] == 100
  1227. assert meta["isMetricsData"]
  1228. assert meta["fields"]["p50(measurements.longtaskcount)"] == "integer"
  1229. assert meta["units"]["p50(measurements.longtaskcount)"] is None
  1230. assert meta["fields"]["p50(measurements.percent)"] == "percentage"
  1231. assert meta["units"]["p50(measurements.percent)"] is None
  1232. assert meta["fields"]["p50(measurements.custom_type)"] == "number"
  1233. assert meta["units"]["p50(measurements.custom_type)"] is None
  1234. def test_custom_measurement_none_type(self):
  1235. self.store_transaction_metric(
  1236. 1,
  1237. metric="measurements.cls",
  1238. entity="metrics_distributions",
  1239. tags={"transaction": "foo_transaction"},
  1240. timestamp=self.min_ago,
  1241. )
  1242. query = {
  1243. "project": [self.project.id],
  1244. "orderby": "p75(measurements.cls)",
  1245. "field": [
  1246. "transaction",
  1247. "p75(measurements.cls)",
  1248. "p99(measurements.cls)",
  1249. "max(measurements.cls)",
  1250. ],
  1251. "statsPeriod": "24h",
  1252. "dataset": "metricsEnhanced",
  1253. "per_page": 50,
  1254. }
  1255. response = self.do_request(query)
  1256. assert response.status_code == 200, response.content
  1257. assert len(response.data["data"]) == 1
  1258. data = response.data["data"]
  1259. meta = response.data["meta"]
  1260. assert data[0]["transaction"] == "foo_transaction"
  1261. assert data[0]["p75(measurements.cls)"] == 1
  1262. assert data[0]["p99(measurements.cls)"] == 1
  1263. assert data[0]["max(measurements.cls)"] == 1
  1264. assert meta["isMetricsData"]
  1265. assert meta["fields"]["p75(measurements.cls)"] == "number"
  1266. assert meta["units"]["p75(measurements.cls)"] is None
  1267. assert meta["fields"]["p99(measurements.cls)"] == "number"
  1268. assert meta["units"]["p99(measurements.cls)"] is None
  1269. assert meta["fields"]["max(measurements.cls)"] == "number"
  1270. assert meta["units"]["max(measurements.cls)"] is None
  1271. def test_custom_measurement_duration_filtering(self):
  1272. self.store_transaction_metric(
  1273. 1,
  1274. metric="measurements.runtime",
  1275. internal_metric="d:transactions/measurements.runtime@hour",
  1276. entity="metrics_distributions",
  1277. tags={"transaction": "foo_transaction"},
  1278. timestamp=self.min_ago,
  1279. )
  1280. self.store_transaction_metric(
  1281. 180,
  1282. metric="measurements.runtime",
  1283. internal_metric="d:transactions/measurements.runtime@hour",
  1284. entity="metrics_distributions",
  1285. tags={"transaction": "bar_transaction"},
  1286. timestamp=self.min_ago,
  1287. )
  1288. query = {
  1289. "project": [self.project.id],
  1290. "field": [
  1291. "transaction",
  1292. "max(measurements.runtime)",
  1293. ],
  1294. "query": "p50(measurements.runtime):>1wk",
  1295. "statsPeriod": "24h",
  1296. "dataset": "metricsEnhanced",
  1297. "per_page": 50,
  1298. }
  1299. response = self.do_request(query)
  1300. assert response.status_code == 200, response.content
  1301. assert len(response.data["data"]) == 1
  1302. data = response.data["data"]
  1303. meta = response.data["meta"]
  1304. assert data[0]["transaction"] == "bar_transaction"
  1305. assert data[0]["max(measurements.runtime)"] == 180
  1306. assert meta["isMetricsData"]
  1307. def test_custom_measurement_size_filtering(self):
  1308. self.store_transaction_metric(
  1309. 1,
  1310. metric="measurements.datacenter_memory",
  1311. internal_metric="d:transactions/measurements.datacenter_memory@petabyte",
  1312. entity="metrics_distributions",
  1313. tags={"transaction": "foo_transaction"},
  1314. timestamp=self.min_ago,
  1315. )
  1316. self.store_transaction_metric(
  1317. 100,
  1318. metric="measurements.datacenter_memory",
  1319. internal_metric="d:transactions/measurements.datacenter_memory@petabyte",
  1320. entity="metrics_distributions",
  1321. tags={"transaction": "bar_transaction"},
  1322. timestamp=self.min_ago,
  1323. )
  1324. query = {
  1325. "project": [self.project.id],
  1326. "field": [
  1327. "transaction",
  1328. "max(measurements.datacenter_memory)",
  1329. ],
  1330. "query": "p50(measurements.datacenter_memory):>5pb",
  1331. "statsPeriod": "24h",
  1332. "dataset": "metricsEnhanced",
  1333. "per_page": 50,
  1334. }
  1335. response = self.do_request(query)
  1336. assert response.status_code == 200, response.content
  1337. assert len(response.data["data"]) == 1
  1338. data = response.data["data"]
  1339. meta = response.data["meta"]
  1340. assert data[0]["transaction"] == "bar_transaction"
  1341. assert data[0]["max(measurements.datacenter_memory)"] == 100
  1342. assert meta["units"]["max(measurements.datacenter_memory)"] == "petabyte"
  1343. assert meta["fields"]["max(measurements.datacenter_memory)"] == "size"
  1344. assert meta["isMetricsData"]
  1345. def test_has_custom_measurement(self):
  1346. self.store_transaction_metric(
  1347. 33,
  1348. metric="measurements.datacenter_memory",
  1349. internal_metric="d:transactions/measurements.datacenter_memory@petabyte",
  1350. entity="metrics_distributions",
  1351. tags={"transaction": "foo_transaction"},
  1352. timestamp=self.min_ago,
  1353. )
  1354. transaction_data = load_data("transaction", timestamp=self.min_ago)
  1355. transaction_data["measurements"]["datacenter_memory"] = {
  1356. "value": 33,
  1357. "unit": "petabyte",
  1358. }
  1359. self.store_event(transaction_data, self.project.id)
  1360. measurement = "measurements.datacenter_memory"
  1361. response = self.do_request(
  1362. {
  1363. "field": ["transaction", measurement],
  1364. "query": "has:measurements.datacenter_memory",
  1365. "dataset": "discover",
  1366. }
  1367. )
  1368. assert response.status_code == 200, response.content
  1369. assert len(response.data["data"]) == 1
  1370. response = self.do_request(
  1371. {
  1372. "field": ["transaction", measurement],
  1373. "query": "!has:measurements.datacenter_memory",
  1374. "dataset": "discover",
  1375. }
  1376. )
  1377. assert response.status_code == 200, response.content
  1378. assert len(response.data["data"]) == 0
  1379. def test_environment_param(self):
  1380. self.create_environment(self.project, name="staging")
  1381. self.store_transaction_metric(
  1382. 1,
  1383. tags={"transaction": "foo_transaction", "environment": "staging"},
  1384. timestamp=self.min_ago,
  1385. )
  1386. self.store_transaction_metric(
  1387. 100,
  1388. tags={"transaction": "foo_transaction"},
  1389. timestamp=self.min_ago,
  1390. )
  1391. query = {
  1392. "project": [self.project.id],
  1393. "environment": "staging",
  1394. "orderby": "p50(transaction.duration)",
  1395. "field": [
  1396. "transaction",
  1397. "environment",
  1398. "p50(transaction.duration)",
  1399. ],
  1400. "statsPeriod": "24h",
  1401. "dataset": "metricsEnhanced",
  1402. "per_page": 50,
  1403. }
  1404. response = self.do_request(query)
  1405. assert response.status_code == 200, response.content
  1406. assert len(response.data["data"]) == 1
  1407. data = response.data["data"]
  1408. meta = response.data["meta"]
  1409. assert data[0]["transaction"] == "foo_transaction"
  1410. assert data[0]["environment"] == "staging"
  1411. assert data[0]["p50(transaction.duration)"] == 1
  1412. assert meta["isMetricsData"]
  1413. def test_environment_query(self):
  1414. self.create_environment(self.project, name="staging")
  1415. self.store_transaction_metric(
  1416. 1,
  1417. tags={"transaction": "foo_transaction", "environment": "staging"},
  1418. timestamp=self.min_ago,
  1419. )
  1420. self.store_transaction_metric(
  1421. 100,
  1422. tags={"transaction": "foo_transaction"},
  1423. timestamp=self.min_ago,
  1424. )
  1425. query = {
  1426. "project": [self.project.id],
  1427. "orderby": "p50(transaction.duration)",
  1428. "field": [
  1429. "transaction",
  1430. "environment",
  1431. "p50(transaction.duration)",
  1432. ],
  1433. "query": "!has:environment",
  1434. "statsPeriod": "24h",
  1435. "dataset": "metricsEnhanced",
  1436. "per_page": 50,
  1437. }
  1438. response = self.do_request(query)
  1439. assert response.status_code == 200, response.content
  1440. assert len(response.data["data"]) == 1
  1441. data = response.data["data"]
  1442. meta = response.data["meta"]
  1443. assert data[0]["transaction"] == "foo_transaction"
  1444. assert data[0]["environment"] is None or data[0]["environment"] == ""
  1445. assert data[0]["p50(transaction.duration)"] == 100
  1446. assert meta["isMetricsData"]
  1447. def test_has_transaction(self):
  1448. self.store_transaction_metric(
  1449. 1,
  1450. tags={},
  1451. timestamp=self.min_ago,
  1452. )
  1453. self.store_transaction_metric(
  1454. 100,
  1455. tags={"transaction": "foo_transaction"},
  1456. timestamp=self.min_ago,
  1457. )
  1458. query = {
  1459. "project": [self.project.id],
  1460. "orderby": "p50(transaction.duration)",
  1461. "field": [
  1462. "transaction",
  1463. "p50(transaction.duration)",
  1464. ],
  1465. "query": "has:transaction",
  1466. "statsPeriod": "24h",
  1467. "dataset": "metricsEnhanced",
  1468. "per_page": 50,
  1469. }
  1470. response = self.do_request(query)
  1471. assert response.status_code == 200, response.content
  1472. assert len(response.data["data"]) == 2
  1473. data = response.data["data"]
  1474. meta = response.data["meta"]
  1475. assert data[0]["transaction"] == "<< unparameterized >>"
  1476. assert data[0]["p50(transaction.duration)"] == 1
  1477. assert data[1]["transaction"] == "foo_transaction"
  1478. assert data[1]["p50(transaction.duration)"] == 100
  1479. assert meta["isMetricsData"]
  1480. query = {
  1481. "project": [self.project.id],
  1482. "orderby": "p50(transaction.duration)",
  1483. "field": [
  1484. "transaction",
  1485. "p50(transaction.duration)",
  1486. ],
  1487. "query": "!has:transaction",
  1488. "statsPeriod": "24h",
  1489. "dataset": "metricsEnhanced",
  1490. "per_page": 50,
  1491. }
  1492. response = self.do_request(query)
  1493. assert response.status_code == 400, response.content
  1494. def test_apdex_transaction_threshold(self):
  1495. ProjectTransactionThresholdOverride.objects.create(
  1496. transaction="foo_transaction",
  1497. project=self.project,
  1498. organization=self.project.organization,
  1499. threshold=600,
  1500. metric=TransactionMetric.LCP.value,
  1501. )
  1502. ProjectTransactionThresholdOverride.objects.create(
  1503. transaction="bar_transaction",
  1504. project=self.project,
  1505. organization=self.project.organization,
  1506. threshold=600,
  1507. metric=TransactionMetric.LCP.value,
  1508. )
  1509. self.store_transaction_metric(
  1510. 1,
  1511. tags={
  1512. "transaction": "foo_transaction",
  1513. constants.METRIC_SATISFACTION_TAG_KEY: constants.METRIC_SATISFIED_TAG_VALUE,
  1514. },
  1515. timestamp=self.min_ago,
  1516. )
  1517. self.store_transaction_metric(
  1518. 1,
  1519. "measurements.lcp",
  1520. tags={
  1521. "transaction": "bar_transaction",
  1522. constants.METRIC_SATISFACTION_TAG_KEY: constants.METRIC_SATISFIED_TAG_VALUE,
  1523. },
  1524. timestamp=self.min_ago,
  1525. )
  1526. response = self.do_request(
  1527. {
  1528. "field": [
  1529. "transaction",
  1530. "apdex()",
  1531. ],
  1532. "orderby": ["apdex()"],
  1533. "query": "event.type:transaction",
  1534. "dataset": "metrics",
  1535. "per_page": 50,
  1536. }
  1537. )
  1538. assert len(response.data["data"]) == 2
  1539. data = response.data["data"]
  1540. meta = response.data["meta"]
  1541. field_meta = meta["fields"]
  1542. assert data[0]["transaction"] == "bar_transaction"
  1543. # Threshold is lcp based
  1544. assert data[0]["apdex()"] == 1
  1545. assert data[1]["transaction"] == "foo_transaction"
  1546. # Threshold is lcp based
  1547. assert data[1]["apdex()"] == 0
  1548. assert meta["isMetricsData"]
  1549. assert field_meta["transaction"] == "string"
  1550. assert field_meta["apdex()"] == "number"
  1551. def test_apdex_project_threshold(self):
  1552. ProjectTransactionThreshold.objects.create(
  1553. project=self.project,
  1554. organization=self.project.organization,
  1555. threshold=600,
  1556. metric=TransactionMetric.LCP.value,
  1557. )
  1558. self.store_transaction_metric(
  1559. 1,
  1560. tags={
  1561. "transaction": "foo_transaction",
  1562. constants.METRIC_SATISFACTION_TAG_KEY: constants.METRIC_SATISFIED_TAG_VALUE,
  1563. },
  1564. timestamp=self.min_ago,
  1565. )
  1566. self.store_transaction_metric(
  1567. 1,
  1568. "measurements.lcp",
  1569. tags={
  1570. "transaction": "bar_transaction",
  1571. constants.METRIC_SATISFACTION_TAG_KEY: constants.METRIC_SATISFIED_TAG_VALUE,
  1572. },
  1573. timestamp=self.min_ago,
  1574. )
  1575. response = self.do_request(
  1576. {
  1577. "field": [
  1578. "transaction",
  1579. "apdex()",
  1580. ],
  1581. "orderby": ["apdex()"],
  1582. "query": "event.type:transaction",
  1583. "dataset": "metrics",
  1584. "per_page": 50,
  1585. }
  1586. )
  1587. assert response.status_code == 200, response.content
  1588. assert len(response.data["data"]) == 2
  1589. data = response.data["data"]
  1590. meta = response.data["meta"]
  1591. field_meta = meta["fields"]
  1592. assert data[0]["transaction"] == "bar_transaction"
  1593. # Threshold is lcp based
  1594. assert data[0]["apdex()"] == 1
  1595. assert data[1]["transaction"] == "foo_transaction"
  1596. # Threshold is lcp based
  1597. assert data[1]["apdex()"] == 0
  1598. assert meta["isMetricsData"]
  1599. assert field_meta["transaction"] == "string"
  1600. assert field_meta["apdex()"] == "number"
  1601. def test_apdex_satisfaction_param(self):
  1602. for function in ["apdex(300)", "user_misery(300)", "count_miserable(user, 300)"]:
  1603. query = {
  1604. "project": [self.project.id],
  1605. "field": [
  1606. "transaction",
  1607. function,
  1608. ],
  1609. "statsPeriod": "24h",
  1610. "dataset": "metricsEnhanced",
  1611. "per_page": 50,
  1612. }
  1613. response = self.do_request(query)
  1614. assert response.status_code == 200, response.content
  1615. assert len(response.data["data"]) == 0
  1616. meta = response.data["meta"]
  1617. assert not meta["isMetricsData"], function
  1618. query = {
  1619. "project": [self.project.id],
  1620. "field": [
  1621. "transaction",
  1622. function,
  1623. ],
  1624. "statsPeriod": "24h",
  1625. "dataset": "metrics",
  1626. "per_page": 50,
  1627. }
  1628. response = self.do_request(query)
  1629. assert response.status_code == 400, function
  1630. assert b"threshold parameter" in response.content, function
  1631. def test_mobile_metrics(self):
  1632. self.store_transaction_metric(
  1633. 0.4,
  1634. "measurements.frames_frozen_rate",
  1635. tags={
  1636. "transaction": "bar_transaction",
  1637. },
  1638. timestamp=self.min_ago,
  1639. )
  1640. query = {
  1641. "project": [self.project.id],
  1642. "field": [
  1643. "transaction",
  1644. "p50(measurements.frames_frozen_rate)",
  1645. ],
  1646. "statsPeriod": "24h",
  1647. "dataset": "metrics",
  1648. "per_page": 50,
  1649. }
  1650. response = self.do_request(query)
  1651. assert response.status_code == 200, response.content
  1652. assert len(response.data["data"]) == 1
  1653. assert response.data["data"][0]["p50(measurements.frames_frozen_rate)"] == 0.4
  1654. def test_merge_null_unparam(self):
  1655. self.store_transaction_metric(
  1656. 1,
  1657. # Transaction: unparam
  1658. tags={
  1659. "transaction": "<< unparameterized >>",
  1660. },
  1661. timestamp=self.min_ago,
  1662. )
  1663. self.store_transaction_metric(
  1664. 2,
  1665. # Transaction:null
  1666. tags={},
  1667. timestamp=self.min_ago,
  1668. )
  1669. query = {
  1670. "project": [self.project.id],
  1671. "field": [
  1672. "transaction",
  1673. "p50(transaction.duration)",
  1674. ],
  1675. "statsPeriod": "24h",
  1676. "dataset": "metrics",
  1677. "per_page": 50,
  1678. }
  1679. response = self.do_request(query)
  1680. assert response.status_code == 200, response.content
  1681. assert len(response.data["data"]) == 1
  1682. assert response.data["data"][0]["p50(transaction.duration)"] == 1.5
  1683. def test_unparam_filter(self):
  1684. self.store_transaction_metric(
  1685. 1,
  1686. # Transaction: unparam
  1687. tags={
  1688. "transaction": "<< unparameterized >>",
  1689. },
  1690. timestamp=self.min_ago,
  1691. )
  1692. self.store_transaction_metric(
  1693. 2,
  1694. # Transaction:null
  1695. tags={},
  1696. timestamp=self.min_ago,
  1697. )
  1698. self.store_transaction_metric(
  1699. 3,
  1700. tags={
  1701. "transaction": "foo_transaction",
  1702. },
  1703. timestamp=self.min_ago,
  1704. )
  1705. query = {
  1706. "project": [self.project.id],
  1707. "field": [
  1708. "transaction",
  1709. "count()",
  1710. ],
  1711. "query": 'transaction:"<< unparameterized >>"',
  1712. "statsPeriod": "24h",
  1713. "dataset": "metrics",
  1714. "per_page": 50,
  1715. }
  1716. response = self.do_request(query)
  1717. assert response.status_code == 200, response.content
  1718. assert len(response.data["data"]) == 1
  1719. assert response.data["data"][0]["transaction"] == "<< unparameterized >>"
  1720. assert response.data["data"][0]["count()"] == 2
  1721. def test_custom_measurements_without_function(self):
  1722. self.store_transaction_metric(
  1723. 33,
  1724. metric="measurements.datacenter_memory",
  1725. internal_metric="d:transactions/measurements.datacenter_memory@petabyte",
  1726. entity="metrics_distributions",
  1727. tags={"transaction": "foo_transaction"},
  1728. timestamp=self.min_ago,
  1729. )
  1730. transaction_data = load_data("transaction", timestamp=self.min_ago)
  1731. transaction_data["measurements"]["datacenter_memory"] = {
  1732. "value": 33,
  1733. "unit": "petabyte",
  1734. }
  1735. self.store_event(transaction_data, self.project.id)
  1736. measurement = "measurements.datacenter_memory"
  1737. response = self.do_request(
  1738. {
  1739. "field": ["transaction", measurement],
  1740. "query": "measurements.datacenter_memory:33pb",
  1741. "dataset": "discover",
  1742. }
  1743. )
  1744. assert response.status_code == 200, response.content
  1745. data = response.data["data"]
  1746. assert len(data) == 1
  1747. assert data[0][measurement] == 33
  1748. meta = response.data["meta"]
  1749. field_meta = meta["fields"]
  1750. unit_meta = meta["units"]
  1751. assert field_meta[measurement] == "size"
  1752. assert unit_meta[measurement] == "petabyte"
  1753. assert not meta["isMetricsData"]
  1754. def test_custom_measurements_with_function(self):
  1755. self.store_transaction_metric(
  1756. 33,
  1757. metric="measurements.datacenter_memory",
  1758. internal_metric="d:transactions/measurements.datacenter_memory@petabyte",
  1759. entity="metrics_distributions",
  1760. tags={"transaction": "foo_transaction"},
  1761. timestamp=self.min_ago,
  1762. )
  1763. transaction_data = load_data("transaction", timestamp=self.min_ago)
  1764. transaction_data["measurements"]["datacenter_memory"] = {
  1765. "value": 33,
  1766. "unit": "petabyte",
  1767. }
  1768. self.store_event(transaction_data, self.project.id)
  1769. measurement = "p50(measurements.datacenter_memory)"
  1770. response = self.do_request(
  1771. {
  1772. "field": ["transaction", measurement],
  1773. "query": "measurements.datacenter_memory:33pb",
  1774. "dataset": "discover",
  1775. }
  1776. )
  1777. assert response.status_code == 200, response.content
  1778. data = response.data["data"]
  1779. assert len(data) == 1
  1780. assert data[0][measurement] == 33
  1781. meta = response.data["meta"]
  1782. field_meta = meta["fields"]
  1783. unit_meta = meta["units"]
  1784. assert field_meta[measurement] == "size"
  1785. assert unit_meta[measurement] == "petabyte"
  1786. assert not meta["isMetricsData"]
  1787. def test_custom_measurements_equation(self):
  1788. self.store_transaction_metric(
  1789. 33,
  1790. metric="measurements.datacenter_memory",
  1791. internal_metric="d:transactions/measurements.datacenter_memory@petabyte",
  1792. entity="metrics_distributions",
  1793. tags={"transaction": "foo_transaction"},
  1794. timestamp=self.min_ago,
  1795. )
  1796. transaction_data = load_data("transaction", timestamp=self.min_ago)
  1797. transaction_data["measurements"]["datacenter_memory"] = {
  1798. "value": 33,
  1799. "unit": "petabyte",
  1800. }
  1801. self.store_event(transaction_data, self.project.id)
  1802. response = self.do_request(
  1803. {
  1804. "field": [
  1805. "transaction",
  1806. "measurements.datacenter_memory",
  1807. "equation|measurements.datacenter_memory / 3",
  1808. ],
  1809. "query": "",
  1810. "dataset": "discover",
  1811. }
  1812. )
  1813. assert response.status_code == 200, response.content
  1814. data = response.data["data"]
  1815. assert len(data) == 1
  1816. assert data[0]["measurements.datacenter_memory"] == 33
  1817. assert data[0]["equation|measurements.datacenter_memory / 3"] == 11
  1818. meta = response.data["meta"]
  1819. assert not meta["isMetricsData"]
  1820. def test_transaction_wildcard(self):
  1821. self.store_transaction_metric(
  1822. 1,
  1823. tags={"transaction": "foo_transaction"},
  1824. timestamp=self.min_ago,
  1825. )
  1826. self.store_transaction_metric(
  1827. 1,
  1828. tags={"transaction": "bar_transaction"},
  1829. timestamp=self.min_ago,
  1830. )
  1831. response = self.do_request(
  1832. {
  1833. "field": [
  1834. "transaction",
  1835. "p90()",
  1836. ],
  1837. "query": "transaction:foo*",
  1838. "dataset": "metrics",
  1839. }
  1840. )
  1841. assert response.status_code == 200, response.content
  1842. data = response.data["data"]
  1843. assert len(data) == 1
  1844. assert data[0]["p90()"] == 1
  1845. meta = response.data["meta"]
  1846. assert meta["isMetricsData"]
  1847. assert data[0]["transaction"] == "foo_transaction"
  1848. def test_transaction_status_wildcard(self):
  1849. self.store_transaction_metric(
  1850. 1,
  1851. tags={"transaction": "foo_transaction", "transaction.status": "foobar"},
  1852. timestamp=self.min_ago,
  1853. )
  1854. response = self.do_request(
  1855. {
  1856. "field": [
  1857. "transaction",
  1858. "p90()",
  1859. ],
  1860. "query": "transaction.status:f*bar",
  1861. "dataset": "metrics",
  1862. }
  1863. )
  1864. assert response.status_code == 200, response.content
  1865. data = response.data["data"]
  1866. assert len(data) == 1
  1867. assert data[0]["p90()"] == 1
  1868. meta = response.data["meta"]
  1869. assert meta["isMetricsData"]
  1870. class OrganizationEventsMetricsEnhancedPerformanceEndpointTestWithMetricLayer(
  1871. OrganizationEventsMetricsEnhancedPerformanceEndpointTest
  1872. ):
  1873. def setUp(self):
  1874. super().setUp()
  1875. self.features["organizations:use-metrics-layer"] = True
  1876. @pytest.mark.xfail(reason="Having not supported")
  1877. def test_custom_measurement_duration_filtering(self):
  1878. super().test_custom_measurement_size_filtering()
  1879. @pytest.mark.xfail(reason="Having not supported")
  1880. def test_having_condition_not_selected(self):
  1881. super().test_having_condition_not_selected()
  1882. @pytest.mark.xfail(reason="Having not supported")
  1883. def test_custom_measurement_size_filtering(self):
  1884. super().test_custom_measurement_size_filtering()
  1885. @pytest.mark.xfail(reason="Having not supported")
  1886. def test_having_condition(self):
  1887. super().test_having_condition()