Skip to content

Commit 0d6e3ad

Browse files
committed
[FSSDK-12369] Add mandatory forced-decision-beats-local-holdout enforcement test
1 parent 65e390c commit 0d6e3ad

1 file changed

Lines changed: 42 additions & 0 deletions

File tree

tests/test_decision_service_holdout.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,6 +1629,48 @@ def test_global_holdout_takes_precedence_over_local_holdout(self):
16291629
# get_holdouts_for_rule should NOT be called — global holdout short-circuited
16301630
mock_local.assert_not_called()
16311631

1632+
def test_forced_decision_beats_100_percent_local_holdout(self):
1633+
"""Forced decision MUST win over a 100% traffic local holdout targeting the same rule.
1634+
1635+
MANDATORY ENFORCEMENT TEST (cross-sdk guideline):
1636+
Setup: User has a forced decision set for rule 'test_experiment'. A local holdout
1637+
targets rule '111127' (same rule) with 100% traffic allocation.
1638+
Assert: The forced decision variation is returned — NOT the holdout variation.
1639+
If this test fails, the per-rule ordering (forced → local holdout → regular) is wrong.
1640+
"""
1641+
from optimizely.optimizely_user_context import OptimizelyUserContext
1642+
1643+
experiment_rule_id = '111127' # id of 'test_experiment'
1644+
forced_variation_key = 'control' # a valid variation key in test_experiment
1645+
1646+
opt = self._make_opt([
1647+
_holdout('lh_full', 'local_full_traffic', included_rules=[experiment_rule_id], traffic=_FULL_TRAFFIC)
1648+
])
1649+
config = opt.config_manager.get_config()
1650+
feature_flag = config.get_feature_from_key('test_feature_in_experiment')
1651+
self.assertIsNotNone(feature_flag)
1652+
1653+
ds = self._decision_svc()
1654+
user_ctx = opt.create_user_context('forced_user', {})
1655+
1656+
# Set forced decision for 'test_feature_in_experiment' with rule 'test_experiment'
1657+
decision_context = OptimizelyUserContext.OptimizelyDecisionContext(
1658+
flag_key='test_feature_in_experiment',
1659+
rule_key='test_experiment'
1660+
)
1661+
forced_decision = OptimizelyUserContext.OptimizelyForcedDecision(
1662+
variation_key=forced_variation_key
1663+
)
1664+
user_ctx.set_forced_decision(decision_context, forced_decision)
1665+
1666+
result = ds.get_decision_for_flag(feature_flag, user_ctx, config)
1667+
1668+
decision = result['decision']
1669+
self.assertIsNotNone(decision)
1670+
# The forced decision variation must be returned, NOT a holdout variation
1671+
self.assertEqual(decision.variation.key, forced_variation_key)
1672+
self.assertNotEqual(decision.source, enums.DecisionSources.HOLDOUT)
1673+
16321674
def test_no_holdouts_at_all_falls_through_to_experiment(self):
16331675
"""When there are no holdouts, decision falls through to experiment evaluation."""
16341676
opt = self._make_opt([])

0 commit comments

Comments
 (0)