test_tagstore_backend.py 54 KB

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