updater 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,290 @@
1
+ require File.join( File.dirname(__FILE__), "spec_helper" )
2
+ require 'logger'
3
+ include Updater
4
+
5
+ def fake_process_status(estat=0)
6
+ stub("Process Status",:pid=>1234,:exit_status=>estat)
7
+ end
8
+
9
+ def fake_iostream
10
+ stub("IOStream").as_null_object
11
+ end
12
+
13
+ ForkWorker.logger = Logger.new(nil)
14
+
15
+ describe ForkWorker do
16
+
17
+ describe "#reap_all_workers" do
18
+ it "should remove workers" do
19
+ Process.should_receive(:waitpid2).with(-1,Process::WNOHANG).twice\
20
+ .and_return([1234,fake_process_status],nil)
21
+ ForkWorker.should_receive(:remove_worker).with(1234).once
22
+ ForkWorker.reap_all_workers
23
+ end
24
+
25
+ it "should not fail if there are no child processes" do
26
+ Process.should_receive(:waitpid2).with(-1,Process::WNOHANG).once\
27
+ .and_raise(Errno::ECHILD)
28
+ lambda{ForkWorker.reap_all_workers}.should_not raise_error
29
+ end
30
+
31
+ end
32
+
33
+ describe "#remove_worker" do
34
+
35
+ it "should silently ignore missing workers" do
36
+ lambda{ForkWorker.remove_worker(1234)}.should_not raise_error
37
+ end
38
+
39
+ it "should remove the worker from the set" do
40
+ worker = ForkWorker::WorkerMonitor.new(1,stub("IOStream").as_null_object)
41
+ ForkWorker.instance_variable_set :@workers, {1234=>worker}
42
+ ForkWorker.remove_worker(1234)
43
+ ForkWorker.instance_variable_get(:@workers).should == {}
44
+ end
45
+
46
+ it "should close the removed workers heartbeat file" do
47
+ ios = mock("IOStream")
48
+ ios.should_receive(:close).and_return(nil)
49
+ worker = ForkWorker::WorkerMonitor.new(1,ios)
50
+ ForkWorker.instance_variable_set :@workers, {1234=>worker}
51
+ ForkWorker.remove_worker(1234)
52
+ end
53
+
54
+ it "should not fail if the heartbeat file is already closed" do
55
+ ios = mock("IOStream")
56
+ ios.should_receive(:close).and_raise(IOError)
57
+ worker = ForkWorker::WorkerMonitor.new(1,ios)
58
+ ForkWorker.instance_variable_set :@workers, {1234=>worker}
59
+ lambda{ForkWorker.remove_worker(1234)}.should_not raise_error
60
+ end
61
+
62
+ end
63
+
64
+ describe "#add_worker" do
65
+
66
+ before :each do
67
+ ForkWorker.initial_setup({})
68
+ end
69
+
70
+ it "should add the new worker to the set" do
71
+ Process.stub!(:fork).and_return(1234)
72
+ ForkWorker.add_worker(1)
73
+ ForkWorker.instance_variable_get(:@workers).values.should include(1)
74
+ ForkWorker.instance_variable_get(:@workers).keys.should include(1234)
75
+ end
76
+
77
+ it "should run a new worker instance in a fork" do
78
+ ForkWorker.instance_variable_set :@workers, {}
79
+ Process.should_receive(:fork).with(no_args()).and_yield.and_return(1234)
80
+ ForkWorker.should_receive(:fork_cleanup).and_return(nil) #
81
+ ForkWorker.should_receive(:new).with(anything(),duck_type(:number,:heartbeat)).and_return(stub("Worker",:run=>nil))
82
+ ForkWorker.add_worker(1)
83
+ end
84
+
85
+ end
86
+
87
+ describe "#spawn_missing_workers" do
88
+
89
+ it "should add a worker with an empty set" do
90
+ ForkWorker.should_receive(:add_worker).with(0)
91
+ ForkWorker.instance_variable_set :@current_workers, 1
92
+ ForkWorker.instance_variable_set :@workers, {}
93
+ ForkWorker.spawn_missing_workers
94
+ end
95
+
96
+ it "should add a worker when there are fewer then needed" do
97
+ ForkWorker.should_receive(:add_worker).with(1)
98
+ ForkWorker.instance_variable_set :@current_workers, 2
99
+ ForkWorker.instance_variable_set :@workers, {1233=>ForkWorker::WorkerMonitor.new(0,nil)}
100
+ ForkWorker.spawn_missing_workers
101
+ end
102
+
103
+ it "should add a worker when one has gon missing" do
104
+ ForkWorker.should_receive(:add_worker).with(0)
105
+ ForkWorker.should_receive(:add_worker).with(2)
106
+ ForkWorker.instance_variable_set :@current_workers, 3
107
+ ForkWorker.instance_variable_set :@workers, {1233=>ForkWorker::WorkerMonitor.new(1,nil)}
108
+ ForkWorker.spawn_missing_workers
109
+ end
110
+
111
+ it "should not add workers if thier are already enough" do
112
+ ForkWorker.should_not_receive(:add_worker)
113
+ ForkWorker.instance_variable_set :@current_workers, 1
114
+ ForkWorker.instance_variable_set :@workers, {1233=>ForkWorker::WorkerMonitor.new(0,nil)}
115
+ ForkWorker.spawn_missing_workers
116
+ end
117
+
118
+ end
119
+
120
+ describe "#initial_setup" do
121
+
122
+ it "should set up a logger when one does not exist" do
123
+ ForkWorker.initial_setup({})
124
+ ForkWorker.logger.should_not be_nil
125
+ %w{debug info warn error fatal}.each do |n|
126
+ ForkWorker.logger.should respond_to(n.to_sym)
127
+ end
128
+ end
129
+
130
+ it "should set workers set to empty" do
131
+ ForkWorker.initial_setup({})
132
+ ForkWorker.instance_variable_get(:@workers).should be_empty
133
+ end
134
+
135
+ it "should create a pipe for children" do
136
+ pipe = ForkWorker.instance_variable_get(:@pipe)
137
+ pipe.length.should ==2
138
+ pipe.each {|io| io.should be_an IO}
139
+ end
140
+
141
+ end
142
+
143
+ describe "#handle_signal_queue" do
144
+
145
+ before :each do
146
+ ForkWorker.initial_setup({})
147
+ end
148
+
149
+ [:QUIT, :INT].each do |sig|
150
+ it "it should exicute a graceful shutdown on #{sig.to_s}" do
151
+ ForkWorker.should_receive(:stop).with(true)
152
+ ForkWorker.stub!(:awaken_master, true)
153
+
154
+ ForkWorker.queue_signal(sig)
155
+ ForkWorker.handle_signal_queue.should be_false
156
+ end
157
+ end
158
+
159
+ it "it should exicute a rapid shutdown on TERM" do
160
+ ForkWorker.should_receive(:stop).with(false)
161
+ ForkWorker.stub!(:awaken_master, true)
162
+
163
+ ForkWorker.queue_signal(:TERM)
164
+ ForkWorker.handle_signal_queue.should be_false
165
+ end
166
+
167
+ [:USR2, :DATA].each do |sig|
168
+ it "should write to the pipe on #{sig.to_s}" do
169
+ ForkWorker.queue_signal(sig)
170
+ ForkWorker.handle_signal_queue.should be_true
171
+ pipe = ForkWorker.instance_variable_get(:@pipe)
172
+ lambda{pipe.first.read(1)}.should_not raise_error
173
+ end
174
+ end
175
+
176
+ it "should do maintance when the queue is empty" do
177
+ ForkWorker.should_receive(:murder_lazy_workers)
178
+ ForkWorker.should_receive(:maintain_worker_count)
179
+ ForkWorker.should_receive(:master_sleep)
180
+
181
+ ForkWorker.handle_signal_queue.should be_true
182
+ end
183
+
184
+ it "should increase max_workers on TTIN and decrease on TTOU" do
185
+ max = ForkWorker.instance_variable_get(:@max_workers)
186
+ ForkWorker.queue_signal(:TTIN)
187
+ ForkWorker.handle_signal_queue.should be_true
188
+ ForkWorker.instance_variable_get(:@max_workers).should == max+1
189
+
190
+ ForkWorker.queue_signal(:TTOU)
191
+ ForkWorker.handle_signal_queue.should be_true
192
+ ForkWorker.instance_variable_get(:@max_workers).should == max
193
+ end
194
+
195
+ it "should never allow max_workers to be less then 1" do
196
+ ForkWorker.instance_variable_set(:@max_workers,1)
197
+ ForkWorker.queue_signal(:TTOU)
198
+ ForkWorker.handle_signal_queue.should be_true
199
+ ForkWorker.instance_variable_get(:@max_workers).should == 1
200
+ end
201
+
202
+ end
203
+
204
+ describe "Master loop control" do
205
+
206
+ before(:each) do
207
+ ForkWorker.initial_setup({})
208
+ end
209
+
210
+ describe "#master_sleep" do
211
+
212
+ it "should return to run maintance if there is no signal" do
213
+ IO.should_receive(:select).and_return(nil)
214
+ ForkWorker.master_sleep.should be_nil
215
+ ForkWorker.instance_variable_get(:@signal_queue).should be_empty
216
+ end
217
+
218
+ it "should return if there is data on self_pipe" do
219
+ self_pipe = ForkWorker.instance_variable_get(:@self_pipe)
220
+ IO.should_receive(:select).and_return([[self_pipe.first],[],[]])
221
+ ForkWorker.master_sleep.should be_nil
222
+ ForkWorker.instance_variable_get(:@signal_queue).should be_empty
223
+ end
224
+
225
+ it "should also add ':DATA' to queue when a stream other then self_pipe is ready." do
226
+ # pending "Testing Error stream not raising error?"
227
+ stream = stub('Extern IO')
228
+ stream.should_receive(:read_nonblock).and_raise(Errno::EAGAIN)
229
+ IO.should_receive(:select).and_return([[stream],[],[]])
230
+ ForkWorker.master_sleep.should be_nil
231
+ ForkWorker.instance_variable_get(:@signal_queue).should include(:DATA)
232
+ end
233
+
234
+ end
235
+
236
+ end
237
+
238
+
239
+ [true,false].each do |graceful|
240
+
241
+ describe "#stop(#{graceful})" do
242
+ before :each do
243
+ ForkWorker.initial_setup({})
244
+ end
245
+
246
+ describe "with no workers" do
247
+ it "should not fail" do
248
+ [true,false].each {|graceful| lambda{ForkWorker.stop(graceful)}.should_not raise_error}
249
+ end
250
+
251
+ it "should not signal any workers" do
252
+ ForkWorker.should_not_receive(:signal_worker)
253
+ [true,false].each {|graceful| ForkWorker.stop(graceful)}
254
+ end
255
+ end
256
+
257
+ describe "with workers" do
258
+
259
+ before :each do
260
+ ForkWorker.instance_variable_set :@workers,
261
+ {
262
+ 1233=>ForkWorker::WorkerMonitor.new(0,fake_iostream),
263
+ 1234=>ForkWorker::WorkerMonitor.new(0,fake_iostream)
264
+ }
265
+ Process.stub!(:waitpid2) do |_1,_2|
266
+ [ ForkWorker.instance_variable_get(:@workers).keys.first,
267
+ fake_process_status
268
+ ]
269
+ end
270
+ end
271
+
272
+ it "should signals each worker to end" do
273
+ ForkWorker.instance_variable_get(:@workers).keys.each do |pid|
274
+ ForkWorker.should_receive(:signal_worker).with(graceful ? :QUIT : :TERM,pid)
275
+ end
276
+ ForkWorker.stop(graceful)
277
+ end
278
+
279
+ it "should not kill workers that successfully quit" do
280
+ ForkWorker.should_not_receive(:signal_worker).with(:KILL,anything())
281
+ ForkWorker.stop(graceful)
282
+ end
283
+
284
+ end
285
+
286
+ end
287
+
288
+ end
289
+
290
+ end
data/spec/lock_spec.rb CHANGED
@@ -2,23 +2,29 @@ require File.join( File.dirname(__FILE__), "spec_helper" )
2
2
 
