describe "Integration" do
  def build_interactor(&block)
    interactor = Class.new.send(:include, Interactor)
    interactor.class_eval(&block) if block
    interactor
  end

  def build_organizer(options = {}, &block)
    organizer = Class.new.send(:include, Interactor::Organizer)
    organizer.organize(options[:organize]) if options[:organize]
    organizer.class_eval(&block) if block
    organizer
  end

  # organizer
  #  ├─ organizer2
  #  │   ├─ interactor2a
  #  │   ├─ interactor2b
  #  │   └─ interactor2c
  #  ├─ interactor3
  #  ├─ organizer4
  #  │   ├─ interactor4a
  #  │   ├─ interactor4b
  #  │   └─ interactor4c
  #  └─ interactor5

  let(:organizer) {
    build_organizer(organize: [organizer2, interactor3, organizer4, interactor5]) do
      around do |interactor|
        context.steps << :around_before
        interactor.call
        context.steps << :around_after
      end

      before do
        context.steps << :before
      end

      after do
        context.steps << :after
      end
    end
  }

  let(:organizer2) {
    build_organizer(organize: [interactor2a, interactor2b, interactor2c]) do
      around do |interactor|
        context.steps << :around_before2
        interactor.call
        context.steps << :around_after2
      end

      before do
        context.steps << :before2
      end

      after do
        context.steps << :after2
      end
    end
  }

  let(:interactor2a) {
    build_interactor do
      around do |interactor|
        context.steps << :around_before2a
        interactor.call
        context.steps << :around_after2a
      end

      before do
        context.steps << :before2a
      end

      after do
        context.steps << :after2a
      end

      def call
        context.steps << :call2a
      end

      def rollback
        context.steps << :rollback2a
      end
    end
  }

  let(:interactor2b) {
    build_interactor do
      around do |interactor|
        context.steps << :around_before2b
        interactor.call
        context.steps << :around_after2b
      end

      before do
        context.steps << :before2b
      end

      after do
        context.steps << :after2b
      end

      def call
        context.steps << :call2b
      end

      def rollback
        context.steps << :rollback2b
      end
    end
  }

  let(:interactor2c) {
    build_interactor do
      around do |interactor|
        context.steps << :around_before2c
        interactor.call
        context.steps << :around_after2c
      end

      before do
        context.steps << :before2c
      end

      after do
        context.steps << :after2c
      end

      def call
        context.steps << :call2c
      end

      def rollback
        context.steps << :rollback2c
      end
    end
  }

  let(:interactor3) {
    build_interactor do
      around do |interactor|
        context.steps << :around_before3
        interactor.call
        context.steps << :around_after3
      end

      before do
        context.steps << :before3
      end

      after do
        context.steps << :after3
      end

      def call
        context.steps << :call3
      end

      def rollback
        context.steps << :rollback3
      end
    end
  }

  let(:organizer4) {
    build_organizer(organize: [interactor4a, interactor4b, interactor4c]) do
      around do |interactor|
        context.steps << :around_before4
        interactor.call
        context.steps << :around_after4
      end

      before do
        context.steps << :before4
      end

      after do
        context.steps << :after4
      end
    end
  }

  let(:interactor4a) {
    build_interactor do
      around do |interactor|
        context.steps << :around_before4a
        interactor.call
        context.steps << :around_after4a
      end

      before do
        context.steps << :before4a
      end

      after do
        context.steps << :after4a
      end

      def call
        context.steps << :call4a
      end

      def rollback
        context.steps << :rollback4a
      end
    end
  }

  let(:interactor4b) {
    build_interactor do
      around do |interactor|
        context.steps << :around_before4b
        interactor.call
        context.steps << :around_after4b
      end

      before do
        context.steps << :before4b
      end

      after do
        context.steps << :after4b
      end

      def call
        context.steps << :call4b
      end

      def rollback
        context.steps << :rollback4b
      end
    end
  }

  let(:interactor4c) {
    build_interactor do
      around do |interactor|
        context.steps << :around_before4c
        interactor.call
        context.steps << :around_after4c
      end

      before do
        context.steps << :before4c
      end

      after do
        context.steps << :after4c
      end

      def call
        context.steps << :call4c
      end

      def rollback
        context.steps << :rollback4c
      end
    end
  }

  let(:interactor5) {
    build_interactor do
      around do |interactor|
        context.steps << :around_before5
        interactor.call
        context.steps << :around_after5
      end

      before do
        context.steps << :before5
      end

      after do
        context.steps << :after5
      end

      def call
        context.steps << :call5
      end

      def rollback
        context.steps << :rollback5
      end
    end
  }

  let(:context) { Interactor::Context.new(steps: []) }

  context "when successful" do
    it "calls and runs hooks in the proper sequence" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :around_before4b, :before4b, :call4b, :after4b, :around_after4b,
        :around_before4c, :before4c, :call4c, :after4c, :around_after4c,
        :after4, :around_after4,
        :around_before5, :before5, :call5, :after5, :around_after5,
        :after, :around_after
      ])
    end
  end

  context "when an around hook fails early" do
    let(:organizer) {
      build_organizer(organize: [organizer2, interactor3, organizer4, interactor5]) do
        around do |interactor|
          context.fail!
          context.steps << :around_before
          interactor.call
          context.steps << :around_after
        end

        before do
          context.fail!
          context.steps << :before
        end

        after do
          context.steps << :after
        end
      end
    }

    it "aborts" do
      expect {
        organizer.call(context)
      }.not_to change {
        context.steps
      }
    end
  end

  context "when an around hook errors early" do
    let(:organizer) {
      build_organizer(organize: [organizer2, interactor3, organizer4, interactor5]) do
        around do |interactor|
          raise "foo"
        end

        before do
          context.fail!
          context.steps << :before
        end

        after do
          context.steps << :after
        end
      end
    }

    it "aborts" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.not_to change {
        context.steps
      }
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end

  context "when a before hook fails" do
    let(:organizer) {
      build_organizer(organize: [organizer2, interactor3, organizer4, interactor5]) do
        around do |interactor|
          context.steps << :around_before
          interactor.call
          context.steps << :around_after
        end

        before do
          context.fail!
          context.steps << :before
        end

        after do
          context.steps << :after
        end
      end
    }

    it "aborts" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before
      ])
    end
  end

  context "when a before hook errors" do
    let(:organizer) {
      build_organizer(organize: [organizer2, interactor3, organizer4, interactor5]) do
        around do |interactor|
          context.steps << :around_before
          interactor.call
          context.steps << :around_after
        end

        before do
          raise "foo"
        end

        after do
          context.steps << :after
        end
      end
    }

    it "aborts" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.to change {
        context.steps
      }.from([]).to([
        :around_before
      ])
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end

  context "when an after hook fails" do
    let(:organizer) {
      build_organizer(organize: [organizer2, interactor3, organizer4, interactor5]) do
        around do |interactor|
          context.steps << :around_before
          interactor.call
          context.steps << :around_after
        end

        before do
          context.steps << :before
        end

        after do
          context.fail!
          context.steps << :after
        end
      end
    }

    it "rolls back successfully called interactors and the failed interactor" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :around_before4b, :before4b, :call4b, :after4b, :around_after4b,
        :around_before4c, :before4c, :call4c, :after4c, :around_after4c,
        :after4, :around_after4,
        :around_before5, :before5, :call5, :after5, :around_after5,
        :rollback5,
        :rollback4c,
        :rollback4b,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end
  end

  context "when an after hook errors" do
    let(:organizer) {
      build_organizer(organize: [organizer2, interactor3, organizer4, interactor5]) do
        around do |interactor|
          context.steps << :around_before
          interactor.call
          context.steps << :around_after
        end

        before do
          context.steps << :before
        end

        after do
          raise "foo"
        end
      end
    }

    it "rolls back successfully called interactors and the failed interactor" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :around_before4b, :before4b, :call4b, :after4b, :around_after4b,
        :around_before4c, :before4c, :call4c, :after4c, :around_after4c,
        :after4, :around_after4,
        :around_before5, :before5, :call5, :after5, :around_after5,
        :rollback5,
        :rollback4c,
        :rollback4b,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end

  context "when an around hook fails late" do
    let(:organizer) {
      build_organizer(organize: [organizer2, interactor3, organizer4, interactor5]) do
        around do |interactor|
          context.steps << :around_before
          interactor.call
          context.fail!
          context.steps << :around_after
        end

        before do
          context.steps << :before
        end

        after do
          context.steps << :after
        end
      end
    }

    it "rolls back successfully called interactors and the failed interactor" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :around_before4b, :before4b, :call4b, :after4b, :around_after4b,
        :around_before4c, :before4c, :call4c, :after4c, :around_after4c,
        :after4, :around_after4,
        :around_before5, :before5, :call5, :after5, :around_after5,
        :after,
        :rollback5,
        :rollback4c,
        :rollback4b,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end
  end

  context "when an around hook errors late" do
    let(:organizer) {
      build_organizer(organize: [organizer2, interactor3, organizer4, interactor5]) do
        around do |interactor|
          context.steps << :around_before
          interactor.call
          raise "foo"
        end

        before do
          context.steps << :before
        end

        after do
          context.steps << :after
        end
      end
    }

    it "rolls back successfully called interactors and the failed interactor" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :around_before4b, :before4b, :call4b, :after4b, :around_after4b,
        :around_before4c, :before4c, :call4c, :after4c, :around_after4c,
        :after4, :around_after4,
        :around_before5, :before5, :call5, :after5, :around_after5,
        :after,
        :rollback5,
        :rollback4c,
        :rollback4b,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end

  context "when a nested around hook fails early" do
    let(:interactor3) {
      build_interactor do
        around do |interactor|
          context.fail!
          context.steps << :around_before3
          interactor.call
          context.steps << :around_after3
        end

        before do
          context.steps << :before3
        end

        after do
          context.steps << :after3
        end

        def call
          context.steps << :call3
        end

        def rollback
          context.steps << :rollback3
        end
      end
    }

    it "rolls back successfully called interactors" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end
  end

  context "when a nested around hook errors early" do
    let(:interactor3) {
      build_interactor do
        around do |interactor|
          raise "foo"
        end

        before do
          context.steps << :before3
        end

        after do
          context.steps << :after3
        end

        def call
          context.steps << :call3
        end

        def rollback
          context.steps << :rollback3
        end
      end
    }

    it "rolls back successfully called interactors" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end

  context "when a nested before hook fails" do
    let(:interactor3) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before3
          interactor.call
          context.steps << :around_after3
        end

        before do
          context.fail!
          context.steps << :before3
        end

        after do
          context.steps << :after3
        end

        def call
          context.steps << :call3
        end

        def rollback
          context.steps << :rollback3
        end
      end
    }

    it "rolls back successfully called interactors" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end
  end

  context "when a nested before hook errors" do
    let(:interactor3) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before3
          interactor.call
          context.steps << :around_after3
        end

        before do
          raise "foo"
        end

        after do
          context.steps << :after3
        end

        def call
          context.steps << :call3
        end

        def rollback
          context.steps << :rollback3
        end
      end
    }

    it "rolls back successfully called interactors" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end

  context "when a nested call fails" do
    let(:interactor3) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before3
          interactor.call
          context.steps << :around_after3
        end

        before do
          context.steps << :before3
        end

        after do
          context.steps << :after3
        end

        def call
          context.fail!
          context.steps << :call3
        end

        def rollback
          context.steps << :rollback3
        end
      end
    }

    it "rolls back successfully called interactors" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end
  end

  context "when a nested call errors" do
    let(:interactor3) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before3
          interactor.call
          context.steps << :around_after3
        end

        before do
          context.steps << :before3
        end

        after do
          context.steps << :after3
        end

        def call
          raise "foo"
        end

        def rollback
          context.steps << :rollback3
        end
      end
    }

    it "rolls back successfully called interactors" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end

  context "when a nested after hook fails" do
    let(:interactor3) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before3
          interactor.call
          context.steps << :around_after3
        end

        before do
          context.steps << :before3
        end

        after do
          context.fail!
          context.steps << :after3
        end

        def call
          context.steps << :call3
        end

        def rollback
          context.steps << :rollback3
        end
      end
    }

    it "rolls back successfully called interactors and the failed interactor" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end
  end

  context "when a nested after hook errors" do
    let(:interactor3) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before3
          interactor.call
          context.steps << :around_after3
        end

        before do
          context.steps << :before3
        end

        after do
          raise "foo"
        end

        def call
          context.steps << :call3
        end

        def rollback
          context.steps << :rollback3
        end
      end
    }

    it "rolls back successfully called interactors and the failed interactor" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end

  context "when a nested around hook fails late" do
    let(:interactor3) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before3
          interactor.call
          context.fail!
          context.steps << :around_after3
        end

        before do
          context.steps << :before3
        end

        after do
          context.steps << :after3
        end

        def call
          context.steps << :call3
        end

        def rollback
          context.steps << :rollback3
        end
      end
    }

    it "rolls back successfully called interactors and the failed interactor" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end
  end

  context "when a nested around hook errors late" do
    let(:interactor3) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before3
          interactor.call
          raise "foo"
        end

        before do
          context.steps << :before3
        end

        after do
          context.steps << :after3
        end

        def call
          context.steps << :call3
        end

        def rollback
          context.steps << :rollback3
        end
      end
    }

    it "rolls back successfully called interactors and the failed interactor" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end

  context "when a deeply nested around hook fails early" do
    let(:interactor4b) {
      build_interactor do
        around do |interactor|
          context.fail!
          context.steps << :around_before4b
          interactor.call
          context.steps << :around_after4b
        end

        before do
          context.steps << :before4b
        end

        after do
          context.steps << :after4b
        end

        def call
          context.steps << :call4b
        end

        def rollback
          context.steps << :rollback4b
        end
      end
    }

    it "rolls back successfully called interactors" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end
  end

  context "when a deeply nested around hook errors early" do
    let(:interactor4b) {
      build_interactor do
        around do |interactor|
          raise "foo"
        end

        before do
          context.steps << :before4b
        end

        after do
          context.steps << :after4b
        end

        def call
          context.steps << :call4b
        end

        def rollback
          context.steps << :rollback4b
        end
      end
    }

    it "rolls back successfully called interactors" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end

  context "when a deeply nested before hook fails" do
    let(:interactor4b) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before4b
          interactor.call
          context.steps << :around_after4b
        end

        before do
          context.fail!
          context.steps << :before4b
        end

        after do
          context.steps << :after4b
        end

        def call
          context.steps << :call4b
        end

        def rollback
          context.steps << :rollback4b
        end
      end
    }

    it "rolls back successfully called interactors" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :around_before4b,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end
  end

  context "when a deeply nested before hook errors" do
    let(:interactor4b) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before4b
          interactor.call
          context.steps << :around_after4b
        end

        before do
          raise "foo"
        end

        after do
          context.steps << :after4b
        end

        def call
          context.steps << :call4b
        end

        def rollback
          context.steps << :rollback4b
        end
      end
    }

    it "rolls back successfully called interactors" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :around_before4b,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end

  context "when a deeply nested call fails" do
    let(:interactor4b) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before4b
          interactor.call
          context.steps << :around_after4b
        end

        before do
          context.steps << :before4b
        end

        after do
          context.steps << :after4b
        end

        def call
          context.fail!
          context.steps << :call4b
        end

        def rollback
          context.steps << :rollback4b
        end
      end
    }

    it "rolls back successfully called interactors" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :around_before4b, :before4b,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end
  end

  context "when a deeply nested call errors" do
    let(:interactor4b) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before4b
          interactor.call
          context.steps << :around_after4b
        end

        before do
          context.steps << :before4b
        end

        after do
          context.steps << :after4b
        end

        def call
          raise "foo"
        end

        def rollback
          context.steps << :rollback4b
        end
      end
    }

    it "rolls back successfully called interactors" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :around_before4b, :before4b,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end

  context "when a deeply nested after hook fails" do
    let(:interactor4b) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before4b
          interactor.call
          context.steps << :around_after4b
        end

        before do
          context.steps << :before4b
        end

        after do
          context.fail!
          context.steps << :after4b
        end

        def call
          context.steps << :call4b
        end

        def rollback
          context.steps << :rollback4b
        end
      end
    }

    it "rolls back successfully called interactors and the failed interactor" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :around_before4b, :before4b, :call4b,
        :rollback4b,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end
  end

  context "when a deeply nested after hook errors" do
    let(:interactor4b) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before4b
          interactor.call
          context.steps << :around_after4b
        end

        before do
          context.steps << :before4b
        end

        after do
          raise "foo"
        end

        def call
          context.steps << :call4b
        end

        def rollback
          context.steps << :rollback4b
        end
      end
    }

    it "rolls back successfully called interactors and the failed interactor" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :around_before4b, :before4b, :call4b,
        :rollback4b,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end

  context "when a deeply nested around hook fails late" do
    let(:interactor4b) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before4b
          interactor.call
          context.fail!
          context.steps << :around_after4b
        end

        before do
          context.steps << :before4b
        end

        after do
          context.steps << :after4b
        end

        def call
          context.steps << :call4b
        end

        def rollback
          context.steps << :rollback4b
        end
      end
    }

    it "rolls back successfully called interactors and the failed interactor" do
      expect {
        organizer.call(context)
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :around_before4b, :before4b, :call4b, :after4b,
        :rollback4b,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end
  end

  context "when a deeply nested around hook errors late" do
    let(:interactor4b) {
      build_interactor do
        around do |interactor|
          context.steps << :around_before4b
          interactor.call
          raise "foo"
        end

        before do
          context.steps << :before4b
        end

        after do
          context.steps << :after4b
        end

        def call
          context.steps << :call4b
        end

        def rollback
          context.steps << :rollback4b
        end
      end
    }

    it "rolls back successfully called interactors and the failed interactor" do
      expect {
        begin
          organizer.call(context)
        rescue
          nil
        end
      }.to change {
        context.steps
      }.from([]).to([
        :around_before, :before,
        :around_before2, :before2,
        :around_before2a, :before2a, :call2a, :after2a, :around_after2a,
        :around_before2b, :before2b, :call2b, :after2b, :around_after2b,
        :around_before2c, :before2c, :call2c, :after2c, :around_after2c,
        :after2, :around_after2,
        :around_before3, :before3, :call3, :after3, :around_after3,
        :around_before4, :before4,
        :around_before4a, :before4a, :call4a, :after4a, :around_after4a,
        :around_before4b, :before4b, :call4b, :after4b,
        :rollback4b,
        :rollback4a,
        :rollback3,
        :rollback2c,
        :rollback2b,
        :rollback2a
      ])
    end

    it "raises the error" do
      expect {
        organizer.call(context)
      }.to raise_error("foo")
    end
  end
end
