@@ -856,3 +856,110 @@ def post_transition_hook(self, context: TransitionContext, state_record) -> None
856856 # Test transition execution
857857 result = transition .transition (context )
858858 assert result ['action' ] == 'email_sent'
859+
860+
861+ def test_skip_validation_flag ():
862+ """
863+ Test the skip_validation flag in TransitionContext.
864+
865+ This test validates step by step:
866+ - Creating a transition with validation logic that would normally fail
867+ - Verifying that validation runs and fails when skip_validation=False (default)
868+ - Verifying that validation is skipped when skip_validation=True
869+ - Confirming that prepare_and_validate respects the skip_validation flag
870+ - Ensuring the transition can execute successfully when validation is skipped
871+
872+ Critical validation: The skip_validation flag provides a mechanism to bypass
873+ validation checks for special cases like system migrations, data imports, or
874+ administrative operations that need to override normal business rules.
875+ """
876+
877+ class StrictValidationTransition (BaseTransition ):
878+ """Test transition with strict validation rules"""
879+
880+ action : str = Field (..., description = 'Action to perform' )
881+
882+ @property
883+ def target_state (self ) -> str :
884+ return TestStateChoices .COMPLETED
885+
886+ def validate_transition (self , context : TransitionContext ) -> bool :
887+ """Validation that only allows transition from IN_PROGRESS state"""
888+ if context .current_state != TestStateChoices .IN_PROGRESS :
889+ raise TransitionValidationError (
890+ f'Can only complete from IN_PROGRESS state, not { context .current_state } ' ,
891+ {'current_state' : context .current_state , 'target_state' : self .target_state },
892+ )
893+ return True
894+
895+ def transition (self , context : TransitionContext ) -> Dict [str , Any ]:
896+ return {'action' : self .action , 'completed' : True }
897+
898+ # Create mock entity
899+ mock_entity = MockEntity ()
900+
901+ # Test 1: Normal validation (skip_validation=False, default behavior)
902+ # This should fail because current_state is CREATED, not IN_PROGRESS
903+ transition = StrictValidationTransition (action = 'test_action' )
904+ context_with_validation = TransitionContext (
905+ entity = mock_entity ,
906+ current_state = TestStateChoices .CREATED , # Invalid state for this transition
907+ target_state = transition .target_state ,
908+ skip_validation = False , # Explicit False (same as default)
909+ )
910+
911+ # Verify that validation fails as expected
912+ with pytest .raises (TransitionValidationError ) as cm :
913+ transition .prepare_and_validate (context_with_validation )
914+
915+ error = cm .value
916+ assert 'Can only complete from IN_PROGRESS state' in str (error )
917+ assert error .context ['current_state' ] == TestStateChoices .CREATED
918+
919+ # Test 2: Skip validation (skip_validation=True)
920+ # This should succeed even though current_state is CREATED
921+ transition_skip = StrictValidationTransition (action = 'skip_validation_action' )
922+ context_skip_validation = TransitionContext (
923+ entity = mock_entity ,
924+ current_state = TestStateChoices .CREATED , # Same invalid state
925+ target_state = transition_skip .target_state ,
926+ skip_validation = True , # Validation should be skipped
927+ )
928+
929+ # This should NOT raise an error because validation is skipped
930+ result = transition_skip .prepare_and_validate (context_skip_validation )
931+
932+ # Verify the transition executed successfully
933+ assert result ['action' ] == 'skip_validation_action'
934+ assert result ['completed' ] is True
935+
936+ # Test 3: Verify default behavior (skip_validation not specified, defaults to False)
937+ transition_default = StrictValidationTransition (action = 'default_action' )
938+ context_default = TransitionContext (
939+ entity = mock_entity ,
940+ current_state = TestStateChoices .CREATED ,
941+ target_state = transition_default .target_state ,
942+ # skip_validation not specified, should default to False
943+ )
944+
945+ # Verify default is False (validation should run and fail)
946+ assert context_default .skip_validation is False
947+
948+ with pytest .raises (TransitionValidationError ) as cm :
949+ transition_default .prepare_and_validate (context_default )
950+
951+ assert 'Can only complete from IN_PROGRESS state' in str (cm .value )
952+
953+ # Test 4: Verify that with correct state and skip_validation=False, it succeeds
954+ transition_valid = StrictValidationTransition (action = 'valid_action' )
955+ context_valid = TransitionContext (
956+ entity = mock_entity ,
957+ current_state = TestStateChoices .IN_PROGRESS , # Correct state
958+ target_state = transition_valid .target_state ,
959+ skip_validation = False ,
960+ )
961+
962+ # This should succeed because state is valid
963+ result_valid = transition_valid .prepare_and_validate (context_valid )
964+ assert result_valid ['action' ] == 'valid_action'
965+ assert result_valid ['completed' ] is True
0 commit comments