1
0
Fork 0
mirror of https://github.com/aasm/aasm synced 2023-03-27 23:22:41 -04:00

unified test and spec (using spec only now)

This commit is contained in:
Thorsten Böttger 2011-10-15 17:32:28 +02:00
parent 04a71b89d7
commit 31258e7636
16 changed files with 462 additions and 715 deletions

View file

@ -13,8 +13,6 @@ Rake::TestTask.new(:test) do |test|
test.verbose = true
end
task :default => :test
require 'rdoc/task'
require 'aasm/version'
require 'sdoc'
@ -25,3 +23,5 @@ Rake::RDocTask.new do |rdoc|
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
task :default => :spec

View file

@ -16,3 +16,4 @@ module Models
end
end

View file

@ -1,3 +1,5 @@
Dir[File.dirname(__FILE__) + "/models/**/*.rb"].each { |f| require File.expand_path(f) }
class Foo
include AASM
aasm_initial_state :open
@ -65,3 +67,118 @@ class Argument
transitions :to => :valid, :from => [:invalid]
end
end
class AuthMachine
include AASM
attr_accessor :activation_code, :activated_at, :deleted_at
aasm_initial_state :pending
aasm_state :passive
aasm_state :pending, :enter => :make_activation_code
aasm_state :active, :enter => :do_activate
aasm_state :suspended
aasm_state :deleted, :enter => :do_delete, :exit => :do_undelete
aasm_event :register do
transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| u.can_register? }
end
aasm_event :activate do
transitions :from => :pending, :to => :active
end
aasm_event :suspend do
transitions :from => [:passive, :pending, :active], :to => :suspended
end
aasm_event :delete do
transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
end
# a dummy event that can never happen
aasm_event :unpassify do
transitions :from => :passive, :to => :active, :guard => Proc.new {|u| false }
end
aasm_event :unsuspend do
transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| u.has_activated? }
transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| u.has_activation_code? }
transitions :from => :suspended, :to => :passive
end
def initialize
# the AR backend uses a before_validate_on_create :aasm_ensure_initial_state
# lets do something similar here for testing purposes.
aasm_enter_initial_state
end
def make_activation_code
@activation_code = 'moo'
end
def do_activate
@activated_at = Time.now
@activation_code = nil
end
def do_delete
@deleted_at = Time.now
end
def do_undelete
@deleted_at = false
end
def can_register?
true
end
def has_activated?
!!@activated_at
end
def has_activation_code?
!!@activation_code
end
end
class ThisNameBetterNotBeInUse
include AASM
aasm_state :initial
aasm_state :symbol
aasm_state :string
aasm_state :array
aasm_state :proc
end
class ChetanPatil
include AASM
aasm_initial_state :sleeping
aasm_state :sleeping
aasm_state :showering
aasm_state :working
aasm_state :dating
aasm_state :prettying_up
aasm_event :wakeup do
transitions :from => :sleeping, :to => [:showering, :working]
end
aasm_event :dress do
transitions :from => :sleeping, :to => :working, :on_transition => :wear_clothes
transitions :from => :showering, :to => [:working, :dating], :on_transition => Proc.new { |obj, *args| obj.wear_clothes(*args) }
transitions :from => :showering, :to => :prettying_up, :on_transition => [:condition_hair, :fix_hair]
end
def wear_clothes(shirt_color, trouser_type)
end
def condition_hair
end
def fix_hair
end
end

View file

@ -333,35 +333,6 @@ describe Baz do
end
class ChetanPatil
include AASM
aasm_initial_state :sleeping
aasm_state :sleeping
aasm_state :showering
aasm_state :working
aasm_state :dating
aasm_state :prettying_up
aasm_event :wakeup do
transitions :from => :sleeping, :to => [:showering, :working]
end
aasm_event :dress do
transitions :from => :sleeping, :to => :working, :on_transition => :wear_clothes
transitions :from => :showering, :to => [:working, :dating], :on_transition => Proc.new { |obj, *args| obj.wear_clothes(*args) }
transitions :from => :showering, :to => :prettying_up, :on_transition => [:condition_hair, :fix_hair]
end
def wear_clothes(shirt_color, trouser_type)
end
def condition_hair
end
def fix_hair
end
end
describe ChetanPatil do
it 'should transition to specified next state (sleeping to showering)' do

