@@ -764,6 +764,75 @@ class InvalidAfterRollbackOperation < MyOperation
764764 . with_message ( 'must provide either a step or a block but not both' )
765765 end
766766 end
767+
768+ context 'when nesting operations with rollback callbacks' do
769+ class InnerFailingOperation < MyOperation
770+ context :notifier
771+
772+ process do
773+ transaction do
774+ after_rollback :notify_inner_rollback
775+ step :fail_step
776+ end
777+ step :after_transaction_step
778+ end
779+
780+ def fail_step ( state )
781+ @notifier . inner_fail_step
782+ error ( :inner_operation_failed )
783+ end
784+
785+ def notify_inner_rollback ( state ) = @notifier . inner_rollback
786+ def after_transaction_step ( state ) = @notifier . inner_after_transaction
787+ end
788+
789+ class OuterOperationWithRollback < MyOperation
790+ context :notifier
791+
792+ process do
793+ transaction do
794+ after_rollback :notify_outer_rollback
795+ step :call_inner_operation
796+ step :after_inner_call_step
797+ step :fail_again
798+ end
799+ step :final_step
800+ end
801+
802+ def call_inner_operation ( state )
803+ state [ :inner_result ] = InnerFailingOperation . call ( { notifier : @notifier } , state [ :input ] )
804+ state
805+ end
806+
807+ def fail_again ( state )
808+ @notifier . outter_fail_step
809+
810+ error ( :outter_operation_failed ) if state [ :inner_result ] . failure?
811+ end
812+
813+ def notify_outer_rollback ( state ) = @notifier . outer_rollback
814+ def after_inner_call_step ( state ) = @notifier . after_inner_call_step
815+ def final_step ( state ) = @notifier . final_step
816+ end
817+
818+ let ( :notifier ) { spy }
819+ let ( :operation ) { OuterOperationWithRollback . new ( notifier : notifier ) }
820+
821+ it 'executes rollback callbacks in the correct order when inner operation fails' do
822+ expect ( notifier ) . to receive ( :inner_fail_step )
823+ expect ( notifier ) . to receive ( :inner_rollback )
824+ expect ( notifier ) . to receive ( :after_inner_call_step )
825+ expect ( notifier ) . to receive ( :outter_fail_step )
826+ expect ( notifier ) . to receive ( :outer_rollback )
827+
828+ # Verify calls that should NOT happen
829+ expect ( notifier ) . to_not receive ( :inner_after_transaction )
830+ expect ( notifier ) . to_not receive ( :final_step )
831+
832+ expect ( operation ) . to fail_on ( zzz : :XXXXXXXXXXXXX )
833+ . with_type ( :outter_operation_failed )
834+ end
835+ end
767836 end
768837 end
769838
0 commit comments