forked from collectiveidea/delayed_job
-
Notifications
You must be signed in to change notification settings - Fork 0
/
worker_spec.rb
216 lines (177 loc) · 7.59 KB
/
worker_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
require 'spec_helper'
describe Delayed::Worker do
def job_create(opts = {})
Delayed::Job.create(opts.merge(:payload_object => SimpleJob.new))
end
describe "backend=" do
it "should set the Delayed::Job constant to the backend" do
@clazz = Class.new
Delayed::Worker.backend = @clazz
Delayed::Job.should == @clazz
end
it "should set backend with a symbol" do
Delayed::Worker.backend = Class.new
Delayed::Worker.backend = :active_record
Delayed::Worker.backend.should == Delayed::Backend::ActiveRecord::Job
end
end
BACKENDS.each do |backend|
describe "with the #{backend} backend" do
before do
Delayed::Worker.backend = backend
Delayed::Job.delete_all
@worker = Delayed::Worker.new(:max_priority => nil, :min_priority => nil, :quiet => true)
SimpleJob.runs = 0
end
describe "running a job" do
it "should fail after Worker.max_run_time" do
begin
old_max_run_time = Delayed::Worker.max_run_time
Delayed::Worker.max_run_time = 1.second
@job = Delayed::Job.create :payload_object => LongRunningJob.new
@worker.run(@job)
@job.reload.last_error.should =~ /expired/
@job.attempts.should == 1
ensure
Delayed::Worker.max_run_time = old_max_run_time
end
end
end
context "worker prioritization" do
before(:each) do
@worker = Delayed::Worker.new(:max_priority => 5, :min_priority => -5, :quiet => true)
end
it "should only work_off jobs that are >= min_priority" do
SimpleJob.runs.should == 0
job_create(:priority => -10)
job_create(:priority => 0)
@worker.work_off
SimpleJob.runs.should == 1
end
it "should only work_off jobs that are <= max_priority" do
SimpleJob.runs.should == 0
job_create(:priority => 10)
job_create(:priority => 0)
@worker.work_off
SimpleJob.runs.should == 1
end
end
context "while running with locked and expired jobs" do
before(:each) do
@worker.name = 'worker1'
end
it "should not run jobs locked by another worker" do
job_create(:locked_by => 'other_worker', :locked_at => (Delayed::Job.db_time_now - 1.minutes))
lambda { @worker.work_off }.should_not change { SimpleJob.runs }
end
it "should run open jobs" do
job_create
lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1)
end
it "should run expired jobs" do
expired_time = Delayed::Job.db_time_now - (1.minutes + Delayed::Worker.max_run_time)
job_create(:locked_by => 'other_worker', :locked_at => expired_time)
lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1)
end
it "should run own jobs" do
job_create(:locked_by => @worker.name, :locked_at => (Delayed::Job.db_time_now - 1.minutes))
lambda { @worker.work_off }.should change { SimpleJob.runs }.from(0).to(1)
end
end
describe "failed jobs" do
before do
# reset defaults
Delayed::Worker.destroy_failed_jobs = true
Delayed::Worker.max_attempts = 25
@job = Delayed::Job.enqueue ErrorJob.new
end
it "should record last_error when destroy_failed_jobs = false, max_attempts = 1" do
Delayed::Worker.destroy_failed_jobs = false
Delayed::Worker.max_attempts = 1
@worker.run(@job)
@job.reload
@job.last_error.should =~ /did not work/
@job.last_error.should =~ /worker_spec.rb/
@job.attempts.should == 1
@job.failed_at.should_not be_nil
end
it "should re-schedule jobs after failing" do
@worker.run(@job)
@job.reload
@job.last_error.should =~ /did not work/
@job.last_error.should =~ /sample_jobs.rb:8:in `perform'/
@job.attempts.should == 1
@job.run_at.should > Delayed::Job.db_time_now - 10.minutes
@job.run_at.should < Delayed::Job.db_time_now + 10.minutes
end
end
context "reschedule" do
before do
@job = Delayed::Job.create :payload_object => SimpleJob.new
end
share_examples_for "any failure more than Worker.max_attempts times" do
context "when the job's payload has an #on_permanent_failure hook" do
before do
@job = Delayed::Job.create :payload_object => OnPermanentFailureJob.new
@job.payload_object.should respond_to :on_permanent_failure
end
it "should run that hook" do
@job.payload_object.should_receive :on_permanent_failure
Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
end
end
context "when the job's payload has no #on_permanent_failure hook" do
# It's a little tricky to test this in a straightforward way,
# because putting a should_not_receive expectation on
# @job.payload_object.on_permanent_failure makes that object
# incorrectly return true to
# payload_object.respond_to? :on_permanent_failure, which is what
# reschedule uses to decide whether to call on_permanent_failure.
# So instead, we just make sure that the payload_object as it
# already stands doesn't respond_to? on_permanent_failure, then
# shove it through the iterated reschedule loop and make sure we
# don't get a NoMethodError (caused by calling that nonexistent
# on_permanent_failure method).
before do
@job.payload_object.should_not respond_to(:on_permanent_failure)
end
it "should not try to run that hook" do
lambda do
Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
end.should_not raise_exception(NoMethodError)
end
end
end
context "and we want to destroy jobs" do
before do
Delayed::Worker.destroy_failed_jobs = true
end
it_should_behave_like "any failure more than Worker.max_attempts times"
it "should be destroyed if it failed more than Worker.max_attempts times" do
@job.should_receive(:destroy)
Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
end
it "should not be destroyed if failed fewer than Worker.max_attempts times" do
@job.should_not_receive(:destroy)
(Delayed::Worker.max_attempts - 1).times { @worker.reschedule(@job) }
end
end
context "and we don't want to destroy jobs" do
before do
Delayed::Worker.destroy_failed_jobs = false
end
it_should_behave_like "any failure more than Worker.max_attempts times"
it "should be failed if it failed more than Worker.max_attempts times" do
@job.reload.failed_at.should == nil
Delayed::Worker.max_attempts.times { @worker.reschedule(@job) }
@job.reload.failed_at.should_not == nil
end
it "should not be failed if it failed fewer than Worker.max_attempts times" do
(Delayed::Worker.max_attempts - 1).times { @worker.reschedule(@job) }
@job.reload.failed_at.should == nil
end
end
end
end
end
end