View file

@ -1,4 +1,3 @@
begin
require 'rubygems'
require 'active_record'
require 'logger'
@ -236,11 +235,3 @@ begin
end
# TODO: figure out how to test ActiveRecord reload! without a database
rescue LoadError => e
if e.message == "no such file to load -- active_record"
puts "You must install active record to run this spec. Install with sudo gem install activerecord"
else
raise
end
end

View file

@ -0,0 +1,66 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
describe 'AuthMachine on initialization' do
before(:each) do
@auth = AuthMachine.new
end
it 'should be in the pending state' do
@auth.aasm_current_state.should == :pending
end
it 'should have an activation code' do
@auth.has_activation_code?.should be_true
@auth.activation_code.should_not be_nil
end
end
describe 'AuthMachine when being unsuspended' do
it 'should be able to be unsuspended' do
@auth = AuthMachine.new
@auth.activate!
@auth.suspend!
@auth.may_unsuspend?.should be_true
end
it 'should not be able to be unsuspended into active' do
@auth = AuthMachine.new
@auth.suspend!
@auth.may_unsuspend?(:active).should_not be_true
end
it 'should not be able to be unpassified' do
@auth = AuthMachine.new
@auth.activate!
@auth.suspend!
@auth.unsuspend!
@auth.may_unpassify?.should_not be_true
end
it 'should be active if previously activated' do
@auth = AuthMachine.new
@auth.activate!
@auth.suspend!
@auth.unsuspend!
@auth.aasm_current_state.should == :active
end
it 'should be pending if not previously activated, but an activation code is present' do
@auth = AuthMachine.new
@auth.suspend!
@auth.unsuspend!
@auth.aasm_current_state.should == :pending
end
it 'should be passive if not previously activated and there is no activation code' do
@auth = AuthMachine.new
@auth.activation_code = nil
@auth.suspend!
@auth.unsuspend!
@auth.aasm_current_state.should == :passive
end
end

View file