3
3
  include Updater
4
4
 
5
+ require File.join( File.dirname(__FILE__), "fooclass" )
6
+
5
7
  describe "Update Locking:" do
6
8
 
7
- class Foo
8
- include DataMapper::Resource
9
-
10
- property :id, Serial
11
- property :name, String
9
+
10
+ class Worker
11
+ attr_accessor :pid
12
+ attr_accessor :name
12
13
 
13
- def bar(*args)
14
- Foo.bar(:instance,*args)
14
+ def initialize(options={})
15
+ @quiet = options[:quiet]
16
+ @name = options[:name] || "host:#{Socket.gethostname} pid:#{Process.pid}" rescue "pid:#{Process.pid}"
17
+ @pid = Process.pid
15
18
  end
16
19
 
17
- end
18
-
19
- Foo.auto_migrate!
20
+ def say(text)
21
+ puts text
22
+ nil
23
+ end
24
+ end
20
25
 
21
26
  before :each do
27
+ Foo.all.destroy!
22
28
  @u = Update.immidiate(Foo,:bar,[])
23
29
  @w = Worker.new(:name=>"first", :quiet=>true)
24
30
  end
@@ -44,36 +50,13 @@ describe "Update Locking:" do
44
50
  @u.lock(@w).should be_true
45
51
  @u.lock(@w).should be_true
