@@ -503,3 +503,134 @@ def __bool__(self):
503503
504504 machine .produce ()
505505 assert model .state == "producing"
506+
507+
508+ class TestEnabledEvents :
509+ def test_no_conditions_same_as_allowed_events (self , campaign_machine ):
510+ """Without conditions, enabled_events should match allowed_events."""
511+ sm = campaign_machine ()
512+ assert [e .id for e in sm .enabled_events ()] == [e .id for e in sm .allowed_events ]
513+
514+ def test_passing_condition_returns_event (self ):
515+ class MyMachine (StateMachine ):
516+ s0 = State (initial = True )
517+ s1 = State (final = True )
518+
519+ go = s0 .to (s1 , cond = "is_ready" )
520+
521+ def is_ready (self ):
522+ return True
523+
524+ sm = MyMachine ()
525+ assert [e .id for e in sm .enabled_events ()] == ["go" ]
526+
527+ def test_failing_condition_excludes_event (self ):
528+ class MyMachine (StateMachine ):
529+ s0 = State (initial = True )
530+ s1 = State (final = True )
531+
532+ go = s0 .to (s1 , cond = "is_ready" )
533+
534+ def is_ready (self ):
535+ return False
536+
537+ sm = MyMachine ()
538+ assert sm .enabled_events () == []
539+
540+ def test_multiple_transitions_one_passes (self ):
541+ """Same event with multiple transitions: included if at least one passes."""
542+
543+ class MyMachine (StateMachine ):
544+ s0 = State (initial = True )
545+ s1 = State ()
546+ s2 = State (final = True )
547+
548+ go = s0 .to (s1 , cond = "cond_false" ) | s0 .to (s2 , cond = "cond_true" )
549+
550+ def cond_false (self ):
551+ return False
552+
553+ def cond_true (self ):
554+ return True
555+
556+ sm = MyMachine ()
557+ assert [e .id for e in sm .enabled_events ()] == ["go" ]
558+
559+ def test_final_state_returns_empty (self , campaign_machine ):
560+ sm = campaign_machine ()
561+ sm .produce ()
562+ sm .deliver ()
563+ assert sm .enabled_events () == []
564+
565+ def test_kwargs_forwarded_to_conditions (self ):
566+ class MyMachine (StateMachine ):
567+ s0 = State (initial = True )
568+ s1 = State (final = True )
569+
570+ go = s0 .to (s1 , cond = "check_value" )
571+
572+ def check_value (self , value = 0 ):
573+ return value > 10
574+
575+ sm = MyMachine ()
576+ assert sm .enabled_events () == []
577+ assert [e .id for e in sm .enabled_events (value = 20 )] == ["go" ]
578+
579+ def test_condition_exception_treated_as_enabled (self ):
580+ """If a condition raises, the event is treated as enabled (permissive)."""
581+
582+ class MyMachine (StateMachine ):
583+ s0 = State (initial = True )
584+ s1 = State (final = True )
585+
586+ go = s0 .to (s1 , cond = "bad_cond" )
587+
588+ def bad_cond (self ):
589+ raise RuntimeError ("boom" )
590+
591+ sm = MyMachine ()
592+ assert [e .id for e in sm .enabled_events ()] == ["go" ]
593+
594+ def test_mixed_enabled_and_disabled (self ):
595+ class MyMachine (StateMachine ):
596+ s0 = State (initial = True )
597+ s1 = State ()
598+ s2 = State (final = True )
599+
600+ go = s0 .to (s1 , cond = "cond_true" )
601+ stop = s0 .to (s2 , cond = "cond_false" )
602+
603+ def cond_true (self ):
604+ return True
605+
606+ def cond_false (self ):
607+ return False
608+
609+ sm = MyMachine ()
610+ assert [e .id for e in sm .enabled_events ()] == ["go" ]
611+
612+ def test_unless_condition (self ):
613+ class MyMachine (StateMachine ):
614+ s0 = State (initial = True )
615+ s1 = State (final = True )
616+
617+ go = s0 .to (s1 , unless = "is_blocked" )
618+
619+ def is_blocked (self ):
620+ return True
621+
622+ sm = MyMachine ()
623+ assert sm .enabled_events () == []
624+
625+ def test_unless_condition_passes (self ):
626+ class MyMachine (StateMachine ):
627+ s0 = State (initial = True )
628+ s1 = State (final = True )
629+
630+ go = s0 .to (s1 , unless = "is_blocked" )
631+
632+ def is_blocked (self ):
633+ return False
634+
635+ sm = MyMachine ()
636+ assert [e .id for e in sm .enabled_events ()] == ["go" ]
0 commit comments