@ -65,15 +65,6 @@ describe AASM::SupportingClasses::Event, 'when firing an event' do
end
describe AASM::SupportingClasses::Event, 'when executing the success callback' do
class ThisNameBetterNotBeInUse
include AASM
aasm_state :initial
aasm_state :symbol
aasm_state :string
aasm_state :array
aasm_state :proc
end
it "should send the success callback if it's a symbol" do
ThisNameBetterNotBeInUse.instance_eval {

View file

@ -0,0 +1,34 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
describe "state machines" do
def number_of_objects(clazz)
ObjectSpace.each_object(clazz) {}
end
def machines
AASM::StateMachine.instance_variable_get("@machines")
end
it "should be created without memory leak" do
machines_count = machines.size
state_count = number_of_objects(AASM::SupportingClasses::State)
event_count = number_of_objects(AASM::SupportingClasses::Event)
transition_count = number_of_objects(AASM::SupportingClasses::StateTransition)
load File.expand_path(File.dirname(__FILE__) + '/../models/process.rb')
machines.size.should == machines_count + 1 # + Process
number_of_objects(Models::Process).should == 0
number_of_objects(AASM::SupportingClasses::State).should == state_count + 3 # + Process
number_of_objects(AASM::SupportingClasses::Event).should == event_count + 2 # + Process
number_of_objects(AASM::SupportingClasses::StateTransition).should == transition_count + 2 # + Process
Models.send(:remove_const, "Process") if Models.const_defined?("Process")
load File.expand_path(File.dirname(__FILE__) + '/../models/process.rb')
machines.size.should == machines_count + 1 # + Process
number_of_objects(AASM::SupportingClasses::State).should == state_count + 3 # + Process
number_of_objects(AASM::SupportingClasses::Event).should == event_count + 2 # + Process
number_of_objects(AASM::SupportingClasses::StateTransition).should == transition_count + 2 # + Process
end
end

View file

@ -1,6 +1,5 @@
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
# TODO These are specs ported from original aasm
describe AASM::SupportingClasses::State do
before(:each) do
@name = :astate
@ -13,20 +12,23 @@ describe AASM::SupportingClasses::State do
it 'should set the name' do
state = new_state
state.name.should == :astate
end
it 'should set the options and expose them as options' do
state = new_state
it 'should set the display_name from name' do
new_state.display_name.should == 'Astate'
end
state.options.should == @options
it 'should set the display_name from options' do
new_state(:display => "A State").display_name.should == 'A State'
end
it 'should set the options and expose them as options' do
new_state.options.should == @options
end
it 'should be equal to a symbol of the same name' do
state = new_state
state.should == :astate
new_state.should == :astate
end
it 'should be equal to a State of the same name' do

View file

@ -1,148 +0,0 @@
require 'test_helper'
class AuthMachine
include AASM
attr_accessor :activation_code, :activated_at, :deleted_at
aasm_initial_state :pending
aasm_state :passive
aasm_state :pending, :enter => :make_activation_code
aasm_state :active, :enter => :do_activate
aasm_state :suspended
aasm_state :deleted, :enter => :do_delete, :exit => :do_undelete
aasm_event :register do
transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| u.can_register? }
end
aasm_event :activate do
transitions :from => :pending, :to => :active
end
aasm_event :suspend do
transitions :from => [:passive, :pending, :active], :to => :suspended
end
aasm_event :delete do
transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
end
# a dummy event that can never happen
aasm_event :unpassify do
transitions :from => :passive, :to => :active, :guard => Proc.new {|u| false }
end
aasm_event :unsuspend do
transitions :from => :suspended, :to => :active, :guard => Proc.new {|u| u.has_activated? }
transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| u.has_activation_code? }
transitions :from => :suspended, :to => :passive
end
def initialize
# the AR backend uses a before_validate_on_create :aasm_ensure_initial_state
# lets do something similar here for testing purposes.
aasm_enter_initial_state
end
def make_activation_code
@activation_code = 'moo'
end
def do_activate
@activated_at = Time.now
@activation_code = nil
end
def do_delete
@deleted_at = Time.now
end
def do_undelete
@deleted_at = false
end
def can_register?
true
end
def has_activated?
!!@activated_at
end
def has_activation_code?
!!@activation_code
end
end
class AuthMachineTest < Test::Unit::TestCase
context 'authentication state machine' do
context 'on initialization' do
setup do
@auth = AuthMachine.new
end
should 'be in the pending state' do
assert_equal :pending, @auth.aasm_current_state
end
should 'have an activation code' do
assert @auth.has_activation_code?
assert_not_nil @auth.activation_code
end
end
context 'when being unsuspended' do
should 'be able to be unsuspended' do
@auth = AuthMachine.new
@auth.activate!
@auth.suspend!
assert @auth.may_unsuspend?
end
should 'not be able to be unsuspended into active' do
@auth = AuthMachine.new
@auth.suspend!
assert_equal false, @auth.may_unsuspend?(:active)
end
should 'not be able to be unpassified' do
@auth = AuthMachine.new
@auth.activate!
@auth.suspend!
@auth.unsuspend!
assert_equal false, @auth.may_unpassify?
end
should 'be active if previously activated' do
@auth = AuthMachine.new
@auth.activate!
@auth.suspend!
@auth.unsuspend!
assert_equal :active, @auth.aasm_current_state
end
should 'be pending if not previously activated, but an activation code is present' do
@auth = AuthMachine.new
@auth.suspend!
@auth.unsuspend!
assert_equal :pending, @auth.aasm_current_state
end
should 'be passive if not previously activated and there is no activation code' do
@auth = AuthMachine.new
@auth.activation_code = nil
@auth.suspend!
@auth.unsuspend!
assert_equal :passive, @auth.aasm_current_state
end
end
end
end

