test_backend.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  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 sentry import options
  9. from sentry.models import (
  10. Environment, GroupAssignee, GroupBookmark, GroupStatus, GroupSubscription,
  11. Release, ReleaseEnvironment, ReleaseProjectEnvironment
  12. )
  13. from sentry.search.base import ANY
  14. from sentry.search.django.backend import get_latest_release
  15. from sentry.search.snuba.backend import SnubaSearchBackend
  16. from sentry.testutils import SnubaTestCase
  17. class SnubaSearchTest(SnubaTestCase):
  18. def setUp(self):
  19. super(SnubaSearchTest, self).setUp()
  20. self.backend = SnubaSearchBackend()
  21. self.environments = {}
  22. base_datetime = (datetime.utcnow() - timedelta(days=7)).replace(tzinfo=pytz.utc)
  23. self.group1 = self.create_group(
  24. project=self.project,
  25. checksum='a' * 32,
  26. message='foo',
  27. times_seen=5,
  28. status=GroupStatus.UNRESOLVED,
  29. last_seen=base_datetime,
  30. first_seen=base_datetime - timedelta(days=31),
  31. )
  32. self.event1 = self.create_event(
  33. event_id='a' * 32,
  34. group=self.group1,
  35. datetime=base_datetime - timedelta(days=31),
  36. message='group1',
  37. stacktrace={
  38. 'frames': [{
  39. 'module': 'group1'
  40. }]},
  41. tags={
  42. 'server': 'example.com',
  43. 'environment': 'production',
  44. }
  45. )
  46. self.event3 = self.create_event(
  47. event_id='c' * 32,
  48. group=self.group1,
  49. datetime=base_datetime,
  50. message='group1',
  51. stacktrace={
  52. 'frames': [{
  53. 'module': 'group1'
  54. }]},
  55. tags={
  56. 'server': 'example.com',
  57. 'environment': 'production',
  58. }
  59. )
  60. self.group2 = self.create_group(
  61. project=self.project,
  62. checksum='b' * 32,
  63. message='bar',
  64. times_seen=10,
  65. status=GroupStatus.RESOLVED,
  66. last_seen=base_datetime - timedelta(days=30),
  67. first_seen=base_datetime - timedelta(days=30),
  68. )
  69. self.event2 = self.create_event(
  70. event_id='b' * 32,
  71. group=self.group2,
  72. datetime=base_datetime - timedelta(days=30),
  73. message='group2',
  74. stacktrace={
  75. 'frames': [{
  76. 'module': 'group2'
  77. }]},
  78. tags={
  79. 'server': 'example.com',
  80. 'environment': 'staging',
  81. 'url': 'http://example.com',
  82. }
  83. )
  84. GroupBookmark.objects.create(
  85. user=self.user,
  86. group=self.group2,
  87. project=self.group2.project,
  88. )
  89. GroupAssignee.objects.create(
  90. user=self.user,
  91. group=self.group2,
  92. project=self.group2.project,
  93. )
  94. GroupSubscription.objects.create(
  95. user=self.user,
  96. group=self.group1,
  97. project=self.group1.project,
  98. is_active=True,
  99. )
  100. GroupSubscription.objects.create(
  101. user=self.user,
  102. group=self.group2,
  103. project=self.group2.project,
  104. is_active=False,
  105. )
  106. def create_event(self, *args, **kwargs):
  107. event = super(SnubaSearchTest, self).create_event(*args, **kwargs)
  108. data = event.data.data
  109. tags = dict(data.get('tags', []))
  110. if tags['environment'] not in self.environments:
  111. self.environments[tags['environment']] = Environment.get_or_create(
  112. event.project,
  113. tags['environment'],
  114. )
  115. return event
  116. def test_query(self):
  117. results = self.backend.query(self.project, query='foo')
  118. assert set(results) == set([self.group1])
  119. results = self.backend.query(self.project, query='bar')
  120. assert set(results) == set([self.group2])
  121. def test_query_with_environment(self):
  122. results = self.backend.query(
  123. self.project,
  124. environment=self.environments['production'],
  125. query='foo')
  126. assert set(results) == set([self.group1])
  127. results = self.backend.query(
  128. self.project,
  129. environment=self.environments['production'],
  130. query='bar')
  131. assert set(results) == set([])
  132. results = self.backend.query(
  133. self.project,
  134. environment=self.environments['staging'],
  135. query='bar')
  136. assert set(results) == set([self.group2])
  137. def test_sort(self):
  138. results = self.backend.query(self.project, sort_by='date')
  139. assert list(results) == [self.group1, self.group2]
  140. results = self.backend.query(self.project, sort_by='new')
  141. assert list(results) == [self.group2, self.group1]
  142. results = self.backend.query(self.project, sort_by='freq')
  143. assert list(results) == [self.group1, self.group2]
  144. results = self.backend.query(self.project, sort_by='priority')
  145. assert list(results) == [self.group1, self.group2]
  146. def test_sort_with_environment(self):
  147. for dt in [
  148. self.group1.first_seen + timedelta(days=1),
  149. self.group1.first_seen + timedelta(days=2),
  150. self.group1.last_seen + timedelta(days=1)]:
  151. self.create_event(
  152. group=self.group2,
  153. datetime=dt,
  154. message='group2',
  155. stacktrace={
  156. 'frames': [{
  157. 'module': 'group2'
  158. }]},
  159. tags={'environment': 'production'}
  160. )
  161. results = self.backend.query(
  162. self.project,
  163. environment=self.environments['production'],
  164. sort_by='date',
  165. )
  166. assert list(results) == [self.group2, self.group1]
  167. results = self.backend.query(
  168. self.project,
  169. environment=self.environments['production'],
  170. sort_by='new',
  171. )
  172. assert list(results) == [self.group2, self.group1]
  173. results = self.backend.query(
  174. self.project,
  175. environment=self.environments['production'],
  176. sort_by='freq',
  177. )
  178. assert list(results) == [self.group2, self.group1]
  179. results = self.backend.query(
  180. self.project,
  181. environment=self.environments['production'],
  182. sort_by='priority',
  183. )
  184. assert list(results) == [self.group2, self.group1]
  185. def test_status(self):
  186. results = self.backend.query(self.project, status=GroupStatus.UNRESOLVED)
  187. assert set(results) == set([self.group1])
  188. results = self.backend.query(self.project, status=GroupStatus.RESOLVED)
  189. assert set(results) == set([self.group2])
  190. def test_status_with_environment(self):
  191. results = self.backend.query(
  192. self.project,
  193. environment=self.environments['production'],
  194. status=GroupStatus.UNRESOLVED)
  195. assert set(results) == set([self.group1])
  196. results = self.backend.query(
  197. self.project,
  198. environment=self.environments['staging'],
  199. status=GroupStatus.RESOLVED)
  200. assert set(results) == set([self.group2])
  201. results = self.backend.query(
  202. self.project,
  203. environment=self.environments['production'],
  204. status=GroupStatus.RESOLVED)
  205. assert set(results) == set([])
  206. def test_tags(self):
  207. results = self.backend.query(
  208. self.project,
  209. tags={'environment': 'staging'})
  210. assert set(results) == set([self.group2])
  211. results = self.backend.query(
  212. self.project,
  213. tags={'environment': 'example.com'})
  214. assert set(results) == set([])
  215. results = self.backend.query(
  216. self.project,
  217. tags={'environment': ANY})
  218. assert set(results) == set([self.group2, self.group1])
  219. results = self.backend.query(
  220. self.project,
  221. tags={'environment': 'staging',
  222. 'server': 'example.com'})
  223. assert set(results) == set([self.group2])
  224. results = self.backend.query(
  225. self.project,
  226. tags={'environment': 'staging',
  227. 'server': ANY})
  228. assert set(results) == set([self.group2])
  229. results = self.backend.query(
  230. self.project,
  231. tags={'environment': 'staging',
  232. 'server': 'bar.example.com'})
  233. assert set(results) == set([])
  234. def test_tags_with_environment(self):
  235. results = self.backend.query(
  236. self.project,
  237. environment=self.environments['production'],
  238. tags={'server': 'example.com'})
  239. assert set(results) == set([self.group1])
  240. results = self.backend.query(
  241. self.project,
  242. environment=self.environments['staging'],
  243. tags={'server': 'example.com'})
  244. assert set(results) == set([self.group2])
  245. results = self.backend.query(
  246. self.project,
  247. environment=self.environments['staging'],
  248. tags={'server': ANY})
  249. assert set(results) == set([self.group2])
  250. results = self.backend.query(
  251. self.project,
  252. environment=self.environments['production'],
  253. tags={'url': 'http://example.com'})
  254. assert set(results) == set([])
  255. results = self.backend.query(
  256. self.project,
  257. environment=self.environments['staging'],
  258. tags={'url': 'http://example.com'})
  259. assert set(results) == set([self.group2])
  260. results = self.backend.query(
  261. self.project,
  262. environment=self.environments['staging'],
  263. tags={'server': 'bar.example.com'})
  264. assert set(results) == set([])
  265. def test_bookmarked_by(self):
  266. results = self.backend.query(self.project, bookmarked_by=self.user)
  267. assert set(results) == set([self.group2])
  268. def test_bookmarked_by_with_environment(self):
  269. results = self.backend.query(
  270. self.project,
  271. environment=self.environments['staging'],
  272. bookmarked_by=self.user)
  273. assert set(results) == set([self.group2])
  274. results = self.backend.query(
  275. self.project,
  276. environment=self.environments['production'],
  277. bookmarked_by=self.user)
  278. assert set(results) == set([])
  279. def test_project(self):
  280. results = self.backend.query(self.create_project(name='other'))
  281. assert set(results) == set([])
  282. def test_pagination(self):
  283. # test with and without max-pre-snuba-candidates enabled
  284. prev_max_pre = options.get('snuba.search.max-pre-snuba-candidates')
  285. options.set('snuba.search.max-pre-snuba-candidates', None)
  286. try:
  287. results = self.backend.query(self.project, limit=1, sort_by='date')
  288. assert set(results) == set([self.group1])
  289. results = self.backend.query(self.project, cursor=results.next, limit=1, sort_by='date')
  290. assert set(results) == set([self.group2])
  291. results = self.backend.query(self.project, cursor=results.next, limit=1, sort_by='date')
  292. assert set(results) == set([])
  293. finally:
  294. options.set('snuba.search.max-pre-snuba-candidates', prev_max_pre)
  295. results = self.backend.query(self.project, limit=1, sort_by='date')
  296. assert set(results) == set([self.group1])
  297. results = self.backend.query(self.project, cursor=results.next, limit=1, sort_by='date')
  298. assert set(results) == set([self.group2])
  299. results = self.backend.query(self.project, cursor=results.next, limit=1, sort_by='date')
  300. assert set(results) == set([])
  301. def test_pagination_with_environment(self):
  302. for dt in [
  303. self.group1.first_seen + timedelta(days=1),
  304. self.group1.first_seen + timedelta(days=2),
  305. self.group1.last_seen + timedelta(days=1)]:
  306. self.create_event(
  307. group=self.group2,
  308. datetime=dt,
  309. message='group2',
  310. stacktrace={
  311. 'frames': [{
  312. 'module': 'group2'
  313. }]},
  314. tags={'environment': 'production'}
  315. )
  316. results = self.backend.query(
  317. self.project,
  318. environment=self.environments['production'],
  319. sort_by='date',
  320. limit=1,
  321. count_hits=True,
  322. )
  323. assert list(results) == [self.group2]
  324. assert results.hits is None # NOQA
  325. results = self.backend.query(
  326. self.project,
  327. environment=self.environments['production'],
  328. sort_by='date',
  329. limit=1,
  330. cursor=results.next,
  331. count_hits=True,
  332. )
  333. assert list(results) == [self.group1]
  334. assert results.hits is None # NOQA
  335. results = self.backend.query(
  336. self.project,
  337. environment=self.environments['production'],
  338. sort_by='date',
  339. limit=1,
  340. cursor=results.next,
  341. count_hits=True,
  342. )
  343. assert list(results) == []
  344. assert results.hits is None # NOQA
  345. def test_age_filter(self):
  346. results = self.backend.query(
  347. self.project,
  348. age_from=self.group2.first_seen,
  349. age_from_inclusive=True,
  350. )
  351. assert set(results) == set([self.group2])
  352. results = self.backend.query(
  353. self.project,
  354. age_to=self.group1.first_seen + timedelta(minutes=1),
  355. age_to_inclusive=True,
  356. )
  357. assert set(results) == set([self.group1])
  358. results = self.backend.query(
  359. self.project,
  360. age_from=self.group1.first_seen,
  361. age_from_inclusive=True,
  362. age_to=self.group1.first_seen + timedelta(minutes=1),
  363. age_to_inclusive=True,
  364. )
  365. assert set(results) == set([self.group1])
  366. def test_age_filter_with_environment(self):
  367. results = self.backend.query(
  368. self.project,
  369. environment=self.environments['production'],
  370. age_from=self.group1.first_seen,
  371. age_from_inclusive=True,
  372. )
  373. assert set(results) == set([self.group1])
  374. results = self.backend.query(
  375. self.project,
  376. environment=self.environments['production'],
  377. age_to=self.group1.first_seen,
  378. age_to_inclusive=True,
  379. )
  380. assert set(results) == set([self.group1])
  381. results = self.backend.query(
  382. self.project,
  383. environment=self.environments['production'],
  384. age_from=self.group1.first_seen,
  385. age_from_inclusive=False,
  386. )
  387. assert set(results) == set([])
  388. self.create_event(
  389. group=self.group1,
  390. datetime=self.group1.first_seen + timedelta(days=1),
  391. message='group1',
  392. stacktrace={
  393. 'frames': [{
  394. 'module': 'group1'
  395. }]},
  396. tags={
  397. 'environment': 'development',
  398. }
  399. )
  400. results = self.backend.query(
  401. self.project,
  402. environment=self.environments['production'],
  403. age_from=self.group1.first_seen,
  404. age_from_inclusive=False,
  405. )
  406. assert set(results) == set([])
  407. results = self.backend.query(
  408. self.project,
  409. environment=self.environments['development'],
  410. age_from=self.group1.first_seen,
  411. age_from_inclusive=False,
  412. )
  413. assert set(results) == set([self.group1])
  414. def test_times_seen_filter(self):
  415. results = self.backend.query(
  416. self.project,
  417. times_seen=2,
  418. )
  419. assert set(results) == set([self.group1])
  420. results = self.backend.query(
  421. self.project,
  422. times_seen_lower=2,
  423. )
  424. assert set(results) == set([self.group1])
  425. results = self.backend.query(
  426. self.project,
  427. times_seen_upper=1,
  428. )
  429. assert set(results) == set([self.group2])
  430. def test_last_seen_filter(self):
  431. results = self.backend.query(
  432. self.project,
  433. last_seen_from=self.group1.last_seen,
  434. last_seen_from_inclusive=True,
  435. )
  436. assert set(results) == set([self.group1])
  437. results = self.backend.query(
  438. self.project,
  439. last_seen_to=self.group2.last_seen + timedelta(minutes=1),
  440. last_seen_to_inclusive=True,
  441. )
  442. assert set(results) == set([self.group2])
  443. results = self.backend.query(
  444. self.project,
  445. last_seen_from=self.group1.last_seen,
  446. last_seen_from_inclusive=True,
  447. last_seen_to=self.group1.last_seen + timedelta(minutes=1),
  448. last_seen_to_inclusive=True,
  449. )
  450. assert set(results) == set([self.group1])
  451. def test_last_seen_filter_with_environment(self):
  452. results = self.backend.query(
  453. self.project,
  454. environment=self.environments['production'],
  455. last_seen_from=self.group1.last_seen,
  456. last_seen_from_inclusive=True,
  457. )
  458. assert set(results) == set([self.group1])
  459. results = self.backend.query(
  460. self.project,
  461. environment=self.environments['production'],
  462. last_seen_to=self.group1.last_seen,
  463. last_seen_to_inclusive=True,
  464. )
  465. assert set(results) == set([self.group1])
  466. results = self.backend.query(
  467. self.project,
  468. environment=self.environments['production'],
  469. last_seen_from=self.group1.last_seen,
  470. last_seen_from_inclusive=False,
  471. )
  472. assert set(results) == set([])
  473. self.create_event(
  474. group=self.group1,
  475. datetime=self.group1.last_seen + timedelta(days=1),
  476. message='group1',
  477. stacktrace={
  478. 'frames': [{
  479. 'module': 'group1'
  480. }]},
  481. tags={
  482. 'environment': 'development',
  483. }
  484. )
  485. self.group1.update(last_seen=self.group1.last_seen + timedelta(days=1))
  486. results = self.backend.query(
  487. self.project,
  488. environment=self.environments['production'],
  489. last_seen_from=self.group1.last_seen,
  490. last_seen_from_inclusive=False,
  491. )
  492. assert set(results) == set([])
  493. results = self.backend.query(
  494. self.project,
  495. date_to=self.group1.last_seen + timedelta(days=1),
  496. environment=self.environments['development'],
  497. last_seen_from=self.group1.last_seen,
  498. last_seen_from_inclusive=False,
  499. )
  500. assert set(results) == set()
  501. results = self.backend.query(
  502. self.project,
  503. date_to=self.group1.last_seen + timedelta(days=1),
  504. environment=self.environments['development'],
  505. last_seen_from=self.group1.last_seen,
  506. last_seen_from_inclusive=True,
  507. )
  508. assert set(results) == set([self.group1])
  509. def test_date_filter(self):
  510. results = self.backend.query(
  511. self.project,
  512. date_from=self.event2.datetime,
  513. )
  514. assert set(results) == set([self.group1, self.group2])
  515. results = self.backend.query(
  516. self.project,
  517. date_to=self.event1.datetime + timedelta(minutes=1),
  518. )
  519. assert set(results) == set([self.group1])
  520. results = self.backend.query(
  521. self.project,
  522. date_from=self.event1.datetime,
  523. date_to=self.event2.datetime + timedelta(minutes=1),
  524. )
  525. assert set(results) == set([self.group1, self.group2])
  526. @pytest.mark.xfail(
  527. not settings.SENTRY_TAGSTORE.startswith('sentry.tagstore.v2'),
  528. reason='unsupported on legacy backend due to insufficient index',
  529. )
  530. def test_date_filter_with_environment(self):
  531. results = self.backend.query(
  532. self.project,
  533. environment=self.environments['production'],
  534. date_from=self.event2.datetime,
  535. )
  536. assert set(results) == set([self.group1])
  537. results = self.backend.query(
  538. self.project,
  539. environment=self.environments['production'],
  540. date_to=self.event1.datetime + timedelta(minutes=1),
  541. )
  542. assert set(results) == set([self.group1])
  543. results = self.backend.query(
  544. self.project,
  545. environment=self.environments['staging'],
  546. date_from=self.event1.datetime,
  547. date_to=self.event2.datetime + timedelta(minutes=1),
  548. )
  549. assert set(results) == set([self.group2])
  550. def test_unassigned(self):
  551. results = self.backend.query(self.project, unassigned=True)
  552. assert set(results) == set([self.group1])
  553. results = self.backend.query(self.project, unassigned=False)
  554. assert set(results) == set([self.group2])
  555. def test_unassigned_with_environment(self):
  556. results = self.backend.query(
  557. self.project,
  558. environment=self.environments['production'],
  559. unassigned=True)
  560. assert set(results) == set([self.group1])
  561. results = self.backend.query(
  562. self.project,
  563. environment=self.environments['staging'],
  564. unassigned=False)
  565. assert set(results) == set([self.group2])
  566. results = self.backend.query(
  567. self.project,
  568. environment=self.environments['production'],
  569. unassigned=False)
  570. assert set(results) == set([])
  571. def test_assigned_to(self):
  572. results = self.backend.query(self.project, assigned_to=self.user)
  573. assert set(results) == set([self.group2])
  574. # test team assignee
  575. ga = GroupAssignee.objects.get(
  576. user=self.user,
  577. group=self.group2,
  578. project=self.group2.project,
  579. )
  580. ga.update(team=self.team, user=None)
  581. assert GroupAssignee.objects.get(id=ga.id).user is None
  582. results = self.backend.query(self.project, assigned_to=self.user)
  583. assert set(results) == set([self.group2])
  584. # test when there should be no results
  585. other_user = self.create_user()
  586. results = self.backend.query(self.project, assigned_to=other_user)
  587. assert set(results) == set([])
  588. owner = self.create_user()
  589. self.create_member(
  590. organization=self.project.organization,
  591. user=owner,
  592. role='owner',
  593. teams=[],
  594. )
  595. # test that owners don't see results for all teams
  596. results = self.backend.query(self.project, assigned_to=owner)
  597. assert set(results) == set([])
  598. def test_assigned_to_with_environment(self):
  599. results = self.backend.query(
  600. self.project,
  601. environment=self.environments['staging'],
  602. assigned_to=self.user)
  603. assert set(results) == set([self.group2])
  604. results = self.backend.query(
  605. self.project,
  606. environment=self.environments['production'],
  607. assigned_to=self.user)
  608. assert set(results) == set([])
  609. def test_subscribed_by(self):
  610. results = self.backend.query(
  611. self.group1.project,
  612. subscribed_by=self.user,
  613. )
  614. assert set(results) == set([self.group1])
  615. def test_subscribed_by_with_environment(self):
  616. results = self.backend.query(
  617. self.group1.project,
  618. environment=self.environments['production'],
  619. subscribed_by=self.user,
  620. )
  621. assert set(results) == set([self.group1])
  622. results = self.backend.query(
  623. self.group1.project,
  624. environment=self.environments['staging'],
  625. subscribed_by=self.user,
  626. )
  627. assert set(results) == set([])
  628. def test_parse_release_latest(self):
  629. with pytest.raises(Release.DoesNotExist):
  630. # no releases exist period
  631. environment = None
  632. result = get_latest_release(self.project, environment)
  633. old = Release.objects.create(
  634. organization_id=self.project.organization_id,
  635. version='old'
  636. )
  637. old.add_project(self.project)
  638. new_date = old.date_added + timedelta(minutes=1)
  639. new = Release.objects.create(
  640. version='new-but-in-environment',
  641. organization_id=self.project.organization_id,
  642. date_released=new_date,
  643. )
  644. new.add_project(self.project)
  645. ReleaseEnvironment.get_or_create(
  646. project=self.project,
  647. release=new,
  648. environment=self.environment,
  649. datetime=new_date,
  650. )
  651. ReleaseProjectEnvironment.get_or_create(
  652. project=self.project,
  653. release=new,
  654. environment=self.environment,
  655. datetime=new_date,
  656. )
  657. newest = Release.objects.create(
  658. version='newest-overall',
  659. organization_id=self.project.organization_id,
  660. date_released=old.date_added + timedelta(minutes=5),
  661. )
  662. newest.add_project(self.project)
  663. # latest overall (no environment filter)
  664. environment = None
  665. result = get_latest_release(self.project, environment)
  666. assert result == newest.version
  667. # latest in environment
  668. environment = self.environment
  669. result = get_latest_release(self.project, environment)
  670. assert result == new.version
  671. with pytest.raises(Release.DoesNotExist):
  672. # environment with no releases
  673. environment = self.create_environment()
  674. result = get_latest_release(self.project, environment)
  675. assert result == new.version
  676. @mock.patch('sentry.utils.snuba.query')
  677. def test_optimized_aggregates(self, query_mock):
  678. query_mock.return_value = {}
  679. def Any(cls):
  680. class Any(object):
  681. def __eq__(self, other):
  682. return isinstance(other, cls)
  683. return Any()
  684. DEFAULT_LIMIT = 100
  685. chunk_growth = options.get('snuba.search.chunk-growth-rate')
  686. limit = (DEFAULT_LIMIT * chunk_growth) + 1
  687. common_args = {
  688. 'start': Any(datetime),
  689. 'end': Any(datetime),
  690. 'filter_keys': {
  691. 'project_id': [self.project.id],
  692. 'issue': [self.group1.id]
  693. },
  694. 'referrer': 'search',
  695. 'groupby': ['issue'],
  696. 'conditions': [],
  697. 'limit': limit,
  698. 'offset': 0,
  699. }
  700. self.backend.query(self.project, query='foo')
  701. assert query_mock.call_args == mock.call(
  702. orderby='-last_seen',
  703. aggregations=[['max', 'timestamp', 'last_seen']],
  704. having=[],
  705. **common_args
  706. )
  707. self.backend.query(self.project, query='foo', sort_by='date', last_seen_from=timezone.now())
  708. assert query_mock.call_args == mock.call(
  709. orderby='-last_seen',
  710. aggregations=[['max', 'timestamp', 'last_seen']],
  711. having=[('last_seen', '>=', Any(int))],
  712. **common_args
  713. )
  714. self.backend.query(self.project, query='foo', sort_by='priority')
  715. assert query_mock.call_args == mock.call(
  716. orderby='-priority',
  717. aggregations=[
  718. ['toUInt32(log(times_seen) * 600) + toUInt32(last_seen)', '', 'priority'],
  719. ['count()', '', 'times_seen'],
  720. ['max', 'timestamp', 'last_seen']
  721. ],
  722. having=[],
  723. **common_args
  724. )
  725. self.backend.query(self.project, query='foo', sort_by='freq', times_seen=5)
  726. assert query_mock.call_args == mock.call(
  727. orderby='-times_seen',
  728. aggregations=[['count()', '', 'times_seen']],
  729. having=[('times_seen', '=', 5)],
  730. **common_args
  731. )
  732. self.backend.query(self.project, query='foo', sort_by='new', age_from=timezone.now())
  733. assert query_mock.call_args == mock.call(
  734. orderby='-first_seen',
  735. aggregations=[['min', 'timestamp', 'first_seen']],
  736. having=[('first_seen', '>=', Any(int))],
  737. **common_args
  738. )
  739. def test_pre_and_post_filtering(self):
  740. prev_max_pre = options.get('snuba.search.max-pre-snuba-candidates')
  741. options.set('snuba.search.max-pre-snuba-candidates', 1)
  742. try:
  743. # normal queries work as expected
  744. results = self.backend.query(self.project, query='foo')
  745. assert set(results) == set([self.group1])
  746. results = self.backend.query(self.project, query='bar')
  747. assert set(results) == set([self.group2])
  748. # no candidate matches in Sentry, immediately return empty paginator
  749. results = self.backend.query(self.project, query='NO MATCHES IN SENTRY')
  750. assert set(results) == set()
  751. # too many candidates, skip pre-filter, requires >1 postfilter queries
  752. results = self.backend.query(self.project)
  753. assert set(results) == set([self.group1, self.group2])
  754. finally:
  755. options.set('snuba.search.max-pre-snuba-candidates', prev_max_pre)