46
52
  end
47
-
48
- describe "#run_with_lock" do
49
-
50
- it "should run an unlocked record" do
51
- u = Update.immidiate(Foo,:bar,[:arg1,:arg2])
52
- Foo.should_receive(:bar).with(:arg1,:arg2)
53
- u.run_with_lock(@w).should be_true
54
- end
55
-
56
- it "should NOT run an already locked record" do
57
- u = Update.immidiate(Foo,:bar,[:arg1,:arg2])
58
- u.lock(Worker.new)
59
- Foo.should_not_receive(:bar)
60
- u.run_with_lock(@w).should be_nil
61
- end
62
-
63
- it "should return false if the update ran but there was an error" do
64
- u = Update.immidiate(Foo,:bar,[:arg1,:arg2])
65
- Foo.should_receive(:bar).with(:arg1,:arg2).and_raise(RuntimeError)
66
- u.run_with_lock(@w).should be_false
67
- end
68
-
69
- end
70
-
53
+
71
54
  it "#clear_locks should clear all locks from a worker" do
72
55
  @v = Update.immidiate(Foo,:bar,[:arg1,:arg2])
73
56
  @u.lock(@w)
74
57
  @v.lock(@w)
75
58
  @u.locked?.should be_true
76
- @w.clear_locks
59
+ Update.clear_locks(@w)
77
60
  @u.reload.locked?.should be_false
