test_organization_events_span_indexed.py 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510
  1. import uuid
  2. from unittest import mock
  3. import pytest
  4. from tests.snuba.api.endpoints.test_organization_events import OrganizationEventsEndpointTestBase
  5. class OrganizationEventsSpanIndexedEndpointTest(OrganizationEventsEndpointTestBase):
  6. is_eap = False
  7. use_rpc = False
  8. """Test the indexed spans dataset.
  9. To run this locally you may need to set the ENABLE_SPANS_CONSUMER flag to True in Snuba.
  10. A way to do this is
  11. 1. run: `sentry devservices down snuba`
  12. 2. clone snuba locally
  13. 3. run: `export ENABLE_SPANS_CONSUMER=True`
  14. 4. run snuba
  15. At this point tests should work locally
  16. Once span ingestion is on by default this will no longer need to be done
  17. """
  18. @property
  19. def dataset(self):
  20. if self.is_eap:
  21. return "spans"
  22. else:
  23. return "spansIndexed"
  24. def do_request(self, query, features=None, **kwargs):
  25. query["useRpc"] = "1" if self.use_rpc else "0"
  26. return super().do_request(query, features, **kwargs)
  27. def setUp(self):
  28. super().setUp()
  29. self.features = {
  30. "organizations:starfish-view": True,
  31. }
  32. @pytest.mark.querybuilder
  33. def test_simple(self):
  34. self.store_spans(
  35. [
  36. self.create_span(
  37. {"description": "foo", "sentry_tags": {"status": "success"}},
  38. start_ts=self.ten_mins_ago,
  39. ),
  40. self.create_span(
  41. {"description": "bar", "sentry_tags": {"status": "invalid_argument"}},
  42. start_ts=self.ten_mins_ago,
  43. ),
  44. ],
  45. is_eap=self.is_eap,
  46. )
  47. response = self.do_request(
  48. {
  49. "field": ["span.status", "description", "count()"],
  50. "query": "",
  51. "orderby": "description",
  52. "project": self.project.id,
  53. "dataset": self.dataset,
  54. }
  55. )
  56. assert response.status_code == 200, response.content
  57. data = response.data["data"]
  58. meta = response.data["meta"]
  59. assert len(data) == 2
  60. assert data == [
  61. {
  62. "span.status": "invalid_argument",
  63. "description": "bar",
  64. "count()": 1,
  65. },
  66. {
  67. "span.status": "ok",
  68. "description": "foo",
  69. "count()": 1,
  70. },
  71. ]
  72. assert meta["dataset"] == self.dataset
  73. def test_spm(self):
  74. self.store_spans(
  75. [
  76. self.create_span(
  77. {"description": "foo", "sentry_tags": {"status": "success"}},
  78. start_ts=self.ten_mins_ago,
  79. ),
  80. ],
  81. is_eap=self.is_eap,
  82. )
  83. response = self.do_request(
  84. {
  85. "field": ["description", "spm()"],
  86. "query": "",
  87. "orderby": "description",
  88. "project": self.project.id,
  89. "dataset": self.dataset,
  90. }
  91. )
  92. assert response.status_code == 200, response.content
  93. data = response.data["data"]
  94. meta = response.data["meta"]
  95. assert len(data) == 1
  96. assert data == [
  97. {
  98. "description": "foo",
  99. "spm()": 1 / (90 * 24 * 60),
  100. },
  101. ]
  102. assert meta["dataset"] == self.dataset
  103. def test_id_fields(self):
  104. self.store_spans(
  105. [
  106. self.create_span(
  107. {"description": "foo", "sentry_tags": {"status": "success"}},
  108. start_ts=self.ten_mins_ago,
  109. ),
  110. self.create_span(
  111. {"description": "bar", "sentry_tags": {"status": "invalid_argument"}},
  112. start_ts=self.ten_mins_ago,
  113. ),
  114. ],
  115. is_eap=self.is_eap,
  116. )
  117. response = self.do_request(
  118. {
  119. "field": ["id", "span_id"],
  120. "query": "",
  121. "orderby": "id",
  122. "project": self.project.id,
  123. "dataset": self.dataset,
  124. }
  125. )
  126. assert response.status_code == 200, response.content
  127. data = response.data["data"]
  128. meta = response.data["meta"]
  129. assert len(data) == 2
  130. for obj in data:
  131. assert obj["id"] == obj["span_id"]
  132. assert meta["dataset"] == self.dataset
  133. def test_sentry_tags_vs_tags(self):
  134. self.store_spans(
  135. [
  136. self.create_span(
  137. {"sentry_tags": {"transaction.method": "foo"}}, start_ts=self.ten_mins_ago
  138. ),
  139. ],
  140. is_eap=self.is_eap,
  141. )
  142. response = self.do_request(
  143. {
  144. "field": ["transaction.method", "count()"],
  145. "query": "",
  146. "orderby": "count()",
  147. "project": self.project.id,
  148. "dataset": self.dataset,
  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]["transaction.method"] == "foo"
  156. assert meta["dataset"] == self.dataset
  157. def test_sentry_tags_syntax(self):
  158. self.store_spans(
  159. [
  160. self.create_span(
  161. {"sentry_tags": {"transaction.method": "foo"}}, start_ts=self.ten_mins_ago
  162. ),
  163. ],
  164. is_eap=self.is_eap,
  165. )
  166. response = self.do_request(
  167. {
  168. "field": ["sentry_tags[transaction.method]", "count()"],
  169. "query": "",
  170. "orderby": "count()",
  171. "project": self.project.id,
  172. "dataset": self.dataset,
  173. }
  174. )
  175. assert response.status_code == 200, response.content
  176. data = response.data["data"]
  177. meta = response.data["meta"]
  178. assert len(data) == 1
  179. assert data[0]["sentry_tags[transaction.method]"] == "foo"
  180. assert meta["dataset"] == self.dataset
  181. def test_module_alias(self):
  182. # Delegates `span.module` to `sentry_tags[category]`. Maps `"db.redis"` spans to the `"cache"` module
  183. self.store_spans(
  184. [
  185. self.create_span(
  186. {
  187. "op": "db.redis",
  188. "description": "EXEC *",
  189. "sentry_tags": {
  190. "description": "EXEC *",
  191. "category": "db",
  192. "op": "db.redis",
  193. "transaction": "/app/index",
  194. },
  195. },
  196. start_ts=self.ten_mins_ago,
  197. ),
  198. ],
  199. is_eap=self.is_eap,
  200. )
  201. response = self.do_request(
  202. {
  203. "field": ["span.module", "span.description"],
  204. "query": "span.module:cache",
  205. "project": self.project.id,
  206. "dataset": self.dataset,
  207. }
  208. )
  209. assert response.status_code == 200, response.content
  210. data = response.data["data"]
  211. meta = response.data["meta"]
  212. assert len(data) == 1
  213. assert data[0]["span.module"] == "cache"
  214. assert data[0]["span.description"] == "EXEC *"
  215. assert meta["dataset"] == self.dataset
  216. def test_device_class_filter_unknown(self):
  217. self.store_spans(
  218. [
  219. self.create_span({"sentry_tags": {"device.class": ""}}, start_ts=self.ten_mins_ago),
  220. ],
  221. is_eap=self.is_eap,
  222. )
  223. response = self.do_request(
  224. {
  225. "field": ["device.class", "count()"],
  226. "query": "device.class:Unknown",
  227. "orderby": "count()",
  228. "project": self.project.id,
  229. "dataset": self.dataset,
  230. }
  231. )
  232. assert response.status_code == 200, response.content
  233. data = response.data["data"]
  234. meta = response.data["meta"]
  235. assert len(data) == 1
  236. assert data[0]["device.class"] == "Unknown"
  237. assert meta["dataset"] == self.dataset
  238. def test_network_span(self):
  239. self.store_spans(
  240. [
  241. self.create_span(
  242. {
  243. "sentry_tags": {
  244. "action": "GET",
  245. "category": "http",
  246. "description": "GET https://*.resource.com",
  247. "domain": "*.resource.com",
  248. "op": "http.client",
  249. "status_code": "200",
  250. "transaction": "/api/0/data/",
  251. "transaction.method": "GET",
  252. "transaction.op": "http.server",
  253. }
  254. },
  255. start_ts=self.ten_mins_ago,
  256. ),
  257. ],
  258. is_eap=self.is_eap,
  259. )
  260. response = self.do_request(
  261. {
  262. "field": ["span.op", "span.status_code"],
  263. "query": "span.module:http span.status_code:200",
  264. "project": self.project.id,
  265. "dataset": self.dataset,
  266. }
  267. )
  268. assert response.status_code == 200, response.content
  269. data = response.data["data"]
  270. meta = response.data["meta"]
  271. assert len(data) == 1
  272. assert data[0]["span.op"] == "http.client"
  273. assert data[0]["span.status_code"] == "200"
  274. assert meta["dataset"] == self.dataset
  275. def test_other_category_span(self):
  276. self.store_spans(
  277. [
  278. self.create_span(
  279. {
  280. "sentry_tags": {
  281. "action": "GET",
  282. "category": "alternative",
  283. "description": "GET https://*.resource.com",
  284. "domain": "*.resource.com",
  285. "op": "alternative",
  286. "status_code": "200",
  287. "transaction": "/api/0/data/",
  288. "transaction.method": "GET",
  289. "transaction.op": "http.server",
  290. }
  291. },
  292. start_ts=self.ten_mins_ago,
  293. ),
  294. ],
  295. is_eap=self.is_eap,
  296. )
  297. response = self.do_request(
  298. {
  299. "field": ["span.op", "span.status_code"],
  300. "query": "span.module:other span.status_code:200",
  301. "project": self.project.id,
  302. "dataset": self.dataset,
  303. }
  304. )
  305. assert response.status_code == 200, response.content
  306. data = response.data["data"]
  307. meta = response.data["meta"]
  308. assert len(data) == 1
  309. assert data[0]["span.op"] == "alternative"
  310. assert data[0]["span.status_code"] == "200"
  311. assert meta["dataset"] == self.dataset
  312. def test_inp_span(self):
  313. replay_id = uuid.uuid4().hex
  314. self.store_spans(
  315. [
  316. self.create_span(
  317. {
  318. "sentry_tags": {
  319. "replay_id": replay_id,
  320. "browser.name": "Chrome",
  321. "transaction": "/pageloads/",
  322. }
  323. },
  324. start_ts=self.ten_mins_ago,
  325. ),
  326. ],
  327. is_eap=self.is_eap,
  328. )
  329. response = self.do_request(
  330. {
  331. "field": ["replay.id", "browser.name", "origin.transaction", "count()"],
  332. "query": f"replay.id:{replay_id} AND browser.name:Chrome AND origin.transaction:/pageloads/",
  333. "orderby": "count()",
  334. "project": self.project.id,
  335. "dataset": self.dataset,
  336. }
  337. )
  338. assert response.status_code == 200, response.content
  339. data = response.data["data"]
  340. meta = response.data["meta"]
  341. assert len(data) == 1
  342. assert data[0]["replay.id"] == replay_id
  343. assert data[0]["browser.name"] == "Chrome"
  344. assert data[0]["origin.transaction"] == "/pageloads/"
  345. assert meta["dataset"] == self.dataset
  346. def test_id_filtering(self):
  347. span = self.create_span({"description": "foo"}, start_ts=self.ten_mins_ago)
  348. self.store_span(span, is_eap=self.is_eap)
  349. response = self.do_request(
  350. {
  351. "field": ["description", "count()"],
  352. "query": f"id:{span['span_id']}",
  353. "orderby": "description",
  354. "project": self.project.id,
  355. "dataset": self.dataset,
  356. }
  357. )
  358. assert response.status_code == 200, response.content
  359. data = response.data["data"]
  360. meta = response.data["meta"]
  361. assert len(data) == 1
  362. assert data[0]["description"] == "foo"
  363. assert meta["dataset"] == self.dataset
  364. response = self.do_request(
  365. {
  366. "field": ["description", "count()"],
  367. "query": f"transaction.id:{span['event_id']}",
  368. "orderby": "description",
  369. "project": self.project.id,
  370. "dataset": self.dataset,
  371. }
  372. )
  373. assert response.status_code == 200, response.content
  374. data = response.data["data"]
  375. meta = response.data["meta"]
  376. assert len(data) == 1
  377. assert data[0]["description"] == "foo"
  378. assert meta["dataset"] == self.dataset
  379. def test_span_op_casing(self):
  380. self.store_spans(
  381. [
  382. self.create_span(
  383. {
  384. "sentry_tags": {
  385. "replay_id": "abc123",
  386. "browser.name": "Chrome",
  387. "transaction": "/pageloads/",
  388. "op": "this is a transaction",
  389. }
  390. },
  391. start_ts=self.ten_mins_ago,
  392. ),
  393. ],
  394. is_eap=self.is_eap,
  395. )
  396. response = self.do_request(
  397. {
  398. "field": ["span.op", "count()"],
  399. "query": 'span.op:"ThIs Is a TraNSActiON"',
  400. "orderby": "count()",
  401. "project": self.project.id,
  402. "dataset": self.dataset,
  403. }
  404. )
  405. assert response.status_code == 200, response.content
  406. data = response.data["data"]
  407. meta = response.data["meta"]
  408. assert len(data) == 1
  409. assert data[0]["span.op"] == "this is a transaction"
  410. assert meta["dataset"] == self.dataset
  411. def test_queue_span(self):
  412. self.store_spans(
  413. [
  414. self.create_span(
  415. {
  416. "measurements": {
  417. "messaging.message.body.size": {"value": 1024, "unit": "byte"},
  418. "messaging.message.receive.latency": {
  419. "value": 1000,
  420. "unit": "millisecond",
  421. },
  422. "messaging.message.retry.count": {"value": 2, "unit": "none"},
  423. },
  424. "sentry_tags": {
  425. "transaction": "queue-processor",
  426. "messaging.destination.name": "events",
  427. "messaging.message.id": "abc123",
  428. "trace.status": "ok",
  429. },
  430. },
  431. start_ts=self.ten_mins_ago,
  432. ),
  433. ],
  434. is_eap=self.is_eap,
  435. )
  436. response = self.do_request(
  437. {
  438. "field": [
  439. "transaction",
  440. "messaging.destination.name",
  441. "messaging.message.id",
  442. "measurements.messaging.message.receive.latency",
  443. "measurements.messaging.message.body.size",
  444. "measurements.messaging.message.retry.count",
  445. "trace.status",
  446. "count()",
  447. ],
  448. "query": 'messaging.destination.name:"events"',
  449. "orderby": "count()",
  450. "project": self.project.id,
  451. "dataset": self.dataset,
  452. }
  453. )
  454. assert response.status_code == 200, response.content
  455. data = response.data["data"]
  456. meta = response.data["meta"]
  457. assert len(data) == 1
  458. assert data[0]["transaction"] == "queue-processor"
  459. assert data[0]["messaging.destination.name"] == "events"
  460. assert data[0]["messaging.message.id"] == "abc123"
  461. assert data[0]["trace.status"] == "ok"
  462. assert data[0]["measurements.messaging.message.receive.latency"] == 1000
  463. assert data[0]["measurements.messaging.message.body.size"] == 1024
  464. assert data[0]["measurements.messaging.message.retry.count"] == 2
  465. assert meta["dataset"] == self.dataset
  466. def test_tag_wildcards(self):
  467. self.store_spans(
  468. [
  469. self.create_span(
  470. {"description": "foo", "tags": {"foo": "BaR"}},
  471. start_ts=self.ten_mins_ago,
  472. ),
  473. self.create_span(
  474. {"description": "qux", "tags": {"foo": "QuX"}},
  475. start_ts=self.ten_mins_ago,
  476. ),
  477. ],
  478. is_eap=self.is_eap,
  479. )
  480. for query in [
  481. "foo:b*",
  482. "foo:*r",
  483. "foo:*a*",
  484. "foo:b*r",
  485. ]:
  486. response = self.do_request(
  487. {
  488. "field": ["foo", "count()"],
  489. "query": query,
  490. "project": self.project.id,
  491. "dataset": self.dataset,
  492. }
  493. )
  494. assert response.status_code == 200, response.content
  495. assert response.data["data"] == [{"foo": "BaR", "count()": 1}]
  496. def test_query_for_missing_tag(self):
  497. self.store_spans(
  498. [
  499. self.create_span(
  500. {"description": "foo"},
  501. start_ts=self.ten_mins_ago,
  502. ),
  503. self.create_span(
  504. {"description": "qux", "tags": {"foo": "bar"}},
  505. start_ts=self.ten_mins_ago,
  506. ),
  507. ],
  508. is_eap=self.is_eap,
  509. )
  510. response = self.do_request(
  511. {
  512. "field": ["foo", "count()"],
  513. "query": 'foo:""',
  514. "project": self.project.id,
  515. "dataset": self.dataset,
  516. }
  517. )
  518. assert response.status_code == 200, response.content
  519. assert response.data["data"] == [{"foo": "", "count()": 1}]
  520. def test_count_field_type(self):
  521. response = self.do_request(
  522. {
  523. "field": ["count()"],
  524. "project": self.project.id,
  525. "dataset": self.dataset,
  526. }
  527. )
  528. assert response.status_code == 200, response.content
  529. assert response.data["meta"]["fields"] == {"count()": "integer"}
  530. assert response.data["meta"]["units"] == {"count()": None}
  531. assert response.data["data"] == [{"count()": 0}]
  532. def test_simple_measurements(self):
  533. keys = [
  534. ("app_start_cold", "duration", "millisecond"),
  535. ("app_start_warm", "duration", "millisecond"),
  536. ("frames_frozen", "number", None),
  537. ("frames_frozen_rate", "percentage", None),
  538. ("frames_slow", "number", None),
  539. ("frames_slow_rate", "percentage", None),
  540. ("frames_total", "number", None),
  541. ("time_to_initial_display", "duration", "millisecond"),
  542. ("time_to_full_display", "duration", "millisecond"),
  543. ("stall_count", "number", None),
  544. ("stall_percentage", "percentage", None),
  545. ("stall_stall_longest_time", "number", None),
  546. ("stall_stall_total_time", "number", None),
  547. ("cls", "number", None),
  548. ("fcp", "duration", "millisecond"),
  549. ("fid", "duration", "millisecond"),
  550. ("fp", "duration", "millisecond"),
  551. ("inp", "duration", "millisecond"),
  552. ("lcp", "duration", "millisecond"),
  553. ("ttfb", "duration", "millisecond"),
  554. ("ttfb.requesttime", "duration", "millisecond"),
  555. ("score.cls", "number", None),
  556. ("score.fcp", "number", None),
  557. ("score.fid", "number", None),
  558. ("score.inp", "number", None),
  559. ("score.lcp", "number", None),
  560. ("score.ttfb", "number", None),
  561. ("score.total", "number", None),
  562. ("score.weight.cls", "number", None),
  563. ("score.weight.fcp", "number", None),
  564. ("score.weight.fid", "number", None),
  565. ("score.weight.inp", "number", None),
  566. ("score.weight.lcp", "number", None),
  567. ("score.weight.ttfb", "number", None),
  568. ("cache.item_size", "number", None),
  569. ("messaging.message.body.size", "number", None),
  570. ("messaging.message.receive.latency", "number", None),
  571. ("messaging.message.retry.count", "number", None),
  572. ("http.response_content_length", "number", None),
  573. ]
  574. self.store_spans(
  575. [
  576. self.create_span(
  577. {
  578. "description": "foo",
  579. "sentry_tags": {"status": "success"},
  580. "tags": {"bar": "bar2"},
  581. },
  582. measurements={k: {"value": i + 1} for i, (k, _, _) in enumerate(keys)},
  583. start_ts=self.ten_mins_ago,
  584. ),
  585. ],
  586. is_eap=self.is_eap,
  587. )
  588. for i, (k, type, unit) in enumerate(keys):
  589. key = f"measurements.{k}"
  590. response = self.do_request(
  591. {
  592. "field": [key],
  593. "query": "description:foo",
  594. "project": self.project.id,
  595. "dataset": self.dataset,
  596. }
  597. )
  598. assert response.status_code == 200, response.content
  599. assert response.data["meta"] == {
  600. "dataset": mock.ANY,
  601. "datasetReason": "unchanged",
  602. "fields": {
  603. key: type,
  604. "id": "string",
  605. "project.name": "string",
  606. },
  607. "isMetricsData": False,
  608. "isMetricsExtractedData": False,
  609. "tips": {},
  610. "units": {
  611. key: unit,
  612. "id": None,
  613. "project.name": None,
  614. },
  615. }
  616. assert response.data["data"] == [
  617. {
  618. key: i + 1,
  619. "id": mock.ANY,
  620. "project.name": self.project.slug,
  621. }
  622. ]
  623. def test_environment(self):
  624. self.create_environment(self.project, name="prod")
  625. self.create_environment(self.project, name="test")
  626. self.store_spans(
  627. [
  628. self.create_span(
  629. {"description": "foo", "sentry_tags": {"environment": "prod"}},
  630. start_ts=self.ten_mins_ago,
  631. ),
  632. self.create_span(
  633. {"description": "foo", "sentry_tags": {"environment": "test"}},
  634. start_ts=self.ten_mins_ago,
  635. ),
  636. ],
  637. is_eap=self.is_eap,
  638. )
  639. response = self.do_request(
  640. {
  641. "field": ["environment", "count()"],
  642. "project": self.project.id,
  643. "environment": "prod",
  644. "dataset": self.dataset,
  645. }
  646. )
  647. assert response.status_code == 200, response.content
  648. assert response.data["data"] == [
  649. {"environment": "prod", "count()": 1},
  650. ]
  651. def test_transaction(self):
  652. self.store_spans(
  653. [
  654. self.create_span(
  655. {"description": "foo", "sentry_tags": {"transaction": "bar"}},
  656. start_ts=self.ten_mins_ago,
  657. ),
  658. ],
  659. is_eap=self.is_eap,
  660. )
  661. response = self.do_request(
  662. {
  663. "field": ["description", "count()"],
  664. "query": "transaction:bar",
  665. "orderby": "description",
  666. "project": self.project.id,
  667. "dataset": self.dataset,
  668. }
  669. )
  670. assert response.status_code == 200, response.content
  671. data = response.data["data"]
  672. meta = response.data["meta"]
  673. assert len(data) == 1
  674. assert data == [
  675. {
  676. "description": "foo",
  677. "count()": 1,
  678. },
  679. ]
  680. assert meta["dataset"] == self.dataset
  681. def test_span_status(self):
  682. self.store_spans(
  683. [
  684. self.create_span(
  685. {"description": "foo", "sentry_tags": {"status": "internal_error"}},
  686. start_ts=self.ten_mins_ago,
  687. ),
  688. ],
  689. is_eap=self.is_eap,
  690. )
  691. response = self.do_request(
  692. {
  693. "field": ["description", "count()"],
  694. "query": "span.status:internal_error",
  695. "orderby": "description",
  696. "project": self.project.id,
  697. "dataset": self.dataset,
  698. }
  699. )
  700. assert response.status_code == 200, response.content
  701. data = response.data["data"]
  702. meta = response.data["meta"]
  703. assert len(data) == 1
  704. assert data == [
  705. {
  706. "description": "foo",
  707. "count()": 1,
  708. },
  709. ]
  710. assert meta["dataset"] == self.dataset
  711. class OrganizationEventsEAPSpanEndpointTest(OrganizationEventsSpanIndexedEndpointTest):
  712. is_eap = True
  713. use_rpc = False
  714. def test_simple(self):
  715. self.store_spans(
  716. [
  717. self.create_span(
  718. {"description": "foo", "sentry_tags": {"status": "success"}},
  719. start_ts=self.ten_mins_ago,
  720. ),
  721. self.create_span(
  722. {"description": "bar", "sentry_tags": {"status": "invalid_argument"}},
  723. start_ts=self.ten_mins_ago,
  724. ),
  725. ],
  726. is_eap=self.is_eap,
  727. )
  728. response = self.do_request(
  729. {
  730. "field": ["span.status", "description", "count()"],
  731. "query": "",
  732. "orderby": "description",
  733. "project": self.project.id,
  734. "dataset": self.dataset,
  735. }
  736. )
  737. assert response.status_code == 200, response.content
  738. data = response.data["data"]
  739. meta = response.data["meta"]
  740. assert len(data) == 2
  741. assert data == [
  742. {
  743. "span.status": "invalid_argument",
  744. "description": "bar",
  745. "count()": 1,
  746. },
  747. {
  748. "span.status": "success",
  749. "description": "foo",
  750. "count()": 1,
  751. },
  752. ]
  753. assert meta["dataset"] == self.dataset
  754. @pytest.mark.xfail(reason="event_id isn't being written to the new table")
  755. def test_id_filtering(self):
  756. super().test_id_filtering()
  757. def test_span_duration(self):
  758. spans = [
  759. self.create_span(
  760. {"description": "bar", "sentry_tags": {"status": "invalid_argument"}},
  761. start_ts=self.ten_mins_ago,
  762. ),
  763. self.create_span(
  764. {"description": "foo", "sentry_tags": {"status": "success"}},
  765. start_ts=self.ten_mins_ago,
  766. ),
  767. ]
  768. self.store_spans(spans, is_eap=self.is_eap)
  769. response = self.do_request(
  770. {
  771. "field": ["span.duration", "description"],
  772. "query": "",
  773. "orderby": "description",
  774. "project": self.project.id,
  775. "dataset": self.dataset,
  776. }
  777. )
  778. assert response.status_code == 200, response.content
  779. data = response.data["data"]
  780. meta = response.data["meta"]
  781. assert len(data) == 2
  782. assert data == [
  783. {
  784. "span.duration": 1000.0,
  785. "description": "bar",
  786. "project.name": self.project.slug,
  787. "id": spans[0]["span_id"],
  788. },
  789. {
  790. "span.duration": 1000.0,
  791. "description": "foo",
  792. "project.name": self.project.slug,
  793. "id": spans[1]["span_id"],
  794. },
  795. ]
  796. assert meta["dataset"] == self.dataset
  797. def test_aggregate_numeric_attr_weighted(self):
  798. self.store_spans(
  799. [
  800. self.create_span(
  801. {
  802. "description": "foo",
  803. "sentry_tags": {"status": "success"},
  804. "tags": {"bar": "bar1"},
  805. },
  806. start_ts=self.ten_mins_ago,
  807. ),
  808. self.create_span(
  809. {
  810. "description": "foo",
  811. "sentry_tags": {"status": "success"},
  812. "tags": {"bar": "bar2"},
  813. },
  814. measurements={"foo": {"value": 5}},
  815. start_ts=self.ten_mins_ago,
  816. ),
  817. self.create_span(
  818. {
  819. "description": "foo",
  820. "sentry_tags": {"status": "success"},
  821. "tags": {"bar": "bar3"},
  822. },
  823. start_ts=self.ten_mins_ago,
  824. ),
  825. ],
  826. is_eap=self.is_eap,
  827. )
  828. response = self.do_request(
  829. {
  830. "field": [
  831. "description",
  832. "count_unique(bar)",
  833. "count_unique(tags[bar])",
  834. "count_unique(tags[bar,string])",
  835. "count()",
  836. "count(span.duration)",
  837. "count(tags[foo, number])",
  838. "sum(tags[foo,number])",
  839. "avg(tags[foo,number])",
  840. "p50(tags[foo,number])",
  841. "p75(tags[foo,number])",
  842. "p95(tags[foo,number])",
  843. "p99(tags[foo,number])",
  844. "p100(tags[foo,number])",
  845. "min(tags[foo,number])",
  846. "max(tags[foo,number])",
  847. ],
  848. "query": "",
  849. "orderby": "description",
  850. "project": self.project.id,
  851. "dataset": self.dataset,
  852. }
  853. )
  854. assert response.status_code == 200, response.content
  855. assert len(response.data["data"]) == 1
  856. data = response.data["data"]
  857. assert data[0] == {
  858. "description": "foo",
  859. "count_unique(bar)": 3,
  860. "count_unique(tags[bar])": 3,
  861. "count_unique(tags[bar,string])": 3,
  862. "count()": 3,
  863. "count(span.duration)": 3,
  864. "count(tags[foo, number])": 1,
  865. "sum(tags[foo,number])": 5.0,
  866. "avg(tags[foo,number])": 5.0,
  867. "p50(tags[foo,number])": 5.0,
  868. "p75(tags[foo,number])": 5.0,
  869. "p95(tags[foo,number])": 5.0,
  870. "p99(tags[foo,number])": 5.0,
  871. "p100(tags[foo,number])": 5.0,
  872. "min(tags[foo,number])": 5.0,
  873. "max(tags[foo,number])": 5.0,
  874. }
  875. def test_numeric_attr_without_space(self):
  876. self.store_spans(
  877. [
  878. self.create_span(
  879. {
  880. "description": "foo",
  881. "sentry_tags": {"status": "success"},
  882. "tags": {"foo": "five"},
  883. },
  884. measurements={"foo": {"value": 5}},
  885. start_ts=self.ten_mins_ago,
  886. ),
  887. ],
  888. is_eap=self.is_eap,
  889. )
  890. response = self.do_request(
  891. {
  892. "field": ["description", "tags[foo,number]", "tags[foo,string]", "tags[foo]"],
  893. "query": "",
  894. "orderby": "description",
  895. "project": self.project.id,
  896. "dataset": self.dataset,
  897. }
  898. )
  899. assert response.status_code == 200, response.content
  900. assert len(response.data["data"]) == 1
  901. data = response.data["data"]
  902. assert data[0]["tags[foo,number]"] == 5
  903. assert data[0]["tags[foo,string]"] == "five"
  904. assert data[0]["tags[foo]"] == "five"
  905. def test_numeric_attr_with_spaces(self):
  906. self.store_spans(
  907. [
  908. self.create_span(
  909. {
  910. "description": "foo",
  911. "sentry_tags": {"status": "success"},
  912. "tags": {"foo": "five"},
  913. },
  914. measurements={"foo": {"value": 5}},
  915. start_ts=self.ten_mins_ago,
  916. ),
  917. ],
  918. is_eap=self.is_eap,
  919. )
  920. response = self.do_request(
  921. {
  922. "field": ["description", "tags[foo, number]", "tags[foo, string]", "tags[foo]"],
  923. "query": "",
  924. "orderby": "description",
  925. "project": self.project.id,
  926. "dataset": self.dataset,
  927. }
  928. )
  929. assert response.status_code == 200, response.content
  930. assert len(response.data["data"]) == 1
  931. data = response.data["data"]
  932. assert data[0]["tags[foo, number]"] == 5
  933. assert data[0]["tags[foo, string]"] == "five"
  934. assert data[0]["tags[foo]"] == "five"
  935. def test_numeric_attr_filtering(self):
  936. self.store_spans(
  937. [
  938. self.create_span(
  939. {
  940. "description": "foo",
  941. "sentry_tags": {"status": "success"},
  942. "tags": {"foo": "five"},
  943. },
  944. measurements={"foo": {"value": 5}},
  945. start_ts=self.ten_mins_ago,
  946. ),
  947. self.create_span(
  948. {"description": "bar", "sentry_tags": {"status": "success", "foo": "five"}},
  949. measurements={"foo": {"value": 8}},
  950. start_ts=self.ten_mins_ago,
  951. ),
  952. ],
  953. is_eap=self.is_eap,
  954. )
  955. response = self.do_request(
  956. {
  957. "field": ["description", "tags[foo,number]"],
  958. "query": "tags[foo,number]:5",
  959. "orderby": "description",
  960. "project": self.project.id,
  961. "dataset": self.dataset,
  962. }
  963. )
  964. assert response.status_code == 200, response.content
  965. assert len(response.data["data"]) == 1
  966. data = response.data["data"]
  967. assert data[0]["tags[foo,number]"] == 5
  968. assert data[0]["description"] == "foo"
  969. def test_long_attr_name(self):
  970. response = self.do_request(
  971. {
  972. "field": ["description", "z" * 201],
  973. "query": "",
  974. "orderby": "description",
  975. "project": self.project.id,
  976. "dataset": self.dataset,
  977. }
  978. )
  979. assert response.status_code == 400, response.content
  980. assert "Is Too Long" in response.data["detail"].title()
  981. def test_numeric_attr_orderby(self):
  982. self.store_spans(
  983. [
  984. self.create_span(
  985. {
  986. "description": "baz",
  987. "sentry_tags": {"status": "success"},
  988. "tags": {"foo": "five"},
  989. },
  990. measurements={"foo": {"value": 71}},
  991. start_ts=self.ten_mins_ago,
  992. ),
  993. self.create_span(
  994. {
  995. "description": "foo",
  996. "sentry_tags": {"status": "success"},
  997. "tags": {"foo": "five"},
  998. },
  999. measurements={"foo": {"value": 5}},
  1000. start_ts=self.ten_mins_ago,
  1001. ),
  1002. self.create_span(
  1003. {
  1004. "description": "bar",
  1005. "sentry_tags": {"status": "success"},
  1006. "tags": {"foo": "five"},
  1007. },
  1008. measurements={"foo": {"value": 8}},
  1009. start_ts=self.ten_mins_ago,
  1010. ),
  1011. ],
  1012. is_eap=self.is_eap,
  1013. )
  1014. response = self.do_request(
  1015. {
  1016. "field": ["description", "tags[foo,number]"],
  1017. "query": "",
  1018. "orderby": ["tags[foo,number]"],
  1019. "project": self.project.id,
  1020. "dataset": self.dataset,
  1021. }
  1022. )
  1023. assert response.status_code == 200, response.content
  1024. assert len(response.data["data"]) == 3
  1025. data = response.data["data"]
  1026. assert data[0]["tags[foo,number]"] == 5
  1027. assert data[0]["description"] == "foo"
  1028. assert data[1]["tags[foo,number]"] == 8
  1029. assert data[1]["description"] == "bar"
  1030. assert data[2]["tags[foo,number]"] == 71
  1031. assert data[2]["description"] == "baz"
  1032. def test_aggregate_numeric_attr(self):
  1033. self.store_spans(
  1034. [
  1035. self.create_span(
  1036. {
  1037. "description": "foo",
  1038. "sentry_tags": {"status": "success"},
  1039. "tags": {"bar": "bar1"},
  1040. },
  1041. start_ts=self.ten_mins_ago,
  1042. ),
  1043. self.create_span(
  1044. {
  1045. "description": "foo",
  1046. "sentry_tags": {"status": "success"},
  1047. "tags": {"bar": "bar2"},
  1048. },
  1049. measurements={"foo": {"value": 5}},
  1050. start_ts=self.ten_mins_ago,
  1051. ),
  1052. ],
  1053. is_eap=self.is_eap,
  1054. )
  1055. response = self.do_request(
  1056. {
  1057. "field": [
  1058. "description",
  1059. "count_unique(bar)",
  1060. "count_unique(tags[bar])",
  1061. "count_unique(tags[bar,string])",
  1062. "count()",
  1063. "count(span.duration)",
  1064. "count(tags[foo, number])",
  1065. "sum(tags[foo,number])",
  1066. "avg(tags[foo,number])",
  1067. "p50(tags[foo,number])",
  1068. "p75(tags[foo,number])",
  1069. "p95(tags[foo,number])",
  1070. "p99(tags[foo,number])",
  1071. "p100(tags[foo,number])",
  1072. "min(tags[foo,number])",
  1073. "max(tags[foo,number])",
  1074. ],
  1075. "query": "",
  1076. "orderby": "description",
  1077. "project": self.project.id,
  1078. "dataset": self.dataset,
  1079. }
  1080. )
  1081. assert response.status_code == 200, response.content
  1082. assert len(response.data["data"]) == 1
  1083. data = response.data["data"]
  1084. assert data[0] == {
  1085. "description": "foo",
  1086. "count_unique(bar)": 2,
  1087. "count_unique(tags[bar])": 2,
  1088. "count_unique(tags[bar,string])": 2,
  1089. "count()": 2,
  1090. "count(span.duration)": 2,
  1091. "count(tags[foo, number])": 1,
  1092. "sum(tags[foo,number])": 5.0,
  1093. "avg(tags[foo,number])": 5.0,
  1094. "p50(tags[foo,number])": 5.0,
  1095. "p75(tags[foo,number])": 5.0,
  1096. "p95(tags[foo,number])": 5.0,
  1097. "p99(tags[foo,number])": 5.0,
  1098. "p100(tags[foo,number])": 5.0,
  1099. "min(tags[foo,number])": 5.0,
  1100. "max(tags[foo,number])": 5.0,
  1101. }
  1102. def test_margin_of_error(self):
  1103. total_samples = 10
  1104. in_group = 5
  1105. spans = []
  1106. for _ in range(in_group):
  1107. spans.append(
  1108. self.create_span(
  1109. {
  1110. "description": "foo",
  1111. "sentry_tags": {"status": "success"},
  1112. "measurements": {"client_sample_rate": {"value": 0.00001}},
  1113. },
  1114. start_ts=self.ten_mins_ago,
  1115. )
  1116. )
  1117. for _ in range(total_samples - in_group):
  1118. spans.append(
  1119. self.create_span(
  1120. {
  1121. "description": "bar",
  1122. "sentry_tags": {"status": "success"},
  1123. "measurements": {"client_sample_rate": {"value": 0.00001}},
  1124. },
  1125. )
  1126. )
  1127. self.store_spans(
  1128. spans,
  1129. is_eap=self.is_eap,
  1130. )
  1131. response = self.do_request(
  1132. {
  1133. "field": [
  1134. "margin_of_error()",
  1135. "lower_count_limit()",
  1136. "upper_count_limit()",
  1137. "count()",
  1138. ],
  1139. "query": "description:foo",
  1140. "project": self.project.id,
  1141. "dataset": self.dataset,
  1142. }
  1143. )
  1144. assert response.status_code == 200, response.content
  1145. assert len(response.data["data"]) == 1
  1146. data = response.data["data"][0]
  1147. margin_of_error = data["margin_of_error()"]
  1148. lower_limit = data["lower_count_limit()"]
  1149. upper_limit = data["upper_count_limit()"]
  1150. extrapolated = data["count()"]
  1151. assert margin_of_error == pytest.approx(0.306, rel=1e-1)
  1152. # How to read this; these results mean that the extrapolated count is
  1153. # 500k, with a lower estimated bound of ~200k, and an upper bound of 800k
  1154. assert lower_limit == pytest.approx(190_000, abs=5000)
  1155. assert extrapolated == pytest.approx(500_000, abs=5000)
  1156. assert upper_limit == pytest.approx(810_000, abs=5000)
  1157. def test_skip_aggregate_conditions_option(self):
  1158. span_1 = self.create_span(
  1159. {"description": "foo", "sentry_tags": {"status": "success"}},
  1160. start_ts=self.ten_mins_ago,
  1161. )
  1162. span_2 = self.create_span(
  1163. {"description": "bar", "sentry_tags": {"status": "invalid_argument"}},
  1164. start_ts=self.ten_mins_ago,
  1165. )
  1166. self.store_spans(
  1167. [span_1, span_2],
  1168. is_eap=self.is_eap,
  1169. )
  1170. response = self.do_request(
  1171. {
  1172. "field": ["description"],
  1173. "query": "description:foo count():>1",
  1174. "orderby": "description",
  1175. "project": self.project.id,
  1176. "dataset": self.dataset,
  1177. "allowAggregateConditions": "0",
  1178. }
  1179. )
  1180. assert response.status_code == 200, response.content
  1181. data = response.data["data"]
  1182. meta = response.data["meta"]
  1183. assert len(data) == 1
  1184. assert data == [
  1185. {
  1186. "description": "foo",
  1187. "project.name": self.project.slug,
  1188. "id": span_1["span_id"],
  1189. },
  1190. ]
  1191. assert meta["dataset"] == self.dataset
  1192. class OrganizationEventsEAPRPCSpanEndpointTest(OrganizationEventsEAPSpanEndpointTest):
  1193. """These tests aren't fully passing yet, currently inheriting xfail from the eap tests"""
  1194. is_eap = True
  1195. use_rpc = True
  1196. def test_extrapolation(self):
  1197. """Extrapolation only changes the number when there's a sample rate"""
  1198. spans = []
  1199. spans.append(
  1200. self.create_span(
  1201. {
  1202. "description": "foo",
  1203. "sentry_tags": {"status": "success"},
  1204. "measurements": {"client_sample_rate": {"value": 0.1}},
  1205. },
  1206. start_ts=self.ten_mins_ago,
  1207. )
  1208. )
  1209. self.store_spans(spans, is_eap=self.is_eap)
  1210. response = self.do_request(
  1211. {
  1212. "field": ["count()"],
  1213. "query": "",
  1214. "project": self.project.id,
  1215. "dataset": self.dataset,
  1216. }
  1217. )
  1218. assert response.status_code == 200, response.content
  1219. data = response.data["data"]
  1220. assert len(data) == 1
  1221. assert data[0]["count()"] == 10
  1222. def test_span_duration(self):
  1223. spans = [
  1224. self.create_span(
  1225. {"description": "bar", "sentry_tags": {"status": "invalid_argument"}},
  1226. start_ts=self.ten_mins_ago,
  1227. ),
  1228. self.create_span(
  1229. {"description": "foo", "sentry_tags": {"status": "success"}},
  1230. start_ts=self.ten_mins_ago,
  1231. ),
  1232. ]
  1233. self.store_spans(spans, is_eap=self.is_eap)
  1234. response = self.do_request(
  1235. {
  1236. "field": ["span.duration", "description"],
  1237. "query": "",
  1238. "orderby": "description",
  1239. "project": self.project.id,
  1240. "dataset": self.dataset,
  1241. }
  1242. )
  1243. assert response.status_code == 200, response.content
  1244. data = response.data["data"]
  1245. meta = response.data["meta"]
  1246. assert len(data) == 2
  1247. assert data == [
  1248. {
  1249. "span.duration": 1000.0,
  1250. "description": "bar",
  1251. "project.name": self.project.slug,
  1252. "id": spans[0]["span_id"],
  1253. },
  1254. {
  1255. "span.duration": 1000.0,
  1256. "description": "foo",
  1257. "project.name": self.project.slug,
  1258. "id": spans[1]["span_id"],
  1259. },
  1260. ]
  1261. assert meta["dataset"] == self.dataset
  1262. @pytest.mark.xfail(reason="weighted functions will not be moved to the RPC")
  1263. def test_aggregate_numeric_attr_weighted(self):
  1264. super().test_aggregate_numeric_attr_weighted()
  1265. def test_aggregate_numeric_attr(self):
  1266. self.store_spans(
  1267. [
  1268. self.create_span(
  1269. {
  1270. "description": "foo",
  1271. "sentry_tags": {"status": "success"},
  1272. "tags": {"bar": "bar1"},
  1273. },
  1274. start_ts=self.ten_mins_ago,
  1275. ),
  1276. self.create_span(
  1277. {
  1278. "description": "foo",
  1279. "sentry_tags": {"status": "success"},
  1280. "tags": {"bar": "bar2"},
  1281. },
  1282. measurements={"foo": {"value": 5}},
  1283. start_ts=self.ten_mins_ago,
  1284. ),
  1285. ],
  1286. is_eap=self.is_eap,
  1287. )
  1288. response = self.do_request(
  1289. {
  1290. "field": [
  1291. "description",
  1292. "count_unique(bar)",
  1293. "count_unique(tags[bar])",
  1294. "count_unique(tags[bar,string])",
  1295. "count()",
  1296. "count(span.duration)",
  1297. "count(tags[foo, number])",
  1298. "sum(tags[foo,number])",
  1299. "avg(tags[foo,number])",
  1300. "p50(tags[foo,number])",
  1301. "p75(tags[foo,number])",
  1302. "p95(tags[foo,number])",
  1303. "p99(tags[foo,number])",
  1304. "p100(tags[foo,number])",
  1305. "min(tags[foo,number])",
  1306. "max(tags[foo,number])",
  1307. ],
  1308. "query": "",
  1309. "orderby": "description",
  1310. "project": self.project.id,
  1311. "dataset": self.dataset,
  1312. }
  1313. )
  1314. assert response.status_code == 200, response.content
  1315. assert len(response.data["data"]) == 1
  1316. data = response.data["data"]
  1317. assert data[0] == {
  1318. "description": "foo",
  1319. "count_unique(bar)": 2,
  1320. "count_unique(tags[bar])": 2,
  1321. "count_unique(tags[bar,string])": 2,
  1322. "count()": 2,
  1323. "count(span.duration)": 2,
  1324. "count(tags[foo, number])": 1,
  1325. "sum(tags[foo,number])": 5.0,
  1326. "avg(tags[foo,number])": 5.0,
  1327. "p50(tags[foo,number])": 5.0,
  1328. "p75(tags[foo,number])": 5.0,
  1329. "p95(tags[foo,number])": 5.0,
  1330. "p99(tags[foo,number])": 5.0,
  1331. "p100(tags[foo,number])": 5.0,
  1332. "min(tags[foo,number])": 5.0,
  1333. "max(tags[foo,number])": 5.0,
  1334. }
  1335. @pytest.mark.xfail(reason="margin will not be moved to the RPC")
  1336. def test_margin_of_error(self):
  1337. super().test_margin_of_error()
  1338. @pytest.mark.xfail(reason="rpc not handling attr_str vs attr_num with same alias")
  1339. def test_numeric_attr_without_space(self):
  1340. super().test_numeric_attr_without_space()
  1341. @pytest.mark.xfail(reason="rpc not handling attr_str vs attr_num with same alias")
  1342. def test_numeric_attr_with_spaces(self):
  1343. super().test_numeric_attr_with_spaces()
  1344. @pytest.mark.xfail(reason="module not migrated over")
  1345. def test_module_alias(self):
  1346. super().test_module_alias()
  1347. @pytest.mark.xfail(reason="wip: not implemented yet")
  1348. def test_inp_span(self):
  1349. super().test_inp_span()
  1350. @pytest.mark.xfail(reason="wip: not implemented yet")
  1351. def test_network_span(self):
  1352. super().test_network_span()
  1353. @pytest.mark.xfail(reason="wip: not implemented yet")
  1354. def test_other_category_span(self):
  1355. super().test_other_category_span()
  1356. @pytest.mark.xfail(reason="wip: not implemented yet")
  1357. def test_queue_span(self):
  1358. super().test_queue_span()
  1359. @pytest.mark.xfail(reason="wip: not implemented yet")
  1360. def test_sentry_tags_syntax(self):
  1361. super().test_sentry_tags_syntax()
  1362. @pytest.mark.xfail(reason="wip: not implemented yet")
  1363. def test_span_op_casing(self):
  1364. super().test_span_op_casing()
  1365. def test_tag_wildcards(self):
  1366. self.store_spans(
  1367. [
  1368. self.create_span(
  1369. {"description": "foo", "tags": {"foo": "bar"}},
  1370. start_ts=self.ten_mins_ago,
  1371. ),
  1372. self.create_span(
  1373. {"description": "qux", "tags": {"foo": "qux"}},
  1374. start_ts=self.ten_mins_ago,
  1375. ),
  1376. ],
  1377. is_eap=self.is_eap,
  1378. )
  1379. for query in [
  1380. "foo:b*",
  1381. "foo:*r",
  1382. "foo:*a*",
  1383. "foo:b*r",
  1384. ]:
  1385. response = self.do_request(
  1386. {
  1387. "field": ["foo", "count()"],
  1388. "query": query,
  1389. "project": self.project.id,
  1390. "dataset": self.dataset,
  1391. }
  1392. )
  1393. assert response.status_code == 200, response.content
  1394. assert response.data["data"] == [{"foo": "bar", "count()": 1}]
  1395. @pytest.mark.xfail(reason="rate not implemented yet")
  1396. def test_spm(self):
  1397. super().test_spm()
  1398. @pytest.mark.xfail(reason="units not implemented yet")
  1399. def test_simple_measurements(self):
  1400. super().test_simple_measurements()