View file

@ -1,43 +0,0 @@
require 'rubygems'
require 'bundler'
begin
Bundler.setup(:default, :development)
rescue Bundler::BundlerError => e
$stderr.puts e.message
$stderr.puts "Run `bundle install` to install missing gems"
exit e.status_code
end
require 'ostruct'
require 'rubygems'
begin
gem 'minitest'
rescue Gem::LoadError
puts 'minitest gem not found'
end
begin
require 'minitest/autorun'
puts 'using minitest'
rescue LoadError
require 'test/unit'
puts 'using test/unit'
end
require 'rr'
require 'shoulda'
class Test::Unit::TestCase
include RR::Adapters::TestUnit
end
begin
require 'ruby-debug'; Debugger.settings[:autoeval] = true; debugger; rubys_debugger = 'annoying'
require 'ruby-debug/completion'
rescue LoadError
end
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
$LOAD_PATH.unshift(File.dirname(__FILE__))
require 'aasm'

View file

View file

@ -1,54 +0,0 @@
require 'test_helper'
class EventTest < Test::Unit::TestCase
def new_event
@event = AASM::SupportingClasses::Event.new(@name, {:success => @success}) do
transitions :to => :closed, :from => [:open, :received]
end
end
context 'event' do
setup do
@name = :close_order
@success = :success_callback
end
should 'set the name' do
assert_equal @name, new_event.name
end
should 'set the success option' do
assert_equal @success, new_event.success
end
should 'create StateTransitions' do
mock(AASM::SupportingClasses::StateTransition).new({:to => :closed, :from => :open})
mock(AASM::SupportingClasses::StateTransition).new({:to => :closed, :from => :received})
new_event
end
context 'when firing' do
should 'raise an AASM::InvalidTransition error if the transitions are empty' do
event = AASM::SupportingClasses::Event.new(:event)
obj = OpenStruct.new
obj.aasm_current_state = :open
assert_raise AASM::InvalidTransition do
event.fire(obj)
end
end
should 'return the state of the first matching transition it finds' do
event = AASM::SupportingClasses::Event.new(:event) do
transitions :to => :closed, :from => [:open, :received]
end
obj = OpenStruct.new
obj.aasm_current_state = :open
assert_equal :closed, event.fire(obj)
end
end
end
end

View file

@ -1,37 +0,0 @@
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
class StateMachineTest < Test::Unit::TestCase
context "state machines" do
should "be created without memory leak" do
assert_equal 1, AASM::StateMachine.instance_variable_get("@machines").size # AuthMachine
assert_number_of_objects AASM::SupportingClasses::State, 5 # AuthMachine
assert_number_of_objects AASM::SupportingClasses::Event, 11 # AuthMachine
assert_number_of_objects AASM::SupportingClasses::StateTransition, 19 # AuthMachine
load File.expand_path(File.dirname(__FILE__) + '/../models/process.rb')
assert_equal 2, AASM::StateMachine.instance_variable_get("@machines").size # AuthMachine + Process
assert_number_of_objects Models::Process, 0
assert_number_of_objects AASM::SupportingClasses::State, 8 # AuthMachine + Process
assert_number_of_objects AASM::SupportingClasses::Event, 13 # AuthMachine + Process
assert_number_of_objects AASM::SupportingClasses::StateTransition, 21 # AuthMachine + Process
Models.send(:remove_const, "Process") if Models.const_defined?("Process")
load File.expand_path(File.dirname(__FILE__) + '/../models/process.rb')
assert_equal 2, AASM::StateMachine.instance_variable_get("@machines").size # AuthMachine + Process
assert_number_of_objects AASM::SupportingClasses::State, 8 # AuthMachine + Process
assert_number_of_objects AASM::SupportingClasses::Event, 13 # AuthMachine + Process
assert_number_of_objects AASM::SupportingClasses::StateTransition, 21 # AuthMachine + Process
end
end
private
def assert_number_of_objects(clazz, num)
count = ObjectSpace.each_object(clazz) {}
assert_equal num, count, "#{num} expected, but we had #{count} #{clazz}"
end
end

