more tests for multiple state machines (callbacks, rest)

This commit is contained in:
Thorsten Böttger 2015-05-26 21:21:04 +12:00
parent 69997ffec8
commit d0172e9863
7 changed files with 473 additions and 176 deletions

View File

@ -0,0 +1,66 @@
module Callbacks
class GuardWithinBlockMultiple
include AASM
def initialize(options={})
@fail_event_guard = options[:fail_event_guard]
@fail_transition_guard = options[:fail_transition_guard]
@log = options[:log]
end
aasm(:left) do
state :open, :initial => true,
:before_enter => :before_enter_open,
:enter => :enter_open,
:after_enter => :after_enter_open,
:before_exit => :before_exit_open,
:exit => :exit_open,
:after_exit => :after_exit_open
state :closed,
:before_enter => :before_enter_closed,
:enter => :enter_closed,
:after_enter => :after_enter_closed,
:before_exit => :before_exit_closed,
:exit => :exit_closed,
:after_exit => :after_exit_closed
event :close, :before => :before, :after => :after, :guard => :event_guard do
transitions :to => :closed, :from => [:open], :after => :transitioning do
guard do
transition_guard
end
end
end
event :open, :before => :before, :after => :after do
transitions :to => :open, :from => :closed
end
end
def log(text)
puts text if @log
end
def before_enter_open; log('before_enter_open'); end
def enter_open; log('enter_open'); end
def before_exit_open; log('before_exit_open'); end
def after_enter_open; log('after_enter_open'); end
def exit_open; log('exit_open'); end
def after_exit_open; log('after_exit_open'); end
def before_enter_closed; log('before_enter_closed'); end
def enter_closed; log('enter_closed'); end
def before_exit_closed; log('before_exit_closed'); end
def exit_closed; log('exit_closed'); end
def after_enter_closed; log('after_enter_closed'); end
def after_exit_closed; log('after_exit_closed'); end
def event_guard; log('event_guard'); !@fail_event_guard; end
def transition_guard; log('transition_guard'); !@fail_transition_guard; end
def transitioning; log('transitioning'); end
def before; log('before'); end
def after; log('after'); end
end
end

View File

@ -0,0 +1,65 @@
module Callbacks
class MultipleTransitionsTransitionGuardMultiple
include AASM
def initialize(options={})
@fail_event_guard = options[:fail_event_guard]
@fail_transition_guard = options[:fail_transition_guard]
@log = options[:log]
end
aasm(:left) do
state :open, :initial => true,
:before_enter => :before_enter_open,
:enter => :enter_open,
:after_enter => :after_enter_open,
:before_exit => :before_exit_open,
:exit => :exit_open,
:after_exit => :after_exit_open
state :closed,
:before_enter => :before_enter_closed,
:enter => :enter_closed,
:after_enter => :after_enter_closed,
:before_exit => :before_exit_closed,
:exit => :exit_closed,
:after_exit => :after_exit_closed
state :failed
event :close, :before => :before, :after => :after, :guard => :event_guard do
transitions :to => :closed, :from => [:open], :guard => :transition_guard, :after => :transitioning
transitions :to => :failed, :from => [:open]
end
event :open, :before => :before, :after => :after do
transitions :to => :open, :from => :closed
end
end
def log(text)
puts text if @log
end
def before_enter_open; log('before_enter_open'); end
def enter_open; log('enter_open'); end
def before_exit_open; log('before_exit_open'); end
def after_enter_open; log('after_enter_open'); end
def exit_open; log('exit_open'); end
def after_exit_open; log('after_exit_open'); end
def before_enter_closed; log('before_enter_closed'); end
def enter_closed; log('enter_closed'); end
def before_exit_closed; log('before_exit_closed'); end
def exit_closed; log('exit_closed'); end
def after_enter_closed; log('after_enter_closed'); end
def after_exit_closed; log('after_exit_closed'); end
def event_guard; log('event_guard'); !@fail_event_guard; end
def transition_guard; log('transition_guard'); !@fail_transition_guard; end
def transitioning; log('transitioning'); end
def before; log('before'); end
def after; log('after'); end
end
end