78
61
  @v.reload.locked?.should be_false
79
62
  end
@@ -0,0 +1,36 @@
1
+ require File.join( File.dirname(__FILE__), "spec_helper" )
2
+
3
+ include Updater
4
+
5
+ require File.join( File.dirname(__FILE__), "fooclass" )
6
+
7
+ describe "named request" do
8
+
9
+ before(:each) do
10
+ Foo.all.destroy!
11
+ end
12
+
13
+ it "should be found by name when target is an instance" do
14
+ f = Foo.create(:name=>'Honey')
15
+ u = Update.immidiate(f,:bar,[:named],:name=>'Now')
16
+ u.name.should ==("Now")
17
+ pending "'for' not implemented"
18
+ Update.for(f, "Now").should ==(u)
19
+ end
20
+
21
+ it "should be found by name when target is a class" do
22
+ u = Update.immidiate(Foo,:bar,[:named],:name=>'Now')
23
+ u.name.should ==("Now")
24
+ pending "'for' not implemented"
25
+ Update.for(Foo, "Now").should ==(u)
26
+ end
27
+
28
+ it "should return all updates for a given target" do
29
+ u1 = Update.immidiate(Foo,:bar,[:arg1,:arg2])
30
+ u2 = Update.immidiate(Foo,:bar,[:arg3,:arg4])
31
+ pending "'for' not implemented"
32
+ Update.for(Foo).should include(u1,u2)
33
+ end
34
+
35
+
36
+ end
@@ -0,0 +1,27 @@
1
+ require File.join( File.dirname(__FILE__), "spec_helper" )
2
+
3
+ include Updater
4
+
5
+ require File.join( File.dirname(__FILE__), "fooclass" )
6
+
7
+ describe "Special Parameter Substitution" do
8
+ before :each do
9
+ Update.clear_all
10
+ @u = Update.chain(Foo,:chained, [:__job__,:__params__,:__self__, 'job params'])
11
+ end
12
+
13
+ it "should substitute __job__ with job that chained in" do
14
+ Foo.should_receive(:chained).with(:arg1,anything(),anything(),'job params')
15
+ @u.run(:arg1)
16
+ end
17
+
18
+ it "should substitute __params__ with params" do
19
+ Foo.should_receive(:chained).with(anything(),:arg2,anything(), 'job params')
20
+ @u.run(:arg1,:arg2)
21
+ end
22
+
23
+ it "should substitute __self__ with the current job" do
24
+ Foo.should_receive(:chained).with(anything(),anything(),@u, 'job params')
25
+ @u.run
26
+ end
27
+ end
@@ -0,0 +1,89 @@
1
+ require File.join( File.dirname(__FILE__), "spec_helper" )
2
+
3
+ include Updater
4
+
5
+ require File.join( File.dirname(__FILE__), "fooclass" )
6
+
7
+ describe "adding an immidiate update request" do
8
+ before(:each) do
9
+ Foo.all.destroy!
10
+ end
11
+ it "with a class target" do
12
+ u = Update.immidiate(Foo,:bar,[])
13
+ u.target.should == Foo
14
+ Update.current.get(u.id).should_not be_nil
15
+ Update.delayed.should == 0
16
+ end
17
+
18
+ it "with an conforming instance target" do
19
+ f = Foo.create
20
+ u = Update.immidiate(f,:bar,[])
21
+ u.target.should == f
22
+ Update.current.get(u.id).should_not be_nil
23
+ Update.delayed.should == 0
24
+ end
25
+
26
+ it "with an custome finder" do
27
+ f = Foo.create(:name=>'baz')
28
+ u = Update.immidiate(Foo,:bar,[],:finder=>:first, :finder_args=>[{:name=>'baz'}])
29
+ u.target.should == f
30
+ Update.current.get(u.id).should_not be_nil
31
+ Update.delayed.should == 0
32
+ end
33
+
34
+ end
35
+
36
+ describe "chained request" do
37
+ before :each do
38
+ Update.clear_all
39
+ end
40
+
41
+ it "should not be in current or delayed queue" do
42
+ u = Update.chain(Foo,:bar,[:error])
43
+ u.time.should be_nil
44
+ Update.current.should_not include(u)
45
+ Update.delayed.should == 0
46
+ end
47
+
48
+ it "should be persistant" do
49
+ u = Update.chain(Foo,:bar,[:error])
50
+ u.should be_persistant
51
+ end
52
+
53
+ end
54
+
55
+ describe "adding an delayed update request" do
56
+ before :each do
57
+ Update.clear_all
58
+ Foo.all.destroy
59
+ end
60
+
61
+
62
+ it "with a class target" do
63
+ u = Update.at(Chronic.parse('tomorrow'),Foo,:bar,[])
64
+ u.target.should == Foo
65
+ Update.current.should_not include(u)
66
+ Update.delayed.should == 1
67
+ end
68
+
69
+ it "with an conforming instance target" do
70
+ f = Foo.create
71
+ u = Update.at(Chronic.parse('tomorrow'),f,:bar,[])
72
+ u.target.should == f
73
+ Update.current.should_not include(u)
74
+ Update.delayed.should == 1
75
+ end
76
+
77
+ it "with an custome finder" do
78
+ f = Foo.create(:name=>'baz')
79
+ u = Update.at(Chronic.parse('tomorrow'),Foo,:bar,[],:finder=>:first, :finder_args=>[{:name=>'baz'}])
80
+ u.target.should == f
81
+ Update.current.should_not include(u)
82
+ Update.delayed.should == 1
83
+ end
84
+
85
+ end
86
+
87
+
88
+
89
+
data/spec/spec_helper.rb CHANGED
@@ -7,7 +7,11 @@ require "spec" # Satisfies Autotest and anyone else not using the Rake tasks
7
7
  require "dm-core"