View file

@ -1,69 +0,0 @@
require 'test_helper'
class StateTest < Test::Unit::TestCase
def new_state(options={})
AASM::SupportingClasses::State.new(@name, @options.merge(options))
end
context 'state' do
setup do
@name = :astate
@options = { :crazy_custom_key => 'key' }
end
should 'set the name' do
assert_equal :astate, new_state.name
end
should 'set the display_name from name' do
assert_equal "Astate", new_state.display_name
end
should 'set the display_name from options' do
assert_equal "A State", new_state(:display => "A State").display_name
end
should 'set the options and expose them as options' do
assert_equal @options, new_state.options
end
should 'equal a symbol of the same name' do
assert_equal new_state, :astate
end
should 'equal a state of the same name' do
assert_equal new_state, new_state
end
should 'send a message to the record for an action if the action is present as a symbol' do
state = new_state(:entering => :foo)
mock(record = Object.new).foo
state.call_action(:entering, record)
end
should 'send a message to the record for an action if the action is present as a string' do
state = new_state(:entering => 'foo')
mock(record = Object.new).foo
state.call_action(:entering, record)
end
should 'call a proc with the record as its argument for an action if the action is present as a proc' do
state = new_state(:entering => Proc.new {|r| r.foobar})
mock(record = Object.new).foobar
state.call_action(:entering, record)
end
should 'send a message to the record for each action if the action is present as an array' do
state = new_state(:entering => [:a, :b, 'c', lambda {|r| r.foobar}])
record = Object.new
mock(record).a
mock(record).b
mock(record).c
mock(record).foobar
state.call_action(:entering, record)
end
end
end

View file

@ -1,75 +0,0 @@
require 'test_helper'
class StateTransitionTest < Test::Unit::TestCase
context 'state transition' do
setup do
@opts = {:from => 'foo', :to => 'bar', :guard => 'g'}
@st = AASM::SupportingClasses::StateTransition.new(@opts)
end
should 'set from, to, and opts attr readers' do
assert_equal @opts[:from], @st.from
assert_equal @opts[:to], @st.to
assert_equal @opts, @st.options
end
should 'pass equality check if from and to are the same' do
obj = OpenStruct.new
obj.from = @opts[:from]
obj.to = @opts[:to]
assert_equal @st, obj
end
should 'fail equality check if from is not the same' do
obj = OpenStruct.new
obj.from = 'blah'
obj.to = @opts[:to]
assert_not_equal @st, obj
end
should 'fail equality check if to is not the same' do
obj = OpenStruct.new
obj.from = @opts[:from]
obj.to = 'blah'
assert_not_equal @st, obj
end
context 'when performing guard checks' do
should 'return true if there is no guard' do
opts = {:from => 'foo', :to => 'bar'}
st = AASM::SupportingClasses::StateTransition.new(opts)
assert st.perform(nil)
end
should 'call the method on the object if guard is a symbol' do
opts = {:from => 'foo', :to => 'bar', :guard => :test_guard}
st = AASM::SupportingClasses::StateTransition.new(opts)
mock(obj = Object.new).test_guard
st.perform(obj)
end
should 'call the method on the object if guard is a string' do
opts = {:from => 'foo', :to => 'bar', :guard => 'test_guard'}
st = AASM::SupportingClasses::StateTransition.new(opts)
mock(obj = Object.new).test_guard
st.perform(obj)
end
should 'call the proc passing the object if guard is a proc' do
opts = {:from => 'foo', :to => 'bar', :guard => Proc.new {|o| o.test_guard}}
st = AASM::SupportingClasses::StateTransition.new(opts)
mock(obj = Object.new).test_guard
st.perform(obj)
end
end
end
end