Skip to content

Commit b83d544

Browse files
author
Phani Raj
authored
fix(feature flags): Guard for None values when comparing person Properties (#129)
* Guard for None values when comparing person Properties
1 parent 72c0ed1 commit b83d544

File tree

2 files changed

+67
-10
lines changed

2 files changed

+67
-10
lines changed

posthog/feature_flags.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
log = logging.getLogger("posthog")
1515

16+
NONE_VALUES_ALLOWED_OPERATORS = ["is_not"]
17+
1618

1719
class InconclusiveMatchError(Exception):
1820
pass
@@ -119,6 +121,9 @@ def match_property(property, property_values) -> bool:
119121

120122
override_value = property_values[key]
121123

124+
if (operator not in NONE_VALUES_ALLOWED_OPERATORS) and override_value is None:
125+
return False
126+
122127
if operator in ("exact", "is_not"):
123128

124129
def compute_exact_match(value, override_value):

posthog/test/test_feature_flags.py

Lines changed: 62 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,60 @@ def test_compute_inactive_flags_locally(self, patch_decide, patch_capture):
960960
self.assertEqual(patch_decide.call_count, 0)
961961
self.assertEqual(patch_capture.call_count, 0)
962962

963+
@mock.patch("posthog.client.decide")
964+
@mock.patch("posthog.client.get")
965+
def test_feature_flags_local_evaluation_None_values(self, patch_get, patch_decide):
966+
client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY)
967+
client.feature_flags = [
968+
{
969+
id: 1,
970+
"name": "Beta Feature",
971+
"key": "beta-feature",
972+
"is_simple_flag": True,
973+
"active": True,
974+
"filters": {
975+
"groups": [
976+
{
977+
"variant": None,
978+
"properties": [
979+
{"key": "latestBuildVersion", "type": "person", "value": ".+", "operator": "regex"},
980+
{"key": "latestBuildVersionMajor", "type": "person", "value": "23", "operator": "gt"},
981+
{"key": "latestBuildVersionMinor", "type": "person", "value": "31", "operator": "gt"},
982+
{"key": "latestBuildVersionPatch", "type": "person", "value": "0", "operator": "gt"},
983+
],
984+
"rollout_percentage": 100,
985+
}
986+
],
987+
},
988+
},
989+
]
990+
991+
feature_flag_match = client.get_feature_flag(
992+
"beta-feature",
993+
"some-distinct-id",
994+
person_properties={
995+
"latestBuildVersion": None,
996+
"latestBuildVersionMajor": None,
997+
"latestBuildVersionMinor": None,
998+
"latestBuildVersionPatch": None,
999+
},
1000+
)
1001+
1002+
self.assertEqual(feature_flag_match, False)
1003+
self.assertEqual(patch_decide.call_count, 0)
1004+
self.assertEqual(patch_get.call_count, 0)
1005+
1006+
feature_flag_match = client.get_feature_flag(
1007+
"beta-feature",
1008+
"some-distinct-id",
1009+
person_properties={
1010+
"latestBuildVersion": "24.32..1",
1011+
"latestBuildVersionMajor": "24",
1012+
"latestBuildVersionMinor": "32",
1013+
"latestBuildVersionPatch": "1",
1014+
},
1015+
)
1016+
9631017
@mock.patch("posthog.client.decide")
9641018
@mock.patch("posthog.client.get")
9651019
def test_feature_flags_local_evaluation_for_cohorts(self, patch_get, patch_decide):
@@ -1714,7 +1768,7 @@ def test_match_properties_is_set(self):
17141768
self.assertTrue(match_property(property_a, {"key": "value"}))
17151769
self.assertTrue(match_property(property_a, {"key": "value2"}))
17161770
self.assertTrue(match_property(property_a, {"key": ""}))
1717-
self.assertTrue(match_property(property_a, {"key": None}))
1771+
self.assertFalse(match_property(property_a, {"key": None}))
17181772

17191773
with self.assertRaises(InconclusiveMatchError):
17201774
match_property(property_a, {"key2": "value"})
@@ -1980,20 +2034,20 @@ def test_none_property_value_with_all_operators(self):
19802034
self.assertTrue(match_property(property_a, {"key": "non"}))
19812035

19822036
property_b = self.property(key="key", value=None, operator="is_set")
1983-
self.assertTrue(match_property(property_b, {"key": None}))
2037+
self.assertFalse(match_property(property_b, {"key": None}))
19842038

19852039
property_c = self.property(key="key", value="no", operator="icontains")
1986-
self.assertTrue(match_property(property_c, {"key": None}))
2040+
self.assertFalse(match_property(property_c, {"key": None}))
19872041
self.assertFalse(match_property(property_c, {"key": "smh"}))
19882042

19892043
property_d = self.property(key="key", value="No", operator="regex")
1990-
self.assertTrue(match_property(property_d, {"key": None}))
2044+
self.assertFalse(match_property(property_d, {"key": None}))
19912045

19922046
property_d_lower_case = self.property(key="key", value="no", operator="regex")
19932047
self.assertFalse(match_property(property_d_lower_case, {"key": None}))
19942048

19952049
property_e = self.property(key="key", value=1, operator="gt")
1996-
self.assertTrue(match_property(property_e, {"key": None}))
2050+
self.assertFalse(match_property(property_e, {"key": None}))
19972051

19982052
property_f = self.property(key="key", value=1, operator="lt")
19992053
self.assertFalse(match_property(property_f, {"key": None}))
@@ -2002,15 +2056,13 @@ def test_none_property_value_with_all_operators(self):
20022056
self.assertFalse(match_property(property_g, {"key": None}))
20032057

20042058
property_h = self.property(key="key", value="Oo", operator="lte")
2005-
self.assertTrue(match_property(property_h, {"key": None}))
2059+
self.assertFalse(match_property(property_h, {"key": None}))
20062060

20072061
property_i = self.property(key="key", value="2022-05-01", operator="is_date_before")
2008-
with self.assertRaises(InconclusiveMatchError):
2009-
self.assertFalse(match_property(property_i, {"key": None}))
2062+
self.assertFalse(match_property(property_i, {"key": None}))
20102063

20112064
property_j = self.property(key="key", value="2022-05-01", operator="is_date_after")
2012-
with self.assertRaises(InconclusiveMatchError):
2013-
self.assertFalse(match_property(property_j, {"key": None}))
2065+
self.assertFalse(match_property(property_j, {"key": None}))
20142066

20152067
property_k = self.property(key="key", value="2022-05-01", operator="is_date_before")
20162068
with self.assertRaises(InconclusiveMatchError):

0 commit comments

Comments
 (0)