updater 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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