123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- from typing import Any
- import pytest
- from pydantic import ValidationError
- from flagpole.operators import (
- ConditionTypeMismatchException,
- ContainsOperator,
- EqualsOperator,
- InOperator,
- NotContainsOperator,
- NotEqualsOperator,
- NotInOperator,
- Operator,
- OperatorKind,
- create_case_insensitive_set_from_list,
- )
- from sentry.testutils.cases import TestCase
- from sentry.utils import json
- class TestCreateCaseInsensitiveSetFromList(TestCase):
- def test_empty_set(self):
- assert create_case_insensitive_set_from_list([]) == set()
- def test_returns_int_set(self):
- assert create_case_insensitive_set_from_list([1, 2, 3]) == {1, 2, 3}
- def test_returns_float_set(self):
- assert create_case_insensitive_set_from_list([1.1, 2.2, 3.3]) == {1.1, 2.2, 3.3}
- def test_returns_lowercase_string_set(self):
- assert create_case_insensitive_set_from_list(["AbC", "DEF"]) == {"abc", "def"}
- def assert_valid_types(operator: type[Operator], expected_types: list[Any]):
- for value in expected_types:
- operator_dict = dict(value=value)
- json_operator = json.dumps(operator_dict)
- try:
- parsed_operator = operator.parse_raw(json_operator)
- except ValidationError as exc:
- raise AssertionError(
- f"Expected value `{value}` to be a valid value for operator '{operator}'"
- ) from exc
- assert parsed_operator.value == value
- def assert_invalid_types(operator: type[Operator], invalid_types: list[Any]):
- for value in invalid_types:
- json_dict = dict(value=value)
- operator_json = json.dumps(json_dict)
- try:
- operator.parse_raw(operator_json)
- except ValidationError:
- continue
- raise AssertionError(
- f"Expected validation error for value: `{value}` for operator `{operator}`"
- )
- class TestInOperators(TestCase):
- def test_invalid_values(self):
- with pytest.raises(ValidationError):
- InOperator(value="bar")
- with pytest.raises(ValidationError):
- InOperator(value=1234)
- def test_is_in(self):
- values = ["bar", "baz"]
- operator = InOperator(kind=OperatorKind.IN, value=values)
- assert operator.match(condition_property="bar", segment_name="test")
- not_operator = NotInOperator(kind=OperatorKind.NOT_IN, value=values)
- assert not not_operator.match(condition_property="bar", segment_name="test")
- int_values = [1, 2]
- operator = InOperator(kind=OperatorKind.IN, value=int_values)
- # Validation check to ensure no type coercion occurs
- assert operator.value == int_values
- assert operator.match(condition_property=2, segment_name="test")
- assert not operator.match(condition_property=3, segment_name="test")
- def test_is_in_numeric_string(self):
- values = ["123", "456"]
- operator = InOperator(kind=OperatorKind.IN, value=values)
- assert operator.value == values
- assert not operator.match(condition_property=123, segment_name="test")
- assert operator.match(condition_property="123", segment_name="test")
- def test_is_not_in(self):
- values = ["bar", "baz"]
- operator = InOperator(kind=OperatorKind.IN, value=values)
- assert not operator.match(condition_property="foo", segment_name="test")
- not_operator = NotInOperator(kind=OperatorKind.NOT_IN, value=values)
- assert not_operator.match(condition_property="foo", segment_name="test")
- def test_is_in_case_insensitivity(self):
- values = ["bAr", "baz"]
- operator = InOperator(kind=OperatorKind.IN, value=values)
- assert operator.match(condition_property="BaR", segment_name="test")
- not_operator = NotInOperator(kind=OperatorKind.NOT_IN, value=values)
- assert not not_operator.match(condition_property="BaR", segment_name="test")
- def test_invalid_property_value(self):
- values = ["bar", "baz"]
- operator = InOperator(kind=OperatorKind.IN, value=values)
- with pytest.raises(ConditionTypeMismatchException):
- operator.match(condition_property=[], segment_name="test")
- not_operator = NotInOperator(kind=OperatorKind.NOT_IN, value=values)
- with pytest.raises(ConditionTypeMismatchException):
- not_operator.match(condition_property=[], segment_name="test")
- def test_valid_json_and_reparse(self):
- values = [["foo", "bar"], [1, 2], [1.1, 2.2], []]
- assert_valid_types(operator=InOperator, expected_types=values)
- assert_valid_types(operator=NotInOperator, expected_types=values)
- def test_invalid_value_type_parsing(self):
- values = ["abc", 1, 2.2, True, None, ["a", 1], [True], [[]], [1, 2.2], [1.1, "2.2"]]
- assert_invalid_types(operator=InOperator, invalid_types=values)
- assert_invalid_types(operator=NotInOperator, invalid_types=values)
- class TestContainsOperators(TestCase):
- def test_does_contain(self):
- operator = ContainsOperator(kind=OperatorKind.CONTAINS, value="bar")
- assert operator.match(condition_property=["foo", "bar"], segment_name="test")
- not_operator = NotContainsOperator(kind=OperatorKind.NOT_CONTAINS, value="bar")
- assert not not_operator.match(condition_property=["foo", "bar"], segment_name="test")
- operator = ContainsOperator(kind=OperatorKind.CONTAINS, value=1)
- assert operator.match(condition_property=[1, 2], segment_name="test")
- assert not operator.match(condition_property=[3, 4], segment_name="test")
- def test_does_not_contain(self):
- values = "baz"
- operator = ContainsOperator(kind=OperatorKind.CONTAINS, value=values)
- assert not operator.match(condition_property=["foo", "bar"], segment_name="test")
- not_operator = NotContainsOperator(kind=OperatorKind.NOT_CONTAINS, value=values)
- assert not_operator.match(condition_property=["foo", "bar"], segment_name="test")
- def test_invalid_property_provided(self):
- values = "baz"
- with pytest.raises(ConditionTypeMismatchException):
- operator = ContainsOperator(kind=OperatorKind.CONTAINS, value=values)
- assert not operator.match(condition_property="oops", segment_name="test")
- with pytest.raises(ConditionTypeMismatchException):
- not_operator = NotContainsOperator(kind=OperatorKind.NOT_CONTAINS, value=values)
- assert not_operator.match(condition_property="oops", segment_name="test")
- def test_valid_json_parsing_with_types(self):
- values = [1, 2.2, "abc"]
- assert_valid_types(operator=ContainsOperator, expected_types=values)
- assert_valid_types(operator=NotContainsOperator, expected_types=values)
- def test_invalid_value_type_parsing(self):
- values: list[Any] = [
- None,
- [],
- dict(foo="bar"),
- [[]],
- ]
- assert_invalid_types(operator=ContainsOperator, invalid_types=values)
- assert_invalid_types(operator=NotContainsOperator, invalid_types=values)
- class TestEqualsOperators(TestCase):
- def test_is_equal_string(self):
- value = "foo"
- operator = EqualsOperator(kind=OperatorKind.EQUALS, value=value)
- assert operator.match(condition_property="foo", segment_name="test")
- not_operator = NotEqualsOperator(kind=OperatorKind.NOT_EQUALS, value=value)
- assert not not_operator.match(condition_property="foo", segment_name="test")
- def test_is_not_equals(self):
- values = "bar"
- operator = EqualsOperator(kind=OperatorKind.EQUALS, value=values)
- assert not operator.match(condition_property="foo", segment_name="test")
- not_operator = NotEqualsOperator(kind=OperatorKind.NOT_EQUALS, value=values)
- assert not_operator.match(condition_property="foo", segment_name="test")
- def test_is_equal_case_insensitive(self):
- values = "bAr"
- operator = EqualsOperator(kind=OperatorKind.EQUALS, value=values)
- assert operator.match(condition_property="BaR", segment_name="test")
- not_operator = NotEqualsOperator(kind=OperatorKind.NOT_EQUALS, value=values)
- assert not not_operator.match(condition_property="BaR", segment_name="test")
- def test_equality_type_mismatch_strings(self):
- values = ["foo", "bar"]
- operator = EqualsOperator(kind=OperatorKind.EQUALS, value=values)
- with pytest.raises(ConditionTypeMismatchException):
- operator.match(condition_property="foo", segment_name="test")
- not_operator = NotEqualsOperator(kind=OperatorKind.NOT_EQUALS, value=values)
- with pytest.raises(ConditionTypeMismatchException):
- not_operator.match(condition_property="foo", segment_name="test")
- def test_valid_json_parsing_with_types(self):
- values = [1, 2.2, "abc", True, False, [], ["foo"], [1], [1.1]]
- assert_valid_types(operator=EqualsOperator, expected_types=values)
- assert_valid_types(operator=NotEqualsOperator, expected_types=values)
- def test_invalid_value_type_parsing(self):
- values = [None, dict(foo="bar")]
- assert_invalid_types(operator=EqualsOperator, invalid_types=values)
- assert_invalid_types(operator=NotEqualsOperator, invalid_types=values)
|