Skip to content

Commit 2eee115

Browse files
runtime rule NO MATCH ❌
1 parent 7a86004 commit 2eee115

File tree

2 files changed

+59
-23
lines changed

2 files changed

+59
-23
lines changed

‎mixpanel/flags/test_local_feature_flags.py‎

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,16 @@
99
from .types import LocalFlagsConfig, ExperimentationFlag, RuleSet, Variant, Rollout, FlagTestUsers, ExperimentationFlags, VariantOverride, SelectedVariant
1010
from .local_feature_flags import LocalFeatureFlagsProvider
1111

12+
TEST_FLAG_KEY = "test_flag"
1213

1314
def create_test_flag(
14-
flag_key: str = "test_flag",
15+
flag_key: str = TEST_FLAG_KEY,
1516
context: str = "distinct_id",
1617
variants: Optional[list[Variant]] = None,
1718
variant_override: Optional[VariantOverride] = None,
1819
rollout_percentage: float = 100.0,
19-
runtime_evaluation: Optional[Dict] = None,
20+
runtime_evaluation_legacy_definition: Optional[Dict] = None,
21+
runtime_evaluation_rule: Optional[Dict] = None,
2022
test_users: Optional[Dict[str, str]] = None,
2123
experiment_id: Optional[str] = None,
2224
is_experiment_active: Optional[bool] = None,
@@ -30,7 +32,8 @@ def create_test_flag(
3032

3133
rollouts = [Rollout(
3234
rollout_percentage=rollout_percentage,
33-
runtime_evaluation_definition=runtime_evaluation,
35+
runtime_evaluation_definition=runtime_evaluation_legacy_definition,
36+
runtime_evaluation_rule=runtime_evaluation_rule,
3437
variant_override=variant_override,
3538
variant_splits=variant_splits
3639
)]
@@ -127,14 +130,14 @@ async def test_get_variant_value_returns_fallback_when_flag_does_not_exist(self)
127130
async def test_get_variant_value_returns_fallback_when_no_context(self):
128131
flag = create_test_flag(context="distinct_id")
129132
await self.setup_flags([flag])
130-
result = self._flags.get_variant_value("test_flag", "fallback", {})
133+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", {})
131134
assert result == "fallback"
132135

133136
@respx.mock
134137
async def test_get_variant_value_returns_fallback_when_wrong_context_key(self):
135138
flag = create_test_flag(context="user_id")
136139
await self.setup_flags([flag])
137-
result = self._flags.get_variant_value("test_flag", "fallback", {"distinct_id": "user123"})
140+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", {"distinct_id": "user123"})
138141
assert result == "fallback"
139142

140143
@respx.mock
@@ -149,7 +152,7 @@ async def test_get_variant_value_returns_test_user_variant_when_configured(self)
149152
)
150153

151154
await self.setup_flags([flag])
152-
result = self._flags.get_variant_value("test_flag", "control", {"distinct_id": "test_user"})
155+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "control", {"distinct_id": "test_user"})
153156
assert result == "true"
154157

155158
@respx.mock
@@ -165,27 +168,59 @@ async def test_get_variant_value_returns_fallback_when_test_user_variant_not_con
165168
await self.setup_flags([flag])
166169
with patch('mixpanel.flags.utils.normalized_hash') as mock_hash:
167170
mock_hash.return_value = 0.5
168-
result = self._flags.get_variant_value("test_flag", "fallback", {"distinct_id": "test_user"})
171+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", {"distinct_id": "test_user"})
169172
assert result == "false"
170173

171174
@respx.mock
172175
async def test_get_variant_value_returns_fallback_when_rollout_percentage_zero(self):
173176
flag = create_test_flag(rollout_percentage=0.0)
174177
await self.setup_flags([flag])
175-
result = self._flags.get_variant_value("test_flag", "fallback", {"distinct_id": "user123"})
178+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", {"distinct_id": "user123"})
176179
assert result == "fallback"
177180

178181
@respx.mock
179182
async def test_get_variant_value_returns_variant_when_rollout_percentage_hundred(self):
180183
flag = create_test_flag(rollout_percentage=100.0)
181184
await self.setup_flags([flag])
182-
result = self._flags.get_variant_value("test_flag", "fallback", {"distinct_id": "user123"})
185+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", {"distinct_id": "user123"})
183186
assert result != "fallback"
184187

