@@ -342,3 +342,43 @@ def test_all_flags_returns_empty_state_if_feature_store_throws_error(caplog):
342342 assert state .valid is False
343343 errlog = get_log_lines (caplog , 'ERROR' )
344344 assert errlog == ['Unable to read flags for all_flag_state: NotImplementedError()' ]
345+
346+
347+ def test_all_flags_state_degrades_per_flag_on_evaluator_error ():
348+ # A flag whose evaluation raises degrades only that flag: the loop must not
349+ # raise UnboundLocalError when the first flag raises, and a failed flag must
350+ # not inherit a previous good flag's prerequisites.
351+ from unittest .mock import MagicMock
352+
353+ store = InMemoryFeatureStore ()
354+ # Ordering matters: 'bad-first' raises on the first iteration (would
355+ # UnboundLocalError if result were read unconditionally); 'bad-last' raises
356+ # after a good flag set result (would reuse the good result's prerequisites).
357+ store .init ({FEATURES : {
358+ 'bad-first' : {'key' : 'bad-first' , 'version' : 1 , 'on' : True , 'fallthrough' : {'variation' : 0 }, 'variations' : ['x' ]},
359+ 'good' : {'key' : 'good' , 'version' : 1 , 'on' : True , 'fallthrough' : {'variation' : 0 }, 'variations' : ['y' ]},
360+ 'bad-last' : {'key' : 'bad-last' , 'version' : 1 , 'on' : True , 'fallthrough' : {'variation' : 0 }, 'variations' : ['z' ]},
361+ }})
362+ client = make_client (store )
363+
364+ good_result = MagicMock ()
365+ good_result .detail = EvaluationDetail ('y' , 0 , {'kind' : 'FALLTHROUGH' })
366+ good_result .prerequisites = ['prereq-of-good' ]
367+
368+ def fake_evaluate (flag , context , event_factory ):
369+ if flag ['key' ] == 'good' :
370+ return good_result
371+ raise RuntimeError ("boom" )
372+
373+ client ._evaluator .evaluate = MagicMock (side_effect = fake_evaluate )
374+
375+ # This must not raise UnboundLocalError.
376+ state = client .all_flags_state (user )
377+ assert state .valid
378+
379+ metadata = state .to_json_dict ()['$flagsState' ]
380+ # The good flag carries its own prerequisites; the failed flags carry none
381+ # (not a neighbor's).
382+ assert metadata ['good' ].get ('prerequisites' ) == ['prereq-of-good' ]
383+ assert 'prerequisites' not in metadata ['bad-first' ]
384+ assert 'prerequisites' not in metadata ['bad-last' ]
0 commit comments