diff --git a/CHANGELOG.md b/CHANGELOG.md index 5970ce62..f2e07bab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.25.0 – 2025-04-15 + +1. Roll out new `/flags` endpoint to 100% of `/decide` traffic, excluding the top 10 customers. + ## 3.24.3 – 2025-04-15 1. Fix hash inclusion/exclusion for flag rollout diff --git a/posthog/client.py b/posthog/client.py index 9608cf17..8ada4e1e 100644 --- a/posthog/client.py +++ b/posthog/client.py @@ -53,7 +53,7 @@ MAX_DICT_SIZE = 50_000 # TODO: Get rid of these when you're done rolling out `/flags` to all customers -ROLLOUT_PERCENTAGE = 0.1 +ROLLOUT_PERCENTAGE = 1 INCLUDED_HASHES = set({"bc94e67150c97dbcbf52549d50a7b80814841dbf"}) # this is PostHog's API key # Explicitly excluding all the API tokens associated with the top 10 customers; we'll get to them soon, but don't want to rollout to them just yet EXCLUDED_HASHES = set( diff --git a/posthog/test/test_client.py b/posthog/test/test_client.py index 53b5d764..8f6c311b 100644 --- a/posthog/test/test_client.py +++ b/posthog/test/test_client.py @@ -271,9 +271,9 @@ def test_capture_exception_logs_when_enabled(self): self.assertEqual(logs.output[0], "ERROR:posthog:test exception\nNoneType: None") self.assertEqual(getattr(logs.records[0], "path"), "one/two/three") - @mock.patch("posthog.client.decide") - def test_basic_capture_with_feature_flags(self, patch_decide): - patch_decide.return_value = {"featureFlags": {"beta-feature": "random-variant"}} + @mock.patch("posthog.client.flags") + def test_basic_capture_with_feature_flags(self, patch_flags): + patch_flags.return_value = {"featureFlags": {"beta-feature": "random-variant"}} client = Client(FAKE_TEST_API_KEY, on_error=self.set_fail, personal_api_key=FAKE_TEST_API_KEY) success, msg = client.capture("distinct_id", "python test event", send_feature_flags=True) @@ -290,11 +290,11 @@ def test_basic_capture_with_feature_flags(self, patch_decide): self.assertEqual(msg["properties"]["$feature/beta-feature"], "random-variant") self.assertEqual(msg["properties"]["$active_feature_flags"], ["beta-feature"]) - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) - @mock.patch("posthog.client.decide") - def test_basic_capture_with_locally_evaluated_feature_flags(self, patch_decide): - patch_decide.return_value = {"featureFlags": {"beta-feature": "random-variant"}} + @mock.patch("posthog.client.flags") + def test_basic_capture_with_locally_evaluated_feature_flags(self, patch_flags): + patch_flags.return_value = {"featureFlags": {"beta-feature": "random-variant"}} client = Client(FAKE_TEST_API_KEY, on_error=self.set_fail, personal_api_key=FAKE_TEST_API_KEY) multivariate_flag = { @@ -380,7 +380,7 @@ def test_basic_capture_with_locally_evaluated_feature_flags(self, patch_decide): self.assertEqual(msg["properties"]["$active_feature_flags"], ["beta-feature-local"]) assert "$feature/beta-feature" not in msg["properties"] - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) # test that flags are not evaluated without local evaluation client.feature_flags = [] @@ -412,9 +412,9 @@ def test_load_feature_flags_quota_limited(self, patch_get): self.assertEqual(client.cohorts, {}) self.assertIn("PostHog feature flags quota limited", logs.output[0]) - @mock.patch("posthog.client.decide") - def test_dont_override_capture_with_local_flags(self, patch_decide): - patch_decide.return_value = {"featureFlags": {"beta-feature": "random-variant"}} + @mock.patch("posthog.client.flags") + def test_dont_override_capture_with_local_flags(self, patch_flags): + patch_flags.return_value = {"featureFlags": {"beta-feature": "random-variant"}} client = Client(FAKE_TEST_API_KEY, on_error=self.set_fail, personal_api_key=FAKE_TEST_API_KEY) multivariate_flag = { @@ -487,11 +487,11 @@ def test_dont_override_capture_with_local_flags(self, patch_decide): assert "$feature/beta-feature" not in msg["properties"] assert "$feature/person-flag" not in msg["properties"] - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) - @mock.patch("posthog.client.decide") - def test_basic_capture_with_feature_flags_returns_active_only(self, patch_decide): - patch_decide.return_value = { + @mock.patch("posthog.client.flags") + def test_basic_capture_with_feature_flags_returns_active_only(self, patch_flags): + patch_flags.return_value = { "featureFlags": {"beta-feature": "random-variant", "alpha-feature": True, "off-feature": False} } @@ -512,8 +512,8 @@ def test_basic_capture_with_feature_flags_returns_active_only(self, patch_decide self.assertEqual(msg["properties"]["$feature/alpha-feature"], True) self.assertEqual(msg["properties"]["$active_feature_flags"], ["beta-feature", "alpha-feature"]) - self.assertEqual(patch_decide.call_count, 1) - patch_decide.assert_called_with( + self.assertEqual(patch_flags.call_count, 1) + patch_flags.assert_called_with( "random_key", "https://us.i.posthog.com", timeout=3, @@ -524,9 +524,9 @@ def test_basic_capture_with_feature_flags_returns_active_only(self, patch_decide disable_geoip=True, ) - @mock.patch("posthog.client.decide") - def test_basic_capture_with_feature_flags_and_disable_geoip_returns_correctly(self, patch_decide): - patch_decide.return_value = { + @mock.patch("posthog.client.flags") + def test_basic_capture_with_feature_flags_and_disable_geoip_returns_correctly(self, patch_flags): + patch_flags.return_value = { "featureFlags": {"beta-feature": "random-variant", "alpha-feature": True, "off-feature": False} } @@ -554,8 +554,8 @@ def test_basic_capture_with_feature_flags_and_disable_geoip_returns_correctly(se self.assertEqual(msg["properties"]["$feature/alpha-feature"], True) self.assertEqual(msg["properties"]["$active_feature_flags"], ["beta-feature", "alpha-feature"]) - self.assertEqual(patch_decide.call_count, 1) - patch_decide.assert_called_with( + self.assertEqual(patch_flags.call_count, 1) + patch_flags.assert_called_with( "random_key", "https://us.i.posthog.com", timeout=12, @@ -566,9 +566,9 @@ def test_basic_capture_with_feature_flags_and_disable_geoip_returns_correctly(se disable_geoip=False, ) - @mock.patch("posthog.client.decide") - def test_basic_capture_with_feature_flags_switched_off_doesnt_send_them(self, patch_decide): - patch_decide.return_value = {"featureFlags": {"beta-feature": "random-variant"}} + @mock.patch("posthog.client.flags") + def test_basic_capture_with_feature_flags_switched_off_doesnt_send_them(self, patch_flags): + patch_flags.return_value = {"featureFlags": {"beta-feature": "random-variant"}} client = Client(FAKE_TEST_API_KEY, on_error=self.set_fail, personal_api_key=FAKE_TEST_API_KEY) success, msg = client.capture("distinct_id", "python test event", send_feature_flags=False) @@ -585,7 +585,7 @@ def test_basic_capture_with_feature_flags_switched_off_doesnt_send_them(self, pa self.assertTrue("$feature/beta-feature" not in msg["properties"]) self.assertTrue("$active_feature_flags" not in msg["properties"]) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) def test_stringifies_distinct_id(self): # A large number that loses precision in node: @@ -942,29 +942,29 @@ def test_disabled(self): self.assertEqual(msg, "disabled") - @mock.patch("posthog.client.decide") - def test_disabled_with_feature_flags(self, patch_decide): + @mock.patch("posthog.client.flags") + def test_disabled_with_feature_flags(self, patch_flags): client = Client(FAKE_TEST_API_KEY, on_error=self.set_fail, disabled=True) response = client.get_feature_flag("beta-feature", "12345") self.assertIsNone(response) - patch_decide.assert_not_called() + patch_flags.assert_not_called() response = client.feature_enabled("beta-feature", "12345") self.assertIsNone(response) - patch_decide.assert_not_called() + patch_flags.assert_not_called() response = client.get_all_flags("12345") self.assertIsNone(response) - patch_decide.assert_not_called() + patch_flags.assert_not_called() response = client.get_feature_flag_payload("key", "12345") self.assertIsNone(response) - patch_decide.assert_not_called() + patch_flags.assert_not_called() response = client.get_all_flags_and_payloads("12345") self.assertEqual(response, {"featureFlags": None, "featureFlagPayloads": None}) - patch_decide.assert_not_called() + patch_flags.assert_not_called() # no capture calls self.assertTrue(client.queue.empty()) @@ -1012,14 +1012,14 @@ def test_disable_geoip_method_overrides_init_on_events(self): client.flush() self.assertTrue("$geoip_disable" not in msg["properties"]) - @mock.patch("posthog.client.decide") - def test_disable_geoip_default_on_decide(self, patch_decide): - patch_decide.return_value = { + @mock.patch("posthog.client.flags") + def test_disable_geoip_default_on_decide(self, patch_flags): + patch_flags.return_value = { "featureFlags": {"beta-feature": "random-variant", "alpha-feature": True, "off-feature": False} } client = Client(FAKE_TEST_API_KEY, on_error=self.set_fail, disable_geoip=False) client.get_feature_flag("random_key", "some_id", disable_geoip=True) - patch_decide.assert_called_with( + patch_flags.assert_called_with( "random_key", "https://us.i.posthog.com", timeout=3, @@ -1029,9 +1029,9 @@ def test_disable_geoip_default_on_decide(self, patch_decide): group_properties={}, disable_geoip=True, ) - patch_decide.reset_mock() + patch_flags.reset_mock() client.feature_enabled("random_key", "feature_enabled_distinct_id", disable_geoip=True) - patch_decide.assert_called_with( + patch_flags.assert_called_with( "random_key", "https://us.i.posthog.com", timeout=3, @@ -1041,9 +1041,9 @@ def test_disable_geoip_default_on_decide(self, patch_decide): group_properties={}, disable_geoip=True, ) - patch_decide.reset_mock() + patch_flags.reset_mock() client.get_all_flags_and_payloads("all_flags_payloads_id") - patch_decide.assert_called_with( + patch_flags.assert_called_with( "random_key", "https://us.i.posthog.com", timeout=3, @@ -1066,9 +1066,9 @@ def raise_effect(): self.assertFalse(client.feature_enabled("example", "distinct_id")) - @mock.patch("posthog.client.decide") - def test_default_properties_get_added_properly(self, patch_decide): - patch_decide.return_value = { + @mock.patch("posthog.client.flags") + def test_default_properties_get_added_properly(self, patch_flags): + patch_flags.return_value = { "featureFlags": {"beta-feature": "random-variant", "alpha-feature": True, "off-feature": False} } client = Client(FAKE_TEST_API_KEY, host="http://app2.posthog.com", on_error=self.set_fail, disable_geoip=False) @@ -1079,7 +1079,7 @@ def test_default_properties_get_added_properly(self, patch_decide): person_properties={"x1": "y1"}, group_properties={"company": {"x": "y"}}, ) - patch_decide.assert_called_with( + patch_flags.assert_called_with( "random_key", "http://app2.posthog.com", timeout=3, @@ -1093,7 +1093,7 @@ def test_default_properties_get_added_properly(self, patch_decide): disable_geoip=False, ) - patch_decide.reset_mock() + patch_flags.reset_mock() client.get_feature_flag( "random_key", "some_id", @@ -1105,7 +1105,7 @@ def test_default_properties_get_added_properly(self, patch_decide): } }, ) - patch_decide.assert_called_with( + patch_flags.assert_called_with( "random_key", "http://app2.posthog.com", timeout=3, @@ -1119,10 +1119,10 @@ def test_default_properties_get_added_properly(self, patch_decide): disable_geoip=False, ) - patch_decide.reset_mock() + patch_flags.reset_mock() # test nones client.get_all_flags_and_payloads("some_id", groups={}, person_properties=None, group_properties=None) - patch_decide.assert_called_with( + patch_flags.assert_called_with( "random_key", "http://app2.posthog.com", timeout=3, @@ -1222,9 +1222,9 @@ def test_mock_system_context( assert context == expected_context - @mock.patch("posthog.client.decide") - def test_get_decide_returns_normalized_decide_response(self, patch_decide): - patch_decide.return_value = { + @mock.patch("posthog.client.flags") + def test_get_decide_returns_normalized_decide_response(self, patch_flags): + patch_flags.return_value = { "featureFlags": {"beta-feature": "random-variant", "alpha-feature": True, "off-feature": False}, "featureFlagPayloads": {"beta-feature": '{"some": "data"}'}, "errorsWhileComputingFlags": False, @@ -1272,9 +1272,9 @@ def test_get_decide_returns_normalized_decide_response(self, patch_decide): "requestId": "test-id", } - @mock.patch("posthog.client.flags") @mock.patch("posthog.client.decide") - def test_get_flags_decision_rollout(self, patch_decide, patch_flags): + @mock.patch("posthog.client.flags") + def test_get_flags_decision_rollout(self, patch_flags, patch_decide): # Set up mock responses decide_response = { "featureFlags": {"flag1": True}, @@ -1291,22 +1291,11 @@ def test_get_flags_decision_rollout(self, patch_decide, patch_flags): client = Client(FAKE_TEST_API_KEY) - # Test 0% rollout - should use decide - with mock.patch("posthog.client.is_token_in_rollout", return_value=False) as mock_rollout: - client.get_flags_decision("distinct_id") - mock_rollout.assert_called_with( - FAKE_TEST_API_KEY, 0.1, included_hashes=INCLUDED_HASHES, excluded_hashes=EXCLUDED_HASHES - ) - patch_decide.assert_called_once() - patch_flags.assert_not_called() - patch_decide.reset_mock() - patch_flags.reset_mock() - # Test 100% rollout - should use flags with mock.patch("posthog.client.is_token_in_rollout", return_value=True) as mock_rollout: client.get_flags_decision("distinct_id") mock_rollout.assert_called_with( - FAKE_TEST_API_KEY, 0.1, included_hashes=INCLUDED_HASHES, excluded_hashes=EXCLUDED_HASHES + FAKE_TEST_API_KEY, 1, included_hashes=INCLUDED_HASHES, excluded_hashes=EXCLUDED_HASHES ) patch_flags.assert_called_once() patch_decide.assert_not_called() diff --git a/posthog/test/test_feature_flags.py b/posthog/test/test_feature_flags.py index c967e77e..e9baa55d 100644 --- a/posthog/test/test_feature_flags.py +++ b/posthog/test/test_feature_flags.py @@ -121,9 +121,9 @@ def test_case_insensitive_matching(self): self.client.get_feature_flag("person-flag", "some-distinct-id", person_properties={"star": "sun"}) ) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") @mock.patch("posthog.client.get") - def test_flag_group_properties(self, patch_get, patch_decide): + def test_flag_group_properties(self, patch_get, patch_flags): self.client.feature_flags = [ { "id": 1, @@ -193,10 +193,10 @@ def test_flag_group_properties(self, patch_get, patch_decide): group_properties={"company": {"name": "Project Name 2"}}, ) ) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) # Now group type mappings are gone, so fall back to /decide/ - patch_decide.return_value = {"featureFlags": {"group-flag": "decide-fallback-value"}} + patch_flags.return_value = {"featureFlags": {"group-flag": "decide-fallback-value"}} self.client.group_type_mapping = {} self.assertEqual( @@ -209,12 +209,12 @@ def test_flag_group_properties(self, patch_get, patch_decide): "decide-fallback-value", ) - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") @mock.patch("posthog.client.get") - def test_flag_with_complex_definition(self, patch_get, patch_decide): - patch_decide.return_value = {"featureFlags": {"complex-flag": "decide-fallback-value"}} + def test_flag_with_complex_definition(self, patch_get, patch_flags): + patch_flags.return_value = {"featureFlags": {"complex-flag": "decide-fallback-value"}} client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY) client.feature_flags = [ { @@ -273,7 +273,7 @@ def test_flag_with_complex_definition(self, patch_get, patch_decide): "complex-flag", "some-distinct-id", person_properties={"region": "USA", "name": "Aloha"} ) ) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) # this distinctIDs hash is < rollout % self.assertTrue( @@ -283,7 +283,7 @@ def test_flag_with_complex_definition(self, patch_get, patch_decide): person_properties={"region": "USA", "email": "a@b.com"}, ) ) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) # will fall back on `/decide`, as all properties present for second group, but that group resolves to false self.assertEqual( @@ -294,27 +294,27 @@ def test_flag_with_complex_definition(self, patch_get, patch_decide): ), "decide-fallback-value", ) - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) - patch_decide.reset_mock() + patch_flags.reset_mock() # same as above self.assertEqual( client.get_feature_flag("complex-flag", "some-distinct-id", person_properties={"doesnt_matter": "1"}), "decide-fallback-value", ) - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) - patch_decide.reset_mock() + patch_flags.reset_mock() # this one will need to fall back self.assertEqual( client.get_feature_flag("complex-flag", "some-distinct-id", person_properties={"region": "USA"}), "decide-fallback-value", ) - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) - patch_decide.reset_mock() + patch_flags.reset_mock() # won't need to fall back when all values are present self.assertFalse( @@ -324,12 +324,12 @@ def test_flag_with_complex_definition(self, patch_get, patch_decide): person_properties={"region": "USA", "email": "a@b.com", "name": "X", "doesnt_matter": "1"}, ) ) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") @mock.patch("posthog.client.get") - def test_feature_flags_fallback_to_decide(self, patch_get, patch_decide): - patch_decide.return_value = {"featureFlags": {"beta-feature": "alakazam", "beta-feature2": "alakazam2"}} + def test_feature_flags_fallback_to_decide(self, patch_get, patch_flags): + patch_flags.return_value = {"featureFlags": {"beta-feature": "alakazam", "beta-feature2": "alakazam2"}} client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY) client.feature_flags = [ { @@ -373,18 +373,18 @@ def test_feature_flags_fallback_to_decide(self, patch_get, patch_decide): feature_flag_match = client.get_feature_flag("beta-feature", "some-distinct-id") self.assertEqual(feature_flag_match, "alakazam") - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) # beta-feature2 fallbacks to decide because region property not given with call feature_flag_match = client.get_feature_flag("beta-feature2", "some-distinct-id") self.assertEqual(feature_flag_match, "alakazam2") - self.assertEqual(patch_decide.call_count, 2) + self.assertEqual(patch_flags.call_count, 2) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") @mock.patch("posthog.client.get") - def test_feature_flags_dont_fallback_to_decide_when_only_local_evaluation_is_true(self, patch_get, patch_decide): - patch_decide.return_value = {"featureFlags": {"beta-feature": "alakazam", "beta-feature2": "alakazam2"}} + def test_feature_flags_dont_fallback_to_decide_when_only_local_evaluation_is_true(self, patch_get, patch_flags): + patch_flags.return_value = {"featureFlags": {"beta-feature": "alakazam", "beta-feature2": "alakazam2"}} client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY) client.feature_flags = [ { @@ -429,12 +429,12 @@ def test_feature_flags_dont_fallback_to_decide_when_only_local_evaluation_is_tru feature_flag_match = client.get_feature_flag("beta-feature", "some-distinct-id", only_evaluate_locally=True) self.assertEqual(feature_flag_match, None) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) feature_flag_match = client.feature_enabled("beta-feature", "some-distinct-id", only_evaluate_locally=True) self.assertEqual(feature_flag_match, None) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) # beta-feature2 should fallback to decide because region property not given with call # but doesn't because only_evaluate_locally is true @@ -444,12 +444,12 @@ def test_feature_flags_dont_fallback_to_decide_when_only_local_evaluation_is_tru feature_flag_match = client.feature_enabled("beta-feature2", "some-distinct-id", only_evaluate_locally=True) self.assertEqual(feature_flag_match, None) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") @mock.patch("posthog.client.get") - def test_feature_flag_never_returns_undefined_during_regular_evaluation(self, patch_get, patch_decide): - patch_decide.return_value = {"featureFlags": {}} + def test_feature_flag_never_returns_undefined_during_regular_evaluation(self, patch_get, patch_flags): + patch_flags.return_value = {"featureFlags": {}} client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY) client.feature_flags = [ { @@ -474,28 +474,28 @@ def test_feature_flag_never_returns_undefined_during_regular_evaluation(self, pa # beta-feature2 falls back to decide, and whatever decide returns is the value self.assertFalse(client.get_feature_flag("beta-feature2", "some-distinct-id")) - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) self.assertFalse(client.feature_enabled("beta-feature2", "some-distinct-id")) - self.assertEqual(patch_decide.call_count, 2) + self.assertEqual(patch_flags.call_count, 2) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") @mock.patch("posthog.client.get") - def test_feature_flag_return_none_when_decide_errors_out(self, patch_get, patch_decide): - patch_decide.side_effect = APIError(400, "Decide error") + def test_feature_flag_return_none_when_decide_errors_out(self, patch_get, patch_flags): + patch_flags.side_effect = APIError(400, "Decide error") client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY) client.feature_flags = [] # beta-feature2 falls back to decide, which on error returns None self.assertIsNone(client.get_feature_flag("beta-feature2", "some-distinct-id")) - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) self.assertIsNone(client.feature_enabled("beta-feature2", "some-distinct-id")) - self.assertEqual(patch_decide.call_count, 2) + self.assertEqual(patch_flags.call_count, 2) - @mock.patch("posthog.client.decide") - def test_experience_continuity_flag_not_evaluated_locally(self, patch_decide): - patch_decide.return_value = {"featureFlags": {"beta-feature": "decide-fallback-value"}} + @mock.patch("posthog.client.flags") + def test_experience_continuity_flag_not_evaluated_locally(self, patch_flags): + patch_flags.return_value = {"featureFlags": {"beta-feature": "decide-fallback-value"}} client = Client(FAKE_TEST_API_KEY, personal_api_key="test") client.feature_flags = [ { @@ -517,12 +517,12 @@ def test_experience_continuity_flag_not_evaluated_locally(self, patch_decide): ] # decide called always because experience_continuity is set self.assertEqual(client.get_feature_flag("beta-feature", "distinct_id"), "decide-fallback-value") - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_get_all_flags_with_fallback(self, patch_decide, patch_capture): - patch_decide.return_value = { + @mock.patch("posthog.client.flags") + def test_get_all_flags_with_fallback(self, patch_flags, patch_capture): + patch_flags.return_value = { "featureFlags": {"beta-feature": "variant-1", "beta-feature2": "variant-2", "disabled-feature": False} } # decide should return the same flags client = self.client @@ -576,13 +576,13 @@ def test_get_all_flags_with_fallback(self, patch_decide, patch_capture): client.get_all_flags("distinct_id"), {"beta-feature": "variant-1", "beta-feature2": "variant-2", "disabled-feature": False}, ) - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) self.assertEqual(patch_capture.call_count, 0) @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_get_all_flags_and_payloads_with_fallback(self, patch_decide, patch_capture): - patch_decide.return_value = { + @mock.patch("posthog.client.flags") + def test_get_all_flags_and_payloads_with_fallback(self, patch_flags, patch_capture): + patch_flags.return_value = { "featureFlags": {"beta-feature": "variant-1", "beta-feature2": "variant-2"}, "featureFlagPayloads": {"beta-feature": 100, "beta-feature2": 300}, } @@ -649,26 +649,26 @@ def test_get_all_flags_and_payloads_with_fallback(self, patch_decide, patch_capt "beta-feature2": 300, }, ) - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) self.assertEqual(patch_capture.call_count, 0) @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_get_all_flags_with_fallback_empty_local_flags(self, patch_decide, patch_capture): - patch_decide.return_value = {"featureFlags": {"beta-feature": "variant-1", "beta-feature2": "variant-2"}} + @mock.patch("posthog.client.flags") + def test_get_all_flags_with_fallback_empty_local_flags(self, patch_flags, patch_capture): + patch_flags.return_value = {"featureFlags": {"beta-feature": "variant-1", "beta-feature2": "variant-2"}} client = self.client client.feature_flags = [] # beta-feature value overridden by /decide self.assertEqual( client.get_all_flags("distinct_id"), {"beta-feature": "variant-1", "beta-feature2": "variant-2"} ) - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) self.assertEqual(patch_capture.call_count, 0) @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_get_all_flags_and_payloads_with_fallback_empty_local_flags(self, patch_decide, patch_capture): - patch_decide.return_value = { + @mock.patch("posthog.client.flags") + def test_get_all_flags_and_payloads_with_fallback_empty_local_flags(self, patch_flags, patch_capture): + patch_flags.return_value = { "featureFlags": {"beta-feature": "variant-1", "beta-feature2": "variant-2"}, "featureFlagPayloads": {"beta-feature": 100, "beta-feature2": 300}, } @@ -679,13 +679,13 @@ def test_get_all_flags_and_payloads_with_fallback_empty_local_flags(self, patch_ client.get_all_flags_and_payloads("distinct_id")["featureFlagPayloads"], {"beta-feature": 100, "beta-feature2": 300}, ) - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) self.assertEqual(patch_capture.call_count, 0) @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_get_all_flags_with_no_fallback(self, patch_decide, patch_capture): - patch_decide.return_value = {"featureFlags": {"beta-feature": "variant-1", "beta-feature2": "variant-2"}} + @mock.patch("posthog.client.flags") + def test_get_all_flags_with_no_fallback(self, patch_flags, patch_capture): + patch_flags.return_value = {"featureFlags": {"beta-feature": "variant-1", "beta-feature2": "variant-2"}} client = self.client client.feature_flags = [ { @@ -720,12 +720,12 @@ def test_get_all_flags_with_no_fallback(self, patch_decide, patch_capture): ] self.assertEqual(client.get_all_flags("distinct_id"), {"beta-feature": True, "disabled-feature": False}) # decide not called because this can be evaluated locally - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) self.assertEqual(patch_capture.call_count, 0) @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_get_all_flags_and_payloads_with_no_fallback(self, patch_decide, patch_capture): + @mock.patch("posthog.client.flags") + def test_get_all_flags_and_payloads_with_no_fallback(self, patch_flags, patch_capture): client = self.client basic_flag = { "id": 1, @@ -770,13 +770,13 @@ def test_get_all_flags_and_payloads_with_no_fallback(self, patch_decide, patch_c client.get_all_flags_and_payloads("distinct_id")["featureFlagPayloads"], {"beta-feature": "new"} ) # decide not called because this can be evaluated locally - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) self.assertEqual(patch_capture.call_count, 0) @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_get_all_flags_with_fallback_but_only_local_evaluation_set(self, patch_decide, patch_capture): - patch_decide.return_value = {"featureFlags": {"beta-feature": "variant-1", "beta-feature2": "variant-2"}} + @mock.patch("posthog.client.flags") + def test_get_all_flags_with_fallback_but_only_local_evaluation_set(self, patch_flags, patch_capture): + patch_flags.return_value = {"featureFlags": {"beta-feature": "variant-1", "beta-feature2": "variant-2"}} client = self.client client.feature_flags = [ { @@ -828,13 +828,13 @@ def test_get_all_flags_with_fallback_but_only_local_evaluation_set(self, patch_d client.get_all_flags("distinct_id", only_evaluate_locally=True), {"beta-feature": True, "disabled-feature": False}, ) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) self.assertEqual(patch_capture.call_count, 0) @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_get_all_flags_and_payloads_with_fallback_but_only_local_evaluation_set(self, patch_decide, patch_capture): - patch_decide.return_value = { + @mock.patch("posthog.client.flags") + def test_get_all_flags_and_payloads_with_fallback_but_only_local_evaluation_set(self, patch_flags, patch_capture): + patch_flags.return_value = { "featureFlags": {"beta-feature": "variant-1", "beta-feature2": "variant-2"}, "featureFlagPayloads": {"beta-feature": 100, "beta-feature2": 300}, } @@ -901,12 +901,12 @@ def test_get_all_flags_and_payloads_with_fallback_but_only_local_evaluation_set( client.get_all_flags_and_payloads("distinct_id", only_evaluate_locally=True)["featureFlagPayloads"], {"beta-feature": "some-payload"}, ) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) self.assertEqual(patch_capture.call_count, 0) @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_compute_inactive_flags_locally(self, patch_decide, patch_capture): + @mock.patch("posthog.client.flags") + def test_compute_inactive_flags_locally(self, patch_flags, patch_capture): client = self.client client.feature_flags = [ { @@ -941,7 +941,7 @@ def test_compute_inactive_flags_locally(self, patch_decide, patch_capture): ] self.assertEqual(client.get_all_flags("distinct_id"), {"beta-feature": True, "disabled-feature": False}) # decide not called because this can be evaluated locally - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) self.assertEqual(patch_capture.call_count, 0) # Now, after a poll interval, flag 1 is inactive, and flag 2 rollout is set to 100%. @@ -978,12 +978,12 @@ def test_compute_inactive_flags_locally(self, patch_decide, patch_capture): ] self.assertEqual(client.get_all_flags("distinct_id"), {"beta-feature": False, "disabled-feature": True}) # decide not called because this can be evaluated locally - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) self.assertEqual(patch_capture.call_count, 0) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") @mock.patch("posthog.client.get") - def test_feature_flags_local_evaluation_None_values(self, patch_get, patch_decide): + def test_feature_flags_local_evaluation_None_values(self, patch_get, patch_flags): client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY) client.feature_flags = [ { @@ -1020,7 +1020,7 @@ def test_feature_flags_local_evaluation_None_values(self, patch_get, patch_decid ) self.assertEqual(feature_flag_match, False) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) self.assertEqual(patch_get.call_count, 0) feature_flag_match = client.get_feature_flag( @@ -1036,9 +1036,9 @@ def test_feature_flags_local_evaluation_None_values(self, patch_get, patch_decid self.assertEqual(feature_flag_match, True) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") @mock.patch("posthog.client.get") - def test_feature_flags_local_evaluation_for_cohorts(self, patch_get, patch_decide): + def test_feature_flags_local_evaluation_for_cohorts(self, patch_get, patch_flags): client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY) client.feature_flags = [ { @@ -1088,7 +1088,7 @@ def test_feature_flags_local_evaluation_for_cohorts(self, patch_get, patch_decid ) self.assertEqual(feature_flag_match, False) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) self.assertEqual(patch_get.call_count, 0) feature_flag_match = client.get_feature_flag( @@ -1096,19 +1096,19 @@ def test_feature_flags_local_evaluation_for_cohorts(self, patch_get, patch_decid ) # even though 'other' property is not present, the cohort should still match since it's an OR condition self.assertEqual(feature_flag_match, True) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) self.assertEqual(patch_get.call_count, 0) feature_flag_match = client.get_feature_flag( "beta-feature", "some-distinct-id", person_properties={"region": "USA", "other": "thing"} ) self.assertEqual(feature_flag_match, True) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) self.assertEqual(patch_get.call_count, 0) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") @mock.patch("posthog.client.get") - def test_feature_flags_local_evaluation_for_negated_cohorts(self, patch_get, patch_decide): + def test_feature_flags_local_evaluation_for_negated_cohorts(self, patch_get, patch_flags): client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY) client.feature_flags = [ { @@ -1160,7 +1160,7 @@ def test_feature_flags_local_evaluation_for_negated_cohorts(self, patch_get, pat ) self.assertEqual(feature_flag_match, False) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) self.assertEqual(patch_get.call_count, 0) feature_flag_match = client.get_feature_flag( @@ -1168,23 +1168,23 @@ def test_feature_flags_local_evaluation_for_negated_cohorts(self, patch_get, pat ) # even though 'other' property is not present, the cohort should still match since it's an OR condition self.assertEqual(feature_flag_match, True) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) self.assertEqual(patch_get.call_count, 0) feature_flag_match = client.get_feature_flag( "beta-feature", "some-distinct-id", person_properties={"region": "USA", "other": "thing"} ) # since 'other' is negated, we return False. Since 'nation' is not present, we can't tell whether the flag should be true or false, so go to decide - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) self.assertEqual(patch_get.call_count, 0) - patch_decide.reset_mock() + patch_flags.reset_mock() feature_flag_match = client.get_feature_flag( "beta-feature", "some-distinct-id", person_properties={"region": "USA", "other": "thing2"} ) self.assertEqual(feature_flag_match, True) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) self.assertEqual(patch_get.call_count, 0) @mock.patch("posthog.client.Poller") @@ -1218,9 +1218,9 @@ def test_load_feature_flags_wrong_key(self): client.debug = True self.assertRaises(APIError, client.load_feature_flags) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") @mock.patch("posthog.client.get") - def test_feature_enabled_simple(self, patch_get, patch_decide): + def test_feature_enabled_simple(self, patch_get, patch_flags): client = Client(FAKE_TEST_API_KEY) client.feature_flags = [ { @@ -1240,11 +1240,11 @@ def test_feature_enabled_simple(self, patch_get, patch_decide): } ] self.assertTrue(client.feature_enabled("beta-feature", "distinct_id")) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") @mock.patch("posthog.client.get") - def test_feature_enabled_simple_is_false(self, patch_get, patch_decide): + def test_feature_enabled_simple_is_false(self, patch_get, patch_flags): client = Client(FAKE_TEST_API_KEY) client.feature_flags = [ { @@ -1264,11 +1264,11 @@ def test_feature_enabled_simple_is_false(self, patch_get, patch_decide): } ] self.assertFalse(client.feature_enabled("beta-feature", "distinct_id")) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") @mock.patch("posthog.client.get") - def test_feature_enabled_simple_is_true_when_rollout_is_undefined(self, patch_get, patch_decide): + def test_feature_enabled_simple_is_true_when_rollout_is_undefined(self, patch_get, patch_flags): client = Client(FAKE_TEST_API_KEY) client.feature_flags = [ { @@ -1288,7 +1288,7 @@ def test_feature_enabled_simple_is_true_when_rollout_is_undefined(self, patch_ge } ] self.assertTrue(client.feature_enabled("beta-feature", "distinct_id")) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) @mock.patch("posthog.client.get") def test_feature_enabled_simple_with_project_api_key(self, patch_get): @@ -1312,9 +1312,9 @@ def test_feature_enabled_simple_with_project_api_key(self, patch_get): ] self.assertTrue(client.feature_enabled("beta-feature", "distinct_id")) - @mock.patch("posthog.client.decide") - def test_feature_enabled_request_multi_variate(self, patch_decide): - patch_decide.return_value = {"featureFlags": {"beta-feature": "variant-1"}} + @mock.patch("posthog.client.flags") + def test_feature_enabled_request_multi_variate(self, patch_flags): + patch_flags.return_value = {"featureFlags": {"beta-feature": "variant-1"}} client = Client(FAKE_TEST_API_KEY, personal_api_key="test") client.feature_flags = [ { @@ -1335,7 +1335,7 @@ def test_feature_enabled_request_multi_variate(self, patch_decide): ] self.assertTrue(client.feature_enabled("beta-feature", "distinct_id")) # decide not called because this can be evaluated locally - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) @mock.patch("posthog.client.get") def test_feature_enabled_simple_without_rollout_percentage(self, patch_get): @@ -1357,9 +1357,9 @@ def test_feature_enabled_simple_without_rollout_percentage(self, patch_get): ] self.assertTrue(client.feature_enabled("beta-feature", "distinct_id")) - @mock.patch("posthog.client.decide") - def test_get_feature_flag(self, patch_decide): - patch_decide.return_value = {"featureFlags": {"beta-feature": "variant-1"}} + @mock.patch("posthog.client.flags") + def test_get_feature_flag(self, patch_flags): + patch_flags.return_value = {"featureFlags": {"beta-feature": "variant-1"}} client = Client(FAKE_TEST_API_KEY, personal_api_key="test") client.feature_flags = [ { @@ -1386,27 +1386,27 @@ def test_get_feature_flag(self, patch_decide): ] self.assertEqual(client.get_feature_flag("beta-feature", "distinct_id"), "variant-1") # decide not called because this can be evaluated locally - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) @mock.patch("posthog.client.Poller") - @mock.patch("posthog.client.decide") - def test_feature_enabled_doesnt_exist(self, patch_decide, patch_poll): + @mock.patch("posthog.client.flags") + def test_feature_enabled_doesnt_exist(self, patch_flags, patch_poll): client = Client(FAKE_TEST_API_KEY) client.feature_flags = [] - patch_decide.return_value = {"featureFlags": {}} + patch_flags.return_value = {"featureFlags": {}} self.assertFalse(client.feature_enabled("doesnt-exist", "distinct_id")) - patch_decide.side_effect = APIError(401, "decide error") + patch_flags.side_effect = APIError(401, "decide error") self.assertIsNone(client.feature_enabled("doesnt-exist", "distinct_id")) @mock.patch("posthog.client.Poller") - @mock.patch("posthog.client.decide") - def test_personal_api_key_doesnt_exist(self, patch_decide, patch_poll): + @mock.patch("posthog.client.flags") + def test_personal_api_key_doesnt_exist(self, patch_flags, patch_poll): client = Client(FAKE_TEST_API_KEY, personal_api_key="test") client.feature_flags = [] - patch_decide.return_value = {"featureFlags": {"feature-flag": True}} + patch_flags.return_value = {"featureFlags": {"feature-flag": True}} self.assertTrue(client.feature_enabled("feature-flag", "distinct_id")) @@ -1422,9 +1422,9 @@ def raise_effect(): self.assertFalse(client.feature_enabled("doesnt-exist", "distinct_id")) - @mock.patch("posthog.client.decide") - def test_get_feature_flag_with_variant_overrides(self, patch_decide): - patch_decide.return_value = {"featureFlags": {"beta-feature": "variant-1"}} + @mock.patch("posthog.client.flags") + def test_get_feature_flag_with_variant_overrides(self, patch_flags): + patch_flags.return_value = {"featureFlags": {"beta-feature": "variant-1"}} client = Client(FAKE_TEST_API_KEY, personal_api_key="test") client.feature_flags = [ { @@ -1460,11 +1460,11 @@ def test_get_feature_flag_with_variant_overrides(self, patch_decide): ) self.assertEqual(client.get_feature_flag("beta-feature", "example_id"), "first-variant") # decide not called because this can be evaluated locally - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) - @mock.patch("posthog.client.decide") - def test_flag_with_clashing_variant_overrides(self, patch_decide): - patch_decide.return_value = {"featureFlags": {"beta-feature": "variant-1"}} + @mock.patch("posthog.client.flags") + def test_flag_with_clashing_variant_overrides(self, patch_flags): + patch_flags.return_value = {"featureFlags": {"beta-feature": "variant-1"}} client = Client(FAKE_TEST_API_KEY, personal_api_key="test") client.feature_flags = [ { @@ -1511,11 +1511,11 @@ def test_flag_with_clashing_variant_overrides(self, patch_decide): "second-variant", ) # decide not called because this can be evaluated locally - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) - @mock.patch("posthog.client.decide") - def test_flag_with_invalid_variant_overrides(self, patch_decide): - patch_decide.return_value = {"featureFlags": {"beta-feature": "variant-1"}} + @mock.patch("posthog.client.flags") + def test_flag_with_invalid_variant_overrides(self, patch_flags): + patch_flags.return_value = {"featureFlags": {"beta-feature": "variant-1"}} client = Client(FAKE_TEST_API_KEY, personal_api_key="test") client.feature_flags = [ { @@ -1551,11 +1551,11 @@ def test_flag_with_invalid_variant_overrides(self, patch_decide): ) self.assertEqual(client.get_feature_flag("beta-feature", "example_id"), "second-variant") # decide not called because this can be evaluated locally - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) - @mock.patch("posthog.client.decide") - def test_flag_with_multiple_variant_overrides(self, patch_decide): - patch_decide.return_value = {"featureFlags": {"beta-feature": "variant-1"}} + @mock.patch("posthog.client.flags") + def test_flag_with_multiple_variant_overrides(self, patch_flags): + patch_flags.return_value = {"featureFlags": {"beta-feature": "variant-1"}} client = Client(FAKE_TEST_API_KEY, personal_api_key="test") client.feature_flags = [ { @@ -1596,10 +1596,10 @@ def test_flag_with_multiple_variant_overrides(self, patch_decide): self.assertEqual(client.get_feature_flag("beta-feature", "example_id"), "third-variant") self.assertEqual(client.get_feature_flag("beta-feature", "another_id"), "second-variant") # decide not called because this can be evaluated locally - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) - @mock.patch("posthog.client.decide") - def test_boolean_feature_flag_payloads_local(self, patch_decide): + @mock.patch("posthog.client.flags") + def test_boolean_feature_flag_payloads_local(self, patch_flags): basic_flag = { "id": 1, "name": "Beta Feature", @@ -1637,12 +1637,12 @@ def test_boolean_feature_flag_payloads_local(self, patch_decide): ), 300, ) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_boolean_feature_flag_payload_decide(self, patch_decide, patch_capture): - patch_decide.return_value = {"featureFlags": {"person-flag": True}, "featureFlagPayloads": {"person-flag": 300}} + @mock.patch("posthog.client.flags") + def test_boolean_feature_flag_payload_decide(self, patch_flags, patch_capture): + patch_flags.return_value = {"featureFlags": {"person-flag": True}, "featureFlagPayloads": {"person-flag": 300}} self.assertEqual( self.client.get_feature_flag_payload( "person-flag", "some-distinct-id", person_properties={"region": "USA"} @@ -1656,12 +1656,12 @@ def test_boolean_feature_flag_payload_decide(self, patch_decide, patch_capture): ), 300, ) - self.assertEqual(patch_decide.call_count, 2) + self.assertEqual(patch_flags.call_count, 2) self.assertEqual(patch_capture.call_count, 1) patch_capture.reset_mock() - @mock.patch("posthog.client.decide") - def test_multivariate_feature_flag_payloads(self, patch_decide): + @mock.patch("posthog.client.flags") + def test_multivariate_feature_flag_payloads(self, patch_flags): multivariate_flag = { "id": 1, "name": "Beta Feature", @@ -1711,7 +1711,7 @@ def test_multivariate_feature_flag_payloads(self, patch_decide): ), "some-payload", ) - self.assertEqual(patch_decide.call_count, 0) + self.assertEqual(patch_flags.call_count, 0) class TestMatchProperties(unittest.TestCase): @@ -2233,9 +2233,9 @@ def test_year_parsing(self): class TestCaptureCalls(unittest.TestCase): @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_capture_is_called(self, patch_decide, patch_capture): - patch_decide.return_value = {"featureFlags": {"decide-flag": "decide-value"}} + @mock.patch("posthog.client.flags") + def test_capture_is_called(self, patch_flags, patch_capture): + patch_flags.return_value = {"featureFlags": {"decide-flag": "decide-value"}} client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY) client.feature_flags = [ { @@ -2326,7 +2326,7 @@ def test_capture_is_called(self, patch_decide, patch_capture): ), "decide-value", ) - self.assertEqual(patch_decide.call_count, 1) + self.assertEqual(patch_flags.call_count, 1) self.assertEqual(patch_capture.call_count, 1) patch_capture.assert_called_with( "some-distinct-id2", @@ -2342,9 +2342,9 @@ def test_capture_is_called(self, patch_decide, patch_capture): ) @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_capture_is_called_with_flag_details(self, patch_decide, patch_capture): - patch_decide.return_value = { + @mock.patch("posthog.client.flags") + def test_capture_is_called_with_flag_details(self, patch_flags, patch_capture): + patch_flags.return_value = { "flags": { "decide-flag": { "key": "decide-flag", @@ -2383,9 +2383,9 @@ def test_capture_is_called_with_flag_details(self, patch_decide, patch_capture): ) @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_capture_is_called_with_flag_details_and_payload(self, patch_decide, patch_capture): - patch_decide.return_value = { + @mock.patch("posthog.client.flags") + def test_capture_is_called_with_flag_details_and_payload(self, patch_flags, patch_capture): + patch_flags.return_value = { "flags": { "decide-flag-with-payload": { "key": "decide-flag-with-payload", @@ -2429,9 +2429,9 @@ def test_capture_is_called_with_flag_details_and_payload(self, patch_decide, pat disable_geoip=None, ) - @mock.patch("posthog.client.decide") - def test_capture_is_called_but_does_not_add_all_flags(self, patch_decide): - patch_decide.return_value = {"featureFlags": {"decide-flag": "decide-value"}} + @mock.patch("posthog.client.flags") + def test_capture_is_called_but_does_not_add_all_flags(self, patch_flags): + patch_flags.return_value = {"featureFlags": {"decide-flag": "decide-value"}} client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY) client.feature_flags = [ { @@ -2479,9 +2479,9 @@ def test_capture_is_called_but_does_not_add_all_flags(self, patch_decide): assert "$active_feature_flags" not in msg["properties"] @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_capture_is_called_in_get_feature_flag_payload(self, patch_decide, patch_capture): - patch_decide.return_value = { + @mock.patch("posthog.client.flags") + def test_capture_is_called_in_get_feature_flag_payload(self, patch_flags, patch_capture): + patch_flags.return_value = { "featureFlags": {"person-flag": True}, "featureFlagPayloads": {"person-flag": 300}, } @@ -2527,7 +2527,7 @@ def test_capture_is_called_in_get_feature_flag_payload(self, patch_decide, patch # Reset mocks for further tests patch_capture.reset_mock() - patch_decide.reset_mock() + patch_flags.reset_mock() # Call get_feature_flag_payload again for the same user; capture should not be called again because we've already reported an event for this distinct_id + flag client.get_feature_flag_payload( @@ -2560,9 +2560,9 @@ def test_capture_is_called_in_get_feature_flag_payload(self, patch_decide, patch patch_capture.reset_mock() @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_disable_geoip_get_flag_capture_call(self, patch_decide, patch_capture): - patch_decide.return_value = {"featureFlags": {"decide-flag": "decide-value"}} + @mock.patch("posthog.client.flags") + def test_disable_geoip_get_flag_capture_call(self, patch_flags, patch_capture): + patch_flags.return_value = {"featureFlags": {"decide-flag": "decide-value"}} client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY, disable_geoip=True) client.feature_flags = [ { @@ -2603,8 +2603,8 @@ def test_disable_geoip_get_flag_capture_call(self, patch_decide, patch_capture): @mock.patch("posthog.client.MAX_DICT_SIZE", 100) @mock.patch.object(Client, "capture") - @mock.patch("posthog.client.decide") - def test_capture_multiple_users_doesnt_out_of_memory(self, patch_decide, patch_capture): + @mock.patch("posthog.client.flags") + def test_capture_multiple_users_doesnt_out_of_memory(self, patch_flags, patch_capture): client = Client(FAKE_TEST_API_KEY, personal_api_key=FAKE_TEST_API_KEY) client.feature_flags = [ { @@ -4724,7 +4724,7 @@ def test_multivariate_flag_consistency(self, patch_get): else: self.assertFalse(feature_flag_match) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") def test_feature_flag_case_sensitive(self, mock_decide): mock_decide.return_value = {"featureFlags": {}} # Ensure decide returns empty flags @@ -4745,7 +4745,7 @@ def test_feature_flag_case_sensitive(self, mock_decide): self.assertFalse(client.feature_enabled("beta-feature", "user1")) self.assertFalse(client.feature_enabled("BETA-FEATURE", "user1")) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") def test_feature_flag_payload_case_sensitive(self, mock_decide): mock_decide.return_value = { "featureFlags": {"Beta-Feature": True}, @@ -4772,7 +4772,7 @@ def test_feature_flag_payload_case_sensitive(self, mock_decide): self.assertIsNone(client.get_feature_flag_payload("beta-feature", "user1")) self.assertIsNone(client.get_feature_flag_payload("BETA-FEATURE", "user1")) - @mock.patch("posthog.client.decide") + @mock.patch("posthog.client.flags") def test_feature_flag_case_sensitive_consistency(self, mock_decide): mock_decide.return_value = { "featureFlags": {"Beta-Feature": True}, diff --git a/posthog/version.py b/posthog/version.py index ec83210a..19441834 100644 --- a/posthog/version.py +++ b/posthog/version.py @@ -1,4 +1,4 @@ -VERSION = "3.24.3" +VERSION = "3.25.0" if __name__ == "__main__": print(VERSION, end="") # noqa: T201