- require File.dirname(__FILE__)+'/../spec_helper'
- describe RuoteMigrator, :type => :process do
- before do
- clear_workflow_storage!
- end
- def define *args, &block
- Processes.define *args, &block
- end
- def launch
- patient_wait_for_with_timeout :signal => "one" do
- @wfid = Processes["test"].launch
- end
- end
- def status reload=false
- return @status if @status and !reload
- @status = Processes.status(@wfid)
- end
- def migrator reload=false
- return @migrator if @migrator and !reload
- @migrator = RuoteMigrator.new(status(reload))
- end
- def reply
- patient_wait_for_with_timeout do
- Workflow.workitems.reply(status.current_workitems.last)
- end
- end
- def current_task_names
- status(true).current_tasks.map(&:process_task_name)
- end
- def define_and_advance
- #define("test"){one :task => 'a'; one :task => 'b'}
- #launch
- #reply
- #dump_process_fixture @wfid, :test_a_b_at_b
- @wfid = restore_process_fixture :test_a_b_at_b
- end
- it "should be able to tell when a definition has changed from a running process" do
- define("test"){one :task => 'a'; one :task => 'b'}
- launch; reply
- (migrator === Processes["test"]).should be_true
- define("test"){one :task => 'c'; one :task => 'd'}
- (migrator === Processes["test"]).should be_false
- end
- it "should raise an error if the existing workitems can't automatically be remapped" do
- define("test"){one :task => "a"}
- launch
- define("test"){one :task => "c"}
- lambda do
- patient_wait_for_with_timeout do
- migrator.update_tree! Processes["test"]
- end
- end.should raise_error(RuoteMigrator::MigrationError)
- end
- it "should be able to deal with a task redefinition by rewinding to the beginning" do
- define_and_advance
- define("test"){one :task => "c"; one :task => "d"; one :task => "e"}
- patient_wait_for_with_timeout do
- migrator.update_tree! Processes["test"], :rewind_silently => true
- end
- current_task_names.should == ["one_c"]
- reply
- current_task_names.should == ["one_d"]
- end
- it "should raise an error if the existing workitems can't be remapped using a supplied mapping" do
- define_and_advance
- define("test"){one :task => "c"; one :task => "d"; one :task => "e"}
- lambda do
- patient_wait_for_with_timeout do
- migrator.update_tree!(Processes["test"], {'task' => 'a'} => {'task' => 'd'})
- end
- end.should raise_error(RuoteMigrator::MigrationError)
- end
- it "should not raise an error if the workitems can be remapped automatically but the mapping doesn't apply" do
- define("test"){one :task => 'a'; one :task => 'b'}
- launch
- current_task_names.should == ["one_a"]
- define("test"){one :task => "c"; one :task => "a"}
- lambda do
- patient_wait_for_with_timeout do
- migrator.update_tree!(Processes["test"], {'task' => 'b'} => {'task' => 'c'})
- end
- end.should_not raise_error
- current_task_names.should == ["one_a"]
- end
- it "should prefer requested mappings over default mappings" do
- define("test"){one :task => 'a'; one :task => 'b'}
- launch
- current_task_names.should == ["one_a"]
- # this definition allows both the default and the supplied mapping to work
- define("test"){one :task => "a"; one :task => "c"}
- patient_wait_for_with_timeout do
- # this mapping will be merged with the default:
- # {'task' => 'a', 'ref' => 'one'} => {'task' => 'a', 'ref' => 'one'},
- # but this should take precedence
- migrator.update_tree!(Processes["test"], {'task' => 'a'} => {'task' => 'c'})
- end
- current_task_names.should == ["one_c"]
- end
- it "should be able to deal with a task redefinition by mapping task name changes" do
- define_and_advance
- define("test"){one :task => "c"; one :task => "d"; one :task => "e"}
- patient_wait_for_with_timeout do
- migrator.update_tree!(Processes["test"], {'task' => 'b'} => {'task' => 'd'})
- end
- current_task_names.should == ["one_d"]
- reply
- current_task_names.should == ["one_e"]
- end
- it "should be able to map task name changes on a complex process" do
- define("test") do
- one :task => "a"
- cursor do
- one :task => "b"
- one :task => "c"
- end
- one :task => "d"
- end
- launch
- current_task_names.should == ["one_a"]
- reply
- current_task_names.should == ["one_b"]
- define("test") do
- cursor do
- one :task => "f"
- cursor do
- one :task => "g"
- end
- end
- one :task => "h"
- end
- patient_wait_for_with_timeout do
- migrator.update_tree!(Processes["test"], {'task' => 'b'} => {'task' => 'g'})
- end
- current_task_names.should == ["one_g"]
- reply
- current_task_names.should == ["one_h"]
- end
- it "should ignore task mappings that don't apply to this process instance, but still apply ones that do" do
- define_and_advance
- define("test"){one :task => "c"; one :task => "d"; one :task => "e"}
- patient_wait_for_with_timeout do
- migrator.update_tree!(Processes["test"], {'task' => 'a'} => {'task' => 'd'}, {'task' => 'b'} => {'task' => 'e'})
- end
- current_task_names.should == ["one_e"]
- end
- it "should allow mapping tasks where two tasks have the same name, but different participants" do
- define_and_advance
- define("test"){one :task => "c"; two :task => "c"}
- patient_wait_for_with_timeout do
- migrator.update_tree!(Processes["test"], {'task' => 'b'} => {'task' => 'c', 'ref' => 'two'})
- end
- current_task_names.should == ["two_c"]
- end
- it "should ensure the task remains the same, if possible" do
- define_and_advance
- define("test"){one :task => "c"; one :task => "b"; one :task => "d"}
- patient_wait_for_with_timeout do
- migrator.update_tree! Processes["test"]
- end
- current_task_names.should == ["one_b"]
- reply
- current_task_names.should == ["one_d"]
- end
- it "should do the right thing when mapping a concurrent process" do
- define("test") do
- concurrence do
- one :task => "a"
- two :task => "b"
- end
- three :task => "c"
- end
- launch
- current_task_names.should == ["one_a", "two_b"]
- define("test") do
- concurrence do
- one :task => "d"
- two :task => "e"
- cursor do
- one :task => "f"
- one :task => "g"
- end
- end
- three :task => "h"
- end
- patient_wait_for_with_timeout :signal => "one" do
- migrator.update_tree!(Processes["test"], {'task' => 'a'} => {'task' => 'g'}, {'task' => 'b'} => {'task' => 'e'})
- end
- current_task_names.should == ["two_e", "one_g"]
- wis = status.workitems
- patient_wait_for_with_timeout :signal => "three" do
- Workflow.workitems.reply(wis.find{|wi|wi.participant_name == "two"})
- Workflow.workitems.reply(wis.find{|wi|wi.participant_name == "one"})
- end
- current_task_names.should == ["three_h"]
- end
- it "should not destroy fields on the workitem" do
- define("test"){set :field => 'my_field', :value => 'yes'; one :task => 'a'}
- launch
- status.workitems.first.fields.should include('my_field' => 'yes')
- define("test"){one :task => 'b'}
- patient_wait_for_with_timeout do
- migrator.update_tree! Processes["test"], {'task' => 'a'} => {'task' => 'b'}
- end
- status(true).workitems.first.fields.should include('my_field' => 'yes')
- end
- it "should be able to jump between subprocesses" do
- define("test") do
- define "sub_1" do
- one :task => 'a'
- end
- define "sub_2" do
- one :task => 'b'
- end
- cursor :tag => 'main' do
- sub_1
- sub_2
- end
- end
- launch
- current_task_names.should == ["one_a"]
- patient_wait_for_with_timeout do
- migrator.update_tree! Processes["test"], {'task' => 'a'} => {'ref' => 'sub_2'}
- end
- current_task_names.should == ["one_b"]
- end
- it "should raise an error if you try to move to a subexpression of a subprocess" do
- pending "There doesn't seem to be a way to detect that this is being done"
- define("test") do
- define "sub_1" do
- one :task => 'a'
- end
- define "sub_2" do
- one :task => 'b'
- one :task => 'c'
- end
- cursor :tag => 'main' do
- sub_1
- sub_2
- end
- end
- launch
- current_task_names.should == ["one_a"]
- lambda do
- patient_wait_for_with_timeout do
- migrator.update_tree! Processes["test"], {'task' => 'a'} => {'task' => 'c'}
- end
- end.should raise_error
- end
- end