123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437 |
- from __future__ import absolute_import
- from sentry.utils.compat import mock
- import pytz
- from datetime import datetime, timedelta
- from django.utils import timezone
- from hashlib import md5
- from sentry import options
- from sentry.api.issue_search import convert_query_values, IssueSearchVisitor, parse_search_query
- from sentry.models import (
- Environment,
- Group,
- GroupAssignee,
- GroupBookmark,
- GroupEnvironment,
- GroupStatus,
- GroupSubscription,
- )
- from sentry.search.snuba.backend import EventsDatasetSnubaSearchBackend
- from sentry.testutils import SnubaTestCase, TestCase, xfail_if_not_postgres
- from sentry.testutils.helpers.datetime import iso_format
- from sentry.utils.snuba import Dataset, SENTRY_SNUBA_MAP, SnubaError
- def date_to_query_format(date):
- return date.strftime("%Y-%m-%dT%H:%M:%S")
- class EventsSnubaSearchTest(TestCase, SnubaTestCase):
- @property
- def backend(self):
- return EventsDatasetSnubaSearchBackend()
- def setUp(self):
- super(EventsSnubaSearchTest, self).setUp()
- self.base_datetime = (datetime.utcnow() - timedelta(days=3)).replace(tzinfo=pytz.utc)
- event1_timestamp = iso_format(self.base_datetime - timedelta(days=21))
- self.event1 = self.store_event(
- data={
- "fingerprint": ["put-me-in-group1"],
- "event_id": "a" * 32,
- "message": "foo. Also, this message is intended to be greater than 256 characters so that we can put some unique string identifier after that point in the string. The purpose of this is in order to verify we are using snuba to search messages instead of Postgres (postgres truncates at 256 characters and clickhouse does not). santryrox.",
- "environment": "production",
- "tags": {"server": "example.com", "sentry:user": "event1@example.com"},
- "timestamp": event1_timestamp,
- "stacktrace": {"frames": [{"module": "group1"}]},
- },
- project_id=self.project.id,
- )
- self.event3 = self.store_event(
- data={
- "fingerprint": ["put-me-in-group1"],
- "event_id": "c" * 32,
- "message": "group1",
- "environment": "production",
- "tags": {"server": "example.com", "sentry:user": "event3@example.com"},
- "timestamp": iso_format(self.base_datetime),
- "stacktrace": {"frames": [{"module": "group1"}]},
- },
- project_id=self.project.id,
- )
- self.group1 = Group.objects.get(id=self.event1.group.id)
- assert self.group1.id == self.event1.group.id
- assert self.group1.id == self.event3.group.id
- assert self.group1.first_seen == self.event1.datetime
- assert self.group1.last_seen == self.event3.datetime
- self.group1.times_seen = 5
- self.group1.status = GroupStatus.UNRESOLVED
- self.group1.save()
- self.store_group(self.group1)
- self.event2 = self.store_event(
- data={
- "fingerprint": ["put-me-in-group2"],
- "event_id": "b" * 32,
- "timestamp": iso_format(self.base_datetime - timedelta(days=20)),
- "message": "bar",
- "stacktrace": {"frames": [{"module": "group2"}]},
- "environment": "staging",
- "tags": {
- "server": "example.com",
- "url": "http://example.com",
- "sentry:user": "event2@example.com",
- },
- },
- project_id=self.project.id,
- )
- self.group2 = Group.objects.get(id=self.event2.group.id)
- assert self.group2.id == self.event2.group.id
- assert self.group2.first_seen == self.group2.last_seen == self.event2.datetime
- self.group2.status = GroupStatus.RESOLVED
- self.group2.times_seen = 10
- self.group2.save()
- self.store_group(self.group2)
- GroupBookmark.objects.create(user=self.user, group=self.group2, project=self.group2.project)
- GroupAssignee.objects.create(user=self.user, group=self.group2, project=self.group2.project)
- GroupSubscription.objects.create(
- user=self.user, group=self.group1, project=self.group1.project, is_active=True
- )
- GroupSubscription.objects.create(
- user=self.user, group=self.group2, project=self.group2.project, is_active=False
- )
- self.environments = {
- "production": self.event1.get_environment(),
- "staging": self.event2.get_environment(),
- }
- def store_event(self, data, *args, **kwargs):
- event = super(EventsSnubaSearchTest, self).store_event(data, *args, **kwargs)
- environment_name = data.get("environment")
- if environment_name:
- GroupEnvironment.objects.filter(
- group_id=event.group_id,
- environment__name=environment_name,
- first_seen__gt=event.datetime,
- ).update(first_seen=event.datetime)
- return event
- def set_up_multi_project(self):
- self.project2 = self.create_project(organization=self.project.organization)
- self.event_p2 = self.store_event(
- data={
- "event_id": "a" * 32,
- "fingerprint": ["put-me-in-groupP2"],
- "timestamp": iso_format(self.base_datetime - timedelta(days=21)),
- "message": "foo",
- "stacktrace": {"frames": [{"module": "group_p2"}]},
- "tags": {"server": "example.com"},
- "environment": "production",
- },
- project_id=self.project2.id,
- )
- self.group_p2 = Group.objects.get(id=self.event_p2.group.id)
- self.group_p2.times_seen = 6
- self.group_p2.last_seen = self.base_datetime - timedelta(days=1)
- self.group_p2.save()
- self.store_group(self.group_p2)
- def build_search_filter(self, query, projects=None, user=None, environments=None):
- user = user if user is not None else self.user
- projects = projects if projects is not None else [self.project]
- return convert_query_values(parse_search_query(query), projects, user, environments)
- def make_query(
- self,
- projects=None,
- search_filter_query=None,
- environments=None,
- sort_by="date",
- limit=None,
- count_hits=False,
- date_from=None,
- date_to=None,
- ):
- search_filters = []
- projects = projects if projects is not None else [self.project]
- if search_filter_query is not None:
- search_filters = self.build_search_filter(
- search_filter_query, projects, environments=environments
- )
- kwargs = {}
- if limit is not None:
- kwargs["limit"] = limit
- return self.backend.query(
- projects,
- search_filters=search_filters,
- environments=environments,
- count_hits=count_hits,
- sort_by=sort_by,
- date_from=date_from,
- date_to=date_to,
- **kwargs
- )
- def test_query(self):
- results = self.make_query(search_filter_query="foo")
- assert set(results) == set([self.group1])
- results = self.make_query(search_filter_query="bar")
- assert set(results) == set([self.group2])
- def test_query_multi_project(self):
- self.set_up_multi_project()
- results = self.make_query([self.project, self.project2], search_filter_query="foo")
- assert set(results) == set([self.group1, self.group_p2])
- def test_query_with_environment(self):
- results = self.make_query(
- environments=[self.environments["production"]], search_filter_query="foo"
- )
- assert set(results) == set([self.group1])
- results = self.make_query(
- environments=[self.environments["production"]], search_filter_query="bar"
- )
- assert set(results) == set([])
- results = self.make_query(
- environments=[self.environments["staging"]], search_filter_query="bar"
- )
- assert set(results) == set([self.group2])
- def test_query_for_text_in_long_message(self):
- results = self.make_query(
- [self.project],
- environments=[self.environments["production"]],
- search_filter_query="santryrox",
- )
- assert set(results) == set([self.group1])
- def test_multi_environments(self):
- self.set_up_multi_project()
- results = self.make_query(
- [self.project, self.project2],
- environments=[self.environments["production"], self.environments["staging"]],
- )
- assert set(results) == set([self.group1, self.group2, self.group_p2])
- def test_query_with_environment_multi_project(self):
- self.set_up_multi_project()
- results = self.make_query(
- [self.project, self.project2],
- environments=[self.environments["production"]],
- search_filter_query="foo",
- )
- assert set(results) == set([self.group1, self.group_p2])
- results = self.make_query(
- [self.project, self.project2],
- environments=[self.environments["production"]],
- search_filter_query="bar",
- )
- assert set(results) == set([])
- def test_sort(self):
- results = self.make_query(sort_by="date")
- assert list(results) == [self.group1, self.group2]
- results = self.make_query(sort_by="new")
- assert list(results) == [self.group2, self.group1]
- results = self.make_query(sort_by="freq")
- assert list(results) == [self.group1, self.group2]
- results = self.make_query(sort_by="priority")
- assert list(results) == [self.group1, self.group2]
- results = self.make_query(sort_by="user")
- assert list(results) == [self.group1, self.group2]
- def test_sort_with_environment(self):
- for dt in [
- self.group1.first_seen + timedelta(days=1),
- self.group1.first_seen + timedelta(days=2),
- self.group1.last_seen + timedelta(days=1),
- ]:
- self.store_event(
- data={
- "fingerprint": ["put-me-in-group2"],
- "timestamp": iso_format(dt),
- "stacktrace": {"frames": [{"module": "group2"}]},
- "environment": "production",
- "message": "group2",
- },
- project_id=self.project.id,
- )
- results = self.make_query(environments=[self.environments["production"]], sort_by="date")
- assert list(results) == [self.group2, self.group1]
- results = self.make_query(environments=[self.environments["production"]], sort_by="new")
- assert list(results) == [self.group2, self.group1]
- results = self.make_query(environments=[self.environments["production"]], sort_by="freq")
- assert list(results) == [self.group2, self.group1]
- results = self.make_query(
- environments=[self.environments["production"]], sort_by="priority"
- )
- assert list(results) == [self.group2, self.group1]
- results = self.make_query(environments=[self.environments["production"]], sort_by="user")
- assert list(results) == [self.group1, self.group2]
- def test_status(self):
- results = self.make_query(search_filter_query="is:unresolved")
- assert set(results) == set([self.group1])
- results = self.make_query(search_filter_query="is:resolved")
- assert set(results) == set([self.group2])
- def test_status_with_environment(self):
- results = self.make_query(
- environments=[self.environments["production"]], search_filter_query="is:unresolved"
- )
- assert set(results) == set([self.group1])
- results = self.make_query(
- environments=[self.environments["staging"]], search_filter_query="is:resolved"
- )
- assert set(results) == set([self.group2])
- results = self.make_query(
- environments=[self.environments["production"]], search_filter_query="is:resolved"
- )
- assert set(results) == set([])
- def test_tags(self):
- results = self.make_query(search_filter_query="environment:staging")
- assert set(results) == set([self.group2])
- results = self.make_query(search_filter_query="environment:example.com")
- assert set(results) == set([])
- results = self.make_query(search_filter_query="has:environment")
- assert set(results) == set([self.group2, self.group1])
- results = self.make_query(search_filter_query="environment:staging server:example.com")
- assert set(results) == set([self.group2])
- results = self.make_query(search_filter_query='url:"http://example.com"')
- assert set(results) == set([self.group2])
- results = self.make_query(search_filter_query="environment:staging has:server")
- assert set(results) == set([self.group2])
- results = self.make_query(search_filter_query="environment:staging server:bar.example.com")
- assert set(results) == set([])
- def test_tags_with_environment(self):
- results = self.make_query(
- environments=[self.environments["production"]], search_filter_query="server:example.com"
- )
- assert set(results) == set([self.group1])
- results = self.make_query(
- environments=[self.environments["staging"]], search_filter_query="server:example.com"
- )
- assert set(results) == set([self.group2])
- results = self.make_query(
- environments=[self.environments["staging"]], search_filter_query="has:server"
- )
- assert set(results) == set([self.group2])
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query='url:"http://example.com"',
- )
- assert set(results) == set([])
- results = self.make_query(
- environments=[self.environments["staging"]],
- search_filter_query='url:"http://example.com"',
- )
- assert set(results) == set([self.group2])
- results = self.make_query(
- environments=[self.environments["staging"]],
- search_filter_query="server:bar.example.com",
- )
- assert set(results) == set([])
- def test_bookmarked_by(self):
- results = self.make_query(search_filter_query="bookmarks:%s" % self.user.username)
- assert set(results) == set([self.group2])
- def test_bookmarked_by_with_environment(self):
- results = self.make_query(
- environments=[self.environments["staging"]],
- search_filter_query="bookmarks:%s" % self.user.username,
- )
- assert set(results) == set([self.group2])
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query="bookmarks:%s" % self.user.username,
- )
- assert set(results) == set([])
- def test_search_filter_query_with_custom_priority_tag(self):
- priority = "high"
- self.store_event(
- data={
- "fingerprint": ["put-me-in-group2"],
- "timestamp": iso_format(self.group2.first_seen + timedelta(days=1)),
- "stacktrace": {"frames": [{"module": "group2"}]},
- "message": "group2",
- "tags": {"priority": priority},
- },
- project_id=self.project.id,
- )
- results = self.make_query(search_filter_query="priority:%s" % priority)
- assert set(results) == set([self.group2])
- def test_search_filter_query_with_custom_priority_tag_and_priority_sort(self):
- priority = "high"
- for i in range(1, 3):
- self.store_event(
- data={
- "fingerprint": ["put-me-in-group1"],
- "timestamp": iso_format(self.group2.last_seen + timedelta(days=i)),
- "stacktrace": {"frames": [{"module": "group1"}]},
- "message": "group1",
- "tags": {"priority": priority},
- },
- project_id=self.project.id,
- )
- self.store_event(
- data={
- "fingerprint": ["put-me-in-group2"],
- "timestamp": iso_format(self.group2.last_seen + timedelta(days=2)),
- "stacktrace": {"frames": [{"module": "group2"}]},
- "message": "group2",
- "tags": {"priority": priority},
- },
- project_id=self.project.id,
- )
- results = self.make_query(search_filter_query="priority:%s" % priority, sort_by="priority")
- assert list(results) == [self.group1, self.group2]
- def test_search_tag_overlapping_with_internal_fields(self):
-
-
- self.store_event(
- data={
- "fingerprint": ["put-me-in-group2"],
- "timestamp": iso_format(self.group2.first_seen + timedelta(days=1)),
- "stacktrace": {"frames": [{"module": "group2"}]},
- "message": "group2",
- "tags": {"email": "tags@example.com"},
- },
- project_id=self.project.id,
- )
- results = self.make_query(search_filter_query="email:tags@example.com")
- assert set(results) == set([self.group2])
- def test_project(self):
- results = self.make_query([self.create_project(name="other")])
- assert set(results) == set([])
- def test_pagination(self):
- for options_set in [
- {"snuba.search.min-pre-snuba-candidates": None},
- {"snuba.search.min-pre-snuba-candidates": 500},
- ]:
- with self.options(options_set):
- results = self.backend.query([self.project], limit=1, sort_by="date")
- assert set(results) == set([self.group1])
- assert not results.prev.has_results
- assert results.next.has_results
- results = self.backend.query(
- [self.project], cursor=results.next, limit=1, sort_by="date"
- )
- assert set(results) == set([self.group2])
- assert results.prev.has_results
- assert not results.next.has_results
-
- results = self.backend.query(
- [self.project], cursor=results.prev, limit=1, sort_by="date"
- )
- assert set(results) == set([self.group1])
- assert results.prev.has_results
- assert results.next.has_results
-
- results = self.backend.query(
- [self.project], cursor=results.prev, limit=1, sort_by="date"
- )
- assert set(results) == set([])
- assert not results.prev.has_results
- assert results.next.has_results
- results = self.backend.query(
- [self.project], cursor=results.next, limit=1, sort_by="date"
- )
- assert set(results) == set([self.group1])
- assert results.prev.has_results
- assert results.next.has_results
- results = self.backend.query(
- [self.project], cursor=results.next, limit=1, sort_by="date"
- )
- assert set(results) == set([self.group2])
- assert results.prev.has_results
- assert not results.next.has_results
- results = self.backend.query(
- [self.project], cursor=results.next, limit=1, sort_by="date"
- )
- assert set(results) == set([])
- assert results.prev.has_results
- assert not results.next.has_results
- def test_pagination_with_environment(self):
- for dt in [
- self.group1.first_seen + timedelta(days=1),
- self.group1.first_seen + timedelta(days=2),
- self.group1.last_seen + timedelta(days=1),
- ]:
- self.store_event(
- data={
- "fingerprint": ["put-me-in-group2"],
- "timestamp": iso_format(dt),
- "environment": "production",
- "message": "group2",
- "stacktrace": {"frames": [{"module": "group2"}]},
- },
- project_id=self.project.id,
- )
- results = self.backend.query(
- [self.project],
- environments=[self.environments["production"]],
- sort_by="date",
- limit=1,
- count_hits=True,
- )
- assert list(results) == [self.group2]
- assert results.hits == 2
- results = self.backend.query(
- [self.project],
- environments=[self.environments["production"]],
- sort_by="date",
- limit=1,
- cursor=results.next,
- count_hits=True,
- )
- assert list(results) == [self.group1]
- assert results.hits == 2
- results = self.backend.query(
- [self.project],
- environments=[self.environments["production"]],
- sort_by="date",
- limit=1,
- cursor=results.next,
- count_hits=True,
- )
- assert list(results) == []
- assert results.hits == 2
- def test_active_at_filter(self):
- results = self.make_query(
- search_filter_query="activeSince:>=%s" % date_to_query_format(self.group2.active_at)
- )
- assert set(results) == set([self.group2])
- results = self.make_query(
- search_filter_query="activeSince:<=%s"
- % date_to_query_format(self.group1.active_at + timedelta(minutes=1))
- )
- assert set(results) == set([self.group1])
- results = self.make_query(
- search_filter_query="activeSince:>=%s activeSince:<=%s"
- % (
- date_to_query_format(self.group1.active_at),
- date_to_query_format(self.group1.active_at + timedelta(minutes=1)),
- )
- )
- assert set(results) == set([self.group1])
- def test_age_filter(self):
- results = self.make_query(
- search_filter_query="firstSeen:>=%s" % date_to_query_format(self.group2.first_seen)
- )
- assert set(results) == set([self.group2])
- results = self.make_query(
- search_filter_query="firstSeen:<=%s"
- % date_to_query_format(self.group1.first_seen + timedelta(minutes=1))
- )
- assert set(results) == set([self.group1])
- results = self.make_query(
- search_filter_query="firstSeen:>=%s firstSeen:<=%s"
- % (
- date_to_query_format(self.group1.first_seen),
- date_to_query_format(self.group1.first_seen + timedelta(minutes=1)),
- )
- )
- assert set(results) == set([self.group1])
- def test_age_filter_with_environment(self):
-
- group1_first_seen = GroupEnvironment.objects.get(
- environment=self.environments["production"], group=self.group1
- ).first_seen
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query="firstSeen:>=%s" % date_to_query_format(group1_first_seen),
- )
- assert set(results) == set([self.group1])
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query="firstSeen:<=%s" % date_to_query_format(group1_first_seen),
- )
- assert set(results) == set([self.group1])
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query="firstSeen:>%s" % date_to_query_format(group1_first_seen),
- )
- assert set(results) == set([])
- self.store_event(
- data={
- "fingerprint": ["put-me-in-group1"],
- "timestamp": iso_format(group1_first_seen + timedelta(days=1)),
- "message": "group1",
- "stacktrace": {"frames": [{"module": "group1"}]},
- "environment": "development",
- },
- project_id=self.project.id,
- )
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query="firstSeen:>%s" % date_to_query_format(group1_first_seen),
- )
- assert set(results) == set([])
- results = self.make_query(
- environments=[Environment.objects.get(name="development")],
- search_filter_query="firstSeen:>%s" % date_to_query_format(group1_first_seen),
- )
- assert set(results) == set([self.group1])
- def test_times_seen_filter(self):
- results = self.make_query([self.project], search_filter_query="times_seen:2")
- assert set(results) == set([self.group1])
- results = self.make_query([self.project], search_filter_query="times_seen:>=2")
- assert set(results) == set([self.group1])
- results = self.make_query([self.project], search_filter_query="times_seen:<=1")
- assert set(results) == set([self.group2])
- def test_last_seen_filter(self):
- results = self.make_query(
- search_filter_query="lastSeen:>=%s" % date_to_query_format(self.group1.last_seen)
- )
- assert set(results) == set([self.group1])
- results = self.make_query(
- search_filter_query="lastSeen:>=%s lastSeen:<=%s"
- % (
- date_to_query_format(self.group1.last_seen),
- date_to_query_format(self.group1.last_seen + timedelta(minutes=1)),
- )
- )
- assert set(results) == set([self.group1])
- def test_last_seen_filter_with_environment(self):
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query="lastSeen:>=%s" % date_to_query_format(self.group1.last_seen),
- )
- assert set(results) == set([self.group1])
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query="lastSeen:<=%s" % date_to_query_format(self.group1.last_seen),
- )
- assert set(results) == set([self.group1])
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query="lastSeen:>%s" % date_to_query_format(self.group1.last_seen),
- )
- assert set(results) == set([])
- self.store_event(
- data={
- "fingerprint": ["put-me-in-group1"],
- "timestamp": iso_format(self.group1.last_seen + timedelta(days=1)),
- "message": "group1",
- "stacktrace": {"frames": [{"module": "group1"}]},
- "environment": "development",
- },
- project_id=self.project.id,
- )
- self.group1.update(last_seen=self.group1.last_seen + timedelta(days=1))
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query="lastSeen:>%s" % date_to_query_format(self.group1.last_seen),
- )
- assert set(results) == set([])
- results = self.make_query(
- environments=[Environment.objects.get(name="development")],
- search_filter_query="lastSeen:>%s" % date_to_query_format(self.group1.last_seen),
- )
- assert set(results) == set()
- results = self.make_query(
- environments=[Environment.objects.get(name="development")],
- search_filter_query="lastSeen:>=%s" % date_to_query_format(self.group1.last_seen),
- )
- assert set(results) == set([self.group1])
- def test_date_filter(self):
- results = self.make_query(
- date_from=self.event2.datetime,
- search_filter_query="timestamp:>=%s" % date_to_query_format(self.event2.datetime),
- )
- assert set(results) == set([self.group1, self.group2])
- results = self.make_query(
- date_to=self.event1.datetime + timedelta(minutes=1),
- search_filter_query="timestamp:<=%s"
- % date_to_query_format(self.event1.datetime + timedelta(minutes=1)),
- )
- assert set(results) == set([self.group1])
- results = self.make_query(
- date_from=self.event1.datetime,
- date_to=self.event2.datetime + timedelta(minutes=1),
- search_filter_query="timestamp:>=%s timestamp:<=%s"
- % (
- date_to_query_format(self.event1.datetime),
- date_to_query_format(self.event2.datetime + timedelta(minutes=1)),
- ),
- )
- assert set(results) == set([self.group1, self.group2])
-
- results = self.make_query(
- date_from=self.event1.datetime,
- date_to=self.event2.datetime + timedelta(minutes=1),
- search_filter_query="timestamp:>=%s timestamp:<=%s"
- % (
- date_to_query_format(self.event1.datetime) + "Z",
- date_to_query_format(self.event2.datetime + timedelta(minutes=1)) + "Z",
- ),
- )
- assert set(results) == set([self.group1, self.group2])
- def test_date_filter_with_environment(self):
- results = self.backend.query(
- [self.project],
- environments=[self.environments["production"]],
- date_from=self.event2.datetime,
- )
- assert set(results) == set([self.group1])
- results = self.backend.query(
- [self.project],
- environments=[self.environments["production"]],
- date_to=self.event1.datetime + timedelta(minutes=1),
- )
- assert set(results) == set([self.group1])
- results = self.backend.query(
- [self.project],
- environments=[self.environments["staging"]],
- date_from=self.event1.datetime,
- date_to=self.event2.datetime + timedelta(minutes=1),
- )
- assert set(results) == set([self.group2])
- def test_unassigned(self):
- results = self.make_query(search_filter_query="is:unassigned")
- assert set(results) == set([self.group1])
- results = self.make_query(search_filter_query="is:assigned")
- assert set(results) == set([self.group2])
- def test_unassigned_with_environment(self):
- results = self.make_query(
- environments=[self.environments["production"]], search_filter_query="is:unassigned"
- )
- assert set(results) == set([self.group1])
- results = self.make_query(
- environments=[self.environments["staging"]], search_filter_query="is:assigned"
- )
- assert set(results) == set([self.group2])
- results = self.make_query(
- environments=[self.environments["production"]], search_filter_query="is:assigned"
- )
- assert set(results) == set([])
- def test_assigned_to(self):
- results = self.make_query(search_filter_query="assigned:%s" % self.user.username)
- assert set(results) == set([self.group2])
-
- ga = GroupAssignee.objects.get(
- user=self.user, group=self.group2, project=self.group2.project
- )
- ga.update(team=self.team, user=None)
- assert GroupAssignee.objects.get(id=ga.id).user is None
- results = self.make_query(search_filter_query="assigned:%s" % self.user.username)
- assert set(results) == set([self.group2])
-
- other_user = self.create_user()
- results = self.make_query(search_filter_query="assigned:%s" % other_user.username)
- assert set(results) == set([])
- owner = self.create_user()
- self.create_member(
- organization=self.project.organization, user=owner, role="owner", teams=[]
- )
-
- results = self.make_query(search_filter_query="assigned:%s" % owner.username)
- assert set(results) == set([])
- def test_assigned_to_with_environment(self):
- results = self.make_query(
- environments=[self.environments["staging"]],
- search_filter_query="assigned:%s" % self.user.username,
- )
- assert set(results) == set([self.group2])
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query="assigned:%s" % self.user.username,
- )
- assert set(results) == set([])
- def test_subscribed_by(self):
- results = self.make_query(
- [self.group1.project], search_filter_query="subscribed:%s" % self.user.username
- )
- assert set(results) == set([self.group1])
- def test_subscribed_by_with_environment(self):
- results = self.make_query(
- [self.group1.project],
- environments=[self.environments["production"]],
- search_filter_query="subscribed:%s" % self.user.username,
- )
- assert set(results) == set([self.group1])
- results = self.make_query(
- [self.group1.project],
- environments=[self.environments["staging"]],
- search_filter_query="subscribed:%s" % self.user.username,
- )
- assert set(results) == set([])
- @mock.patch("sentry.utils.snuba.raw_query")
- def test_snuba_not_called_optimization(self, query_mock):
- assert self.make_query(search_filter_query="status:unresolved").results == [self.group1]
- assert not query_mock.called
- assert (
- self.make_query(
- search_filter_query="last_seen:>%s" % date_to_query_format(timezone.now()),
- sort_by="date",
- ).results
- == []
- )
- assert query_mock.called
- @mock.patch("sentry.utils.snuba.raw_query")
- def test_optimized_aggregates(self, query_mock):
-
-
- query_mock.return_value = {"data": [], "totals": {"total": 0}}
- def Any(cls):
- class Any(object):
- def __eq__(self, other):
- return isinstance(other, cls)
- return Any()
- DEFAULT_LIMIT = 100
- chunk_growth = options.get("snuba.search.chunk-growth-rate")
- limit = int(DEFAULT_LIMIT * chunk_growth)
- common_args = {
- "arrayjoin": None,
- "dataset": Dataset.Events,
- "start": Any(datetime),
- "end": Any(datetime),
- "filter_keys": {
- "project_id": [self.project.id],
- "group_id": [self.group1.id, self.group2.id],
- },
- "referrer": "search",
- "groupby": ["group_id"],
- "conditions": [[["positionCaseInsensitive", ["message", "'foo'"]], "!=", 0]],
- "selected_columns": [],
- "limit": limit,
- "offset": 0,
- "totals": True,
- "turbo": False,
- "sample": 1,
- }
- self.make_query(search_filter_query="status:unresolved")
- assert not query_mock.called
- self.make_query(
- search_filter_query="last_seen:>=%s foo" % date_to_query_format(timezone.now()),
- sort_by="date",
- )
- query_mock.call_args[1]["aggregations"].sort()
- assert query_mock.call_args == mock.call(
- orderby=["-last_seen", "group_id"],
- aggregations=[
- ["multiply(toUInt64(max(timestamp)), 1000)", "", "last_seen"],
- ["uniq", "group_id", "total"],
- ],
- having=[["last_seen", ">=", Any(int)]],
- **common_args
- )
- self.make_query(search_filter_query="foo", sort_by="priority")
- query_mock.call_args[1]["aggregations"].sort()
- assert query_mock.call_args == mock.call(
- orderby=["-priority", "group_id"],
- aggregations=[
- ["count()", "", "times_seen"],
- ["multiply(toUInt64(max(timestamp)), 1000)", "", "last_seen"],
- ["toUInt64(plus(multiply(log(times_seen), 600), last_seen))", "", "priority"],
- ["uniq", "group_id", "total"],
- ],
- having=[],
- **common_args
- )
- self.make_query(search_filter_query="times_seen:5 foo", sort_by="freq")
- query_mock.call_args[1]["aggregations"].sort()
- assert query_mock.call_args == mock.call(
- orderby=["-times_seen", "group_id"],
- aggregations=[["count()", "", "times_seen"], ["uniq", "group_id", "total"]],
- having=[["times_seen", "=", 5]],
- **common_args
- )
- self.make_query(search_filter_query="foo", sort_by="user")
- query_mock.call_args[1]["aggregations"].sort()
- assert query_mock.call_args == mock.call(
- orderby=["-user_count", "group_id"],
- aggregations=[
- ["uniq", "group_id", "total"],
- ["uniq", "tags[sentry:user]", "user_count"],
- ],
- having=[],
- **common_args
- )
- def test_pre_and_post_filtering(self):
- prev_max_pre = options.get("snuba.search.max-pre-snuba-candidates")
- options.set("snuba.search.max-pre-snuba-candidates", 1)
- try:
-
- results = self.make_query(search_filter_query="foo")
- assert set(results) == set([self.group1])
- results = self.make_query(search_filter_query="bar")
- assert set(results) == set([self.group2])
-
- results = self.make_query(search_filter_query="NO MATCHES IN SENTRY")
- assert set(results) == set()
-
- results = self.make_query()
- assert set(results) == set([self.group1, self.group2])
- finally:
- options.set("snuba.search.max-pre-snuba-candidates", prev_max_pre)
- def test_optimizer_enabled(self):
- prev_optimizer_enabled = options.get("snuba.search.pre-snuba-candidates-optimizer")
- options.set("snuba.search.pre-snuba-candidates-optimizer", True)
- try:
- results = self.make_query(
- search_filter_query="server:example.com",
- environments=[self.environments["production"]],
- )
- assert set(results) == set([self.group1])
- finally:
- options.set("snuba.search.pre-snuba-candidates-optimizer", prev_optimizer_enabled)
- def test_search_out_of_range(self):
- the_date = datetime(2000, 1, 1, 0, 0, 0, tzinfo=pytz.utc)
- results = self.make_query(
- search_filter_query="event.timestamp:>%s event.timestamp:<%s" % (the_date, the_date),
- date_from=the_date,
- date_to=the_date,
- )
- assert set(results) == set([])
- def test_hits_estimate(self):
-
-
-
- for i in range(400):
- event = self.store_event(
- data={
- "event_id": md5("event {}".format(i).encode("utf-8")).hexdigest(),
- "fingerprint": ["put-me-in-group{}".format(i)],
- "timestamp": iso_format(self.base_datetime - timedelta(days=21)),
- "message": "group {} event".format(i),
- "stacktrace": {"frames": [{"module": "module {}".format(i)}]},
- "tags": {"match": "{}".format(i % 2)},
- "environment": "production",
- },
- project_id=self.project.id,
- )
- group = event.group
- group.times_seen = 5
- group.status = GroupStatus.UNRESOLVED if i % 3 == 0 else GroupStatus.RESOLVED
- group.save()
- self.store_group(group)
-
-
-
- with self.options(
- {
-
- "snuba.search.max-pre-snuba-candidates": 5,
- "snuba.search.hits-sample-size": 50,
- }
- ):
- first_results = self.make_query(
- search_filter_query="is:unresolved match:1", limit=10, count_hits=True
- )
-
-
-
- assert first_results.hits > 10
-
-
- second_results = self.make_query(
- search_filter_query="is:unresolved match:1", limit=10, count_hits=True
- )
- assert first_results.results == second_results.results
-
-
- third_results = self.make_query(
- search_filter_query="is:unresolved match:0", limit=10, count_hits=True
- )
- assert third_results.hits > 10
- assert third_results.results != second_results.results
- def test_first_release(self):
-
- results = self.make_query(search_filter_query="first_release:%s" % "fake")
- assert set(results) == set([])
-
-
- release_1 = self.create_release(self.project)
- results = self.make_query(search_filter_query="first_release:%s" % release_1.version)
- assert set(results) == set([])
-
- group = self.store_event(
- data={
- "fingerprint": ["put-me-in-group9001"],
- "event_id": "a" * 32,
- "message": "hello",
- "environment": "production",
- "tags": {"server": "example.com"},
- "release": release_1.version,
- "stacktrace": {"frames": [{"module": "group1"}]},
- },
- project_id=self.project.id,
- ).group
- results = self.make_query(search_filter_query="first_release:%s" % release_1.version)
- assert set(results) == set([group])
- def test_first_release_environments(self):
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query="first_release:%s" % "fake",
- )
- assert set(results) == set([])
- release = self.create_release(self.project)
- group_env = GroupEnvironment.get_or_create(
- group_id=self.group1.id, environment_id=self.environments["production"].id
- )[0]
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query="first_release:%s" % release.version,
- )
- assert set(results) == set([])
- group_env.first_release = release
- group_env.save()
- results = self.make_query(
- environments=[self.environments["production"]],
- search_filter_query="first_release:%s" % release.version,
- )
- assert set(results) == set([self.group1])
- def test_query_enclosed_in_quotes(self):
- results = self.make_query(search_filter_query='"foo"')
- assert set(results) == set([self.group1])
- results = self.make_query(search_filter_query='"bar"')
- assert set(results) == set([self.group2])
- @xfail_if_not_postgres("Wildcard searching only supported in Postgres")
- def test_wildcard(self):
- escaped_event = self.store_event(
- data={
- "fingerprint": ["hello-there"],
- "event_id": "f" * 32,
- "message": "somet[hing]",
- "environment": "production",
- "tags": {"server": "example.net"},
- "timestamp": iso_format(self.base_datetime),
- "stacktrace": {"frames": [{"module": "group1"}]},
- },
- project_id=self.project.id,
- )
-
-
- results = self.make_query(search_filter_query="environment:production so*t")
- assert set(results) == set([escaped_event.group])
-
- results = self.make_query(search_filter_query="environment:production SO*t")
- assert set(results) == set([escaped_event.group])
- results = self.make_query(search_filter_query="environment:production so*zz")
- assert set(results) == set()
- results = self.make_query(search_filter_query="environment:production [hing]")
- assert set(results) == set([escaped_event.group])
- results = self.make_query(search_filter_query="environment:production s*]")
- assert set(results) == set([escaped_event.group])
- results = self.make_query(search_filter_query="environment:production server:example.*")
- assert set(results) == set([self.group1, escaped_event.group])
- results = self.make_query(search_filter_query="environment:production !server:*net")
- assert set(results) == set([self.group1])
-
-
-
-
-
-
-
-
-
-
-
- def test_null_tags(self):
- tag_event = self.store_event(
- data={
- "fingerprint": ["hello-there"],
- "event_id": "f" * 32,
- "message": "something",
- "environment": "production",
- "tags": {"server": "example.net"},
- "timestamp": iso_format(self.base_datetime),
- "stacktrace": {"frames": [{"module": "group1"}]},
- },
- project_id=self.project.id,
- )
- no_tag_event = self.store_event(
- data={
- "fingerprint": ["hello-there-2"],
- "event_id": "5" * 32,
- "message": "something",
- "environment": "production",
- "timestamp": iso_format(self.base_datetime),
- "stacktrace": {"frames": [{"module": "group2"}]},
- },
- project_id=self.project.id,
- )
- results = self.make_query(search_filter_query="environment:production !server:*net")
- assert set(results) == set([self.group1, no_tag_event.group])
- results = self.make_query(search_filter_query="environment:production server:*net")
- assert set(results) == set([tag_event.group])
- results = self.make_query(search_filter_query="environment:production !server:example.net")
- assert set(results) == set([self.group1, no_tag_event.group])
- results = self.make_query(search_filter_query="environment:production server:example.net")
- assert set(results) == set([tag_event.group])
- results = self.make_query(search_filter_query="environment:production has:server")
- assert set(results) == set([self.group1, tag_event.group])
- results = self.make_query(search_filter_query="environment:production !has:server")
- assert set(results) == set([no_tag_event.group])
- def test_null_promoted_tags(self):
- tag_event = self.store_event(
- data={
- "fingerprint": ["hello-there"],
- "event_id": "f" * 32,
- "message": "something",
- "environment": "production",
- "tags": {"logger": "csp"},
- "timestamp": iso_format(self.base_datetime),
- "stacktrace": {"frames": [{"module": "group1"}]},
- },
- project_id=self.project.id,
- )
- no_tag_event = self.store_event(
- data={
- "fingerprint": ["hello-there-2"],
- "event_id": "5" * 32,
- "message": "something",
- "environment": "production",
- "timestamp": iso_format(self.base_datetime),
- "stacktrace": {"frames": [{"module": "group2"}]},
- },
- project_id=self.project.id,
- )
- results = self.make_query(search_filter_query="environment:production !logger:*sp")
- assert set(results) == set([self.group1, no_tag_event.group])
- results = self.make_query(search_filter_query="environment:production logger:*sp")
- assert set(results) == set([tag_event.group])
- results = self.make_query(search_filter_query="environment:production !logger:csp")
- assert set(results) == set([self.group1, no_tag_event.group])
- results = self.make_query(search_filter_query="environment:production logger:csp")
- assert set(results) == set([tag_event.group])
- results = self.make_query(search_filter_query="environment:production has:logger")
- assert set(results) == set([tag_event.group])
- results = self.make_query(search_filter_query="environment:production !has:logger")
- assert set(results) == set([self.group1, no_tag_event.group])
- def test_sort_multi_project(self):
- self.set_up_multi_project()
- results = self.make_query([self.project, self.project2], sort_by="date")
- assert list(results) == [self.group1, self.group_p2, self.group2]
- results = self.make_query([self.project, self.project2], sort_by="new")
- assert list(results) == [self.group2, self.group_p2, self.group1]
- results = self.make_query([self.project, self.project2], sort_by="freq")
- assert list(results) == [self.group1, self.group_p2, self.group2]
- results = self.make_query([self.project, self.project2], sort_by="priority")
- assert list(results) == [self.group1, self.group2, self.group_p2]
- results = self.make_query([self.project, self.project2], sort_by="user")
- assert list(results) == [self.group1, self.group2, self.group_p2]
- def test_first_release_any_or_no_environments(self):
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- group_a_event_1 = self.store_event(
- data={
- "fingerprint": ["group_a"],
- "event_id": "aaa" + ("1" * 29),
- "environment": "example_staging",
- "release": "release_1",
- },
- project_id=self.project.id,
- )
- group_a_event_2 = self.store_event(
- data={
- "fingerprint": ["group_a"],
- "event_id": "aaa" + ("2" * 29),
- "environment": "example_production",
- "release": "release_2",
- },
- project_id=self.project.id,
- )
- group_a = group_a_event_1.group
-
- prod_env = group_a_event_2.get_environment()
- staging_env = group_a_event_1.get_environment()
-
-
- group_b_event_1 = self.store_event(
- data={
- "fingerprint": ["group_b"],
- "event_id": "bbb" + ("1" * 29),
- "release": "release_1",
- },
- project_id=self.project.id,
- )
- assert group_b_event_1.get_environment().name == u""
- group_b = group_b_event_1.group
-
-
- group_c_event_1 = self.store_event(
- data={
- "fingerprint": ["group_c"],
- "event_id": "ccc" + ("1" * 29),
- "release": "release_2",
- },
- project_id=self.project.id,
- )
- assert group_c_event_1.get_environment().name == u""
- group_c = group_c_event_1.group
-
- results = self.make_query(search_filter_query="first_release:%s" % "release_1")
- assert set(results) == set([group_a, group_b])
- results = self.make_query(
- environments=[staging_env, prod_env],
- search_filter_query="first_release:%s" % "release_1",
- )
- assert set(results) == set([group_a])
- results = self.make_query(
- environments=[staging_env], search_filter_query="first_release:%s" % "release_1"
- )
- assert set(results) == set([group_a])
- results = self.make_query(
- environments=[prod_env], search_filter_query="first_release:%s" % "release_1"
- )
- assert set(results) == set([])
-
- results = self.make_query(search_filter_query="first_release:%s" % "release_2")
- assert set(results) == set([group_a, group_c])
- results = self.make_query(
- environments=[staging_env, prod_env],
- search_filter_query="first_release:%s" % "release_2",
- )
- assert set(results) == set([group_a])
- results = self.make_query(
- environments=[staging_env], search_filter_query="first_release:%s" % "release_2"
- )
- assert set(results) == set([])
- results = self.make_query(
- environments=[prod_env], search_filter_query="first_release:%s" % "release_2"
- )
- assert set(results) == set([group_a])
- def test_all_fields_do_not_error(self):
-
-
-
- def test_query(query):
- try:
- self.make_query(search_filter_query=query)
- except SnubaError as e:
- self.fail("Query %s errored. Error info: %s" % (query, e))
- for key in SENTRY_SNUBA_MAP:
- if key in ["project.id", "issue.id"]:
- continue
- test_query("has:%s" % key)
- test_query("!has:%s" % key)
- if key == "error.handled":
- val = 1
- elif key in IssueSearchVisitor.numeric_keys:
- val = "123"
- elif key in IssueSearchVisitor.date_keys:
- val = "2019-01-01"
- elif key in IssueSearchVisitor.boolean_keys:
- val = "true"
- else:
- val = "abadcafedeadbeefdeaffeedabadfeed"
- test_query("!%s:%s" % (key, val))
- test_query("%s:%s" % (key, val))
|