@@ -1866,7 +1866,8 @@ def test_global_holdout_exclude_td_true_blocks_ab_experiment(self):
18661866
18671867 def test_global_holdout_exclude_td_true_no_td_returns_non_holdout_decision (self ):
18681868 """When exclude_targeted_deliveries=True but no TD experiments match,
1869- returns a non-holdout decision with the bypassed holdout attached."""
1869+ returns a non-holdout decision with the bypassed holdout attached
1870+ and includes the rollout evaluation reason."""
18701871 opt = self ._make_opt_with_td (
18711872 [_holdout_with_etd ('gh1' , 'global_exclude_td' , exclude_targeted_deliveries = True )],
18721873 )
@@ -1881,6 +1882,12 @@ def test_global_holdout_exclude_td_true_no_td_returns_non_holdout_decision(self)
18811882 self .assertNotEqual (decision .source , enums .DecisionSources .HOLDOUT )
18821883 self .assertIsNotNone (result .get ('holdout_decision' ))
18831884
1885+ expected_reason = (
1886+ "Holdout 'global_exclude_td' has excludeTargetedDeliveries enabled, "
1887+ "continuing to rollout evaluation."
1888+ )
1889+ self .assertIn (expected_reason , result ['reasons' ])
1890+
18841891 # ------------------------------------------------------------------
18851892 # Test 5: Local holdout on delivery rule with exclude_targeted_deliveries=True
18861893 # ------------------------------------------------------------------
@@ -2019,7 +2026,8 @@ def test_global_holdout_exclude_td_true_td_match_has_holdout_decision(self):
20192026
20202027 def test_global_holdout_exclude_td_true_no_td_has_holdout_decision (self ):
20212028 """When exclude_targeted_deliveries=True and no TD matches,
2022- result is non-holdout but holdout_decision key is present."""
2029+ result is non-holdout but holdout_decision key is present,
2030+ and reasons include the rollout evaluation continuation message."""
20232031 opt = self ._make_opt_with_td (
20242032 [_holdout_with_etd ('gh1' , 'global_exclude_td' , exclude_targeted_deliveries = True )],
20252033 )
@@ -2036,18 +2044,38 @@ def test_global_holdout_exclude_td_true_no_td_has_holdout_decision(self):
20362044 self .assertIsNotNone (holdout_dec )
20372045 self .assertEqual (holdout_dec .source , enums .DecisionSources .HOLDOUT )
20382046
2047+ expected_reason = (
2048+ "Holdout 'global_exclude_td' has excludeTargetedDeliveries enabled, "
2049+ "continuing to rollout evaluation."
2050+ )
2051+ self .assertIn (expected_reason , result ['reasons' ])
2052+
20392053 # ------------------------------------------------------------------
20402054 # Test 11: Holdout impression event dispatched for bypassed holdout
20412055 # ------------------------------------------------------------------
20422056
20432057 def test_holdout_impression_sent_when_td_evaluated (self ):
20442058 """When exclude_targeted_deliveries=True and TD matches,
2045- two impression events are sent: one for holdout, one for TD."""
2059+ two impression events are sent: one for holdout, one for TD,
2060+ and decision_event_dispatched is True in notification."""
20462061 opt = self ._make_opt_with_td (
20472062 [_holdout_with_etd ('gh1' , 'global_exclude_td' , exclude_targeted_deliveries = True )],
20482063 experiment_type = 'td' ,
20492064 )
2050- with mock .patch .object (opt , '_send_impression_event' ) as mock_send :
2065+
2066+ captured_notifications : list [dict [str , object ]] = []
2067+
2068+ def capture_notification (notification_type : str , user_id : str ,
2069+ user_attributes : dict [str , object ],
2070+ decision_info : dict [str , object ]) -> None :
2071+ captured_notifications .append (decision_info .copy ())
2072+
2073+ opt .notification_center .add_notification_listener (
2074+ enums .NotificationTypes .DECISION ,
2075+ capture_notification
2076+ )
2077+
2078+ with mock .patch .object (opt , '_send_impression_event' , wraps = opt ._send_impression_event ) as mock_send :
20512079 user_ctx = opt .create_user_context ('testUserId' , {})
20522080 user_ctx .decide ('test_feature_in_experiment' )
20532081
@@ -2056,3 +2084,9 @@ def test_holdout_impression_sent_when_td_evaluated(self):
20562084 for call in mock_send .call_args_list ]
20572085 self .assertIn (str (enums .DecisionSources .HOLDOUT ), call_rule_types )
20582086 self .assertIn (str (enums .DecisionSources .FEATURE_TEST ), call_rule_types )
2087+
2088+ self .assertEqual (len (captured_notifications ), 1 )
2089+ self .assertTrue (
2090+ captured_notifications [0 ].get ('decision_event_dispatched' ),
2091+ 'decision_event_dispatched should be True when holdout impression is sent'
2092+ )
0 commit comments