View File

@ -0,0 +1,44 @@
module Callbacks
class PrivateMethodMultiple
include AASM
def initialize(options={})
@fail_event_guard = options[:fail_event_guard]
@fail_transition_guard = options[:fail_transition_guard]
@log = options[:log]
reset_data
end
def reset_data
@data = []
end
def data
@data.join(' ')
end
aasm(:left) do
state :open, :initial => true
state :closed
event :close, :after => :after_event do
transitions :to => :closed, :from => [:open]
end
event :open, :after => :after_event do
transitions :to => :open, :from => :closed
end
end
def log(text)
@data << text
puts text if @log
end
def aasm_write_state(*args); log('aasm_write_state'); true; end
private
def after_event; log('after_event'); end
end
end

View File

@ -0,0 +1,61 @@
module Callbacks
class WithArgsMultiple
include AASM
def initialize(options={})
@log = options[:log]
reset_data
end
def reset_data
@data = []
end
def data
@data.join(' ')
end
aasm(:left) do
state :open, :initial => true,
:before_enter => :before_enter_open,
:after_enter => :after_enter_open,
:before_exit => :before_exit_open,
:after_exit => :after_exit_open
state :closed,
:before_enter => :before_enter_closed,
:after_enter => :after_enter_closed,
:before_exit => :before_exit_closed,
:after_exit => :after_exit_closed
event :close, :before => :before, :after => :after do
transitions :to => :closed, :from => [:open], :after => :transition_proc
end
event :open, :before => :before, :after => :after do
transitions :to => :open, :from => :closed
end
end
def log(text)
@data << text
puts text if @log
end
def aasm_write_state(*args); log('aasm_write_state'); true; end
def before_enter_open(*args); log("before_enter_open(#{args.map(&:inspect).join(',')})"); end
def before_exit_open(*args); log("before_exit_open(#{args.map(&:inspect).join(',')})"); end
def after_enter_open(*args); log("after_enter_open(#{args.map(&:inspect).join(',')})"); end
def after_exit_open(*args); log("after_exit_open(#{args.map(&:inspect).join(',')})"); end
def before_enter_closed(*args); log("before_enter_closed(#{args.map(&:inspect).join(',')})"); end
def before_exit_closed(*args); log("before_enter_closed(#{args.map(&:inspect).join(',')})"); end
def after_enter_closed(*args); log("after_enter_closed(#{args.map(&:inspect).join(',')})"); end
def after_exit_closed(*args); log("after_exit_closed(#{args.map(&:inspect).join(',')})"); end
def before(arg1, *args); log("before(#{arg1.inspect},#{args.map(&:inspect).join(',')})"); end
def transition_proc(arg1, arg2); log("transition_proc(#{arg1.inspect},#{arg2.inspect})"); end
def after(*args); log("after(#{args.map(&:inspect).join(',')})"); end
end
end

View File

@ -0,0 +1,26 @@
module Callbacks
class WithStateArgMultiple
include AASM
aasm(:left) do
state :open, :inital => true
state :closed
state :out_to_lunch
event :close, :before => :before_method, :after => :after_method do
transitions :to => :closed, :from => [:open], :after => :transition_method
transitions :to => :out_to_lunch, :from => [:open], :after => :transition_method2
end
end
def before_method(arg); end
def after_method(arg); end
def transition_method(arg); end
def transition_method2(arg); end
end
end

View File

@ -0,0 +1,35 @@
class FooMultiple
include AASM
aasm(:left) do
state :open, :initial => true, :before_exit => :before_exit
state :closed, :before_enter => :before_enter
state :final
event :close, :success => :success_callback do
transitions :from => [:open], :to => [:closed]
end
event :null do
transitions :from => [:open], :to => [:closed, :final], :guard => :always_false
end
end
def always_false
false
end
def success_callback
end
def before_enter
end
def before_exit
end
end
class FooTwoMultiple < FooMultiple
include AASM
aasm(:left) do
state :foo
end
end