188+
# TODO Joshua start here
189+
# TODO problem test doesn't fail
190+
@respx.mock
191+
async def test_get_variant_value_respects_runtime_evaluation_rule_satisfied(self):
192+
runtime_eval = {"oops": "sorry"}
193+
flag = create_test_flag(runtime_evaluation_rule=runtime_eval)
194+
await self.setup_flags([flag])
195+
context = {
196+
"distinct_id": "user123",
197+
"custom_properties": {
198+
"plan": "premium",
199+
"region": "US"
200+
}
201+
}
202+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", context)
203+
assert result != "fallback"
204+
205+
@respx.mock
206+
async def test_get_variant_value_respects_runtime_evaluation_rule_not_satisfied(self):
207+
runtime_eval = {"oops": "sorry"}
208+
flag = create_test_flag(runtime_evaluation_rule=runtime_eval)
209+
await self.setup_flags([flag])
210+
context = {
211+
"distinct_id": "user123",
212+
"custom_properties": {
213+
"plan": "premium",
214+
"region": "US"
215+
}
216+
}
217+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", context)
218+
assert result == "fallback"
219+
185220
@respx.mock
186221
async def test_get_variant_value_respects_runtime_evaluation_satisfied(self):
187222
runtime_eval = {"plan": "premium", "region": "US"}
188-
flag = create_test_flag(runtime_evaluation=runtime_eval)
223+
flag = create_test_flag(runtime_evaluation_legacy_definition=runtime_eval)
189224
await self.setup_flags([flag])
190225
context = {
191226
"distinct_id": "user123",
@@ -194,13 +229,13 @@ async def test_get_variant_value_respects_runtime_evaluation_satisfied(self):
194229
"region": "US"
195230
}
196231
}
197-
result = self._flags.get_variant_value("test_flag", "fallback", context)
232+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", context)
198233
assert result != "fallback"
199234

200235
@respx.mock
201236
async def test_get_variant_value_returns_fallback_when_runtime_evaluation_not_satisfied(self):
202237
runtime_eval = {"plan": "premium", "region": "US"}
203-
flag = create_test_flag(runtime_evaluation=runtime_eval)
238+
flag = create_test_flag(runtime_evaluation_legacy_definition=runtime_eval)
204239
await self.setup_flags([flag])
205240
context = {
206241
"distinct_id": "user123",
@@ -209,7 +244,7 @@ async def test_get_variant_value_returns_fallback_when_runtime_evaluation_not_sa
209244
"region": "US"
210245
}
211246
}
212-
result = self._flags.get_variant_value("test_flag", "fallback", context)
247+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", context)
213248
assert result == "fallback"
214249

215250
@respx.mock
@@ -221,7 +256,7 @@ async def test_get_variant_value_picks_correct_variant_with_hundred_percent_spli
221256
]
222257
flag = create_test_flag(variants=variants, rollout_percentage=100.0)
223258
await self.setup_flags([flag])
224-
result = self._flags.get_variant_value("test_flag", "fallback", {"distinct_id": "user123"})
259+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", {"distinct_id": "user123"})
225260
assert result == "variant_a"
226261

227262
@respx.mock
@@ -234,7 +269,7 @@ async def test_get_variant_value_picks_correct_variant_with_half_migrated_group_
234269
variant_splits = {"A": 0.0, "B": 100.0, "C": 0.0}
235270
flag = create_test_flag(variants=variants, rollout_percentage=100.0, variant_splits=variant_splits)
236271
await self.setup_flags([flag])
237-
result = self._flags.get_variant_value("test_flag", "fallback", {"distinct_id": "user123"})
272+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", {"distinct_id": "user123"})
238273
assert result == "variant_b"
239274

240275
@respx.mock
@@ -247,7 +282,7 @@ async def test_get_variant_value_picks_correct_variant_with_full_migrated_group_
247282
variant_splits = {"A": 0.0, "B": 0.0, "C": 100.0}
248283
flag = create_test_flag(variants=variants, rollout_percentage=100.0, variant_splits=variant_splits)
249284
await self.setup_flags([flag])
250-
result = self._flags.get_variant_value("test_flag", "fallback", {"distinct_id": "user123"})
285+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", {"distinct_id": "user123"})
251286
assert result == "variant_c"
252287