8
8
 
9
9
  require 'updater'
10
- require 'updater/worker'
10
+ require 'updater/thread_worker'
11
+ require 'updater/fork_worker'
12
+ require 'updater/orm/datamapper'
13
+
14
+ Updater::Update.orm = Updater::ORM::DataMapper
11
15
 
12
16
  DataMapper.setup(:default, 'sqlite3::memory:')
13
17
  DataMapper.auto_migrate!
@@ -15,3 +19,4 @@ DataMapper.auto_migrate!
15
19
  require 'timecop'
16
20
  require 'chronic'
17
21
 
22
+
@@ -2,10 +2,10 @@ require File.join( File.dirname(__FILE__), "spec_helper" )
2
2
 
3
3
  include Updater
4
4
 
5
- describe Worker do
5
+ describe ThreadWorker do
6
6
 
7
7
  it "should not print anything when quiet" do
8
- w = Worker.new :quiet=>true
8
+ w = ThreadWorker.new :quiet=>true
9
9
  out = StringIO.new
10
10
  $stdout = out
11
11
  w.say "hello world"
@@ -14,7 +14,7 @@ describe Worker do
14
14
  end
15
15
 
16
16
  it "should have a name" do
17
- Worker.new.name.should be_a String
17
+ ThreadWorker.new.name.should be_a String
18
18
  end
