def test_valid(self): for status_string, status_val in STATUS_QUERY_CHOICES.items(): filters = [ SearchFilter(SearchKey("status"), "=", SearchValue(status_string)) ] result = convert_query_values(filters, [self.project], self.user, None) assert result[0].value.raw_value == status_val filters = [ SearchFilter(SearchKey("status"), "=", SearchValue(status_val)) ] result = convert_query_values(filters, [self.project], self.user, None) assert result[0].value.raw_value == status_val
def test_valid_converter(self): filters = [SearchFilter(SearchKey("assigned_to"), "=", SearchValue("me"))] expected = value_converters["assigned_to"]( filters[0].value.raw_value, [self.project], self.user, None ) filters = convert_query_values(filters, [self.project], self.user, None) assert filters[0].value.raw_value == expected
def test_parens_in_query(self): assert parse_search_query( "TypeError Anonymous function(app/javascript/utils/transform-object-keys)" ) == [ SearchFilter( key=SearchKey(name="message"), operator="=", value=SearchValue(raw_value="TypeError Anonymous function"), ), SearchFilter( key=SearchKey(name="message"), operator="=", value=SearchValue( raw_value="(app/javascript/utils/transform-object-keys)"), ), ]
def test_allowed_keys(self): config = SearchConfig(allowed_keys=["good_key"]) assert parse_search_query("good_key:123 bad_key:123 text") == [ SearchFilter(key=SearchKey(name="good_key"), operator="=", value=SearchValue("123")), SearchFilter(key=SearchKey(name="bad_key"), operator="=", value=SearchValue("123")), SearchFilter(key=SearchKey(name="message"), operator="=", value=SearchValue("text")), ] with pytest.raises(InvalidSearchQuery, match="Invalid key for this search"): assert parse_search_query("good_key:123 bad_key:123 text", config=config) assert parse_search_query("good_key:123 text", config=config) == [ SearchFilter(key=SearchKey(name="good_key"), operator="=", value=SearchValue("123")), SearchFilter(key=SearchKey(name="message"), operator="=", value=SearchValue("text")), ]
def test_timestamp_rollup(self): assert parse_search_query( "timestamp.to_hour:2018-01-01T05:06:07+00:00") == [ SearchFilter( key=SearchKey(name="timestamp.to_hour"), operator=">=", value=SearchValue(raw_value=datetime.datetime( 2018, 1, 1, 5, 1, 7, tzinfo=timezone.utc)), ), SearchFilter( key=SearchKey(name="timestamp.to_hour"), operator="<", value=SearchValue(raw_value=datetime.datetime( 2018, 1, 1, 5, 12, 7, tzinfo=timezone.utc)), ), ]
def test_key_remapping(self): config = SearchConfig(key_mappings={"target_value": ["someValue", "legacy-value"]}) assert parse_search_query( "someValue:123 legacy-value:456 normal_value:hello", config=config ) == [ SearchFilter( key=SearchKey(name="target_value"), operator="=", value=SearchValue("123") ), SearchFilter( key=SearchKey(name="target_value"), operator="=", value=SearchValue("456") ), SearchFilter( key=SearchKey(name="normal_value"), operator="=", value=SearchValue("hello") ), ]
def test_is_query_status(self): for status_string, status_val in STATUS_CHOICES.items(): assert parse_search_query('is:%s' % status_string) == [ SearchFilter( key=SearchKey(name='status'), operator='=', value=SearchValue(status_val), ), ] assert parse_search_query('!is:%s' % status_string) == [ SearchFilter( key=SearchKey(name='status'), operator='!=', value=SearchValue(status_val), ), ]
def test_empty_spaces_stripped_correctly(self): assert parse_search_query( "event.type:transaction transaction:/organizations/:orgId/discover/results/" ) == [ SearchFilter( key=SearchKey(name="event.type"), operator="=", value=SearchValue(raw_value="transaction"), ), SearchFilter( key=SearchKey(name="transaction"), operator="=", value=SearchValue( raw_value="/organizations/:orgId/discover/results/"), ), ]
def test_parse_search_query_quoted_val(self): assert parse_search_query('release:"a release"') == [ SearchFilter( key=SearchKey(name='release'), operator='=', value=SearchValue(raw_value='a release', type='string'), ), ]
def test_escaping_quotes(self): search_filter = parse_search_query(r"title:a\"b") assert search_filter == [ SearchFilter(key=SearchKey(name="title"), operator="=", value=SearchValue(r'a"b')) ] search_filter = search_filter[0] # the slash should be removed in the final value assert search_filter.value.value == 'a"b'
def test_trailing_escaping_backslashes(self): search_filter = parse_search_query(r"title:a\\") assert search_filter == [ SearchFilter(key=SearchKey(name="title"), operator="=", value=SearchValue(r"a\\")) ] search_filter = search_filter[0] # the extra slash should be removed in the final value assert search_filter.value.value == "a\\"
def setUp(self): super(ParseBooleanSearchQueryTest, self).setUp() self.term1 = SearchFilter( key=SearchKey(name='user.email'), operator="=", value=SearchValue(raw_value='*****@*****.**'), ) self.term2 = SearchFilter( key=SearchKey(name='user.email'), operator="=", value=SearchValue(raw_value='*****@*****.**'), ) self.term3 = SearchFilter( key=SearchKey(name='user.email'), operator="=", value=SearchValue(raw_value='*****@*****.**'), )
def test_custom_explicit_tag(self): assert parse_search_query( "tags[fruit]:apple release:1.2.1 tags[project_id]:123") == [ SearchFilter( key=SearchKey(name="tags[fruit]"), operator="=", value=SearchValue(raw_value="apple"), ), SearchFilter(key=SearchKey(name="release"), operator="=", value=SearchValue(raw_value="1.2.1")), SearchFilter( key=SearchKey(name="tags[project_id]"), operator="=", value=SearchValue(raw_value="123"), ), ]
def test_parse_search_query_quoted_key(self): assert parse_search_query('"hi:there":value') == [ SearchFilter( key=SearchKey(name='hi:there'), operator='=', value=SearchValue(raw_value='value'), ), ]
def test_empty_string(self): assert parse_search_query('device.family:""') == [ SearchFilter( key=SearchKey(name='device.family'), operator='=', value=SearchValue(raw_value=''), ), ]
def test_numeric_filter_with_decimals(self): assert parse_search_query("transaction.duration:>3.1415") == [ SearchFilter( key=SearchKey(name="transaction.duration"), operator=">", value=SearchValue(raw_value=3.1415), ) ]
def test_duration_filter(self): assert parse_search_query("transaction.duration:>500s") == [ SearchFilter( key=SearchKey(name="transaction.duration"), operator=">", value=SearchValue(raw_value=500000.0), ) ]
def test_empty_filter_value(self): assert parse_search_query('device.family:""') == [ SearchFilter( key=SearchKey(name="device.family"), operator="=", value=SearchValue(raw_value="") ) ] with self.assertRaisesRegexp(InvalidSearchQuery, "Empty string after 'device.family:'"): parse_search_query("device.family:")
def test_tab_within_quote(self): assert parse_search_query('release:"a\trelease"') == [ SearchFilter( key=SearchKey(name="release"), operator="=", value=SearchValue(raw_value="a\trelease"), ) ]
def test_sooo_many_quotes(self): assert parse_search_query('device.family:"\\"\\"\\"\\"\\"\\"\\"\\"\\"\\""') == [ SearchFilter( key=SearchKey(name="device.family"), operator="=", value=SearchValue(raw_value='""""""""""'), ) ]
def test_rel_time_filter(self): now = timezone.now() with freeze_time(now): assert parse_search_query('some_rel_date:+7d') == [ SearchFilter( key=SearchKey(name='some_rel_date'), operator="<=", value=SearchValue(raw_value=now - timedelta(days=7), ), ), ] assert parse_search_query('some_rel_date:-2w') == [ SearchFilter( key=SearchKey(name='some_rel_date'), operator=">=", value=SearchValue(raw_value=now - timedelta(days=14), ), ), ]
def test_newline_within_quote(self): assert parse_search_query('release:"a\nrelease"') == [ SearchFilter( key=SearchKey(name='release'), operator='=', value=SearchValue(raw_value='a\nrelease') ), ]
def test_key_remapping(self): class RemapVisitor(SearchVisitor): key_mappings = {"target_value": ["someValue", "legacy-value"]} tree = event_search_grammar.parse( "someValue:123 legacy-value:456 normal_value:hello") assert RemapVisitor().visit(tree) == [ SearchFilter(key=SearchKey(name="target_value"), operator="=", value=SearchValue("123")), SearchFilter(key=SearchKey(name="target_value"), operator="=", value=SearchValue("456")), SearchFilter(key=SearchKey(name="normal_value"), operator="=", value=SearchValue("hello")), ]
def test_quoted_val(self): assert parse_search_query('release:"a release"') == [ SearchFilter( key=SearchKey(name="release"), operator="=", value=SearchValue(raw_value="a release"), ) ] assert parse_search_query('!release:"a release"') == [ SearchFilter(key=SearchKey(name="release"), operator="!=", value=SearchValue("a release")) ] assert parse_search_query('release:["a release"]') == [ SearchFilter( key=SearchKey(name="release"), operator="IN", value=SearchValue(raw_value=["a release"]), ) ] assert parse_search_query('release:["a release","b release"]') == [ SearchFilter( key=SearchKey(name="release"), operator="IN", value=SearchValue(raw_value=["a release", "b release"]), ) ] assert parse_search_query( 'release:["a release", "b release", "c release"]') == [ SearchFilter( key=SearchKey(name="release"), operator="IN", value=SearchValue( raw_value=["a release", "b release", "c release"]), ) ] assert parse_search_query('!release:["a release","b release"]') == [ SearchFilter( key=SearchKey(name="release"), operator="NOT IN", value=SearchValue(raw_value=["a release", "b release"]), ) ] assert parse_search_query('release:["a release"] hello:["123"]') == [ SearchFilter( key=SearchKey(name="release"), operator="IN", value=SearchValue(raw_value=["a release"]), ), SearchFilter( key=SearchKey(name="hello"), operator="IN", value=SearchValue(raw_value=["123"]), ), ]
def test_numeric_filter(self): # Numeric format should still return a string if field isn't whitelisted assert parse_search_query('random_field:>500') == [ SearchFilter( key=SearchKey(name='random_field'), operator="=", value=SearchValue(raw_value='>500'), ), ]
def test_not_has_tag(self): # unquoted key assert parse_search_query('!has:release') == [ SearchFilter( key=SearchKey(name='release'), operator='=', value=SearchValue(''), ), ] # quoted key assert parse_search_query('!has:"hi:there"') == [ SearchFilter( key=SearchKey(name='hi:there'), operator='=', value=SearchValue(''), ), ]
def test_valid_converter(self): filters = [SearchFilter(SearchKey('assigned_to'), '=', SearchValue('me'))] expected = value_converters['assigned_to']( filters[0].value.raw_value, [self.project], self.user, ) filters = convert_query_values(filters, [self.project], self.user) assert filters[0].value.raw_value == expected
def test_duration_filter_overrides_numeric_shorthand(self): # 2m should mean 2 minutes for duration filters (as opposed to 2 million) assert parse_search_query("transaction.duration:>2m") == [ SearchFilter( key=SearchKey(name="transaction.duration"), operator=">", value=SearchValue(raw_value=120000.0), ) ]
def test_has_tag(self): # unquoted key assert parse_search_query("has:release") == [ SearchFilter( key=SearchKey(name="release"), operator="!=", value=SearchValue(raw_value="") ) ] # quoted key assert parse_search_query('has:"hi:there"') == [ SearchFilter( key=SearchKey(name="hi:there"), operator="!=", value=SearchValue(raw_value="") ) ] # malformed key with self.assertRaises(InvalidSearchQuery): parse_search_query('has:"hi there"')
def test_key_mappings(self): # Test a couple of keys to ensure things are working as expected assert parse_search_query("bookmarks:123") == [ SearchFilter( key=SearchKey(name="bookmarked_by"), operator="=", value=SearchValue("123") ) ] assert parse_search_query("first-release:123") == [ SearchFilter( key=SearchKey(name="first_release"), operator="=", value=SearchValue("123") ) ] assert parse_search_query("first-release:123 non_mapped:456") == [ SearchFilter( key=SearchKey(name="first_release"), operator="=", value=SearchValue("123") ), SearchFilter(key=SearchKey(name="non_mapped"), operator="=", value=SearchValue("456")), ]