test_tagstore_backend.py 54 KB

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