19
19
 
20
20
  describe "loop thread control:" do
@@ -35,7 +35,7 @@ describe Worker do
35
35
 
36
36
  specify "The loop should run when the worker is started" do
37
37
  pending
38
- worker = Worker.new(:quiet=>true, :name=>"testing")
38
+ worker = ThreadWorker.new(:quiet=>true, :name=>"testing")
39
39
  Update.should_receive(:work_off).with(worker).once.and_return(nil)
40
40
  t = Thread.new do
41
41
  worker.start
@@ -73,13 +73,13 @@ describe "working off jobs:" do
73
73
  describe "Update#work_off" do
74
74
 
75
75
  before :each do
76
- Update.all.destroy!
76
+ Update.clear_all
77
77
  end
78
78
 
79
79
  it "should run and immidiate job"do
80
80
  u = Update.immidiate(Foo,:bar,[:arg1,:arg2])
81
81
  Foo.should_receive(:bar).with(:arg1,:arg2)
82
- Update.work_off(Worker.new)
82
+ Update.work_off(ThreadWorker.new)
83
83
  end
84
84
 
85
85
  it "should aviod conflicts among mutiple workers" do
@@ -87,14 +87,14 @@ describe "working off jobs:" do
87
87
  u2 = Update.immidiate(Foo,:baz,[:arg2])
