|
1 | 1 | import json |
2 | 2 | import unittest |
3 | 3 | from datetime import date, datetime |
| 4 | +from unittest.mock import patch, MagicMock |
| 5 | +from parameterized import parameterized |
4 | 6 |
|
5 | 7 | import mock |
6 | 8 | import pytest |
7 | 9 | import requests |
8 | 10 |
|
9 | | -from posthog.request import DatetimeSerializer, QuotaLimitError, batch_post, decide, determine_server_host |
| 11 | +from posthog.request import ( |
| 12 | + DatetimeSerializer, |
| 13 | + QuotaLimitError, |
| 14 | + batch_post, |
| 15 | + decide, |
| 16 | + determine_server_host, |
| 17 | + normalize_decide_response, |
| 18 | +) |
10 | 19 | from posthog.test.test_utils import TEST_API_KEY |
| 20 | +from posthog.types import FeatureFlag, FlagMetadata, FlagReason, LegacyFlagMetadata |
11 | 21 |
|
12 | 22 |
|
13 | 23 | class TestRequests(unittest.TestCase): |
@@ -75,6 +85,90 @@ def test_normal_decide_response(self): |
75 | 85 | response = decide("fake_key", "fake_host") |
76 | 86 | self.assertEqual(response["featureFlags"], {"flag1": True}) |
77 | 87 |
|
| 88 | + @parameterized.expand([(True,), (False,)]) |
| 89 | + def test_normalize_decide_response_v4(self, has_errors: bool): |
| 90 | + resp = { |
| 91 | + "flags": { |
| 92 | + "my-flag": FeatureFlag( |
| 93 | + key="my-flag", |
| 94 | + enabled=True, |
| 95 | + variant="test-variant", |
| 96 | + reason=FlagReason( |
| 97 | + code="matched_condition", condition_index=0, description="Matched condition set 1" |
| 98 | + ), |
| 99 | + metadata=FlagMetadata(id=1, payload='{"some": "json"}', version=2, description="test-description"), |
| 100 | + ) |
| 101 | + }, |
| 102 | + "errorsWhileComputingFlags": has_errors, |
| 103 | + "requestId": "test-id", |
| 104 | + } |
| 105 | + |
| 106 | + result = normalize_decide_response(resp) |
| 107 | + |
| 108 | + flag = result["flags"]["my-flag"] |
| 109 | + self.assertEqual(flag.key, "my-flag") |
| 110 | + self.assertTrue(flag.enabled) |
| 111 | + self.assertEqual(flag.variant, "test-variant") |
| 112 | + self.assertEqual(flag.get_value(), "test-variant") |
| 113 | + self.assertEqual( |
| 114 | + flag.reason, FlagReason(code="matched_condition", condition_index=0, description="Matched condition set 1") |
| 115 | + ) |
| 116 | + self.assertEqual( |
| 117 | + flag.metadata, FlagMetadata(id=1, payload='{"some": "json"}', version=2, description="test-description") |
| 118 | + ) |
| 119 | + self.assertEqual(result["errorsWhileComputingFlags"], has_errors) |
| 120 | + self.assertEqual(result["requestId"], "test-id") |
| 121 | + |
| 122 | + def test_normalize_decide_response_legacy(self): |
| 123 | + # Test legacy response format with "featureFlags" and "featureFlagPayloads" |
| 124 | + resp = { |
| 125 | + "featureFlags": {"my-flag": "test-variant"}, |
| 126 | + "featureFlagPayloads": {"my-flag": "{\"some\": \"json-payload\"}"}, |
| 127 | + "errorsWhileComputingFlags": False, |
| 128 | + "requestId": "test-id", |
| 129 | + } |
| 130 | + |
| 131 | + result = normalize_decide_response(resp) |
| 132 | + |
| 133 | + flag = result["flags"]["my-flag"] |
| 134 | + self.assertEqual(flag.key, "my-flag") |
| 135 | + self.assertTrue(flag.enabled) |
| 136 | + self.assertEqual(flag.variant, "test-variant") |
| 137 | + self.assertEqual(flag.get_value(), "test-variant") |
| 138 | + self.assertIsNone(flag.reason) |
| 139 | + self.assertEqual( |
| 140 | + flag.metadata, LegacyFlagMetadata(payload='{"some": "json-payload"}') |
| 141 | + ) |
| 142 | + self.assertFalse(result["errorsWhileComputingFlags"]) |
| 143 | + self.assertEqual(result["requestId"], "test-id") |
| 144 | + # Verify legacy fields are removed |
| 145 | + self.assertNotIn("featureFlags", result) |
| 146 | + self.assertNotIn("featureFlagPayloads", result) |
| 147 | + |
| 148 | + def test_normalize_decide_response_boolean_flag(self): |
| 149 | + # Test legacy response with boolean flag |
| 150 | + resp = { |
| 151 | + "featureFlags": {"my-flag": True}, |
| 152 | + "errorsWhileComputingFlags": False |
| 153 | + } |
| 154 | + |
| 155 | + result = normalize_decide_response(resp) |
| 156 | + |
| 157 | + self.assertIn("requestId", result) |
| 158 | + self.assertIsNone(result["requestId"]) |
| 159 | + |
| 160 | + flag = result["flags"]["my-flag"] |
| 161 | + self.assertEqual(flag.key, "my-flag") |
| 162 | + self.assertTrue(flag.enabled) |
| 163 | + self.assertIsNone(flag.variant) |
| 164 | + self.assertIsNone(flag.reason) |
| 165 | + self.assertEqual( |
| 166 | + flag.metadata, LegacyFlagMetadata(payload=None) |
| 167 | + ) |
| 168 | + self.assertFalse(result["errorsWhileComputingFlags"]) |
| 169 | + self.assertNotIn("featureFlags", result) |
| 170 | + self.assertNotIn("featureFlagPayloads", result) |
| 171 | + |
78 | 172 |
|
79 | 173 | @pytest.mark.parametrize( |
80 | 174 | "host, expected", |
|
0 commit comments