test_tagstore_backend.py 57 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672
  1. from datetime import timedelta
  2. from functools import cached_property
  3. from unittest import mock
  4. import pytest
  5. from django.utils import timezone
  6. from sentry.issues.grouptype import ProfileFileIOGroupType
  7. from sentry.models.environment import Environment
  8. from sentry.models.release import Release
  9. from sentry.models.releaseprojectenvironment import ReleaseProjectEnvironment, ReleaseStages
  10. from sentry.search.events.constants import (
  11. RELEASE_STAGE_ALIAS,
  12. SEMVER_ALIAS,
  13. SEMVER_BUILD_ALIAS,
  14. SEMVER_PACKAGE_ALIAS,
  15. )
  16. from sentry.tagstore.exceptions import (
  17. GroupTagKeyNotFound,
  18. GroupTagValueNotFound,
  19. TagKeyNotFound,
  20. TagValueNotFound,
  21. )
  22. from sentry.tagstore.snuba.backend import SnubaTagStorage
  23. from sentry.tagstore.types import GroupTagValue, TagValue
  24. from sentry.testutils.abstract import Abstract
  25. from sentry.testutils.cases import PerformanceIssueTestCase, SnubaTestCase, TestCase
  26. from sentry.testutils.helpers.datetime import before_now, iso_format
  27. from sentry.utils.eventuser import EventUser
  28. from sentry.utils.samples import load_data
  29. from tests.sentry.issues.test_utils import SearchIssueTestMixin
  30. exception = {
  31. "values": [
  32. {
  33. "type": "ValidationError",
  34. "value": "Bad request",
  35. "stacktrace": {
  36. "frames": [
  37. {
  38. "function": "?",
  39. "filename": "http://localhost:1337/error.js",
  40. "lineno": 29,
  41. "colno": 3,
  42. "in_app": False,
  43. }
  44. ]
  45. },
  46. }
  47. ]
  48. }
  49. class TagStorageTest(TestCase, SnubaTestCase, SearchIssueTestMixin, PerformanceIssueTestCase):
  50. def setUp(self):
  51. super().setUp()
  52. self.ts = SnubaTagStorage()
  53. self.proj1 = self.create_project()
  54. env1 = "test"
  55. env2 = "test2"
  56. self.env3 = Environment.objects.create(
  57. organization_id=self.proj1.organization_id, name="test3"
  58. )
  59. self.now = timezone.now().replace(microsecond=0)
  60. self.store_event(
  61. data={
  62. "event_id": "1" * 32,
  63. "message": "message 1",
  64. "platform": "python",
  65. "environment": env1,
  66. "fingerprint": ["group-1"],
  67. "timestamp": iso_format(self.now - timedelta(seconds=1)),
  68. "tags": {
  69. "foo": "bar",
  70. "baz": "quux",
  71. "sentry:release": 100,
  72. "sentry:user": "id:user1",
  73. },
  74. "user": {"id": "user1"},
  75. "exception": exception,
  76. },
  77. project_id=self.proj1.id,
  78. )
  79. self.proj1group1 = self.store_event(
  80. data={
  81. "event_id": "2" * 32,
  82. "message": "message 1",
  83. "platform": "python",
  84. "environment": env1,
  85. "fingerprint": ["group-1"],
  86. "timestamp": iso_format(self.now - timedelta(seconds=2)),
  87. "tags": {
  88. "foo": "bar",
  89. "baz": "quux",
  90. "sentry:release": 200,
  91. "sentry:user": "id:user2",
  92. },
  93. "user": {"id": "user2"},
  94. "exception": exception,
  95. },
  96. project_id=self.proj1.id,
  97. ).group
  98. self.proj1group2 = self.store_event(
  99. data={
  100. "event_id": "3" * 32,
  101. "message": "message 2",
  102. "platform": "python",
  103. "environment": env1,
  104. "fingerprint": ["group-2"],
  105. "timestamp": iso_format(self.now - timedelta(seconds=2)),
  106. "tags": {"browser": "chrome", "sentry:user": "id:user1"},
  107. "user": {"id": "user1"},
  108. },
  109. project_id=self.proj1.id,
  110. ).group
  111. self.store_event(
  112. data={
  113. "event_id": "4" * 32,
  114. "message": "message2",
  115. "platform": "python",
  116. "environment": env2,
  117. "fingerprint": ["group-1"],
  118. "timestamp": iso_format(self.now - timedelta(seconds=2)),
  119. "tags": {"foo": "bar"},
  120. },
  121. project_id=self.proj1.id,
  122. )
  123. self.proj1env1 = Environment.objects.get(name=env1)
  124. self.proj1env2 = Environment.objects.get(name=env2)
  125. @cached_property
  126. def perf_group_and_env(self):
  127. env_name = "test"
  128. env = Environment.objects.get(name=env_name)
  129. event_data = load_data("transaction-n-plus-one", timestamp=before_now(minutes=10))
  130. event_data["environment"] = env_name
  131. event = self.create_performance_issue(
  132. event_data={
  133. **event_data,
  134. "event_id": "a" * 32,
  135. "timestamp": iso_format(self.now - timedelta(seconds=1)),
  136. "start_timestamp": iso_format(self.now - timedelta(seconds=1)),
  137. "tags": {"foo": "bar", "biz": "baz"},
  138. "release": "releaseme",
  139. }
  140. )
  141. self.create_performance_issue(
  142. event_data={
  143. **event_data,
  144. "event_id": "b" * 32,
  145. "timestamp": iso_format(self.now - timedelta(seconds=2)),
  146. "start_timestamp": iso_format(self.now - timedelta(seconds=2)),
  147. "tags": {"foo": "quux"},
  148. "release": "releaseme",
  149. }
  150. )
  151. perf_group = event.group
  152. return perf_group, env
  153. @cached_property
  154. def generic_group_and_env(self):
  155. env = Environment.objects.get(name="test")
  156. _, _, group_info = self.store_search_issue(
  157. self.project.id,
  158. self.user.id,
  159. [f"{ProfileFileIOGroupType.type_id}-group1"],
  160. env.name,
  161. timezone.now().replace(hour=0, minute=0, second=0) + timedelta(minutes=1),
  162. [("foo", "bar"), ("biz", "baz")],
  163. "releaseme",
  164. )
  165. assert group_info is not None
  166. return group_info.group, env
  167. def test_get_group_tag_keys_and_top_values(self):
  168. result = list(
  169. self.ts.get_group_tag_keys_and_top_values(
  170. self.proj1group1,
  171. [self.proj1env1.id],
  172. tenant_ids={"referrer": "r", "organization_id": 1234},
  173. )
  174. )
  175. tags = [r.key for r in result]
  176. assert set(tags) == {"foo", "baz", "environment", "sentry:release", "sentry:user", "level"}
  177. result.sort(key=lambda r: r.key)
  178. assert result[0].key == "baz"
  179. assert result[0].top_values[0].value == "quux"
  180. assert result[0].count == 2
  181. assert result[4].key == "sentry:release"
  182. assert result[4].count == 2
  183. top_release_values = result[4].top_values
  184. assert len(top_release_values) == 2
  185. assert {v.value for v in top_release_values} == {"100", "200"}
  186. assert all(v.times_seen == 1 for v in top_release_values)
  187. # Now with only a specific set of keys,
  188. result = list(
  189. self.ts.get_group_tag_keys_and_top_values(
  190. self.proj1group1,
  191. [self.proj1env1.id],
  192. keys=["environment", "sentry:release"],
  193. tenant_ids={"referrer": "r", "organization_id": 1234},
  194. )
  195. )
  196. tags = [r.key for r in result]
  197. assert set(tags) == {"environment", "sentry:release"}
  198. result.sort(key=lambda r: r.key)
  199. assert result[0].key == "environment"
  200. assert result[0].top_values[0].value == "test"
  201. assert result[1].key == "sentry:release"
  202. top_release_values = result[1].top_values
  203. assert len(top_release_values) == 2
  204. assert {v.value for v in top_release_values} == {"100", "200"}
  205. assert all(v.times_seen == 1 for v in top_release_values)
  206. def test_get_group_tag_keys_and_top_values_perf_issue(self):
  207. perf_group, env = self.perf_group_and_env
  208. result = list(
  209. self.ts.get_group_tag_keys_and_top_values(
  210. perf_group,
  211. [env.id],
  212. tenant_ids={"referrer": "r", "organization_id": 1234},
  213. )
  214. )
  215. tags = [r.key for r in result]
  216. assert set(tags) == {
  217. "biz",
  218. "browser",
  219. "browser.name",
  220. "client_os",
  221. "client_os.name",
  222. "device",
  223. "device.family",
  224. "environment",
  225. "foo",
  226. "level",
  227. "runtime",
  228. "runtime.name",
  229. "sentry:release",
  230. "sentry:user",
  231. "transaction",
  232. "url",
  233. }
  234. result.sort(key=lambda r: r.key)
  235. assert result[0].key == "biz"
  236. assert result[0].top_values[0].value == "baz"
  237. assert result[0].count == 1
  238. assert result[12].key == "sentry:release"
  239. assert result[12].count == 2
  240. top_release_values = result[12].top_values
  241. assert len(top_release_values) == 1
  242. assert {v.value for v in top_release_values} == {"releaseme"}
  243. assert all(v.times_seen == 2 for v in top_release_values)
  244. # Now with only a specific set of keys,
  245. result = list(
  246. self.ts.get_group_tag_keys_and_top_values(
  247. perf_group,
  248. [env.id],
  249. keys=["environment", "sentry:release"],
  250. tenant_ids={"referrer": "r", "organization_id": 1234},
  251. )
  252. )
  253. tags = [r.key for r in result]
  254. assert set(tags) == {"environment", "sentry:release"}
  255. result.sort(key=lambda r: r.key)
  256. assert result[0].key == "environment"
  257. assert result[0].top_values[0].value == "test"
  258. assert result[1].key == "sentry:release"
  259. top_release_values = result[1].top_values
  260. assert len(top_release_values) == 1
  261. assert {v.value for v in top_release_values} == {"releaseme"}
  262. assert all(v.times_seen == 2 for v in top_release_values)
  263. def test_get_group_tag_keys_and_top_values_generic_issue(self):
  264. group, env = self.generic_group_and_env
  265. result = list(
  266. self.ts.get_group_tag_keys_and_top_values(
  267. group, [env.id], tenant_ids={"referrer": "r", "organization_id": 1234}
  268. )
  269. )
  270. tags = [r.key for r in result]
  271. assert set(tags) == {"foo", "biz", "environment", "sentry:user", "level", "sentry:release"}
  272. result.sort(key=lambda r: r.key)
  273. assert result[0].key == "biz"
  274. assert result[0].top_values[0].value == "baz"
  275. assert result[0].count == 1
  276. assert result[4].key == "sentry:release"
  277. assert result[4].count == 1
  278. top_release_values = result[4].top_values
  279. assert len(top_release_values) == 1
  280. assert {v.value for v in top_release_values} == {"releaseme"}
  281. assert all(v.times_seen == 1 for v in top_release_values)
  282. # Now with only a specific set of keys,
  283. result = list(
  284. self.ts.get_group_tag_keys_and_top_values(
  285. group,
  286. [env.id],
  287. keys=["environment", "sentry:release"],
  288. tenant_ids={"referrer": "r", "organization_id": 1234},
  289. )
  290. )
  291. tags = [r.key for r in result]
  292. assert set(tags) == {"environment", "sentry:release"}
  293. result.sort(key=lambda r: r.key)
  294. assert result[0].key == "environment"
  295. assert result[0].top_values[0].value == "test"
  296. assert result[1].key == "sentry:release"
  297. top_release_values = result[1].top_values
  298. assert len(top_release_values) == 1
  299. assert {v.value for v in top_release_values} == {"releaseme"}
  300. assert all(v.times_seen == 1 for v in top_release_values)
  301. # assert False
  302. def test_get_top_group_tag_values(self):
  303. resp = self.ts.get_top_group_tag_values(
  304. self.proj1group1,
  305. self.proj1env1.id,
  306. "foo",
  307. 1,
  308. tenant_ids={"referrer": "r", "organization_id": 1234},
  309. )
  310. assert len(resp) == 1
  311. assert resp[0].times_seen == 2
  312. assert resp[0].key == "foo"
  313. assert resp[0].value == "bar"
  314. assert resp[0].group_id == self.proj1group1.id
  315. def test_get_top_group_tag_values_perf(self):
  316. perf_group, env = self.perf_group_and_env
  317. resp = self.ts.get_top_group_tag_values(
  318. perf_group,
  319. env.id,
  320. "foo",
  321. 2,
  322. tenant_ids={"referrer": "r", "organization_id": 1234},
  323. )
  324. assert len(resp) == 2
  325. assert resp[0].times_seen == 1
  326. assert resp[0].key == "foo"
  327. assert resp[0].value == "bar"
  328. assert resp[0].group_id == perf_group.id
  329. assert resp[1].times_seen == 1
  330. assert resp[1].key == "foo"
  331. assert resp[1].value == "quux"
  332. assert resp[1].group_id == perf_group.id
  333. def test_get_top_group_tag_values_generic(self):
  334. group, env = self.generic_group_and_env
  335. resp = self.ts.get_top_group_tag_values(
  336. group, env.id, "foo", 1, tenant_ids={"referrer": "r", "organization_id": 1234}
  337. )
  338. assert len(resp) == 1
  339. assert resp[0].times_seen == 1
  340. assert resp[0].key == "foo"
  341. assert resp[0].value == "bar"
  342. assert resp[0].group_id == group.id
  343. def test_get_group_tag_value_count(self):
  344. assert (
  345. self.ts.get_group_tag_value_count(
  346. self.proj1group1,
  347. self.proj1env1.id,
  348. "foo",
  349. tenant_ids={"referrer": "r", "organization_id": 1234},
  350. )
  351. == 2
  352. )
  353. def test_get_group_tag_value_count_perf(self):
  354. perf_group, env = self.perf_group_and_env
  355. assert (
  356. self.ts.get_group_tag_value_count(
  357. perf_group, env.id, "foo", {"referrer": "r", "organization_id": 1234}
  358. )
  359. == 2
  360. )
  361. def test_get_group_tag_value_count_generic(self):
  362. group, env = self.generic_group_and_env
  363. assert (
  364. self.ts.get_group_tag_value_count(
  365. group, env.id, "foo", {"referrer": "r", "organization_id": 1234}
  366. )
  367. == 1
  368. )
  369. def test_get_tag_keys(self):
  370. expected_keys = {
  371. "baz",
  372. "browser",
  373. "environment",
  374. "foo",
  375. "sentry:release",
  376. "sentry:user",
  377. "level",
  378. }
  379. keys = {
  380. k.key: k
  381. for k in self.ts.get_tag_keys(
  382. project_id=self.proj1.id,
  383. environment_id=self.proj1env1.id,
  384. tenant_ids={"referrer": "r", "organization_id": 1234},
  385. )
  386. }
  387. assert set(keys) == expected_keys
  388. keys = {
  389. k.key: k
  390. for k in self.ts.get_tag_keys(
  391. project_id=self.proj1.id,
  392. environment_id=self.proj1env1.id,
  393. include_values_seen=True,
  394. tenant_ids={"referrer": "r", "organization_id": 1234},
  395. )
  396. }
  397. assert set(keys) == expected_keys
  398. def test_get_tag_keys_removed_from_denylist(self):
  399. denylist_keys = frozenset(["browser", "sentry:release"])
  400. expected_keys = {
  401. "baz",
  402. "environment",
  403. "foo",
  404. "sentry:user",
  405. "level",
  406. }
  407. keys = {
  408. k.key: k
  409. for k in self.ts.get_tag_keys(
  410. project_id=self.proj1.id,
  411. environment_id=self.proj1env1.id,
  412. denylist=denylist_keys,
  413. tenant_ids={"referrer": "r", "organization_id": 1234},
  414. )
  415. }
  416. assert set(keys) == expected_keys
  417. keys = {
  418. k.key: k
  419. for k in self.ts.get_tag_keys(
  420. project_id=self.proj1.id,
  421. environment_id=self.proj1env1.id,
  422. tenant_ids={"referrer": "r", "organization_id": 1234},
  423. )
  424. }
  425. expected_keys |= {"browser", "sentry:release"}
  426. assert set(keys) == expected_keys
  427. def test_get_group_tag_key(self):
  428. with pytest.raises(GroupTagKeyNotFound):
  429. self.ts.get_group_tag_key(
  430. group=self.proj1group1,
  431. environment_id=self.proj1env1.id,
  432. key="notreal",
  433. tenant_ids={"referrer": "r", "organization_id": 1234},
  434. )
  435. assert (
  436. self.ts.get_group_tag_key(
  437. group=self.proj1group1,
  438. environment_id=self.proj1env1.id,
  439. key="foo",
  440. tenant_ids={"referrer": "r", "organization_id": 1234},
  441. ).key
  442. == "foo"
  443. )
  444. keys = {
  445. k.key: k
  446. for k in self.ts.get_group_tag_keys(
  447. self.proj1group1,
  448. [self.proj1env1.id],
  449. tenant_ids={"referrer": "r", "organization_id": 1234},
  450. )
  451. }
  452. assert set(keys) == {"baz", "environment", "foo", "sentry:release", "sentry:user", "level"}
  453. def test_get_group_tag_key_perf(self):
  454. perf_group, env = self.perf_group_and_env
  455. with pytest.raises(GroupTagKeyNotFound):
  456. self.ts.get_group_tag_key(
  457. group=perf_group,
  458. environment_id=env.id,
  459. key="notreal",
  460. tenant_ids={"referrer": "r", "organization_id": 1234},
  461. )
  462. assert (
  463. self.ts.get_group_tag_key(
  464. group=perf_group,
  465. environment_id=self.proj1env1.id,
  466. key="foo",
  467. tenant_ids={"referrer": "r", "organization_id": 1234},
  468. ).key
  469. == "foo"
  470. )
  471. keys = {
  472. k.key: k
  473. for k in self.ts.get_group_tag_keys(
  474. perf_group,
  475. [env.id],
  476. tenant_ids={"referrer": "r", "organization_id": 1234},
  477. )
  478. }
  479. assert set(keys) == {
  480. "biz",
  481. "browser",
  482. "browser.name",
  483. "client_os",
  484. "client_os.name",
  485. "device",
  486. "device.family",
  487. "environment",
  488. "foo",
  489. "level",
  490. "runtime",
  491. "runtime.name",
  492. "sentry:release",
  493. "sentry:user",
  494. "transaction",
  495. "url",
  496. }
  497. def test_get_group_tag_key_generic(self):
  498. group, env = self.generic_group_and_env
  499. with pytest.raises(GroupTagKeyNotFound):
  500. self.ts.get_group_tag_key(
  501. group=group,
  502. environment_id=env.id,
  503. key="notreal",
  504. tenant_ids={"referrer": "r", "organization_id": 1234},
  505. )
  506. assert (
  507. self.ts.get_group_tag_key(
  508. group=group,
  509. environment_id=self.proj1env1.id,
  510. key="foo",
  511. tenant_ids={"referrer": "r", "organization_id": 1234},
  512. ).key
  513. == "foo"
  514. )
  515. keys = {
  516. k.key: k
  517. for k in self.ts.get_group_tag_keys(
  518. group, [env.id], tenant_ids={"referrer": "r", "organization_id": 1234}
  519. )
  520. }
  521. assert set(keys) == {"biz", "environment", "foo", "sentry:user", "level", "sentry:release"}
  522. def test_get_group_tag_value(self):
  523. with pytest.raises(GroupTagValueNotFound):
  524. self.ts.get_group_tag_value(
  525. project_id=self.proj1.id,
  526. group_id=self.proj1group1.id,
  527. environment_id=self.proj1env1.id,
  528. key="foo",
  529. value="notreal",
  530. tenant_ids={"referrer": "r", "organization_id": 1234},
  531. )
  532. assert (
  533. self.ts.get_group_tag_values(
  534. group=self.proj1group1,
  535. environment_id=self.proj1env1.id,
  536. key="notreal",
  537. tenant_ids={"referrer": "r", "organization_id": 1234},
  538. )
  539. == set()
  540. )
  541. assert (
  542. list(
  543. self.ts.get_group_tag_values(
  544. group=self.proj1group1,
  545. environment_id=self.proj1env1.id,
  546. tenant_ids={"referrer": "r", "organization_id": 1234},
  547. key="foo",
  548. )
  549. )[0].value
  550. == "bar"
  551. )
  552. assert (
  553. self.ts.get_group_tag_value(
  554. project_id=self.proj1.id,
  555. group_id=self.proj1group1.id,
  556. environment_id=self.proj1env1.id,
  557. key="foo",
  558. value="bar",
  559. tenant_ids={"referrer": "r", "organization_id": 1234},
  560. ).value
  561. == "bar"
  562. )
  563. def test_get_tag_key(self):
  564. with pytest.raises(TagKeyNotFound):
  565. self.ts.get_tag_key(
  566. project_id=self.proj1.id,
  567. environment_id=self.proj1env1.id,
  568. key="notreal",
  569. tenant_ids={"referrer": "r", "organization_id": 1234},
  570. )
  571. def test_get_tag_value(self):
  572. with pytest.raises(TagValueNotFound):
  573. self.ts.get_tag_value(
  574. project_id=self.proj1.id,
  575. environment_id=self.proj1env1.id,
  576. key="foo",
  577. value="notreal",
  578. tenant_ids={"referrer": "r", "organization_id": 1234},
  579. )
  580. def test_get_tag_value_label(self):
  581. assert self.ts.get_tag_value_label("foo", "notreal") == "notreal"
  582. assert self.ts.get_tag_value_label("sentry:user", None) is None
  583. assert self.ts.get_tag_value_label("sentry:user", "id:stuff") == "stuff"
  584. assert self.ts.get_tag_value_label("sentry:user", "email:stuff") == "stuff"
  585. assert self.ts.get_tag_value_label("sentry:user", "username:stuff") == "stuff"
  586. assert self.ts.get_tag_value_label("sentry:user", "ip:stuff") == "stuff"
  587. def test_get_groups_user_counts(self):
  588. assert self.ts.get_groups_user_counts(
  589. project_ids=[self.proj1.id],
  590. group_ids=[self.proj1group1.id, self.proj1group2.id],
  591. environment_ids=[self.proj1env1.id],
  592. tenant_ids={"referrer": "r", "organization_id": 1234},
  593. ) == {self.proj1group1.id: 2, self.proj1group2.id: 1}
  594. # test filtering by date range where there shouldn't be results
  595. assert (
  596. self.ts.get_groups_user_counts(
  597. project_ids=[self.proj1.id],
  598. group_ids=[self.proj1group1.id, self.proj1group2.id],
  599. environment_ids=[self.proj1env1.id],
  600. start=self.now - timedelta(days=5),
  601. end=self.now - timedelta(days=4),
  602. tenant_ids={"referrer": "r", "organization_id": 1234},
  603. )
  604. == {}
  605. )
  606. def test_get_groups_user_counts_no_environments(self):
  607. self.store_event(
  608. data={
  609. "event_id": "3" * 32,
  610. "message": "message 1",
  611. "platform": "python",
  612. "fingerprint": ["group-1"],
  613. "timestamp": iso_format(self.now - timedelta(seconds=1)),
  614. "tags": {
  615. "foo": "bar",
  616. "baz": "quux",
  617. "sentry:release": 100,
  618. "sentry:user": "id:user3",
  619. },
  620. "user": {"id": "user3"},
  621. "exception": exception,
  622. },
  623. project_id=self.proj1.id,
  624. )
  625. assert self.ts.get_groups_user_counts(
  626. project_ids=[self.proj1.id],
  627. group_ids=[self.proj1group1.id, self.proj1group2.id],
  628. environment_ids=None,
  629. tenant_ids={"referrer": "r", "organization_id": 1234},
  630. ) == {self.proj1group1.id: 3, self.proj1group2.id: 1}
  631. @mock.patch("sentry.analytics.record")
  632. def test_get_group_tag_values_for_users(self, mock_record):
  633. result = self.ts.get_group_tag_values_for_users(
  634. [
  635. EventUser(
  636. project_id=self.proj1.id,
  637. email=None,
  638. username=None,
  639. name=None,
  640. ip_address=None,
  641. user_ident="user1",
  642. )
  643. ],
  644. tenant_ids={"referrer": "r", "organization_id": 1234},
  645. )
  646. assert len(result) == 2
  647. assert {v.group_id for v in result} == {self.proj1group1.id, self.proj1group2.id}
  648. assert {v.last_seen for v in result} == {
  649. self.now - timedelta(seconds=1),
  650. self.now - timedelta(seconds=2),
  651. }
  652. result.sort(key=lambda x: x.last_seen)
  653. assert result[0].last_seen == self.now - timedelta(seconds=2)
  654. assert result[1].last_seen == self.now - timedelta(seconds=1)
  655. for v in result:
  656. assert v.value == "user1"
  657. result = self.ts.get_group_tag_values_for_users(
  658. [
  659. EventUser(
  660. project_id=self.proj1.id,
  661. email=None,
  662. username=None,
  663. name=None,
  664. ip_address=None,
  665. user_ident="user2",
  666. )
  667. ],
  668. tenant_ids={"referrer": "r", "organization_id": 1234},
  669. )
  670. assert len(result) == 1
  671. assert result[0].value == "user2"
  672. assert result[0].last_seen == self.now - timedelta(seconds=2)
  673. mock_record.assert_called_with(
  674. "eventuser_endpoint.request",
  675. project_id=self.proj1.id,
  676. endpoint="sentry.tagstore.snuba.backend.SnubaTagStorage.get_group_tag_values_for_users",
  677. )
  678. def test_get_release_tags(self):
  679. tags = list(
  680. self.ts.get_release_tags(self.proj1.organization_id, [self.proj1.id], None, ["100"])
  681. )
  682. assert len(tags) == 1
  683. one_second_ago = self.now - timedelta(seconds=1)
  684. assert tags[0].last_seen == one_second_ago
  685. assert tags[0].first_seen == one_second_ago
  686. assert tags[0].times_seen == 1
  687. assert tags[0].key == "sentry:release"
  688. def test_get_release_tags_uses_release_project_environment(self):
  689. tags = list(
  690. self.ts.get_release_tags(self.proj1.organization_id, [self.proj1.id], None, ["100"])
  691. )
  692. assert len(tags) == 1
  693. one_second_ago = self.now - timedelta(seconds=1)
  694. assert tags[0].last_seen == one_second_ago
  695. assert tags[0].first_seen == one_second_ago
  696. assert tags[0].times_seen == 1
  697. one_day_ago = self.now - timedelta(days=1)
  698. two_days_ago = self.now - timedelta(days=2)
  699. self.store_event(
  700. data={
  701. "event_id": "5" * 32,
  702. "message": "message3",
  703. "platform": "python",
  704. "environment": None,
  705. "fingerprint": ["group-1"],
  706. "timestamp": iso_format(one_day_ago),
  707. "tags": {
  708. "sentry:release": 100,
  709. },
  710. },
  711. project_id=self.proj1.id,
  712. )
  713. release = Release.objects.create(version="100", organization=self.organization)
  714. ReleaseProjectEnvironment.objects.create(
  715. release_id=release.id,
  716. project_id=self.proj1.id,
  717. environment_id=self.env3.id,
  718. first_seen=one_day_ago,
  719. )
  720. self.store_event(
  721. data={
  722. "event_id": "6" * 32,
  723. "message": "message3",
  724. "platform": "python",
  725. "environment": None,
  726. "fingerprint": ["group-1"],
  727. "timestamp": iso_format(two_days_ago),
  728. "tags": {
  729. "sentry:release": 100,
  730. },
  731. },
  732. project_id=self.proj1.id,
  733. )
  734. tags = list(
  735. self.ts.get_release_tags(self.proj1.organization_id, [self.proj1.id], None, ["100"])
  736. )
  737. assert tags[0].last_seen == one_second_ago
  738. assert tags[0].first_seen == one_day_ago
  739. assert (
  740. tags[0].times_seen == 2
  741. ) # Isn't 3 because start was limited by the ReleaseProjectEnvironment entry
  742. def test_get_group_event_filter(self):
  743. assert self.ts.get_group_event_filter(
  744. self.proj1.id,
  745. self.proj1group1.id,
  746. [self.proj1env1.id],
  747. {"foo": "bar"},
  748. None,
  749. None,
  750. tenant_ids={"referrer": "r", "organization_id": 1234},
  751. ) == {"event_id__in": {"1" * 32, "2" * 32}}
  752. assert self.ts.get_group_event_filter(
  753. self.proj1.id,
  754. self.proj1group1.id,
  755. [self.proj1env1.id],
  756. {"foo": "bar"},
  757. (self.now - timedelta(seconds=1)),
  758. None,
  759. tenant_ids={"referrer": "r", "organization_id": 1234},
  760. ) == {"event_id__in": {"1" * 32}}
  761. assert self.ts.get_group_event_filter(
  762. self.proj1.id,
  763. self.proj1group1.id,
  764. [self.proj1env1.id],
  765. {"foo": "bar"},
  766. None,
  767. (self.now - timedelta(seconds=1)),
  768. tenant_ids={"referrer": "r", "organization_id": 1234},
  769. ) == {"event_id__in": {"2" * 32}}
  770. assert self.ts.get_group_event_filter(
  771. self.proj1.id,
  772. self.proj1group1.id,
  773. [self.proj1env1.id, self.proj1env2.id],
  774. {"foo": "bar"},
  775. None,
  776. None,
  777. tenant_ids={"referrer": "r", "organization_id": 1234},
  778. ) == {"event_id__in": {"1" * 32, "2" * 32, "4" * 32}}
  779. assert self.ts.get_group_event_filter(
  780. self.proj1.id,
  781. self.proj1group1.id,
  782. [self.proj1env1.id],
  783. {"foo": "bar", "sentry:release": "200"}, # AND
  784. None,
  785. None,
  786. tenant_ids={"referrer": "r", "organization_id": 1234},
  787. ) == {"event_id__in": {"2" * 32}}
  788. assert self.ts.get_group_event_filter(
  789. self.proj1.id,
  790. self.proj1group2.id,
  791. [self.proj1env1.id],
  792. {"browser": "chrome"},
  793. None,
  794. None,
  795. tenant_ids={"referrer": "r", "organization_id": 1234},
  796. ) == {"event_id__in": {"3" * 32}}
  797. assert (
  798. self.ts.get_group_event_filter(
  799. self.proj1.id,
  800. self.proj1group2.id,
  801. [self.proj1env1.id],
  802. {"browser": "ie"},
  803. None,
  804. None,
  805. tenant_ids={"referrer": "r", "organization_id": 1234},
  806. )
  807. is None
  808. )
  809. def test_get_tag_value_paginator(self):
  810. from sentry.tagstore.types import TagValue
  811. assert list(
  812. self.ts.get_tag_value_paginator(
  813. self.proj1.id,
  814. self.proj1env1.id,
  815. "sentry:user",
  816. tenant_ids={"referrer": "r", "organization_id": 1234},
  817. ).get_result(10)
  818. ) == [
  819. TagValue(
  820. key="sentry:user",
  821. value="id:user1",
  822. times_seen=2,
  823. first_seen=self.now - timedelta(seconds=2),
  824. last_seen=self.now - timedelta(seconds=1),
  825. ),
  826. TagValue(
  827. key="sentry:user",
  828. value="id:user2",
  829. times_seen=1,
  830. first_seen=self.now - timedelta(seconds=2),
  831. last_seen=self.now - timedelta(seconds=2),
  832. ),
  833. ]
  834. assert list(
  835. self.ts.get_tag_value_paginator(
  836. self.proj1.id,
  837. self.proj1env1.id,
  838. "sentry:user",
  839. query="user1",
  840. tenant_ids={"referrer": "r", "organization_id": 1234},
  841. ).get_result(10)
  842. ) == [
  843. TagValue(
  844. key="sentry:user",
  845. value="id:user1",
  846. times_seen=2,
  847. first_seen=self.now - timedelta(seconds=2),
  848. last_seen=self.now - timedelta(seconds=1),
  849. )
  850. ]
  851. def test_get_tag_value_paginator_with_dates(self):
  852. from sentry.tagstore.types import TagValue
  853. day_ago = self.now - timedelta(days=1)
  854. two_days_ago = self.now - timedelta(days=2)
  855. assert list(
  856. self.ts.get_tag_value_paginator(
  857. self.proj1.id,
  858. self.proj1env1.id,
  859. "sentry:user",
  860. start=day_ago,
  861. end=self.now,
  862. tenant_ids={"referrer": "r", "organization_id": 1234},
  863. ).get_result(10)
  864. ) == [
  865. TagValue(
  866. key="sentry:user",
  867. value="id:user1",
  868. times_seen=2,
  869. first_seen=self.now - timedelta(seconds=2),
  870. last_seen=self.now - timedelta(seconds=1),
  871. ),
  872. TagValue(
  873. key="sentry:user",
  874. value="id:user2",
  875. times_seen=1,
  876. first_seen=self.now - timedelta(seconds=2),
  877. last_seen=self.now - timedelta(seconds=2),
  878. ),
  879. ]
  880. day_ago = self.now - timedelta(days=1)
  881. assert (
  882. list(
  883. self.ts.get_tag_value_paginator(
  884. self.proj1.id,
  885. self.proj1env1.id,
  886. "sentry:user",
  887. start=two_days_ago,
  888. end=day_ago,
  889. tenant_ids={"referrer": "r", "organization_id": 1234},
  890. ).get_result(10)
  891. )
  892. == []
  893. )
  894. def test_numeric_tag_value_paginator(self):
  895. from sentry.tagstore.types import TagValue
  896. assert list(
  897. self.ts.get_tag_value_paginator(
  898. self.proj1.id,
  899. self.proj1env1.id,
  900. "stack.lineno",
  901. tenant_ids={"referrer": "r", "organization_id": 1234},
  902. ).get_result(10)
  903. ) == [
  904. TagValue(
  905. key="stack.lineno",
  906. value="29",
  907. times_seen=2,
  908. first_seen=self.now - timedelta(seconds=2),
  909. last_seen=self.now - timedelta(seconds=1),
  910. )
  911. ]
  912. assert list(
  913. self.ts.get_tag_value_paginator(
  914. self.proj1.id,
  915. self.proj1env1.id,
  916. "stack.lineno",
  917. query="30",
  918. tenant_ids={"referrer": "r", "organization_id": 1234},
  919. ).get_result(10)
  920. ) == [
  921. TagValue(
  922. key="stack.lineno",
  923. value="29",
  924. times_seen=2,
  925. first_seen=self.now - timedelta(seconds=2),
  926. last_seen=self.now - timedelta(seconds=1),
  927. )
  928. ]
  929. def test_get_group_tag_value_iter(self):
  930. from sentry.tagstore.types import GroupTagValue
  931. assert list(
  932. self.ts.get_group_tag_value_iter(
  933. self.proj1group1,
  934. [self.proj1env1.id],
  935. "sentry:user",
  936. tenant_ids={"referrer": "r", "organization_id": 1234},
  937. )
  938. ) == [
  939. GroupTagValue(
  940. group_id=self.proj1group1.id,
  941. key="sentry:user",
  942. value="id:user1",
  943. times_seen=1,
  944. first_seen=self.now - timedelta(seconds=1),
  945. last_seen=self.now - timedelta(seconds=1),
  946. ),
  947. GroupTagValue(
  948. group_id=self.proj1group1.id,
  949. key="sentry:user",
  950. value="id:user2",
  951. times_seen=1,
  952. first_seen=self.now - timedelta(seconds=2),
  953. last_seen=self.now - timedelta(seconds=2),
  954. ),
  955. ]
  956. def test_get_group_tag_value_iter_perf(self):
  957. from sentry.tagstore.types import GroupTagValue
  958. group, env = self.perf_group_and_env
  959. assert list(
  960. self.ts.get_group_tag_value_iter(
  961. group,
  962. [env.id],
  963. "foo",
  964. tenant_ids={"referrer": "r", "organization_id": 1234},
  965. )
  966. ) == [
  967. GroupTagValue(
  968. group_id=group.id,
  969. key="foo",
  970. value="bar",
  971. times_seen=1,
  972. first_seen=self.now - timedelta(seconds=1),
  973. last_seen=self.now - timedelta(seconds=1),
  974. ),
  975. GroupTagValue(
  976. group_id=group.id,
  977. key="foo",
  978. value="quux",
  979. times_seen=1,
  980. first_seen=self.now - timedelta(seconds=2),
  981. last_seen=self.now - timedelta(seconds=2),
  982. ),
  983. ]
  984. def test_get_group_tag_value_paginator(self):
  985. from sentry.tagstore.types import GroupTagValue
  986. assert list(
  987. self.ts.get_group_tag_value_paginator(
  988. self.proj1group1,
  989. [self.proj1env1.id],
  990. "sentry:user",
  991. tenant_ids={"referrer": "r", "organization_id": 1234},
  992. ).get_result(10)
  993. ) == [
  994. GroupTagValue(
  995. group_id=self.proj1group1.id,
  996. key="sentry:user",
  997. value="id:user1",
  998. times_seen=1,
  999. first_seen=self.now - timedelta(seconds=1),
  1000. last_seen=self.now - timedelta(seconds=1),
  1001. ),
  1002. GroupTagValue(
  1003. group_id=self.proj1group1.id,
  1004. key="sentry:user",
  1005. value="id:user2",
  1006. times_seen=1,
  1007. first_seen=self.now - timedelta(seconds=2),
  1008. last_seen=self.now - timedelta(seconds=2),
  1009. ),
  1010. ]
  1011. def test_get_group_tag_value_paginator_perf(self):
  1012. from sentry.tagstore.types import GroupTagValue
  1013. group, env = self.perf_group_and_env
  1014. assert list(
  1015. self.ts.get_group_tag_value_paginator(
  1016. group,
  1017. [env.id],
  1018. "foo",
  1019. tenant_ids={"referrer": "r", "organization_id": 1234},
  1020. ).get_result(10)
  1021. ) == [
  1022. GroupTagValue(
  1023. group_id=group.id,
  1024. key="foo",
  1025. value="bar",
  1026. times_seen=1,
  1027. first_seen=self.now - timedelta(seconds=1),
  1028. last_seen=self.now - timedelta(seconds=1),
  1029. ),
  1030. GroupTagValue(
  1031. group_id=group.id,
  1032. key="foo",
  1033. value="quux",
  1034. times_seen=1,
  1035. first_seen=self.now - timedelta(seconds=2),
  1036. last_seen=self.now - timedelta(seconds=2),
  1037. ),
  1038. ]
  1039. def test_get_group_tag_value_paginator_times_seen(self):
  1040. from sentry.tagstore.types import GroupTagValue
  1041. self.store_event(
  1042. data={
  1043. "event_id": "5" * 32,
  1044. "message": "message 1",
  1045. "platform": "python",
  1046. "environment": self.proj1env1.name,
  1047. "fingerprint": ["group-1"],
  1048. "timestamp": iso_format(self.now - timedelta(seconds=2)),
  1049. "tags": {
  1050. "foo": "bar",
  1051. "baz": "quux",
  1052. "sentry:release": 100,
  1053. "sentry:user": "id:user2",
  1054. },
  1055. "user": {"id": "user2"},
  1056. "exception": exception,
  1057. },
  1058. project_id=self.proj1.id,
  1059. )
  1060. assert list(
  1061. self.ts.get_group_tag_value_paginator(
  1062. self.proj1group1,
  1063. [self.proj1env1.id],
  1064. "sentry:user",
  1065. order_by="-times_seen",
  1066. tenant_ids={"referrer": "r", "organization_id": 1234},
  1067. ).get_result(10)
  1068. ) == [
  1069. GroupTagValue(
  1070. group_id=self.proj1group1.id,
  1071. key="sentry:user",
  1072. value="id:user2",
  1073. times_seen=2,
  1074. first_seen=self.now - timedelta(seconds=2),
  1075. last_seen=self.now - timedelta(seconds=2),
  1076. ),
  1077. GroupTagValue(
  1078. group_id=self.proj1group1.id,
  1079. key="sentry:user",
  1080. value="id:user1",
  1081. times_seen=1,
  1082. first_seen=self.now - timedelta(seconds=1),
  1083. last_seen=self.now - timedelta(seconds=1),
  1084. ),
  1085. ]
  1086. def test_get_group_tag_value_paginator_times_seen_perf(self):
  1087. from sentry.tagstore.types import GroupTagValue
  1088. group, env = self.perf_group_and_env
  1089. event_data = load_data("transaction-n-plus-one", timestamp=before_now(minutes=10))
  1090. self.create_performance_issue(
  1091. event_data={
  1092. **event_data,
  1093. "event_id": "a" * 32,
  1094. "timestamp": iso_format(self.now - timedelta(seconds=1)),
  1095. "start_timestamp": iso_format(self.now - timedelta(seconds=1)),
  1096. "tags": {"foo": "bar", "biz": "baz"},
  1097. "release": "releaseme",
  1098. "environment": env.name,
  1099. }
  1100. )
  1101. assert list(
  1102. self.ts.get_group_tag_value_paginator(
  1103. group,
  1104. [env.id],
  1105. "foo",
  1106. order_by="-times_seen",
  1107. tenant_ids={"referrer": "r", "organization_id": 1234},
  1108. ).get_result(10)
  1109. ) == [
  1110. GroupTagValue(
  1111. group_id=group.id,
  1112. key="foo",
  1113. value="bar",
  1114. times_seen=2,
  1115. first_seen=self.now - timedelta(seconds=1),
  1116. last_seen=self.now - timedelta(seconds=1),
  1117. ),
  1118. GroupTagValue(
  1119. group_id=group.id,
  1120. key="foo",
  1121. value="quux",
  1122. times_seen=1,
  1123. first_seen=self.now - timedelta(seconds=2),
  1124. last_seen=self.now - timedelta(seconds=2),
  1125. ),
  1126. ]
  1127. # mock default value only for "limit" argument of get_group_tag_value_iter()
  1128. # it is set to 1 to avoid creating 1000+ tags for the test
  1129. @mock.patch.object(
  1130. SnubaTagStorage.get_group_tag_value_iter,
  1131. "__defaults__",
  1132. (
  1133. (),
  1134. "-first_seen",
  1135. 1,
  1136. 0,
  1137. None,
  1138. ),
  1139. )
  1140. def test_get_group_tag_value_paginator_sort_by_last_seen(self):
  1141. # the tag with "quux" value has the lowest "first_seen"
  1142. self.store_event(
  1143. data={
  1144. "event_id": "5" * 32,
  1145. "message": "message 1",
  1146. "platform": "python",
  1147. "environment": "test",
  1148. "fingerprint": ["group-1"],
  1149. "timestamp": iso_format(self.now - timedelta(seconds=5)),
  1150. "tags": {
  1151. "foo": "quux",
  1152. },
  1153. "user": {"id": "user1"},
  1154. "exception": exception,
  1155. },
  1156. project_id=self.proj1.id,
  1157. )
  1158. # the tag with "quux" value has the highest "last_seen"
  1159. self.store_event(
  1160. data={
  1161. "event_id": "6" * 32,
  1162. "message": "message 1",
  1163. "platform": "python",
  1164. "environment": "test",
  1165. "fingerprint": ["group-1"],
  1166. "timestamp": iso_format(self.now),
  1167. "tags": {
  1168. "foo": "quux",
  1169. },
  1170. "user": {"id": "user1"},
  1171. "exception": exception,
  1172. },
  1173. project_id=self.proj1.id,
  1174. )
  1175. top_key = self.ts.get_group_tag_value_paginator(
  1176. self.proj1group1,
  1177. [],
  1178. "foo",
  1179. tenant_ids={"referrer": "r", "organization_id": 1234},
  1180. ).get_result(1)[0]
  1181. # top key should be "quux" as it's the most recent than "bar"
  1182. assert top_key.value == "quux"
  1183. def test_get_group_seen_values_for_environments(self):
  1184. assert self.ts.get_group_seen_values_for_environments(
  1185. [self.proj1.id],
  1186. [self.proj1group1.id],
  1187. [self.proj1env1.id],
  1188. tenant_ids={"referrer": "r", "organization_id": 1234},
  1189. ) == {
  1190. self.proj1group1.id: {
  1191. "first_seen": self.now - timedelta(seconds=2),
  1192. "last_seen": self.now - timedelta(seconds=1),
  1193. "times_seen": 2,
  1194. }
  1195. }
  1196. # test where there should be no results because of time filters
  1197. assert (
  1198. self.ts.get_group_seen_values_for_environments(
  1199. [self.proj1.id],
  1200. [self.proj1group1.id],
  1201. [self.proj1env1.id],
  1202. start=self.now - timedelta(hours=5),
  1203. end=self.now - timedelta(hours=4),
  1204. tenant_ids={"referrer": "r", "organization_id": 1234},
  1205. )
  1206. == {}
  1207. )
  1208. class ProfilingTagStorageTest(TestCase, SnubaTestCase, SearchIssueTestMixin):
  1209. def setUp(self):
  1210. super().setUp()
  1211. self.ts = SnubaTagStorage()
  1212. def test_get_profiling_groups_user_counts_simple(self):
  1213. first_group_fingerprint = f"{ProfileFileIOGroupType.type_id}-group1"
  1214. first_group_timestamp_start = timezone.now() - timedelta(days=5)
  1215. self.store_search_issue(
  1216. self.project.id,
  1217. 1,
  1218. [first_group_fingerprint],
  1219. self.environment.name,
  1220. first_group_timestamp_start + timedelta(minutes=1),
  1221. )
  1222. self.store_search_issue(
  1223. self.project.id,
  1224. 1,
  1225. [first_group_fingerprint],
  1226. self.environment.name,
  1227. first_group_timestamp_start + timedelta(minutes=2),
  1228. )
  1229. self.store_search_issue(
  1230. self.project.id,
  1231. 2,
  1232. [first_group_fingerprint],
  1233. self.environment.name,
  1234. first_group_timestamp_start + timedelta(minutes=3),
  1235. )
  1236. event, issue_occurrence, group_info = self.store_search_issue(
  1237. self.project.id,
  1238. 3,
  1239. [first_group_fingerprint],
  1240. None,
  1241. first_group_timestamp_start + timedelta(minutes=4),
  1242. )
  1243. assert group_info is not None
  1244. first_group = group_info.group
  1245. second_group_fingerprint = f"{ProfileFileIOGroupType.type_id}-group2"
  1246. second_group_timestamp_start = timezone.now() - timedelta(hours=5)
  1247. for incr in range(1, 5):
  1248. event, issue_occurrence, group_info = self.store_search_issue(
  1249. self.project.id,
  1250. incr,
  1251. [second_group_fingerprint],
  1252. self.environment.name if incr != 4 else None,
  1253. second_group_timestamp_start + timedelta(minutes=incr),
  1254. )
  1255. assert group_info is not None
  1256. second_group = group_info.group
  1257. assert self.ts.get_generic_groups_user_counts(
  1258. [self.project.id],
  1259. group_ids=[first_group.id, second_group.id],
  1260. environment_ids=[self.environment.id],
  1261. tenant_ids={"referrer": "r", "organization_id": 1234},
  1262. ) == {first_group.id: 2, second_group.id: 3}
  1263. assert self.ts.get_generic_groups_user_counts(
  1264. [self.project.id],
  1265. group_ids=[first_group.id, second_group.id],
  1266. environment_ids=None,
  1267. tenant_ids={"referrer": "r", "organization_id": 1234},
  1268. ) == {first_group.id: 3, second_group.id: 4}
  1269. def test_get_profiling_group_list_tag_value_by_environment(self):
  1270. group_fingerprint = f"{ProfileFileIOGroupType.type_id}-group1"
  1271. start_timestamp = timezone.now() - timedelta(hours=1)
  1272. first_event_ts = start_timestamp + timedelta(minutes=1)
  1273. self.store_search_issue(
  1274. self.project.id,
  1275. 1,
  1276. [group_fingerprint],
  1277. self.environment.name,
  1278. first_event_ts,
  1279. )
  1280. last_event_ts = start_timestamp + timedelta(hours=1)
  1281. event, occurrence, group_info = self.store_search_issue(
  1282. self.project.id,
  1283. 1,
  1284. [group_fingerprint],
  1285. self.environment.name,
  1286. last_event_ts,
  1287. )
  1288. assert group_info is not None
  1289. group = group_info.group
  1290. group_seen_stats = self.ts.get_generic_group_list_tag_value(
  1291. [group.project_id],
  1292. [group.id],
  1293. [self.environment.id],
  1294. "environment",
  1295. self.environment.name,
  1296. tenant_ids={"referrer": "r", "organization_id": 1234},
  1297. )
  1298. assert group_seen_stats == {
  1299. group.id: GroupTagValue(
  1300. key="environment",
  1301. value=self.environment.name,
  1302. group_id=group.id,
  1303. times_seen=2,
  1304. first_seen=first_event_ts.replace(microsecond=0),
  1305. last_seen=last_event_ts.replace(microsecond=0),
  1306. )
  1307. }
  1308. class BaseSemverTest(TestCase, SnubaTestCase):
  1309. __test__ = Abstract(__module__, __qualname__)
  1310. KEY: str
  1311. def setUp(self):
  1312. super().setUp()
  1313. self.ts = SnubaTagStorage()
  1314. def run_test(self, query, expected_versions, environment=None, project=None):
  1315. if project is None:
  1316. project = self.project
  1317. assert list(
  1318. self.ts.get_tag_value_paginator(
  1319. project.id,
  1320. environment.id if environment else None,
  1321. self.KEY,
  1322. query=query,
  1323. ).get_result(10)
  1324. ) == [
  1325. TagValue(
  1326. key=self.KEY,
  1327. value=v,
  1328. times_seen=None,
  1329. first_seen=None,
  1330. last_seen=None,
  1331. )
  1332. for v in expected_versions
  1333. ]
  1334. class GetTagValuePaginatorForProjectsSemverTest(BaseSemverTest):
  1335. KEY = SEMVER_ALIAS
  1336. def test_semver(self):
  1337. env_2 = self.create_environment()
  1338. project_2 = self.create_project()
  1339. self.create_release(version="test@1.0.0.0+123", additional_projects=[project_2])
  1340. self.create_release(version="test@1.2.3.4", environments=[self.environment, env_2])
  1341. self.create_release(version="test@1.20.0.0-alpha", environments=[self.environment])
  1342. self.create_release(version="test@1.20.3.0-beta+789", environments=[env_2])
  1343. self.create_release(version="test@1.20.3.4", environments=[env_2])
  1344. self.create_release(version="test2@2.0.0.0+456", environments=[self.environment, env_2])
  1345. self.create_release(version="z_test@1.0.0.0")
  1346. self.create_release(version="z_test@2.0.0.0+456", additional_projects=[project_2])
  1347. # This shouldn't appear for any semver autocomplete
  1348. self.create_release(version="test@abc123", additional_projects=[project_2])
  1349. self.run_test(
  1350. None,
  1351. [
  1352. "2.0.0.0",
  1353. "1.20.3.4",
  1354. "1.20.3.0-beta",
  1355. "1.20.0.0-alpha",
  1356. "1.2.3.4",
  1357. "1.0.0.0",
  1358. ],
  1359. )
  1360. self.run_test(
  1361. "",
  1362. [
  1363. "2.0.0.0",
  1364. "1.20.3.4",
  1365. "1.20.3.0-beta",
  1366. "1.20.0.0-alpha",
  1367. "1.2.3.4",
  1368. "1.0.0.0",
  1369. ],
  1370. )
  1371. # These should all be equivalent
  1372. self.run_test("1", ["1.20.3.4", "1.20.3.0-beta", "1.20.0.0-alpha", "1.2.3.4", "1.0.0.0"])
  1373. self.run_test("1.", ["1.20.3.4", "1.20.3.0-beta", "1.20.0.0-alpha", "1.2.3.4", "1.0.0.0"])
  1374. self.run_test("1.*", ["1.20.3.4", "1.20.3.0-beta", "1.20.0.0-alpha", "1.2.3.4", "1.0.0.0"])
  1375. self.run_test("1.*", ["1.0.0.0"], project=project_2)
  1376. self.run_test("1.2", ["1.20.3.4", "1.20.3.0-beta", "1.20.0.0-alpha", "1.2.3.4"])
  1377. self.run_test("", ["2.0.0.0", "1.20.0.0-alpha", "1.2.3.4"], self.environment)
  1378. self.run_test("", ["2.0.0.0", "1.20.3.4", "1.20.3.0-beta", "1.2.3.4"], env_2)
  1379. self.run_test("1", ["1.20.0.0-alpha", "1.2.3.4"], self.environment)
  1380. self.run_test("1", ["1.20.3.4", "1.20.3.0-beta", "1.2.3.4"], env_2)
  1381. # Test packages handling
  1382. self.run_test(
  1383. "test",
  1384. [
  1385. "test2@2.0.0.0",
  1386. "test@1.20.3.4",
  1387. "test@1.20.3.0-beta",
  1388. "test@1.20.0.0-alpha",
  1389. "test@1.2.3.4",
  1390. "test@1.0.0.0",
  1391. ],
  1392. )
  1393. self.run_test("test", ["test@1.0.0.0"], project=project_2)
  1394. self.run_test("test2", ["test2@2.0.0.0"])
  1395. self.run_test("z", ["z_test@2.0.0.0", "z_test@1.0.0.0"])
  1396. self.run_test("z", ["z_test@2.0.0.0"], project=project_2)
  1397. self.run_test(
  1398. "test@",
  1399. [
  1400. "test@1.20.3.4",
  1401. "test@1.20.3.0-beta",
  1402. "test@1.20.0.0-alpha",
  1403. "test@1.2.3.4",
  1404. "test@1.0.0.0",
  1405. ],
  1406. )
  1407. self.run_test(
  1408. "test@*",
  1409. [
  1410. "test@1.20.3.4",
  1411. "test@1.20.3.0-beta",
  1412. "test@1.20.0.0-alpha",
  1413. "test@1.2.3.4",
  1414. "test@1.0.0.0",
  1415. ],
  1416. )
  1417. self.run_test(
  1418. "test@1.2",
  1419. ["test@1.20.3.4", "test@1.20.3.0-beta", "test@1.20.0.0-alpha", "test@1.2.3.4"],
  1420. )
  1421. class GetTagValuePaginatorForProjectsSemverPackageTest(BaseSemverTest):
  1422. KEY = SEMVER_PACKAGE_ALIAS
  1423. def test_semver_package(self):
  1424. env_2 = self.create_environment()
  1425. project_2 = self.create_project()
  1426. self.create_release(version="test@1.0.0.0+123", additional_projects=[project_2])
  1427. self.create_release(version="test@1.2.0.0-alpha", environments=[self.environment])
  1428. self.create_release(version="test2@2.0.0.0+456", environments=[self.environment, env_2])
  1429. self.create_release(version="z_test@2.0.0.0+456", additional_projects=[project_2])
  1430. # This shouldn't appear for any semver autocomplete
  1431. self.create_release(version="test@abc123", additional_projects=[project_2])
  1432. self.run_test(None, ["test", "test2", "z_test"])
  1433. self.run_test("", ["test", "test2", "z_test"])
  1434. self.run_test("t", ["test", "test2"])
  1435. self.run_test("test", ["test", "test2"])
  1436. self.run_test("test2", ["test2"])
  1437. self.run_test("z", ["z_test"])
  1438. self.run_test("", ["test", "z_test"], project=project_2)
  1439. self.run_test("", ["test", "test2"], self.environment)
  1440. self.run_test("", ["test2"], env_2)
  1441. class GetTagValuePaginatorForProjectsReleaseStageTest(TestCase, SnubaTestCase):
  1442. def setUp(self):
  1443. super().setUp()
  1444. self.ts = SnubaTagStorage()
  1445. def run_test(self, query, expected_releases, environment=None, project=None):
  1446. if project is None:
  1447. project = self.project
  1448. assert list(
  1449. self.ts.get_tag_value_paginator(
  1450. project.id,
  1451. environment.id if environment else None,
  1452. RELEASE_STAGE_ALIAS,
  1453. query=query,
  1454. ).get_result(10)
  1455. ) == [
  1456. TagValue(
  1457. key=RELEASE_STAGE_ALIAS,
  1458. value=r.version,
  1459. times_seen=None,
  1460. first_seen=None,
  1461. last_seen=None,
  1462. )
  1463. for r in expected_releases
  1464. ]
  1465. def test_release_stage(self):
  1466. replaced_release = self.create_release(
  1467. version="replaced_release",
  1468. environments=[self.environment],
  1469. adopted=timezone.now(),
  1470. unadopted=timezone.now(),
  1471. )
  1472. adopted_release = self.create_release(
  1473. version="adopted_release", environments=[self.environment], adopted=timezone.now()
  1474. )
  1475. not_adopted_release = self.create_release(
  1476. version="not_adopted_release", environments=[self.environment]
  1477. )
  1478. env_2 = self.create_environment()
  1479. project_2 = self.create_project()
  1480. self.run_test(ReleaseStages.ADOPTED, [adopted_release], environment=self.environment)
  1481. self.run_test(
  1482. ReleaseStages.LOW_ADOPTION, [not_adopted_release], environment=self.environment
  1483. )
  1484. self.run_test(ReleaseStages.REPLACED, [replaced_release], environment=self.environment)
  1485. self.run_test(ReleaseStages.ADOPTED, [], environment=env_2)
  1486. self.run_test(ReleaseStages.ADOPTED, [], project=project_2, environment=self.environment)
  1487. class GetTagValuePaginatorForProjectsSemverBuildTest(BaseSemverTest):
  1488. KEY = SEMVER_BUILD_ALIAS
  1489. def test_semver_package(self):
  1490. env_2 = self.create_environment()
  1491. project_2 = self.create_project()
  1492. self.create_release(version="test@1.0.0.0+123", additional_projects=[project_2])
  1493. self.create_release(version="test@1.0.0.0+456")
  1494. self.create_release(version="test@1.2.0.0", environments=[self.environment])
  1495. self.create_release(version="test@1.2.1.0+124", environments=[self.environment])
  1496. self.create_release(version="test@2.0.0.0+456", environments=[self.environment, env_2])
  1497. self.create_release(version="test@2.0.1.0+457a", additional_projects=[project_2])
  1498. self.create_release(version="test@2.0.1.0+789", additional_projects=[project_2])
  1499. # This shouldn't appear for any semver autocomplete
  1500. self.create_release(version="test@abc123", additional_projects=[project_2])
  1501. self.run_test(None, ["123", "124", "456", "457a", "789"])
  1502. self.run_test("", ["123", "124", "456", "457a", "789"])
  1503. self.run_test("1", ["123", "124"])
  1504. self.run_test("123", ["123"])
  1505. self.run_test("4", ["456", "457a"])
  1506. self.run_test("1", ["123"], project=project_2)
  1507. self.run_test("1", ["124"], self.environment)
  1508. self.run_test("4", ["456", "457a"])
  1509. self.run_test("4", ["456"], env_2)