88
88
  Foo.should_receive(:bar).with(:arg1)
89
89
  Foo.should_receive(:baz).with(:arg2)
90
- Update.work_off(Worker.new(:name=>"first", :quiet=>true))
91
- Update.work_off(Worker.new(:name=>"second", :quiet=>true))
90
+ Update.work_off(ThreadWorker.new(:name=>"first", :quiet=>true))
91
+ Update.work_off(ThreadWorker.new(:name=>"second", :quiet=>true))
92
92
  end
93
93
 
94
94
  it "should return 0 if there are more jobs waiting" do
95
95
  u1 = Update.immidiate(Foo,:bar,[:arg1])
96
96
  u2 = Update.immidiate(Foo,:baz,[:arg2])
97
- Update.work_off(Worker.new(:name=>"first", :quiet=>true)).should == 0
97
+ Update.work_off(ThreadWorker.new(:name=>"first", :quiet=>true)).should == 0
98
98
  end
99
99
 
100
100
  it "should return the number of seconds till the next job if there are no jobs to be run" do
@@ -102,12 +102,12 @@ describe "working off jobs:" do
102
102
  u1 = Update.at(Time.now + 30, Foo,:bar,[:arg1])
103
103
  Update.at(Time.now + 35, Foo,:bar,[:arg1])
104
104
  Update.at(Time.now + 1000, Foo,:bar,[:arg1])
105
- Update.work_off(Worker.new(:name=>"first", :quiet=>true)).should == 30
105
+ Update.work_off(ThreadWorker.new(:name=>"first", :quiet=>true)).should == 30
106
106
  end
107
107
 
108
108
  it "should return nil if the job queue is empty" do
109
109
  u1 = Update.immidiate(Foo,:bar,[:arg1])
110
- Update.work_off(Worker.new(:name=>"first", :quiet=>true)).should be_nil
110
+ Update.work_off(ThreadWorker.new(:name=>"first", :quiet=>true)).should be_nil
111
111
  end
112
112
 
113
113
  end
@@ -0,0 +1,48 @@
1
+ require File.join( File.dirname(__FILE__), "spec_helper" )
2
+
3
+ include Updater
4
+
5
+ require File.join( File.dirname(__FILE__), "fooclass" )
6
+
7
+ describe "running an update" do
8
+
9
+ before :each do
10
+ Update.clear_all
11
+ Foo.all.destroy!
12
+ end
13
+
14
+ it "should call the named method with a class target" do
15
+ u = Update.immidiate(Foo,:bar,[:arg1,:arg2])
16
+ Foo.should_receive(:bar).with(:arg1,:arg2)
17
+ u.run
18
+ end
19
+
20
+ it "should call the named method with an conforming instance target" do
21
+ f = Foo.create
22
+ u = Update.immidiate(f,:bar,[:arg1,:arg2])
23
+ Foo.should_receive(:bar).with(:instance,:arg1,:arg2)
24
+ u.run
25
+ end
26
+
27
+ it "should delete the record once it is run" do
28
+ u = Update.immidiate(Foo,:bar,[:arg1,:arg2])
29
+ Foo.should_receive(:bar).with(:arg1,:arg2)
30
+ u.run
31
+ u.should_not be_saved #NOTE: not a theological statment
32
+ end
33
+
34
+ it "should delete the record if there is a failure" do
35
+ u = Update.immidiate(Foo,:bar,[:arg1,:arg2])
36
+ Foo.should_receive(:bar).with(:arg1,:arg2).and_raise(RuntimeError)
37
+ u.run
38
+ u.should_not be_saved #NOTE: not a theological statment
39
+ end
40
+
41
+ it "should NOT delete the record if it is a chain record" do
42
+ u = Update.chain(Foo,:bar,[:arg1,:arg2])
43
+ Foo.should_receive(:bar).with(:arg1,:arg2).and_raise(RuntimeError)
44
+ u.run
45
+ u.should be_saved
46
+ end
47
+
48
+ end