253288
@respx.mock
@@ -258,7 +293,7 @@ async def test_get_variant_value_picks_overriden_variant(self):
258293
]
259294
flag = create_test_flag(variants=variants, variant_override=VariantOverride(key="B"))
260295
await self.setup_flags([flag])
261-
result = self._flags.get_variant_value("test_flag", "control", {"distinct_id": "user123"})
296+
result = self._flags.get_variant_value(TEST_FLAG_KEY, "control", {"distinct_id": "user123"})
262297
assert result == "variant_b"
263298

264299
@respx.mock
@@ -267,7 +302,7 @@ async def test_get_variant_value_tracks_exposure_when_variant_selected(self):
267302
await self.setup_flags([flag])
268303
with patch('mixpanel.flags.utils.normalized_hash') as mock_hash:
269304
mock_hash.return_value = 0.5
270-
_ = self._flags.get_variant_value("test_flag", "fallback", {"distinct_id": "user123"})
305+
_ = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", {"distinct_id": "user123"})
271306
self._mock_tracker.assert_called_once()
272307

273308
@respx.mock
@@ -292,7 +327,7 @@ async def test_get_variant_value_tracks_exposure_with_correct_properties(self, e
292327

293328
with patch('mixpanel.flags.utils.normalized_hash') as mock_hash:
294329
mock_hash.return_value = 0.5
295-
_ = self._flags.get_variant_value("test_flag", "fallback", {"distinct_id": distinct_id})
330+
_ = self._flags.get_variant_value(TEST_FLAG_KEY, "fallback", {"distinct_id": distinct_id})
296331

297332
self._mock_tracker.assert_called_once()
298333

@@ -364,7 +399,7 @@ async def test_track_exposure_event_successfully_tracks(self):
364399
await self.setup_flags([flag])
365400

366401
variant = SelectedVariant(key="treatment", variant_value="treatment")
367-
self._flags.track_exposure_event("test_flag", variant, {"distinct_id": "user123"})
402+
self._flags.track_exposure_event(TEST_FLAG_KEY, variant, {"distinct_id": "user123"})
368403

369404
self._mock_tracker.assert_called_once()
370405

@@ -394,7 +429,7 @@ async def test_is_enabled_returns_true_for_true_variant_value(self):
394429
]
395430
flag = create_test_flag(variants=variants, rollout_percentage=100.0)
396431
await self.setup_flags([flag])
397-
result = self._flags.is_enabled("test_flag", {"distinct_id": "user123"})
432+
result = self._flags.is_enabled(TEST_FLAG_KEY, {"distinct_id": "user123"})
398433
assert result == True
399434

400435
@respx.mock
@@ -419,7 +454,7 @@ async def track_fetch_calls(self):
419454
async with polling_limit_check:
420455
await polling_limit_check.wait_for(lambda: polling_iterations >= len(flags_in_order))
421456

422-
result2 = self._flags_with_polling.get_variant_value("test_flag", "fallback", {"distinct_id": "user123"})
457+
result2 = self._flags_with_polling.get_variant_value(TEST_FLAG_KEY, "fallback", {"distinct_id": "user123"})
423458
assert result2 != "fallback"
424459

425460
class TestLocalFeatureFlagsProviderSync:
@@ -465,5 +500,5 @@ def track_fetch_calls(self):
465500
self.setup_flags_with_polling(flags_in_order)
466501
polling_event.wait(timeout=5.0)
467502
assert (polling_iterations >= 3 )
468-
result2 = self._flags_with_polling.get_variant_value("test_flag", "fallback", {"distinct_id": "user123"})
503+
result2 = self._flags_with_polling.get_variant_value(TEST_FLAG_KEY, "fallback", {"distinct_id": "user123"})
469504
assert result2 != "fallback"

‎mixpanel/flags/types.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class VariantOverride(BaseModel):
3131
class Rollout(BaseModel):
3232
rollout_percentage: float
3333
runtime_evaluation_definition: Optional[Dict[str, str]] = None
34+
runtime_evaluation_rule: Optional[Dict[str, str]] = None
3435
variant_override: Optional[VariantOverride] = None
3536
variant_splits: Optional[Dict[str,float]] = None
3637

0 commit comments

Comments
 (0)