View File

@ -52,17 +52,17 @@ describe 'callbacks for the new DSL' do
}.to raise_error(AASM::InvalidTransition)
end
# it "it handles private callback methods as well" do
# show_debug_log = false
it "handles private callback methods as well" do
show_debug_log = false
# callback = Callbacks::PrivateMethod.new(:log => show_debug_log)
# callback.aasm.current_state
callback = Callbacks::PrivateMethodMultiple.new(:log => show_debug_log)
callback.aasm(:left).current_state
# # puts "------- close!"
# expect {
# callback.close!
# }.to_not raise_error
# end
# puts "------- close!"
expect {
callback.close!
}.to_not raise_error
end
context "if the transition guard fails" do
it "does not run any state callback if guard is defined inline" do
@ -90,206 +90,206 @@ describe 'callbacks for the new DSL' do
}.to raise_error(AASM::InvalidTransition)
end
# it "does not run transition_guard twice for multiple permitted transitions" do
# show_debug_log = false
# callback = Callbacks::MultipleTransitionsTransitionGuard.new(:log => show_debug_log, :fail_transition_guard => true)
# callback.aasm.current_state
it "does not run transition_guard twice for multiple permitted transitions" do
show_debug_log = false
callback = Callbacks::MultipleTransitionsTransitionGuardMultiple.new(:log => show_debug_log, :fail_transition_guard => true)
callback.aasm(:left).current_state
# unless show_debug_log
# expect(callback).to receive(:before).once.ordered
# expect(callback).to receive(:event_guard).once.ordered.and_return(true)
# expect(callback).to receive(:transition_guard).once.ordered.and_return(false)
# expect(callback).to receive(:event_guard).once.ordered.and_return(true)
# expect(callback).to receive(:before_exit_open).once.ordered
# expect(callback).to receive(:exit_open).once.ordered
# expect(callback).to receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
# expect(callback).to receive(:after_exit_open).once.ordered
# expect(callback).to receive(:after).once.ordered
unless show_debug_log
expect(callback).to receive(:before).once.ordered
expect(callback).to receive(:event_guard).once.ordered.and_return(true)
expect(callback).to receive(:transition_guard).once.ordered.and_return(false)
expect(callback).to receive(:event_guard).once.ordered.and_return(true)
expect(callback).to receive(:before_exit_open).once.ordered
expect(callback).to receive(:exit_open).once.ordered
expect(callback).to receive(:aasm_write_state).once.ordered.and_return(true) # this is when the state changes
expect(callback).to receive(:after_exit_open).once.ordered
expect(callback).to receive(:after).once.ordered
# expect(callback).to_not receive(:transitioning)
# expect(callback).to_not receive(:before_enter_closed)
# expect(callback).to_not receive(:enter_closed)
# expect(callback).to_not receive(:after_enter_closed)
# end
expect(callback).to_not receive(:transitioning)
expect(callback).to_not receive(:before_enter_closed)
expect(callback).to_not receive(:enter_closed)
expect(callback).to_not receive(:after_enter_closed)
end
# callback.close!
# expect(callback.aasm.current_state).to eql :failed
# end
callback.close!
expect(callback.aasm(:left).current_state).to eql :failed
end
# it "does not run any state callback if guard is defined with block" do
# callback = Callbacks::GuardWithinBlock.new #(:log => true, :fail_transition_guard => true)
# callback.aasm.current_state
it "does not run any state callback if guard is defined with block" do
callback = Callbacks::GuardWithinBlockMultiple.new #(:log => true, :fail_transition_guard => true)
callback.aasm(:left).current_state
# expect(callback).to receive(:before).once.ordered
# expect(callback).to receive(:event_guard).once.ordered.and_return(true)
# expect(callback).to receive(:transition_guard).once.ordered.and_return(false)
# expect(callback).to_not receive(:before_exit_open)
# expect(callback).to_not receive(:exit_open)
# expect(callback).to_not receive(:transitioning)
# expect(callback).to_not receive(:before_enter_closed)
# expect(callback).to_not receive(:enter_closed)
# expect(callback).to_not receive(:aasm_write_state)
# expect(callback).to_not receive(:after_exit_open)
# expect(callback).to_not receive(:after_enter_closed)
# expect(callback).to_not receive(:after)
expect(callback).to receive(:before).once.ordered
expect(callback).to receive(:event_guard).once.ordered.and_return(true)
expect(callback).to receive(:transition_guard).once.ordered.and_return(false)
expect(callback).to_not receive(:before_exit_open)
expect(callback).to_not receive(:exit_open)
expect(callback).to_not receive(:transitioning)
expect(callback).to_not receive(:before_enter_closed)
expect(callback).to_not receive(:enter_closed)
expect(callback).to_not receive(:aasm_write_state)
expect(callback).to_not receive(:after_exit_open)
expect(callback).to_not receive(:after_enter_closed)
expect(callback).to_not receive(:after)
# expect {
# callback.close!
# }.to raise_error(AASM::InvalidTransition)
# end
expect {
callback.close!
}.to raise_error(AASM::InvalidTransition)
end
end
# it "should properly pass arguments" do
# cb = Callbacks::WithArgs.new(:log => false)
# cb.aasm.current_state
it "should properly pass arguments" do
cb = Callbacks::WithArgsMultiple.new(:log => false)
cb.aasm(:left).current_state
# cb.reset_data
# cb.close!(:arg1, :arg2)
# expect(cb.data).to eql 'before(:arg1,:arg2) before_exit_open(:arg1,:arg2) transition_proc(:arg1,:arg2) before_enter_closed(:arg1,:arg2) aasm_write_state after_exit_open(:arg1,:arg2) after_enter_closed(:arg1,:arg2) after(:arg1,:arg2)'
# end
cb.reset_data
cb.close!(:arg1, :arg2)
expect(cb.data).to eql 'before(:arg1,:arg2) before_exit_open(:arg1,:arg2) transition_proc(:arg1,:arg2) before_enter_closed(:arg1,:arg2) aasm_write_state after_exit_open(:arg1,:arg2) after_enter_closed(:arg1,:arg2) after(:arg1,:arg2)'
end
# it "should call the callbacks given the to-state as argument" do
# cb = Callbacks::WithStateArg.new
# expect(cb).to receive(:before_method).with(:arg1).once.ordered
# expect(cb).to receive(:transition_method).never
# expect(cb).to receive(:transition_method2).with(:arg1).once.ordered
# expect(cb).to receive(:after_method).with(:arg1).once.ordered
# cb.close!(:out_to_lunch, :arg1)
it "should call the callbacks given the to-state as argument" do
cb = Callbacks::WithStateArgMultiple.new
expect(cb).to receive(:before_method).with(:arg1).once.ordered
expect(cb).to receive(:transition_method).never
expect(cb).to receive(:transition_method2).with(:arg1).once.ordered
expect(cb).to receive(:after_method).with(:arg1).once.ordered
cb.close!(:out_to_lunch, :arg1)
# cb = Callbacks::WithStateArg.new
# some_object = double('some object')
# expect(cb).to receive(:before_method).with(some_object).once.ordered
# expect(cb).to receive(:transition_method2).with(some_object).once.ordered
# expect(cb).to receive(:after_method).with(some_object).once.ordered
# cb.close!(:out_to_lunch, some_object)
# end
cb = Callbacks::WithStateArgMultiple.new
some_object = double('some object')
expect(cb).to receive(:before_method).with(some_object).once.ordered
expect(cb).to receive(:transition_method2).with(some_object).once.ordered
expect(cb).to receive(:after_method).with(some_object).once.ordered
cb.close!(:out_to_lunch, some_object)
end
# it "should call the proper methods just with arguments" do
# cb = Callbacks::WithStateArg.new
# expect(cb).to receive(:before_method).with(:arg1).once.ordered
# expect(cb).to receive(:transition_method).with(:arg1).once.ordered
# expect(cb).to receive(:transition_method).never
# expect(cb).to receive(:after_method).with(:arg1).once.ordered
# cb.close!(:arg1)
it "should call the proper methods just with arguments" do
cb = Callbacks::WithStateArgMultiple.new
expect(cb).to receive(:before_method).with(:arg1).once.ordered
expect(cb).to receive(:transition_method).with(:arg1).once.ordered
expect(cb).to receive(:transition_method).never
expect(cb).to receive(:after_method).with(:arg1).once.ordered
cb.close!(:arg1)
# cb = Callbacks::WithStateArg.new
# some_object = double('some object')
# expect(cb).to receive(:before_method).with(some_object).once.ordered
# expect(cb).to receive(:transition_method).with(some_object).once.ordered
# expect(cb).to receive(:transition_method).never
# expect(cb).to receive(:after_method).with(some_object).once.ordered
# cb.close!(some_object)
# end
end
cb = Callbacks::WithStateArgMultiple.new
some_object = double('some object')
expect(cb).to receive(:before_method).with(some_object).once.ordered
expect(cb).to receive(:transition_method).with(some_object).once.ordered
expect(cb).to receive(:transition_method).never
expect(cb).to receive(:after_method).with(some_object).once.ordered
cb.close!(some_object)
end
end # callbacks for the new DSL
# describe 'event callbacks' do
# describe "with an error callback defined" do
# before do
# class Foo
# # this hack is needed to allow testing of parameters, since RSpec
# # destroys a method's arity when mocked
# attr_accessor :data
describe 'event callbacks' do
describe "with an error callback defined" do
before do
class FooMultiple
# this hack is needed to allow testing of parameters, since RSpec
# destroys a method's arity when mocked
attr_accessor :data
# aasm do
# event :safe_close, :success => :success_callback, :error => :error_callback do
# transitions :to => :closed, :from => [:open]
# end
# end
# end
aasm(:left) do
event :safe_close, :success => :success_callback, :error => :error_callback do
transitions :to => :closed, :from => [:open]
end
end
end
# @foo = Foo.new
# end
@foo = FooMultiple.new
end
# context "error_callback defined" do
# it "should run error_callback if an exception is raised" do
# def @foo.error_callback(e)
# @data = [e]
# end
context "error_callback defined" do
it "should run error_callback if an exception is raised" do
def @foo.error_callback(e)
@data = [e]
end
# allow(@foo).to receive(:before_enter).and_raise(e = StandardError.new)
allow(@foo).to receive(:before_enter).and_raise(e = StandardError.new)
# @foo.safe_close!
# expect(@foo.data).to eql [e]
# end
@foo.safe_close!
expect(@foo.data).to eql [e]
end
# it "should run error_callback without parameters if callback does not support any" do
# def @foo.error_callback(e)
# @data = []
# end
it "should run error_callback without parameters if callback does not support any" do
def @foo.error_callback(e)
@data = []
end
# allow(@foo).to receive(:before_enter).and_raise(e = StandardError.new)
allow(@foo).to receive(:before_enter).and_raise(e = StandardError.new)
# @foo.safe_close!('arg1', 'arg2')
# expect(@foo.data).to eql []
# end
@foo.safe_close!('arg1', 'arg2')
expect(@foo.data).to eql []
end
# it "should run error_callback with parameters if callback supports them" do
# def @foo.error_callback(e, arg1, arg2)
# @data = [arg1, arg2]
# end
it "should run error_callback with parameters if callback supports them" do
def @foo.error_callback(e, arg1, arg2)
@data = [arg1, arg2]
end
# allow(@foo).to receive(:before_enter).and_raise(e = StandardError.new)
allow(@foo).to receive(:before_enter).and_raise(e = StandardError.new)
# @foo.safe_close!('arg1', 'arg2')
# expect(@foo.data).to eql ['arg1', 'arg2']
# end
# end
@foo.safe_close!('arg1', 'arg2')
expect(@foo.data).to eql ['arg1', 'arg2']
end
end
# it "should raise NoMethodError if exception is raised and error_callback is declared but not defined" do
# allow(@foo).to receive(:before_enter).and_raise(StandardError)
# expect{@foo.safe_close!}.to raise_error(NoMethodError)
# end
it "should raise NoMethodError if exception is raised and error_callback is declared but not defined" do
allow(@foo).to receive(:before_enter).and_raise(StandardError)
expect{@foo.safe_close!}.to raise_error(NoMethodError)
end
# it "should propagate an error if no error callback is declared" do
# allow(@foo).to receive(:before_enter).and_raise("Cannot enter safe")
# expect{@foo.close!}.to raise_error(StandardError, "Cannot enter safe")
# end
# end
it "should propagate an error if no error callback is declared" do
allow(@foo).to receive(:before_enter).and_raise("Cannot enter safe")
expect{@foo.close!}.to raise_error(StandardError, "Cannot enter safe")
end
end
# describe "with aasm_event_fired defined" do
# before do
# @foo = Foo.new
# def @foo.aasm_event_fired(event, from, to); end
# end
describe "with aasm_event_fired defined" do
before do
@foo = FooMultiple.new
def @foo.aasm_event_fired(event, from, to); end
end
# it 'should call it for successful bang fire' do
# expect(@foo).to receive(:aasm_event_fired).with(:close, :open, :closed)
# @foo.close!
# end
it 'should call it for successful bang fire' do
expect(@foo).to receive(:aasm_event_fired).with(:close, :open, :closed)
@foo.close!
end
# it 'should call it for successful non-bang fire' do
# expect(@foo).to receive(:aasm_event_fired)
# @foo.close
# end
it 'should call it for successful non-bang fire' do
expect(@foo).to receive(:aasm_event_fired)
@foo.close
end
# it 'should not call it for failing bang fire' do
# allow(@foo.aasm).to receive(:set_current_state_with_persistence).and_return(false)
# expect(@foo).not_to receive(:aasm_event_fired)
# @foo.close!
# end
# end
it 'should not call it for failing bang fire' do
allow(@foo.aasm(:left)).to receive(:set_current_state_with_persistence).and_return(false)
expect(@foo).not_to receive(:aasm_event_fired)
@foo.close!
end
end
# describe "with aasm_event_failed defined" do
# before do
# @foo = Foo.new
# def @foo.aasm_event_failed(event, from); end
# end
describe "with aasm_event_failed defined" do
before do
@foo = FooMultiple.new
def @foo.aasm_event_failed(event, from); end
end
# it 'should call it when transition failed for bang fire' do
# expect(@foo).to receive(:aasm_event_failed).with(:null, :open)
# expect {@foo.null!}.to raise_error(AASM::InvalidTransition)
# end
it 'should call it when transition failed for bang fire' do
expect(@foo).to receive(:aasm_event_failed).with(:null, :open)
expect {@foo.null!}.to raise_error(AASM::InvalidTransition)
end
# it 'should call it when transition failed for non-bang fire' do
# expect(@foo).to receive(:aasm_event_failed).with(:null, :open)
# expect {@foo.null}.to raise_error(AASM::InvalidTransition)
# end
it 'should call it when transition failed for non-bang fire' do
expect(@foo).to receive(:aasm_event_failed).with(:null, :open)
expect {@foo.null}.to raise_error(AASM::InvalidTransition)
end
# it 'should not call it if persist fails for bang fire' do
# allow(@foo.aasm).to receive(:set_current_state_with_persistence).and_return(false)
# expect(@foo).to receive(:aasm_event_failed)
# @foo.close!
# end
# end
it 'should not call it if persist fails for bang fire' do
allow(@foo.aasm(:left)).to receive(:set_current_state_with_persistence).and_return(false)
expect(@foo).to receive(:aasm_event_failed)
@foo.close!
end
end
# end
end # event callbacks