test_backend.py 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365
  1. from __future__ import absolute_import
  2. import mock
  3. import pytz
  4. import pytest
  5. from datetime import datetime, timedelta
  6. from django.conf import settings
  7. from django.utils import timezone
  8. from hashlib import md5
  9. from sentry import options
  10. from sentry.api.issue_search import convert_query_values, IssueSearchVisitor, parse_search_query
  11. from sentry.models import (
  12. Environment,
  13. Group,
  14. GroupAssignee,
  15. GroupBookmark,
  16. GroupEnvironment,
  17. GroupStatus,
  18. GroupSubscription,
  19. )
  20. from sentry.search.snuba.backend import SnubaSearchBackend
  21. from sentry.testutils import SnubaTestCase, TestCase, xfail_if_not_postgres
  22. from sentry.testutils.helpers.datetime import iso_format
  23. from sentry.utils.snuba import SENTRY_SNUBA_MAP, SnubaError
  24. def date_to_query_format(date):
  25. return date.strftime("%Y-%m-%dT%H:%M:%S")
  26. class SnubaSearchTest(TestCase, SnubaTestCase):
  27. def setUp(self):
  28. super(SnubaSearchTest, self).setUp()
  29. self.backend = SnubaSearchBackend()
  30. self.base_datetime = (datetime.utcnow() - timedelta(days=3)).replace(tzinfo=pytz.utc)
  31. event1_timestamp = iso_format(self.base_datetime - timedelta(days=21))
  32. self.event1 = self.store_event(
  33. data={
  34. "fingerprint": ["put-me-in-group1"],
  35. "event_id": "a" * 32,
  36. "message": "foo",
  37. "environment": "production",
  38. "tags": {"server": "example.com"},
  39. "timestamp": event1_timestamp,
  40. "stacktrace": {"frames": [{"module": "group1"}]},
  41. },
  42. project_id=self.project.id,
  43. )
  44. self.event3 = self.store_event(
  45. data={
  46. "fingerprint": ["put-me-in-group1"],
  47. "event_id": "c" * 32,
  48. "message": "group1",
  49. "environment": "production",
  50. "tags": {"server": "example.com"},
  51. "timestamp": iso_format(self.base_datetime),
  52. "stacktrace": {"frames": [{"module": "group1"}]},
  53. },
  54. project_id=self.project.id,
  55. )
  56. self.group1 = Group.objects.get(id=self.event1.group.id)
  57. assert self.group1.id == self.event1.group.id
  58. assert self.group1.id == self.event3.group.id
  59. assert self.group1.first_seen == self.event1.datetime
  60. assert self.group1.last_seen == self.event3.datetime
  61. self.group1.times_seen = 5
  62. self.group1.status = GroupStatus.UNRESOLVED
  63. self.group1.save()
  64. self.event2 = self.store_event(
  65. data={
  66. "fingerprint": ["put-me-in-group2"],
  67. "event_id": "b" * 32,
  68. "timestamp": iso_format(self.base_datetime - timedelta(days=20)),
  69. "message": "bar",
  70. "stacktrace": {"frames": [{"module": "group2"}]},
  71. "environment": "staging",
  72. "tags": {"server": "example.com", "url": "http://example.com"},
  73. },
  74. project_id=self.project.id,
  75. )
  76. self.group2 = Group.objects.get(id=self.event2.group.id)
  77. assert self.group2.id == self.event2.group.id
  78. assert self.group2.first_seen == self.group2.last_seen == self.event2.datetime
  79. self.group2.status = GroupStatus.RESOLVED
  80. self.group2.times_seen = 10
  81. self.group2.save()
  82. GroupBookmark.objects.create(user=self.user, group=self.group2, project=self.group2.project)
  83. GroupAssignee.objects.create(user=self.user, group=self.group2, project=self.group2.project)
  84. GroupSubscription.objects.create(
  85. user=self.user, group=self.group1, project=self.group1.project, is_active=True
  86. )
  87. GroupSubscription.objects.create(
  88. user=self.user, group=self.group2, project=self.group2.project, is_active=False
  89. )
  90. self.environments = {
  91. "production": self.event1.get_environment(),
  92. "staging": self.event2.get_environment(),
  93. }
  94. def store_event(self, data, *args, **kwargs):
  95. event = super(SnubaSearchTest, self).store_event(data, *args, **kwargs)
  96. environment_name = data.get("environment")
  97. if environment_name:
  98. GroupEnvironment.objects.filter(
  99. group_id=event.group_id,
  100. environment__name=environment_name,
  101. first_seen__gt=event.datetime,
  102. ).update(first_seen=event.datetime)
  103. return event
  104. def set_up_multi_project(self):
  105. self.project2 = self.create_project(organization=self.project.organization)
  106. self.event_p2 = self.store_event(
  107. data={
  108. "event_id": "a" * 32,
  109. "fingerprint": ["put-me-in-groupP2"],
  110. "timestamp": iso_format(self.base_datetime - timedelta(days=21)),
  111. "message": "foo",
  112. "stacktrace": {"frames": [{"module": "group_p2"}]},
  113. "tags": {"server": "example.com"},
  114. "environment": "production",
  115. },
  116. project_id=self.project2.id,
  117. )
  118. self.group_p2 = Group.objects.get(id=self.event_p2.group.id)
  119. self.group_p2.times_seen = 6
  120. self.group_p2.last_seen = self.base_datetime - timedelta(days=1)
  121. self.group_p2.save()
  122. def build_search_filter(self, query, projects=None, user=None, environments=None):
  123. user = user if user is not None else self.user
  124. projects = projects if projects is not None else [self.project]
  125. return convert_query_values(parse_search_query(query), projects, user, environments)
  126. def make_query(
  127. self,
  128. projects=None,
  129. search_filter_query=None,
  130. environments=None,
  131. sort_by="date",
  132. limit=None,
  133. count_hits=False,
  134. date_from=None,
  135. date_to=None,
  136. ):
  137. search_filters = []
  138. projects = projects if projects is not None else [self.project]
  139. if search_filter_query is not None:
  140. search_filters = self.build_search_filter(
  141. search_filter_query, projects, environments=environments
  142. )
  143. kwargs = {}
  144. if limit is not None:
  145. kwargs["limit"] = limit
  146. return self.backend.query(
  147. projects,
  148. search_filters=search_filters,
  149. environments=environments,
  150. count_hits=count_hits,
  151. sort_by=sort_by,
  152. date_from=date_from,
  153. date_to=date_to,
  154. **kwargs
  155. )
  156. def test_query(self):
  157. results = self.make_query(search_filter_query="foo")
  158. assert set(results) == set([self.group1])
  159. results = self.make_query(search_filter_query="bar")
  160. assert set(results) == set([self.group2])
  161. def test_query_multi_project(self):
  162. self.set_up_multi_project()
  163. results = self.make_query([self.project, self.project2], search_filter_query="foo")
  164. assert set(results) == set([self.group1, self.group_p2])
  165. def test_query_with_environment(self):
  166. results = self.make_query(
  167. environments=[self.environments["production"]], search_filter_query="foo"
  168. )
  169. assert set(results) == set([self.group1])
  170. results = self.make_query(
  171. environments=[self.environments["production"]], search_filter_query="bar"
  172. )
  173. assert set(results) == set([])
  174. results = self.make_query(
  175. environments=[self.environments["staging"]], search_filter_query="bar"
  176. )
  177. assert set(results) == set([self.group2])
  178. def test_multi_environments(self):
  179. self.set_up_multi_project()
  180. results = self.make_query(
  181. [self.project, self.project2],
  182. environments=[self.environments["production"], self.environments["staging"]],
  183. )
  184. assert set(results) == set([self.group1, self.group2, self.group_p2])
  185. def test_query_with_environment_multi_project(self):
  186. self.set_up_multi_project()
  187. results = self.make_query(
  188. [self.project, self.project2],
  189. environments=[self.environments["production"]],
  190. search_filter_query="foo",
  191. )
  192. assert set(results) == set([self.group1, self.group_p2])
  193. results = self.make_query(
  194. [self.project, self.project2],
  195. environments=[self.environments["production"]],
  196. search_filter_query="bar",
  197. )
  198. assert set(results) == set([])
  199. def test_sort(self):
  200. results = self.make_query(sort_by="date")
  201. assert list(results) == [self.group1, self.group2]
  202. results = self.make_query(sort_by="new")
  203. assert list(results) == [self.group2, self.group1]
  204. results = self.make_query(sort_by="freq")
  205. assert list(results) == [self.group1, self.group2]
  206. results = self.make_query(sort_by="priority")
  207. assert list(results) == [self.group1, self.group2]
  208. def test_sort_multi_project(self):
  209. self.set_up_multi_project()
  210. results = self.make_query([self.project, self.project2], sort_by="date")
  211. assert list(results) == [self.group1, self.group_p2, self.group2]
  212. results = self.make_query([self.project, self.project2], sort_by="new")
  213. assert list(results) == [self.group2, self.group_p2, self.group1]
  214. results = self.make_query([self.project, self.project2], sort_by="freq")
  215. assert list(results) == [self.group1, self.group_p2, self.group2]
  216. results = self.make_query([self.project, self.project2], sort_by="priority")
  217. assert list(results) == [self.group1, self.group2, self.group_p2]
  218. def test_sort_with_environment(self):
  219. for dt in [
  220. self.group1.first_seen + timedelta(days=1),
  221. self.group1.first_seen + timedelta(days=2),
  222. self.group1.last_seen + timedelta(days=1),
  223. ]:
  224. self.store_event(
  225. data={
  226. "fingerprint": ["put-me-in-group2"],
  227. "timestamp": iso_format(dt),
  228. "stacktrace": {"frames": [{"module": "group2"}]},
  229. "environment": "production",
  230. "message": "group2",
  231. },
  232. project_id=self.project.id,
  233. )
  234. results = self.make_query(environments=[self.environments["production"]], sort_by="date")
  235. assert list(results) == [self.group2, self.group1]
  236. results = self.make_query(environments=[self.environments["production"]], sort_by="new")
  237. assert list(results) == [self.group2, self.group1]
  238. results = self.make_query(environments=[self.environments["production"]], sort_by="freq")
  239. assert list(results) == [self.group2, self.group1]
  240. results = self.make_query(
  241. environments=[self.environments["production"]], sort_by="priority"
  242. )
  243. assert list(results) == [self.group2, self.group1]
  244. def test_status(self):
  245. results = self.make_query(search_filter_query="is:unresolved")
  246. assert set(results) == set([self.group1])
  247. results = self.make_query(search_filter_query="is:resolved")
  248. assert set(results) == set([self.group2])
  249. def test_status_with_environment(self):
  250. results = self.make_query(
  251. environments=[self.environments["production"]], search_filter_query="is:unresolved"
  252. )
  253. assert set(results) == set([self.group1])
  254. results = self.make_query(
  255. environments=[self.environments["staging"]], search_filter_query="is:resolved"
  256. )
  257. assert set(results) == set([self.group2])
  258. results = self.make_query(
  259. environments=[self.environments["production"]], search_filter_query="is:resolved"
  260. )
  261. assert set(results) == set([])
  262. def test_tags(self):
  263. results = self.make_query(search_filter_query="environment:staging")
  264. assert set(results) == set([self.group2])
  265. results = self.make_query(search_filter_query="environment:example.com")
  266. assert set(results) == set([])
  267. results = self.make_query(search_filter_query="has:environment")
  268. assert set(results) == set([self.group2, self.group1])
  269. results = self.make_query(search_filter_query="environment:staging server:example.com")
  270. assert set(results) == set([self.group2])
  271. results = self.make_query(search_filter_query='url:"http://example.com"')
  272. assert set(results) == set([self.group2])
  273. results = self.make_query(search_filter_query="environment:staging has:server")
  274. assert set(results) == set([self.group2])
  275. results = self.make_query(search_filter_query="environment:staging server:bar.example.com")
  276. assert set(results) == set([])
  277. def test_tags_with_environment(self):
  278. results = self.make_query(
  279. environments=[self.environments["production"]], search_filter_query="server:example.com"
  280. )
  281. assert set(results) == set([self.group1])
  282. results = self.make_query(
  283. environments=[self.environments["staging"]], search_filter_query="server:example.com"
  284. )
  285. assert set(results) == set([self.group2])
  286. results = self.make_query(
  287. environments=[self.environments["staging"]], search_filter_query="has:server"
  288. )
  289. assert set(results) == set([self.group2])
  290. results = self.make_query(
  291. environments=[self.environments["production"]],
  292. search_filter_query='url:"http://example.com"',
  293. )
  294. assert set(results) == set([])
  295. results = self.make_query(
  296. environments=[self.environments["staging"]],
  297. search_filter_query='url:"http://example.com"',
  298. )
  299. assert set(results) == set([self.group2])
  300. results = self.make_query(
  301. environments=[self.environments["staging"]],
  302. search_filter_query="server:bar.example.com",
  303. )
  304. assert set(results) == set([])
  305. def test_bookmarked_by(self):
  306. results = self.make_query(search_filter_query="bookmarks:%s" % self.user.username)
  307. assert set(results) == set([self.group2])
  308. def test_bookmarked_by_with_environment(self):
  309. results = self.make_query(
  310. environments=[self.environments["staging"]],
  311. search_filter_query="bookmarks:%s" % self.user.username,
  312. )
  313. assert set(results) == set([self.group2])
  314. results = self.make_query(
  315. environments=[self.environments["production"]],
  316. search_filter_query="bookmarks:%s" % self.user.username,
  317. )
  318. assert set(results) == set([])
  319. def test_search_filter_query_with_custom_priority_tag(self):
  320. priority = "high"
  321. self.store_event(
  322. data={
  323. "fingerprint": ["put-me-in-group2"],
  324. "timestamp": iso_format(self.group2.first_seen + timedelta(days=1)),
  325. "stacktrace": {"frames": [{"module": "group2"}]},
  326. "message": "group2",
  327. "tags": {"priority": priority},
  328. },
  329. project_id=self.project.id,
  330. )
  331. results = self.make_query(search_filter_query="priority:%s" % priority)
  332. assert set(results) == set([self.group2])
  333. def test_search_filter_query_with_custom_priority_tag_and_priority_sort(self):
  334. priority = "high"
  335. for i in range(1, 3):
  336. self.store_event(
  337. data={
  338. "fingerprint": ["put-me-in-group1"],
  339. "timestamp": iso_format(self.group2.last_seen + timedelta(days=i)),
  340. "stacktrace": {"frames": [{"module": "group1"}]},
  341. "message": "group1",
  342. "tags": {"priority": priority},
  343. },
  344. project_id=self.project.id,
  345. )
  346. self.store_event(
  347. data={
  348. "fingerprint": ["put-me-in-group2"],
  349. "timestamp": iso_format(self.group2.last_seen + timedelta(days=2)),
  350. "stacktrace": {"frames": [{"module": "group2"}]},
  351. "message": "group2",
  352. "tags": {"priority": priority},
  353. },
  354. project_id=self.project.id,
  355. )
  356. results = self.make_query(search_filter_query="priority:%s" % priority, sort_by="priority")
  357. assert list(results) == [self.group1, self.group2]
  358. def test_project(self):
  359. results = self.make_query([self.create_project(name="other")])
  360. assert set(results) == set([])
  361. def test_pagination(self):
  362. for options_set in [
  363. {"snuba.search.min-pre-snuba-candidates": None},
  364. {"snuba.search.min-pre-snuba-candidates": 500},
  365. ]:
  366. with self.options(options_set):
  367. results = self.backend.query([self.project], limit=1, sort_by="date")
  368. assert set(results) == set([self.group1])
  369. assert not results.prev.has_results
  370. assert results.next.has_results
  371. results = self.backend.query(
  372. [self.project], cursor=results.next, limit=1, sort_by="date"
  373. )
  374. assert set(results) == set([self.group2])
  375. assert results.prev.has_results
  376. assert not results.next.has_results
  377. # note: previous cursor
  378. results = self.backend.query(
  379. [self.project], cursor=results.prev, limit=1, sort_by="date"
  380. )
  381. assert set(results) == set([self.group1])
  382. assert results.prev.has_results
  383. assert results.next.has_results
  384. # note: previous cursor, paging too far into 0 results
  385. results = self.backend.query(
  386. [self.project], cursor=results.prev, limit=1, sort_by="date"
  387. )
  388. assert set(results) == set([])
  389. assert not results.prev.has_results
  390. assert results.next.has_results
  391. results = self.backend.query(
  392. [self.project], cursor=results.next, limit=1, sort_by="date"
  393. )
  394. assert set(results) == set([self.group1])
  395. assert results.prev.has_results
  396. assert results.next.has_results
  397. results = self.backend.query(
  398. [self.project], cursor=results.next, limit=1, sort_by="date"
  399. )
  400. assert set(results) == set([self.group2])
  401. assert results.prev.has_results
  402. assert not results.next.has_results
  403. results = self.backend.query(
  404. [self.project], cursor=results.next, limit=1, sort_by="date"
  405. )
  406. assert set(results) == set([])
  407. assert results.prev.has_results
  408. assert not results.next.has_results
  409. def test_pagination_with_environment(self):
  410. for dt in [
  411. self.group1.first_seen + timedelta(days=1),
  412. self.group1.first_seen + timedelta(days=2),
  413. self.group1.last_seen + timedelta(days=1),
  414. ]:
  415. self.store_event(
  416. data={
  417. "fingerprint": ["put-me-in-group2"],
  418. "timestamp": iso_format(dt),
  419. "environment": "production",
  420. "message": "group2",
  421. "stacktrace": {"frames": [{"module": "group2"}]},
  422. },
  423. project_id=self.project.id,
  424. )
  425. results = self.backend.query(
  426. [self.project],
  427. environments=[self.environments["production"]],
  428. sort_by="date",
  429. limit=1,
  430. count_hits=True,
  431. )
  432. assert list(results) == [self.group2]
  433. assert results.hits == 2
  434. results = self.backend.query(
  435. [self.project],
  436. environments=[self.environments["production"]],
  437. sort_by="date",
  438. limit=1,
  439. cursor=results.next,
  440. count_hits=True,
  441. )
  442. assert list(results) == [self.group1]
  443. assert results.hits == 2
  444. results = self.backend.query(
  445. [self.project],
  446. environments=[self.environments["production"]],
  447. sort_by="date",
  448. limit=1,
  449. cursor=results.next,
  450. count_hits=True,
  451. )
  452. assert list(results) == []
  453. assert results.hits == 2
  454. def test_active_at_filter(self):
  455. results = self.make_query(
  456. search_filter_query="activeSince:>=%s" % date_to_query_format(self.group2.active_at)
  457. )
  458. assert set(results) == set([self.group2])
  459. results = self.make_query(
  460. search_filter_query="activeSince:<=%s"
  461. % date_to_query_format(self.group1.active_at + timedelta(minutes=1))
  462. )
  463. assert set(results) == set([self.group1])
  464. results = self.make_query(
  465. search_filter_query="activeSince:>=%s activeSince:<=%s"
  466. % (
  467. date_to_query_format(self.group1.active_at),
  468. date_to_query_format(self.group1.active_at + timedelta(minutes=1)),
  469. )
  470. )
  471. assert set(results) == set([self.group1])
  472. def test_age_filter(self):
  473. results = self.make_query(
  474. search_filter_query="firstSeen:>=%s" % date_to_query_format(self.group2.first_seen)
  475. )
  476. assert set(results) == set([self.group2])
  477. results = self.make_query(
  478. search_filter_query="firstSeen:<=%s"
  479. % date_to_query_format(self.group1.first_seen + timedelta(minutes=1))
  480. )
  481. assert set(results) == set([self.group1])
  482. results = self.make_query(
  483. search_filter_query="firstSeen:>=%s firstSeen:<=%s"
  484. % (
  485. date_to_query_format(self.group1.first_seen),
  486. date_to_query_format(self.group1.first_seen + timedelta(minutes=1)),
  487. )
  488. )
  489. assert set(results) == set([self.group1])
  490. def test_age_filter_with_environment(self):
  491. # add time instead to make it greater than or less than as needed.
  492. group1_first_seen = GroupEnvironment.objects.get(
  493. environment=self.environments["production"], group=self.group1
  494. ).first_seen
  495. results = self.make_query(
  496. environments=[self.environments["production"]],
  497. search_filter_query="firstSeen:>=%s" % date_to_query_format(group1_first_seen),
  498. )
  499. assert set(results) == set([self.group1])
  500. results = self.make_query(
  501. environments=[self.environments["production"]],
  502. search_filter_query="firstSeen:<=%s" % date_to_query_format(group1_first_seen),
  503. )
  504. assert set(results) == set([self.group1])
  505. results = self.make_query(
  506. environments=[self.environments["production"]],
  507. search_filter_query="firstSeen:>%s" % date_to_query_format(group1_first_seen),
  508. )
  509. assert set(results) == set([])
  510. self.store_event(
  511. data={
  512. "fingerprint": ["put-me-in-group1"],
  513. "timestamp": iso_format(group1_first_seen + timedelta(days=1)),
  514. "message": "group1",
  515. "stacktrace": {"frames": [{"module": "group1"}]},
  516. "environment": "development",
  517. },
  518. project_id=self.project.id,
  519. )
  520. results = self.make_query(
  521. environments=[self.environments["production"]],
  522. search_filter_query="firstSeen:>%s" % date_to_query_format(group1_first_seen),
  523. )
  524. assert set(results) == set([])
  525. results = self.make_query(
  526. environments=[Environment.objects.get(name="development")],
  527. search_filter_query="firstSeen:>%s" % date_to_query_format(group1_first_seen),
  528. )
  529. assert set(results) == set([self.group1])
  530. def test_times_seen_filter(self):
  531. results = self.make_query([self.project], search_filter_query="times_seen:2")
  532. assert set(results) == set([self.group1])
  533. results = self.make_query([self.project], search_filter_query="times_seen:>=2")
  534. assert set(results) == set([self.group1])
  535. results = self.make_query([self.project], search_filter_query="times_seen:<=1")
  536. assert set(results) == set([self.group2])
  537. def test_last_seen_filter(self):
  538. results = self.make_query(
  539. search_filter_query="lastSeen:>=%s" % date_to_query_format(self.group1.last_seen)
  540. )
  541. assert set(results) == set([self.group1])
  542. results = self.make_query(
  543. search_filter_query="lastSeen:>=%s lastSeen:<=%s"
  544. % (
  545. date_to_query_format(self.group1.last_seen),
  546. date_to_query_format(self.group1.last_seen + timedelta(minutes=1)),
  547. )
  548. )
  549. assert set(results) == set([self.group1])
  550. def test_last_seen_filter_with_environment(self):
  551. results = self.make_query(
  552. environments=[self.environments["production"]],
  553. search_filter_query="lastSeen:>=%s" % date_to_query_format(self.group1.last_seen),
  554. )
  555. assert set(results) == set([self.group1])
  556. results = self.make_query(
  557. environments=[self.environments["production"]],
  558. search_filter_query="lastSeen:<=%s" % date_to_query_format(self.group1.last_seen),
  559. )
  560. assert set(results) == set([self.group1])
  561. results = self.make_query(
  562. environments=[self.environments["production"]],
  563. search_filter_query="lastSeen:>%s" % date_to_query_format(self.group1.last_seen),
  564. )
  565. assert set(results) == set([])
  566. self.store_event(
  567. data={
  568. "fingerprint": ["put-me-in-group1"],
  569. "timestamp": iso_format(self.group1.last_seen + timedelta(days=1)),
  570. "message": "group1",
  571. "stacktrace": {"frames": [{"module": "group1"}]},
  572. "environment": "development",
  573. },
  574. project_id=self.project.id,
  575. )
  576. self.group1.update(last_seen=self.group1.last_seen + timedelta(days=1))
  577. results = self.make_query(
  578. environments=[self.environments["production"]],
  579. search_filter_query="lastSeen:>%s" % date_to_query_format(self.group1.last_seen),
  580. )
  581. assert set(results) == set([])
  582. results = self.make_query(
  583. environments=[Environment.objects.get(name="development")],
  584. search_filter_query="lastSeen:>%s" % date_to_query_format(self.group1.last_seen),
  585. )
  586. assert set(results) == set()
  587. results = self.make_query(
  588. environments=[Environment.objects.get(name="development")],
  589. search_filter_query="lastSeen:>=%s" % date_to_query_format(self.group1.last_seen),
  590. )
  591. assert set(results) == set([self.group1])
  592. def test_date_filter(self):
  593. results = self.make_query(
  594. date_from=self.event2.datetime,
  595. search_filter_query="timestamp:>=%s" % date_to_query_format(self.event2.datetime),
  596. )
  597. assert set(results) == set([self.group1, self.group2])
  598. results = self.make_query(
  599. date_to=self.event1.datetime + timedelta(minutes=1),
  600. search_filter_query="timestamp:<=%s"
  601. % date_to_query_format(self.event1.datetime + timedelta(minutes=1)),
  602. )
  603. assert set(results) == set([self.group1])
  604. results = self.make_query(
  605. date_from=self.event1.datetime,
  606. date_to=self.event2.datetime + timedelta(minutes=1),
  607. search_filter_query="timestamp:>=%s timestamp:<=%s"
  608. % (
  609. date_to_query_format(self.event1.datetime),
  610. date_to_query_format(self.event2.datetime + timedelta(minutes=1)),
  611. ),
  612. )
  613. assert set(results) == set([self.group1, self.group2])
  614. # Test with `Z` utc marker, should be equivalent
  615. results = self.make_query(
  616. date_from=self.event1.datetime,
  617. date_to=self.event2.datetime + timedelta(minutes=1),
  618. search_filter_query="timestamp:>=%s timestamp:<=%s"
  619. % (
  620. date_to_query_format(self.event1.datetime) + "Z",
  621. date_to_query_format(self.event2.datetime + timedelta(minutes=1)) + "Z",
  622. ),
  623. )
  624. assert set(results) == set([self.group1, self.group2])
  625. @pytest.mark.xfail(
  626. not settings.SENTRY_TAGSTORE.startswith("sentry.tagstore.v2"),
  627. reason="unsupported on legacy backend due to insufficient index",
  628. )
  629. def test_date_filter_with_environment(self):
  630. results = self.backend.query(
  631. [self.project],
  632. environments=[self.environments["production"]],
  633. date_from=self.event2.datetime,
  634. )
  635. assert set(results) == set([self.group1])
  636. results = self.backend.query(
  637. [self.project],
  638. environments=[self.environments["production"]],
  639. date_to=self.event1.datetime + timedelta(minutes=1),
  640. )
  641. assert set(results) == set([self.group1])
  642. results = self.backend.query(
  643. [self.project],
  644. environments=[self.environments["staging"]],
  645. date_from=self.event1.datetime,
  646. date_to=self.event2.datetime + timedelta(minutes=1),
  647. )
  648. assert set(results) == set([self.group2])
  649. def test_unassigned(self):
  650. results = self.make_query(search_filter_query="is:unassigned")
  651. assert set(results) == set([self.group1])
  652. results = self.make_query(search_filter_query="is:assigned")
  653. assert set(results) == set([self.group2])
  654. def test_unassigned_with_environment(self):
  655. results = self.make_query(
  656. environments=[self.environments["production"]], search_filter_query="is:unassigned"
  657. )
  658. assert set(results) == set([self.group1])
  659. results = self.make_query(
  660. environments=[self.environments["staging"]], search_filter_query="is:assigned"
  661. )
  662. assert set(results) == set([self.group2])
  663. results = self.make_query(
  664. environments=[self.environments["production"]], search_filter_query="is:assigned"
  665. )
  666. assert set(results) == set([])
  667. def test_assigned_to(self):
  668. results = self.make_query(search_filter_query="assigned:%s" % self.user.username)
  669. assert set(results) == set([self.group2])
  670. # test team assignee
  671. ga = GroupAssignee.objects.get(
  672. user=self.user, group=self.group2, project=self.group2.project
  673. )
  674. ga.update(team=self.team, user=None)
  675. assert GroupAssignee.objects.get(id=ga.id).user is None
  676. results = self.make_query(search_filter_query="assigned:%s" % self.user.username)
  677. assert set(results) == set([self.group2])
  678. # test when there should be no results
  679. other_user = self.create_user()
  680. results = self.make_query(search_filter_query="assigned:%s" % other_user.username)
  681. assert set(results) == set([])
  682. owner = self.create_user()
  683. self.create_member(
  684. organization=self.project.organization, user=owner, role="owner", teams=[]
  685. )
  686. # test that owners don't see results for all teams
  687. results = self.make_query(search_filter_query="assigned:%s" % owner.username)
  688. assert set(results) == set([])
  689. def test_assigned_to_with_environment(self):
  690. results = self.make_query(
  691. environments=[self.environments["staging"]],
  692. search_filter_query="assigned:%s" % self.user.username,
  693. )
  694. assert set(results) == set([self.group2])
  695. results = self.make_query(
  696. environments=[self.environments["production"]],
  697. search_filter_query="assigned:%s" % self.user.username,
  698. )
  699. assert set(results) == set([])
  700. def test_subscribed_by(self):
  701. results = self.make_query(
  702. [self.group1.project], search_filter_query="subscribed:%s" % self.user.username
  703. )
  704. assert set(results) == set([self.group1])
  705. def test_subscribed_by_with_environment(self):
  706. results = self.make_query(
  707. [self.group1.project],
  708. environments=[self.environments["production"]],
  709. search_filter_query="subscribed:%s" % self.user.username,
  710. )
  711. assert set(results) == set([self.group1])
  712. results = self.make_query(
  713. [self.group1.project],
  714. environments=[self.environments["staging"]],
  715. search_filter_query="subscribed:%s" % self.user.username,
  716. )
  717. assert set(results) == set([])
  718. @mock.patch("sentry.utils.snuba.raw_query")
  719. def test_snuba_not_called_optimization(self, query_mock):
  720. assert self.make_query(search_filter_query="foo").results == [self.group1]
  721. assert not query_mock.called
  722. assert (
  723. self.make_query(
  724. search_filter_query="last_seen:>%s foo" % date_to_query_format(timezone.now()),
  725. sort_by="date",
  726. ).results
  727. == []
  728. )
  729. assert query_mock.called
  730. @mock.patch("sentry.utils.snuba.raw_query")
  731. def test_optimized_aggregates(self, query_mock):
  732. # TODO this test is annoyingly fragile and breaks in hard-to-see ways
  733. # any time anything about the snuba query changes
  734. query_mock.return_value = {"data": [], "totals": {"total": 0}}
  735. def Any(cls):
  736. class Any(object):
  737. def __eq__(self, other):
  738. return isinstance(other, cls)
  739. return Any()
  740. DEFAULT_LIMIT = 100
  741. chunk_growth = options.get("snuba.search.chunk-growth-rate")
  742. limit = int(DEFAULT_LIMIT * chunk_growth)
  743. common_args = {
  744. "start": Any(datetime),
  745. "end": Any(datetime),
  746. "filter_keys": {"project_id": [self.project.id], "issue": [self.group1.id]},
  747. "referrer": "search",
  748. "groupby": ["issue"],
  749. "conditions": [[["positionCaseInsensitive", ["message", "'foo'"]], "!=", 0]],
  750. "selected_columns": [],
  751. "limit": limit,
  752. "offset": 0,
  753. "totals": True,
  754. "turbo": False,
  755. "sample": 1,
  756. }
  757. self.make_query(search_filter_query="foo")
  758. assert not query_mock.called
  759. self.make_query(
  760. search_filter_query="last_seen:>=%s foo" % date_to_query_format(timezone.now()),
  761. sort_by="date",
  762. )
  763. assert query_mock.call_args == mock.call(
  764. orderby=["-last_seen", "issue"],
  765. aggregations=[
  766. ["uniq", "issue", "total"],
  767. ["multiply(toUInt64(max(timestamp)), 1000)", "", "last_seen"],
  768. ],
  769. having=[["last_seen", ">=", Any(int)]],
  770. **common_args
  771. )
  772. self.make_query(search_filter_query="foo", sort_by="priority")
  773. assert query_mock.call_args == mock.call(
  774. orderby=["-priority", "issue"],
  775. aggregations=[
  776. ["toUInt64(plus(multiply(log(times_seen), 600), last_seen))", "", "priority"],
  777. ["count()", "", "times_seen"],
  778. ["uniq", "issue", "total"],
  779. ["multiply(toUInt64(max(timestamp)), 1000)", "", "last_seen"],
  780. ],
  781. having=[],
  782. **common_args
  783. )
  784. self.make_query(search_filter_query="times_seen:5 foo", sort_by="freq")
  785. assert query_mock.call_args == mock.call(
  786. orderby=["-times_seen", "issue"],
  787. aggregations=[["count()", "", "times_seen"], ["uniq", "issue", "total"]],
  788. having=[["times_seen", "=", 5]],
  789. **common_args
  790. )
  791. def test_pre_and_post_filtering(self):
  792. prev_max_pre = options.get("snuba.search.max-pre-snuba-candidates")
  793. options.set("snuba.search.max-pre-snuba-candidates", 1)
  794. try:
  795. # normal queries work as expected
  796. results = self.make_query(search_filter_query="foo")
  797. assert set(results) == set([self.group1])
  798. results = self.make_query(search_filter_query="bar")
  799. assert set(results) == set([self.group2])
  800. # no candidate matches in Sentry, immediately return empty paginator
  801. results = self.make_query(search_filter_query="NO MATCHES IN SENTRY")
  802. assert set(results) == set()
  803. # too many candidates, skip pre-filter, requires >1 postfilter queries
  804. results = self.make_query()
  805. assert set(results) == set([self.group1, self.group2])
  806. finally:
  807. options.set("snuba.search.max-pre-snuba-candidates", prev_max_pre)
  808. def test_optimizer_enabled(self):
  809. prev_optimizer_enabled = options.get("snuba.search.pre-snuba-candidates-optimizer")
  810. options.set("snuba.search.pre-snuba-candidates-optimizer", True)
  811. try:
  812. results = self.make_query(
  813. search_filter_query="server:example.com",
  814. environments=[self.environments["production"]],
  815. )
  816. assert set(results) == set([self.group1])
  817. finally:
  818. options.set("snuba.search.pre-snuba-candidates-optimizer", prev_optimizer_enabled)
  819. def test_search_out_of_range(self):
  820. the_date = datetime(2000, 1, 1, 0, 0, 0, tzinfo=pytz.utc)
  821. results = self.make_query(
  822. search_filter_query="event.timestamp:>%s event.timestamp:<%s" % (the_date, the_date),
  823. date_from=the_date,
  824. date_to=the_date,
  825. )
  826. assert set(results) == set([])
  827. def test_hits_estimate(self):
  828. # 400 Groups/Events
  829. # Every 3rd one is Unresolved
  830. # Evey 2nd one has tag match=1
  831. for i in range(400):
  832. event = self.store_event(
  833. data={
  834. "event_id": md5("event {}".format(i)).hexdigest(),
  835. "fingerprint": ["put-me-in-group{}".format(i)],
  836. "timestamp": iso_format(self.base_datetime - timedelta(days=21)),
  837. "message": "group {} event".format(i),
  838. "stacktrace": {"frames": [{"module": "module {}".format(i)}]},
  839. "tags": {"match": "{}".format(i % 2)},
  840. "environment": "production",
  841. },
  842. project_id=self.project.id,
  843. )
  844. group = event.group
  845. group.times_seen = 5
  846. group.status = GroupStatus.UNRESOLVED if i % 3 == 0 else GroupStatus.RESOLVED
  847. group.save()
  848. # Sample should estimate there are roughly 66 overall matching groups
  849. # based on a random sample of 100 (or $sample_size) of the total 200
  850. # snuba matches, of which 33% should pass the postgres filter.
  851. with self.options(
  852. {
  853. # Too small to pass all django candidates down to snuba
  854. "snuba.search.max-pre-snuba-candidates": 5,
  855. "snuba.search.hits-sample-size": 50,
  856. }
  857. ):
  858. first_results = self.make_query(
  859. search_filter_query="is:unresolved match:1", limit=10, count_hits=True
  860. )
  861. # Deliberately do not assert that the value is within some margin
  862. # of error, as this will fail tests at some rate corresponding to
  863. # our confidence interval.
  864. assert first_results.hits > 10
  865. # When searching for the same tags, we should get the same set of
  866. # hits as the sampling is based on the hash of the query.
  867. second_results = self.make_query(
  868. search_filter_query="is:unresolved match:1", limit=10, count_hits=True
  869. )
  870. assert first_results.results == second_results.results
  871. # When using a different search, we should get a different sample
  872. # but still should have some hits.
  873. third_results = self.make_query(
  874. search_filter_query="is:unresolved match:0", limit=10, count_hits=True
  875. )
  876. assert third_results.hits > 10
  877. assert third_results.results != second_results.results
  878. def test_first_release(self):
  879. # expect no groups within the results since there are no releases
  880. results = self.make_query(search_filter_query="first_release:%s" % "fake")
  881. assert set(results) == set([])
  882. # expect no groups even though there is a release; since no group
  883. # is attached to a release
  884. release_1 = self.create_release(self.project)
  885. results = self.make_query(search_filter_query="first_release:%s" % release_1.version)
  886. assert set(results) == set([])
  887. # mark group1's first_release to be release_1.
  888. # group1 should show up for the same query as the previous query (see above)
  889. self.group1.first_release = release_1
  890. self.group1.save()
  891. results = self.make_query(search_filter_query="first_release:%s" % release_1.version)
  892. assert set(results) == set([self.group1])
  893. def test_first_release_environments(self):
  894. results = self.make_query(
  895. environments=[self.environments["production"]],
  896. search_filter_query="first_release:%s" % "fake",
  897. )
  898. assert set(results) == set([])
  899. release = self.create_release(self.project)
  900. group_env = GroupEnvironment.get_or_create(
  901. group_id=self.group1.id, environment_id=self.environments["production"].id
  902. )[0]
  903. results = self.make_query(
  904. environments=[self.environments["production"]],
  905. search_filter_query="first_release:%s" % release.version,
  906. )
  907. assert set(results) == set([])
  908. group_env.first_release = release
  909. group_env.save()
  910. results = self.make_query(
  911. environments=[self.environments["production"]],
  912. search_filter_query="first_release:%s" % release.version,
  913. )
  914. assert set(results) == set([self.group1])
  915. def test_first_release_any_or_no_environments(self):
  916. # test scenarios for tickets:
  917. # SEN-571
  918. # ISSUE-432
  919. # given the following setup:
  920. #
  921. # groups table:
  922. # group first_release
  923. # A 1
  924. # B 1
  925. # C 2
  926. #
  927. # groupenvironments table:
  928. # group environment first_release
  929. # A staging 1
  930. # A production 2
  931. #
  932. # when querying by first release, the appropriate set of groups should be displayed:
  933. #
  934. # first_release: 1
  935. # env=[]: A, B
  936. # env=[production, staging]: A
  937. # env=[staging]: A
  938. # env=[production]: nothing
  939. #
  940. # first_release: 2
  941. # env=[]: A, C
  942. # env=[production, staging]: A
  943. # env=[staging]: nothing
  944. # env=[production]: A
  945. # create an issue/group whose events that occur in 2 distinct environments
  946. group_a_event_1 = self.store_event(
  947. data={
  948. "fingerprint": ["group_a"],
  949. "event_id": "aaa" + ("1" * 29),
  950. "environment": "example_staging",
  951. "release": "release_1",
  952. },
  953. project_id=self.project.id,
  954. )
  955. group_a_event_2 = self.store_event(
  956. data={
  957. "fingerprint": ["group_a"],
  958. "event_id": "aaa" + ("2" * 29),
  959. "environment": "example_production",
  960. "release": "release_2",
  961. },
  962. project_id=self.project.id,
  963. )
  964. group_a = group_a_event_1.group
  965. # get the environments for group_a
  966. prod_env = group_a_event_2.get_environment()
  967. staging_env = group_a_event_1.get_environment()
  968. # create an issue/group whose event that occur in no environments
  969. # but will be tied to release release_1
  970. group_b_event_1 = self.store_event(
  971. data={
  972. "fingerprint": ["group_b"],
  973. "event_id": "bbb" + ("1" * 29),
  974. "release": "release_1",
  975. },
  976. project_id=self.project.id,
  977. )
  978. assert group_b_event_1.get_environment().name == u"" # has no environment
  979. group_b = group_b_event_1.group
  980. # create an issue/group whose event that occur in no environments
  981. # but will be tied to release release_2
  982. group_c_event_1 = self.store_event(
  983. data={
  984. "fingerprint": ["group_c"],
  985. "event_id": "ccc" + ("1" * 29),
  986. "release": "release_2",
  987. },
  988. project_id=self.project.id,
  989. )
  990. assert group_c_event_1.get_environment().name == u"" # has no environment
  991. group_c = group_c_event_1.group
  992. # query by release release_1
  993. results = self.make_query(search_filter_query="first_release:%s" % "release_1")
  994. assert set(results) == set([group_a, group_b])
  995. results = self.make_query(
  996. environments=[staging_env, prod_env],
  997. search_filter_query="first_release:%s" % "release_1",
  998. )
  999. assert set(results) == set([group_a])
  1000. results = self.make_query(
  1001. environments=[staging_env], search_filter_query="first_release:%s" % "release_1"
  1002. )
  1003. assert set(results) == set([group_a])
  1004. results = self.make_query(
  1005. environments=[prod_env], search_filter_query="first_release:%s" % "release_1"
  1006. )
  1007. assert set(results) == set([])
  1008. # query by release release_2
  1009. results = self.make_query(search_filter_query="first_release:%s" % "release_2")
  1010. assert set(results) == set([group_a, group_c])
  1011. results = self.make_query(
  1012. environments=[staging_env, prod_env],
  1013. search_filter_query="first_release:%s" % "release_2",
  1014. )
  1015. assert set(results) == set([group_a])
  1016. results = self.make_query(
  1017. environments=[staging_env], search_filter_query="first_release:%s" % "release_2"
  1018. )
  1019. assert set(results) == set([])
  1020. results = self.make_query(
  1021. environments=[prod_env], search_filter_query="first_release:%s" % "release_2"
  1022. )
  1023. assert set(results) == set([group_a])
  1024. def test_query_enclosed_in_quotes(self):
  1025. results = self.make_query(search_filter_query='"foo"')
  1026. assert set(results) == set([self.group1])
  1027. results = self.make_query(search_filter_query='"bar"')
  1028. assert set(results) == set([self.group2])
  1029. @xfail_if_not_postgres("Wildcard searching only supported in Postgres")
  1030. def test_wildcard(self):
  1031. escaped_event = self.store_event(
  1032. data={
  1033. "fingerprint": ["hello-there"],
  1034. "event_id": "f" * 32,
  1035. "message": "somet[hing]",
  1036. "environment": "production",
  1037. "tags": {"server": "example.net"},
  1038. "timestamp": iso_format(self.base_datetime),
  1039. "stacktrace": {"frames": [{"module": "group1"}]},
  1040. },
  1041. project_id=self.project.id,
  1042. )
  1043. # Note: Adding in `environment:production` so that we make sure we query
  1044. # in both snuba and postgres
  1045. results = self.make_query(search_filter_query="environment:production so*t")
  1046. assert set(results) == set([escaped_event.group])
  1047. # Make sure it's case insensitive
  1048. results = self.make_query(search_filter_query="environment:production SO*t")
  1049. assert set(results) == set([escaped_event.group])
  1050. results = self.make_query(search_filter_query="environment:production so*zz")
  1051. assert set(results) == set()
  1052. results = self.make_query(search_filter_query="environment:production [hing]")
  1053. assert set(results) == set([escaped_event.group])
  1054. results = self.make_query(search_filter_query="environment:production s*]")
  1055. assert set(results) == set([escaped_event.group])
  1056. results = self.make_query(search_filter_query="environment:production server:example.*")
  1057. assert set(results) == set([self.group1, escaped_event.group])
  1058. results = self.make_query(search_filter_query="environment:production !server:*net")
  1059. assert set(results) == set([self.group1])
  1060. # TODO: Disabling tests that use [] syntax for the moment. Re-enable
  1061. # these if we decide to add back in, or remove if this comment has been
  1062. # here a while.
  1063. # results = self.make_query(
  1064. # search_filter_query='environment:production [s][of][mz]',
  1065. # )
  1066. # assert set(results) == set([escaped_event.group])
  1067. # results = self.make_query(
  1068. # search_filter_query='environment:production [z][of][mz]',
  1069. # )
  1070. # assert set(results) == set()
  1071. def test_null_tags(self):
  1072. tag_event = self.store_event(
  1073. data={
  1074. "fingerprint": ["hello-there"],
  1075. "event_id": "f" * 32,
  1076. "message": "something",
  1077. "environment": "production",
  1078. "tags": {"server": "example.net"},
  1079. "timestamp": iso_format(self.base_datetime),
  1080. "stacktrace": {"frames": [{"module": "group1"}]},
  1081. },
  1082. project_id=self.project.id,
  1083. )
  1084. no_tag_event = self.store_event(
  1085. data={
  1086. "fingerprint": ["hello-there-2"],
  1087. "event_id": "5" * 32,
  1088. "message": "something",
  1089. "environment": "production",
  1090. "timestamp": iso_format(self.base_datetime),
  1091. "stacktrace": {"frames": [{"module": "group2"}]},
  1092. },
  1093. project_id=self.project.id,
  1094. )
  1095. results = self.make_query(search_filter_query="environment:production !server:*net")
  1096. assert set(results) == set([self.group1, no_tag_event.group])
  1097. results = self.make_query(search_filter_query="environment:production server:*net")
  1098. assert set(results) == set([tag_event.group])
  1099. results = self.make_query(search_filter_query="environment:production !server:example.net")
  1100. assert set(results) == set([self.group1, no_tag_event.group])
  1101. results = self.make_query(search_filter_query="environment:production server:example.net")
  1102. assert set(results) == set([tag_event.group])
  1103. results = self.make_query(search_filter_query="environment:production has:server")
  1104. assert set(results) == set([self.group1, tag_event.group])
  1105. results = self.make_query(search_filter_query="environment:production !has:server")
  1106. assert set(results) == set([no_tag_event.group])
  1107. def test_null_promoted_tags(self):
  1108. tag_event = self.store_event(
  1109. data={
  1110. "fingerprint": ["hello-there"],
  1111. "event_id": "f" * 32,
  1112. "message": "something",
  1113. "environment": "production",
  1114. "tags": {"logger": "csp"},
  1115. "timestamp": iso_format(self.base_datetime),
  1116. "stacktrace": {"frames": [{"module": "group1"}]},
  1117. },
  1118. project_id=self.project.id,
  1119. )
  1120. no_tag_event = self.store_event(
  1121. data={
  1122. "fingerprint": ["hello-there-2"],
  1123. "event_id": "5" * 32,
  1124. "message": "something",
  1125. "environment": "production",
  1126. "timestamp": iso_format(self.base_datetime),
  1127. "stacktrace": {"frames": [{"module": "group2"}]},
  1128. },
  1129. project_id=self.project.id,
  1130. )
  1131. results = self.make_query(search_filter_query="environment:production !logger:*sp")
  1132. assert set(results) == set([self.group1, no_tag_event.group])
  1133. results = self.make_query(search_filter_query="environment:production logger:*sp")
  1134. assert set(results) == set([tag_event.group])
  1135. results = self.make_query(search_filter_query="environment:production !logger:csp")
  1136. assert set(results) == set([self.group1, no_tag_event.group])
  1137. results = self.make_query(search_filter_query="environment:production logger:csp")
  1138. assert set(results) == set([tag_event.group])
  1139. results = self.make_query(search_filter_query="environment:production has:logger")
  1140. assert set(results) == set([tag_event.group])
  1141. results = self.make_query(search_filter_query="environment:production !has:logger")
  1142. assert set(results) == set([self.group1, no_tag_event.group])
  1143. def test_all_fields_do_not_error(self):
  1144. # Just a sanity check to make sure that all fields can be succesfully
  1145. # searched on without returning type errors and other schema related
  1146. # issues.
  1147. def test_query(query):
  1148. try:
  1149. self.make_query(search_filter_query=query)
  1150. except SnubaError as e:
  1151. self.fail("Query %s errored. Error info: %s" % (query, e))
  1152. for key in SENTRY_SNUBA_MAP:
  1153. if key in ["project.id", "issue.id"]:
  1154. continue
  1155. test_query("has:%s" % key)
  1156. test_query("!has:%s" % key)
  1157. if key in IssueSearchVisitor.numeric_keys:
  1158. val = "123"
  1159. elif key in IssueSearchVisitor.date_keys:
  1160. val = "2019-01-01"
  1161. else:
  1162. val = "hello"
  1163. test_query("!%s:%s" % (key, val))
  1164. test_query("%s:%s" % (key, val))