test_organization_spans_aggregation.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. import hashlib
  2. from datetime import timedelta
  3. from unittest import mock
  4. from uuid import uuid4
  5. from django.urls import reverse
  6. from snuba_sdk import Column, Condition, Function, Op
  7. from sentry.api.endpoints.organization_spans_aggregation import NULL_GROUP
  8. from sentry.testutils.cases import APITestCase, SnubaTestCase
  9. from sentry.testutils.helpers.datetime import before_now
  10. from sentry.testutils.helpers.options import override_options
  11. from sentry.testutils.pytest.fixtures import django_db_all
  12. from sentry.utils.samples import load_data
  13. MOCK_SNUBA_RESPONSE = {
  14. "data": [
  15. {
  16. "trace_id": "a" * 32,
  17. "transaction_id": "80fe542aea4945ffbe612646987ee449",
  18. "count": 71,
  19. "spans": [
  20. [
  21. "root_1",
  22. 1,
  23. "parent_1",
  24. "e238e6c2e2466b07",
  25. "api/0/foo",
  26. "other",
  27. "2023-09-13 17:12:19",
  28. 100,
  29. 1000,
  30. 1000,
  31. ],
  32. [
  33. "B1",
  34. 0,
  35. "root_1",
  36. "B",
  37. "connect",
  38. "db",
  39. "2023-09-13 17:12:19",
  40. 150,
  41. 50,
  42. 50.0,
  43. ],
  44. [
  45. "C1",
  46. 0,
  47. "root_1",
  48. "C",
  49. "resolve_conditions",
  50. "discover.endpoint",
  51. "2023-09-13 17:12:19",
  52. 155,
  53. 0,
  54. 10.0,
  55. ],
  56. [
  57. "D1",
  58. 0,
  59. "C1",
  60. "D",
  61. "resolve_orderby",
  62. "discover.snql",
  63. "2023-09-13 17:12:19",
  64. 157,
  65. 0,
  66. 20.0,
  67. ],
  68. [
  69. "E1",
  70. 0,
  71. "C1",
  72. NULL_GROUP,
  73. "resolve_columns",
  74. "discover.snql",
  75. "2023-09-13 17:12:19",
  76. 157,
  77. 0,
  78. 20.0,
  79. ],
  80. ],
  81. },
  82. {
  83. "trace_id": "b" * 32,
  84. "transaction_id": "86b21833d1854d9b811000b91e7fccfa",
  85. "count": 71,
  86. "spans": [
  87. [
  88. "root_2",
  89. 1,
  90. "parent_2",
  91. "e238e6c2e2466b07",
  92. "bind_organization_context",
  93. "other",
  94. "2023-09-13 17:12:39",
  95. 100,
  96. 700,
  97. 0.0,
  98. ],
  99. [
  100. "B2",
  101. 0,
  102. "root_2",
  103. "B",
  104. "connect",
  105. "db",
  106. "2023-09-13 17:12:39",
  107. 110,
  108. 10,
  109. 30.0,
  110. ],
  111. [
  112. "C2",
  113. 0,
  114. "root_2",
  115. "C",
  116. "resolve_conditions",
  117. "discover.endpoint",
  118. "2023-09-13 17:12:39",
  119. 115,
  120. 0,
  121. 40.0,
  122. ],
  123. [
  124. "D2",
  125. 0,
  126. "C2",
  127. "D",
  128. "resolve_orderby",
  129. "discover.snql",
  130. "2023-09-13 17:12:39",
  131. 150,
  132. 0,
  133. 10.0,
  134. ],
  135. [
  136. "D2-duplicate",
  137. 0,
  138. "C2",
  139. "D",
  140. "resolve_orderby",
  141. "discover.snql",
  142. "2023-09-13 17:12:40",
  143. 155,
  144. 0,
  145. 20.0,
  146. ],
  147. [
  148. "E2",
  149. 0,
  150. "C2",
  151. NULL_GROUP,
  152. "resolve_columns",
  153. "discover.snql",
  154. "2023-09-13 17:12:39",
  155. 157,
  156. 0,
  157. 20.0,
  158. ],
  159. ],
  160. },
  161. ]
  162. }
  163. class OrganizationIndexedSpansAggregationTest(APITestCase, SnubaTestCase):
  164. url_name = "sentry-api-0-organization-spans-aggregation"
  165. FEATURES = [
  166. "organizations:spans-first-ui",
  167. "organizations:performance-view",
  168. ]
  169. def setUp(self):
  170. super().setUp()
  171. self.login_as(user=self.user)
  172. self.day_ago = before_now(days=1).replace(hour=10, minute=0, second=0, microsecond=0)
  173. self.url = reverse(
  174. self.url_name,
  175. kwargs={"organization_id_or_slug": self.project.organization.slug},
  176. )
  177. @override_options({"indexed-spans.agg-span-waterfall.enable": True})
  178. @mock.patch("sentry.api.endpoints.organization_spans_aggregation.raw_snql_query")
  179. def test_simple(self, mock_query):
  180. mock_query.side_effect = [MOCK_SNUBA_RESPONSE]
  181. with self.feature(self.FEATURES):
  182. response = self.client.get(
  183. self.url,
  184. data={"transaction": "api/0/foo", "statsPeriod": "1d"},
  185. format="json",
  186. )
  187. assert response.data
  188. data = response.data
  189. root_fingerprint = hashlib.md5(b"e238e6c2e2466b07").hexdigest()[:16]
  190. assert root_fingerprint in data
  191. assert data[root_fingerprint]["count()"] == 2
  192. assert data[root_fingerprint]["description"] == "api/0/foo"
  193. assert round(data[root_fingerprint]["avg(duration)"]) == 850
  194. assert data[root_fingerprint]["samples"] == {
  195. ("80fe542aea4945ffbe612646987ee449", "root_1"),
  196. ("86b21833d1854d9b811000b91e7fccfa", "root_2"),
  197. }
  198. assert data[root_fingerprint]["sample_spans"] == [
  199. {
  200. "transaction": "80fe542aea4945ffbe612646987ee449",
  201. "timestamp": 1694625139.1,
  202. "span": "root_1",
  203. "trace": "a" * 32,
  204. },
  205. {
  206. "transaction": "86b21833d1854d9b811000b91e7fccfa",
  207. "timestamp": 1694625159.1,
  208. "span": "root_2",
  209. "trace": "b" * 32,
  210. },
  211. ]
  212. fingerprint = hashlib.md5(b"e238e6c2e2466b07-B").hexdigest()[:16]
  213. assert data[fingerprint]["description"] == "connect"
  214. assert round(data[fingerprint]["avg(duration)"]) == 30
  215. fingerprint = hashlib.md5(b"e238e6c2e2466b07-C-D").hexdigest()[:16]
  216. assert data[fingerprint]["description"] == "resolve_orderby"
  217. assert data[fingerprint]["avg(exclusive_time)"] == 15.0
  218. assert data[fingerprint]["count()"] == 2
  219. fingerprint = hashlib.md5(b"e238e6c2e2466b07-C-D2").hexdigest()[:16]
  220. assert data[fingerprint]["description"] == "resolve_orderby"
  221. assert data[fingerprint]["avg(exclusive_time)"] == 20.0
  222. assert data[fingerprint]["count()"] == 1
  223. @override_options({"indexed-spans.agg-span-waterfall.enable": True})
  224. @mock.patch("sentry.api.endpoints.organization_spans_aggregation.raw_snql_query")
  225. def test_offset_logic(self, mock_query):
  226. mock_query.side_effect = [MOCK_SNUBA_RESPONSE]
  227. with self.feature(self.FEATURES):
  228. response = self.client.get(
  229. self.url,
  230. data={"transaction": "api/0/foo", "statsPeriod": "1d"},
  231. format="json",
  232. )
  233. assert response.data
  234. data = response.data
  235. root_fingerprint = hashlib.md5(b"e238e6c2e2466b07").hexdigest()[:16]
  236. assert root_fingerprint in data
  237. assert data[root_fingerprint]["avg(absolute_offset)"] == 0.0
  238. fingerprint = hashlib.md5(b"e238e6c2e2466b07-B").hexdigest()[:16]
  239. assert data[fingerprint]["avg(absolute_offset)"] == 30.0
  240. fingerprint = hashlib.md5(b"e238e6c2e2466b07-C").hexdigest()[:16]
  241. assert data[fingerprint]["avg(absolute_offset)"] == 35.0
  242. fingerprint = hashlib.md5(b"e238e6c2e2466b07-C-D").hexdigest()[:16]
  243. assert data[fingerprint]["avg(absolute_offset)"] == 53.5
  244. fingerprint = hashlib.md5(b"e238e6c2e2466b07-C-D2").hexdigest()[:16]
  245. assert data[fingerprint]["avg(absolute_offset)"] == 1075.0
  246. @override_options({"indexed-spans.agg-span-waterfall.enable": True})
  247. @mock.patch("sentry.api.endpoints.organization_spans_aggregation.raw_snql_query")
  248. def test_null_group_fallback(self, mock_query):
  249. mock_query.side_effect = [MOCK_SNUBA_RESPONSE]
  250. with self.feature(self.FEATURES):
  251. response = self.client.get(
  252. self.url,
  253. data={"transaction": "api/0/foo", "statsPeriod": "1d"},
  254. format="json",
  255. )
  256. assert response.data
  257. data = response.data
  258. root_fingerprint = hashlib.md5(b"e238e6c2e2466b07-C-discover.snql").hexdigest()[:16]
  259. assert root_fingerprint in data
  260. assert data[root_fingerprint]["description"] == ""
  261. assert data[root_fingerprint]["count()"] == 2
  262. @override_options({"indexed-spans.agg-span-waterfall.enable": True})
  263. @mock.patch("sentry.api.endpoints.organization_spans_aggregation.raw_snql_query")
  264. def test_http_method_filter(self, mock_query):
  265. with self.feature(self.FEATURES):
  266. self.client.get(
  267. self.url,
  268. data={"transaction": "api/0/foo", "http.method": "GET", "statsPeriod": "1d"},
  269. format="json",
  270. )
  271. assert (
  272. Condition(
  273. lhs=Function(
  274. function="ifNull",
  275. parameters=[
  276. Column(
  277. name="sentry_tags[transaction.method]",
  278. ),
  279. "",
  280. ],
  281. alias=None,
  282. ),
  283. op=Op.EQ,
  284. rhs="GET",
  285. )
  286. in mock_query.mock_calls[0].args[0].query.where
  287. )
  288. class OrganizationNodestoreSpansAggregationTest(APITestCase, SnubaTestCase):
  289. url_name = "sentry-api-0-organization-spans-aggregation"
  290. FEATURES = [
  291. "organizations:spans-first-ui",
  292. "organizations:performance-view",
  293. ]
  294. def get_start_end(self, duration):
  295. return self.day_ago, self.day_ago + timedelta(milliseconds=duration)
  296. def create_event(
  297. self,
  298. trace,
  299. transaction,
  300. spans,
  301. parent_span_id,
  302. project_id,
  303. tags=None,
  304. duration=4000,
  305. span_id=None,
  306. measurements=None,
  307. trace_context=None,
  308. environment=None,
  309. **kwargs,
  310. ):
  311. start, end = self.get_start_end(duration)
  312. data = load_data(
  313. "transaction",
  314. trace=trace,
  315. spans=spans,
  316. timestamp=end,
  317. start_timestamp=start,
  318. trace_context=trace_context,
  319. )
  320. data["transaction"] = transaction
  321. data["contexts"]["trace"]["parent_span_id"] = parent_span_id
  322. if span_id:
  323. data["contexts"]["trace"]["span_id"] = span_id
  324. if measurements:
  325. for key, value in measurements.items():
  326. data["measurements"][key]["value"] = value
  327. if tags is not None:
  328. data["tags"] = tags
  329. if environment is not None:
  330. data["environment"] = environment
  331. with self.feature(self.FEATURES):
  332. return self.store_event(data, project_id=project_id, assert_no_errors=False, **kwargs)
  333. def setUp(self):
  334. super().setUp()
  335. self.login_as(user=self.user)
  336. self.day_ago = before_now(days=1).replace(hour=10, minute=0, second=0, microsecond=0)
  337. self.span_ids_event_1 = dict(
  338. zip(["A", "B", "C", "D", "E"], [uuid4().hex[:16] for _ in range(5)])
  339. )
  340. self.trace_id_1 = uuid4().hex
  341. self.root_event_1 = self.create_event(
  342. trace=self.trace_id_1,
  343. trace_context={
  344. "trace_id": self.trace_id_1,
  345. "span_id": self.span_ids_event_1["A"],
  346. "exclusive_time": 100,
  347. },
  348. transaction="api/0/foo",
  349. spans=[
  350. {
  351. "same_process_as_parent": True,
  352. "op": "db",
  353. "description": "connect",
  354. "span_id": self.span_ids_event_1["B"],
  355. "trace_id": self.trace_id_1,
  356. "parent_span_id": self.span_ids_event_1["A"],
  357. "exclusive_time": 50.0,
  358. "data": {
  359. "duration": 0.050,
  360. "offset": 0.050,
  361. "span.group": "B",
  362. "span.description": "connect",
  363. },
  364. "sentry_tags": {
  365. "description": "connect",
  366. },
  367. },
  368. {
  369. "same_process_as_parent": True,
  370. "op": "discover.endpoint",
  371. "description": "resolve_conditions",
  372. "span_id": self.span_ids_event_1["C"],
  373. "trace_id": self.trace_id_1,
  374. "parent_span_id": self.span_ids_event_1["A"],
  375. "exclusive_time": 10,
  376. "data": {
  377. "duration": 0.00,
  378. "offset": 0.055,
  379. "span.group": "C",
  380. "span.description": "connect",
  381. },
  382. "sentry_tags": {
  383. "description": "connect",
  384. },
  385. },
  386. {
  387. "same_process_as_parent": True,
  388. "op": "discover.snql",
  389. "description": "resolve_orderby",
  390. "span_id": self.span_ids_event_1["D"],
  391. "trace_id": self.trace_id_1,
  392. "parent_span_id": self.span_ids_event_1["C"],
  393. "exclusive_time": 20,
  394. "data": {
  395. "duration": 0.00,
  396. "offset": 0.057,
  397. "span.group": "D",
  398. "span.description": "resolve_orderby",
  399. },
  400. "sentry_tags": {
  401. "description": "resolve_orderby",
  402. },
  403. },
  404. {
  405. "same_process_as_parent": True,
  406. "op": "discover.snql",
  407. "description": "resolve_columns",
  408. "span_id": self.span_ids_event_1["E"],
  409. "trace_id": self.trace_id_1,
  410. "parent_span_id": self.span_ids_event_1["C"],
  411. "exclusive_time": 20,
  412. "data": {
  413. "duration": 0.00,
  414. "offset": 0.057,
  415. "span.description": "resolve_columns",
  416. },
  417. },
  418. ],
  419. parent_span_id=None,
  420. project_id=self.project.id,
  421. duration=1000,
  422. environment="production",
  423. )
  424. self.span_ids_event_2 = dict(
  425. zip(["A", "B", "C", "D", "D2", "E"], [uuid4().hex[:16] for _ in range(6)])
  426. )
  427. self.trace_id_2 = uuid4().hex
  428. self.root_event_2 = self.create_event(
  429. trace=self.trace_id_2,
  430. trace_context={
  431. "trace_id": self.trace_id_2,
  432. "span_id": self.span_ids_event_2["A"],
  433. "exclusive_time": 100,
  434. },
  435. transaction="api/0/foo",
  436. spans=[
  437. {
  438. "same_process_as_parent": True,
  439. "op": "db",
  440. "description": "connect",
  441. "span_id": self.span_ids_event_2["B"],
  442. "trace_id": self.trace_id_2,
  443. "parent_span_id": self.span_ids_event_2["A"],
  444. "exclusive_time": 50.0,
  445. "data": {
  446. "duration": 0.010,
  447. "offset": 0.010,
  448. "span.group": "B",
  449. "span.description": "connect",
  450. },
  451. "sentry_tags": {
  452. "description": "connect",
  453. },
  454. },
  455. {
  456. "same_process_as_parent": True,
  457. "op": "discover.endpoint",
  458. "description": "resolve_conditions",
  459. "span_id": self.span_ids_event_2["C"],
  460. "trace_id": self.trace_id_2,
  461. "parent_span_id": self.span_ids_event_2["A"],
  462. "exclusive_time": 10,
  463. "data": {
  464. "duration": 0.00,
  465. "offset": 0.015,
  466. "span.group": "C",
  467. "span.description": "connect",
  468. },
  469. "sentry_tags": {
  470. "description": "connect",
  471. },
  472. },
  473. {
  474. "same_process_as_parent": True,
  475. "op": "discover.snql",
  476. "description": "resolve_orderby",
  477. "span_id": self.span_ids_event_2["D"],
  478. "trace_id": self.trace_id_2,
  479. "parent_span_id": self.span_ids_event_2["C"],
  480. "exclusive_time": 10,
  481. "data": {
  482. "duration": 0.00,
  483. "offset": 0.050,
  484. "span.group": "D",
  485. "span.description": "resolve_orderby",
  486. },
  487. "sentry_tags": {
  488. "description": "resolve_orderby",
  489. },
  490. },
  491. {
  492. "same_process_as_parent": True,
  493. "op": "discover.snql",
  494. "description": "resolve_orderby",
  495. "span_id": self.span_ids_event_2["D2"],
  496. "trace_id": self.trace_id_2,
  497. "parent_span_id": self.span_ids_event_2["C"],
  498. "exclusive_time": 20,
  499. "data": {
  500. "duration": 0.00,
  501. "offset": 1.055,
  502. "span.group": "D",
  503. "span.description": "resolve_orderby",
  504. },
  505. "sentry_tags": {
  506. "description": "resolve_orderby",
  507. },
  508. },
  509. {
  510. "same_process_as_parent": True,
  511. "op": "discover.snql",
  512. "description": "resolve_columns",
  513. "span_id": self.span_ids_event_2["E"],
  514. "trace_id": self.trace_id_2,
  515. "parent_span_id": self.span_ids_event_2["C"],
  516. "exclusive_time": 20,
  517. "data": {
  518. "duration": 0.00,
  519. "offset": 0.057,
  520. "span.description": "resolve_columns",
  521. },
  522. },
  523. ],
  524. parent_span_id=None,
  525. project_id=self.project.id,
  526. duration=700,
  527. environment="development",
  528. )
  529. self.url = reverse(
  530. self.url_name,
  531. kwargs={"organization_id_or_slug": self.project.organization.slug},
  532. )
  533. @django_db_all
  534. def test_simple(self):
  535. with self.feature(self.FEATURES):
  536. response = self.client.get(
  537. self.url,
  538. data={"transaction": "api/0/foo"},
  539. format="json",
  540. )
  541. assert response.data
  542. data = response.data
  543. root_fingerprint = hashlib.md5(b"e238e6c2e2466b07").hexdigest()[:16]
  544. assert root_fingerprint in data
  545. assert data[root_fingerprint]["count()"] == 2
  546. assert data[root_fingerprint]["description"] == "api/0/foo"
  547. assert round(data[root_fingerprint]["avg(duration)"]) == 850
  548. assert data[root_fingerprint]["samples"] == {
  549. (
  550. self.root_event_1.event_id,
  551. self.span_ids_event_1["A"],
  552. ),
  553. (
  554. self.root_event_2.event_id,
  555. self.span_ids_event_2["A"],
  556. ),
  557. }
  558. assert data[root_fingerprint]["sample_spans"] == [
  559. {
  560. "transaction": self.root_event_1.event_id,
  561. "timestamp": self.root_event_1.data["start_timestamp"],
  562. "span": self.span_ids_event_1["A"],
  563. "trace": self.root_event_1.data["contexts"]["trace"]["trace_id"],
  564. },
  565. {
  566. "transaction": self.root_event_2.event_id,
  567. "timestamp": self.root_event_2.data["start_timestamp"],
  568. "span": self.span_ids_event_2["A"],
  569. "trace": self.root_event_2.data["contexts"]["trace"]["trace_id"],
  570. },
  571. ]
  572. fingerprint = hashlib.md5(b"e238e6c2e2466b07-B").hexdigest()[:16]
  573. assert data[fingerprint]["description"] == "connect"
  574. assert round(data[fingerprint]["avg(duration)"]) == 30
  575. fingerprint = hashlib.md5(b"e238e6c2e2466b07-C-D").hexdigest()[:16]
  576. assert data[fingerprint]["description"] == "resolve_orderby"
  577. assert data[fingerprint]["avg(exclusive_time)"] == 15.0
  578. assert data[fingerprint]["count()"] == 2
  579. fingerprint = hashlib.md5(b"e238e6c2e2466b07-C-D2").hexdigest()[:16]
  580. assert data[fingerprint]["description"] == "resolve_orderby"
  581. assert data[fingerprint]["avg(exclusive_time)"] == 20.0
  582. assert data[fingerprint]["count()"] == 1
  583. @django_db_all
  584. def test_offset_logic(self):
  585. with self.feature(self.FEATURES):
  586. response = self.client.get(
  587. self.url,
  588. data={"transaction": "api/0/foo"},
  589. format="json",
  590. )
  591. assert response.data
  592. data = response.data
  593. root_fingerprint = hashlib.md5(b"e238e6c2e2466b07").hexdigest()[:16]
  594. assert root_fingerprint in data
  595. assert data[root_fingerprint]["avg(absolute_offset)"] == 0.0
  596. fingerprint = hashlib.md5(b"e238e6c2e2466b07-B").hexdigest()[:16]
  597. assert data[fingerprint]["avg(absolute_offset)"] == 30.0
  598. fingerprint = hashlib.md5(b"e238e6c2e2466b07-C").hexdigest()[:16]
  599. assert data[fingerprint]["avg(absolute_offset)"] == 35.0
  600. fingerprint = hashlib.md5(b"e238e6c2e2466b07-C-D").hexdigest()[:16]
  601. assert data[fingerprint]["avg(absolute_offset)"] == 53.5
  602. fingerprint = hashlib.md5(b"e238e6c2e2466b07-C-D2").hexdigest()[:16]
  603. assert data[fingerprint]["avg(absolute_offset)"] == 1075.0
  604. @django_db_all
  605. def test_null_group_fallback(self):
  606. with self.feature(self.FEATURES):
  607. response = self.client.get(
  608. self.url,
  609. data={"transaction": "api/0/foo"},
  610. format="json",
  611. )
  612. assert response.data
  613. data = response.data
  614. root_fingerprint = hashlib.md5(b"e238e6c2e2466b07-C-discover.snql").hexdigest()[:16]
  615. assert root_fingerprint in data
  616. assert data[root_fingerprint]["description"] == ""
  617. assert data[root_fingerprint]["count()"] == 2
  618. @django_db_all
  619. def test_http_method_filter(self):
  620. with self.feature(self.FEATURES):
  621. response = self.client.get(
  622. self.url,
  623. data={"transaction": "api/0/foo", "http.method": "GET"},
  624. format="json",
  625. )
  626. assert response.data
  627. data = response.data
  628. root_fingerprint = hashlib.md5(b"e238e6c2e2466b07").hexdigest()[:16]
  629. assert root_fingerprint in data
  630. assert data[root_fingerprint]["count()"] == 2
  631. with self.feature(self.FEATURES):
  632. response = self.client.get(
  633. self.url,
  634. data={"transaction": "api/0/foo", "http.method": "POST"},
  635. format="json",
  636. )
  637. assert response.data == {}
  638. @django_db_all
  639. def test_environment_filter(self):
  640. with self.feature(self.FEATURES):
  641. response = self.client.get(
  642. self.url,
  643. data={
  644. "transaction": "api/0/foo",
  645. "environment": "production",
  646. },
  647. format="json",
  648. )
  649. assert response.data
  650. data = response.data
  651. root_fingerprint = hashlib.md5(b"e238e6c2e2466b07").hexdigest()[:16]
  652. assert root_fingerprint in data
  653. assert data[root_fingerprint]["count()"] == 1
  654. with self.feature(self.FEATURES):
  655. response = self.client.get(
  656. self.url,
  657. data={
  658. "transaction": "api/0/foo",
  659. "environment": ["production", "development"],
  660. "forceNodestore": "true",
  661. },
  662. format="json",
  663. )
  664. assert response.data
  665. data = response.data
  666. root_fingerprint = hashlib.md5(b"e238e6c2e2466b07").hexdigest()[:16]
  667. assert root_fingerprint in data
  668. assert data[root_fingerprint]["count()"] == 2