adomokos / light-service

Series of Actions with an emphasis on simplicity.
MIT License
837 stars 67 forks source link

ContextFactory fully executes an Organizer when used with callback action #138

Closed jeremy-hanna closed 6 years ago

jeremy-hanna commented 6 years ago

Summary:

Trying to use a ContextFactory for testing an Organizer with a with_callback (and possibly other methods) will lead to full execution of the Organizer to build the returned context.

Failure recreation:

# create a new organizer Callback and Action
module TestDoubles
  class CallbackOrganizer
    extend LightService::Organizer

    def self.call(number)
      with(:number => number).reduce(actions)
    end

    def self.actions
      [
        AddsOneAction,
        with_callback(CallbackAction, [
          AddsTwoAction,
          AddsThreeAction
        ])
      ]
    end
  end

  class CallbackAction
    extend LightService::Action
    expects :number, :callback

    executed do |context|
      context.number += 10
      context.number =
        context.callback.call(context).fetch(:number)
    end
  end
end

RSpec.describe TestDoubles::AddsTwoAction do
  it "does not execute a callback entirely" do
    context = LightService::Testing::ContextFactory
                      .make_from(TestDoubles::CallbackOrganizer)
                      .for(described_class)
                      .with(:number => 0)

    # add 1, add 10, then stop before executing first add 2
    expect(context.number).to eq(11)
  end
end

# => Failures:

  1) TestDoubles::AddsTwoAction does not execute a callback entirely from a ContextFactory
     Failure/Error: expect(context.number).to eq(11)

       expected: 11
            got: 16

Technicals:

What is going on is the ContextFactory enumerates through the duplicated organizer.actions, however when used with a with_callback(FooAction, [ SomeStepAction ]) the callback action and subsequent steps get flattened into a lazily executed Proc here.

Therefore a ContextFactoryOrganizer.organizer .actions will never have the action constant in the array to match on.

Proposed Solution: The ContextFactoryOrganizer is going to have to get smarter and build out an Action execution path tree for these steps and wrap them in a new organizer that halts at the expected Action

adomokos commented 6 years ago

Thank you for filing this issue. When I pushed logic from Orchestrators to Organizers, I don't think I tested all the different components, like ContextFactory. It works with the original Organizer methods, but might not work with the new ones.

I'll look into it or PRs to fix issues are happily accepted!

jeremy-hanna commented 6 years ago

https://github.com/adomokos/light-service/pull/141 resolves this issue