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

View file

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

View file

@ -1,3 +1,5 @@
Dir[File.dirname(__FILE__) + "/models/**/*.rb"].each { |f| require File.expand_path(f) }
class Foo class Foo
include AASM include AASM
aasm_initial_state :open aasm_initial_state :open
@ -65,3 +67,118 @@ class Argument
transitions :to => :valid, :from => [:invalid] transitions :to => :valid, :from => [:invalid]
end end
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 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 describe ChetanPatil do
it 'should transition to specified next state (sleeping to showering)' do it 'should transition to specified next state (sleeping to showering)' do

View file

@ -1,246 +1,237 @@
begin require 'rubygems'
require 'rubygems' require 'active_record'
require 'active_record' require 'logger'
require 'logger'
load_schema load_schema
ActiveRecord::Base.logger = Logger.new(STDERR) ActiveRecord::Base.logger = Logger.new(STDERR)
class Gate < ActiveRecord::Base class Gate < ActiveRecord::Base
include AASM include AASM
# Fake this column for testing purposes # Fake this column for testing purposes
attr_accessor :aasm_state attr_accessor :aasm_state
aasm_state :opened aasm_state :opened
aasm_state :closed aasm_state :closed
aasm_event :view do aasm_event :view do
transitions :to => :read, :from => [:needs_attention] transitions :to => :read, :from => [:needs_attention]
end
end
class Reader < ActiveRecord::Base
def aasm_read_state
"fi"
end
include AASM
end
class Writer < ActiveRecord::Base
def aasm_write_state(state)
"fo"
end
include AASM
end
class Transient < ActiveRecord::Base
def aasm_write_state_without_persistence(state)
"fum"
end
include AASM
end
class Simple < ActiveRecord::Base
include AASM
aasm_column :status
end
class Derivate < Simple
end
class Thief < ActiveRecord::Base
set_table_name "thieves"
include AASM
aasm_initial_state Proc.new { |thief| thief.skilled ? :rich : :jailed }
aasm_state :rich
aasm_state :jailed
attr_accessor :skilled, :aasm_state
end
shared_examples_for "aasm model" do
it "should include AASM::Persistence::ActiveRecordPersistence" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence)
end
it "should include AASM::Persistence::ActiveRecordPersistence::InstanceMethods" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
end
end
describe Gate, "class methods" do
before(:each) do
@klass = Gate
end
it_should_behave_like "aasm model"
it "should include AASM::Persistence::ActiveRecordPersistence::ReadState" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
end
it "should include AASM::Persistence::ActiveRecordPersistence::WriteState" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
end
it "should include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
end
end
describe Reader, "class methods" do
before(:each) do
@klass = Reader
end
it_should_behave_like "aasm model"
it "should not include AASM::Persistence::ActiveRecordPersistence::ReadState" do
@klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
end
it "should include AASM::Persistence::ActiveRecordPersistence::WriteState" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
end
it "should include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
end
end
describe Writer, "class methods" do
before(:each) do
@klass = Writer
end
it_should_behave_like "aasm model"
it "should include AASM::Persistence::ActiveRecordPersistence::ReadState" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
end
it "should not include AASM::Persistence::ActiveRecordPersistence::WriteState" do
@klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
end
it "should include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
end
end
describe Transient, "class methods" do
before(:each) do
@klass = Transient
end
it_should_behave_like "aasm model"
it "should include AASM::Persistence::ActiveRecordPersistence::ReadState" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
end
it "should include AASM::Persistence::ActiveRecordPersistence::WriteState" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
end
it "should not include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
@klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
end
end
describe Gate, "instance methods" do
it "should respond to aasm read state when not previously defined" do
Gate.new.should respond_to(:aasm_read_state)
end
it "should respond to aasm write state when not previously defined" do
Gate.new.should respond_to(:aasm_write_state)
end
it "should respond to aasm write state without persistence when not previously defined" do
Gate.new.should respond_to(:aasm_write_state_without_persistence)
end
it "should return the initial state when new and the aasm field is nil" do
Gate.new.aasm_current_state.should == :opened
end
it "should return the aasm column when new and the aasm field is not nil" do
foo = Gate.new
foo.aasm_state = "closed"
foo.aasm_current_state.should == :closed
end
it "should return the aasm column when not new and the aasm_column is not nil" do
foo = Gate.new
foo.stub!(:new_record?).and_return(false)
foo.aasm_state = "state"
foo.aasm_current_state.should == :state
end
it "should allow a nil state" do
foo = Gate.new
foo.stub!(:new_record?).and_return(false)
foo.aasm_state = nil
foo.aasm_current_state.should be_nil
end
it "should have aasm_ensure_initial_state" do
foo = Gate.new
foo.send :aasm_ensure_initial_state
end
it "should call aasm_ensure_initial_state on validation before create" do
foo = Gate.new
foo.should_receive(:aasm_ensure_initial_state).and_return(true)
foo.valid?
end
it "should call aasm_ensure_initial_state on validation before create" do
foo = Gate.new
foo.stub!(:new_record?).and_return(false)
foo.should_not_receive(:aasm_ensure_initial_state)
foo.valid?
end
end
describe 'Derivates' do
it "should have the same states as it's parent" do
Derivate.aasm_states.should == Simple.aasm_states
end
it "should have the same events as it's parent" do
Derivate.aasm_events.should == Simple.aasm_events
end
it "should have the same column as it's parent" do
Derivate.aasm_column.should == :status
end
end
describe AASM::Persistence::ActiveRecordPersistence::NamedScopeMethods do
context "Does not already respond_to? the scope name" do
it "should add a scope" do
Simple.should_not respond_to(:unknown_scope)
Simple.aasm_state :unknown_scope
Simple.should respond_to(:unknown_scope)
Simple.unknown_scope.class.should == ActiveRecord::Relation
end
end
context "Already respond_to? the scope name" do
it "should not add a scope" do
Simple.aasm_state :new
Simple.should respond_to(:new)
Simple.new.class.should == Simple
end
end
end
describe 'Thieves' do
it 'should be rich if they\'re skilled' do
Thief.new(:skilled => true).aasm_current_state.should == :rich
end
it 'should be jailed if they\'re unskilled' do
Thief.new(:skilled => false).aasm_current_state.should == :jailed
end
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
end end
class Reader < ActiveRecord::Base
def aasm_read_state
"fi"
end
include AASM
end
class Writer < ActiveRecord::Base
def aasm_write_state(state)
"fo"
end
include AASM
end
class Transient < ActiveRecord::Base
def aasm_write_state_without_persistence(state)
"fum"
end
include AASM
end
class Simple < ActiveRecord::Base
include AASM
aasm_column :status
end
class Derivate < Simple
end
class Thief < ActiveRecord::Base
set_table_name "thieves"
include AASM
aasm_initial_state Proc.new { |thief| thief.skilled ? :rich : :jailed }
aasm_state :rich
aasm_state :jailed
attr_accessor :skilled, :aasm_state
end
shared_examples_for "aasm model" do
it "should include AASM::Persistence::ActiveRecordPersistence" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence)
end
it "should include AASM::Persistence::ActiveRecordPersistence::InstanceMethods" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
end
end
describe Gate, "class methods" do
before(:each) do
@klass = Gate
end
it_should_behave_like "aasm model"
it "should include AASM::Persistence::ActiveRecordPersistence::ReadState" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
end
it "should include AASM::Persistence::ActiveRecordPersistence::WriteState" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
end
it "should include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
end
end
describe Reader, "class methods" do
before(:each) do
@klass = Reader
end
it_should_behave_like "aasm model"
it "should not include AASM::Persistence::ActiveRecordPersistence::ReadState" do
@klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
end
it "should include AASM::Persistence::ActiveRecordPersistence::WriteState" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
end
it "should include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
end
end
describe Writer, "class methods" do
before(:each) do
@klass = Writer
end
it_should_behave_like "aasm model"
it "should include AASM::Persistence::ActiveRecordPersistence::ReadState" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
end
it "should not include AASM::Persistence::ActiveRecordPersistence::WriteState" do
@klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
end
it "should include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
end
end
describe Transient, "class methods" do
before(:each) do
@klass = Transient
end
it_should_behave_like "aasm model"
it "should include AASM::Persistence::ActiveRecordPersistence::ReadState" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::ReadState)
end
it "should include AASM::Persistence::ActiveRecordPersistence::WriteState" do
@klass.included_modules.should be_include(AASM::Persistence::ActiveRecordPersistence::WriteState)
end
it "should not include AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence" do
@klass.included_modules.should_not be_include(AASM::Persistence::ActiveRecordPersistence::WriteStateWithoutPersistence)
end
end
describe Gate, "instance methods" do
it "should respond to aasm read state when not previously defined" do
Gate.new.should respond_to(:aasm_read_state)
end
it "should respond to aasm write state when not previously defined" do
Gate.new.should respond_to(:aasm_write_state)
end
it "should respond to aasm write state without persistence when not previously defined" do
Gate.new.should respond_to(:aasm_write_state_without_persistence)
end
it "should return the initial state when new and the aasm field is nil" do
Gate.new.aasm_current_state.should == :opened
end
it "should return the aasm column when new and the aasm field is not nil" do
foo = Gate.new
foo.aasm_state = "closed"
foo.aasm_current_state.should == :closed
end
it "should return the aasm column when not new and the aasm_column is not nil" do
foo = Gate.new
foo.stub!(:new_record?).and_return(false)
foo.aasm_state = "state"
foo.aasm_current_state.should == :state
end
it "should allow a nil state" do
foo = Gate.new
foo.stub!(:new_record?).and_return(false)
foo.aasm_state = nil
foo.aasm_current_state.should be_nil
end
it "should have aasm_ensure_initial_state" do
foo = Gate.new
foo.send :aasm_ensure_initial_state
end
it "should call aasm_ensure_initial_state on validation before create" do
foo = Gate.new
foo.should_receive(:aasm_ensure_initial_state).and_return(true)
foo.valid?
end
it "should call aasm_ensure_initial_state on validation before create" do
foo = Gate.new
foo.stub!(:new_record?).and_return(false)
foo.should_not_receive(:aasm_ensure_initial_state)
foo.valid?
end
end
describe 'Derivates' do
it "should have the same states as it's parent" do
Derivate.aasm_states.should == Simple.aasm_states
end
it "should have the same events as it's parent" do
Derivate.aasm_events.should == Simple.aasm_events
end
it "should have the same column as it's parent" do
Derivate.aasm_column.should == :status
end
end
describe AASM::Persistence::ActiveRecordPersistence::NamedScopeMethods do
context "Does not already respond_to? the scope name" do
it "should add a scope" do
Simple.should_not respond_to(:unknown_scope)
Simple.aasm_state :unknown_scope
Simple.should respond_to(:unknown_scope)
Simple.unknown_scope.class.should == ActiveRecord::Relation
end
end
context "Already respond_to? the scope name" do
it "should not add a scope" do
Simple.aasm_state :new
Simple.should respond_to(:new)
Simple.new.class.should == Simple
end
end
end
describe 'Thieves' do
it 'should be rich if they\'re skilled' do
Thief.new(:skilled => true).aasm_current_state.should == :rich
end
it 'should be jailed if they\'re unskilled' do
Thief.new(:skilled => false).aasm_current_state.should == :jailed
end
end
# TODO: figure out how to test ActiveRecord reload! without a database

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 end
describe AASM::SupportingClasses::Event, 'when executing the success callback' do 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 it "should send the success callback if it's a symbol" do
ThisNameBetterNotBeInUse.instance_eval { 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')) require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
# TODO These are specs ported from original aasm
describe AASM::SupportingClasses::State do describe AASM::SupportingClasses::State do
before(:each) do before(:each) do
@name = :astate @name = :astate
@ -13,20 +12,23 @@ describe AASM::SupportingClasses::State do
it 'should set the name' do it 'should set the name' do
state = new_state state = new_state
state.name.should == :astate state.name.should == :astate
end end
it 'should set the options and expose them as options' do it 'should set the display_name from name' do
state = new_state 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 end
it 'should be equal to a symbol of the same name' do it 'should be equal to a symbol of the same name' do
state = new_state new_state.should == :astate
state.should == :astate
end end
it 'should be equal to a State of the same name' do it 'should be equal to a State of the same name' do
@ -50,16 +52,16 @@ describe AASM::SupportingClasses::State do
state.call_action(:entering, record) state.call_action(:entering, record)
end end
it 'should send a message to the record for each action' do it 'should send a message to the record for each action' do
state = new_state(:entering => [:a, :b, "c", lambda {|r| r.foobar }]) state = new_state(:entering => [:a, :b, "c", lambda {|r| r.foobar }])
record = mock('record') record = mock('record')
record.should_receive(:a) record.should_receive(:a)
record.should_receive(:b) record.should_receive(:b)
record.should_receive(:c) record.should_receive(:c)
record.should_receive(:foobar) record.should_receive(:foobar)
state.call_action(:entering, record) state.call_action(:entering, record)
end end

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