resque-cedar 1.20.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.
Files changed (64) hide show
  1. data/HISTORY.md +354 -0
  2. data/LICENSE +20 -0
  3. data/README.markdown +908 -0
  4. data/Rakefile +70 -0
  5. data/bin/resque +81 -0
  6. data/bin/resque-web +27 -0
  7. data/lib/resque.rb +385 -0
  8. data/lib/resque/coder.rb +27 -0
  9. data/lib/resque/errors.rb +10 -0
  10. data/lib/resque/failure.rb +96 -0
  11. data/lib/resque/failure/airbrake.rb +17 -0
  12. data/lib/resque/failure/base.rb +64 -0
  13. data/lib/resque/failure/hoptoad.rb +33 -0
  14. data/lib/resque/failure/multiple.rb +54 -0
  15. data/lib/resque/failure/redis.rb +51 -0
  16. data/lib/resque/failure/thoughtbot.rb +33 -0
  17. data/lib/resque/helpers.rb +64 -0
  18. data/lib/resque/job.rb +223 -0
  19. data/lib/resque/multi_json_coder.rb +37 -0
  20. data/lib/resque/multi_queue.rb +73 -0
  21. data/lib/resque/plugin.rb +66 -0
  22. data/lib/resque/queue.rb +117 -0
  23. data/lib/resque/server.rb +248 -0
  24. data/lib/resque/server/public/favicon.ico +0 -0
  25. data/lib/resque/server/public/idle.png +0 -0
  26. data/lib/resque/server/public/jquery-1.3.2.min.js +19 -0
  27. data/lib/resque/server/public/jquery.relatize_date.js +95 -0
  28. data/lib/resque/server/public/poll.png +0 -0
  29. data/lib/resque/server/public/ranger.js +73 -0
  30. data/lib/resque/server/public/reset.css +44 -0
  31. data/lib/resque/server/public/style.css +86 -0
  32. data/lib/resque/server/public/working.png +0 -0
  33. data/lib/resque/server/test_helper.rb +19 -0
  34. data/lib/resque/server/views/error.erb +1 -0
  35. data/lib/resque/server/views/failed.erb +67 -0
  36. data/lib/resque/server/views/key_sets.erb +19 -0
  37. data/lib/resque/server/views/key_string.erb +11 -0
  38. data/lib/resque/server/views/layout.erb +44 -0
  39. data/lib/resque/server/views/next_more.erb +10 -0
  40. data/lib/resque/server/views/overview.erb +4 -0
  41. data/lib/resque/server/views/queues.erb +49 -0
  42. data/lib/resque/server/views/stats.erb +62 -0
  43. data/lib/resque/server/views/workers.erb +109 -0
  44. data/lib/resque/server/views/working.erb +72 -0
  45. data/lib/resque/stat.rb +53 -0
  46. data/lib/resque/tasks.rb +61 -0
  47. data/lib/resque/version.rb +3 -0
  48. data/lib/resque/worker.rb +557 -0
  49. data/lib/tasks/redis.rake +161 -0
  50. data/lib/tasks/resque.rake +2 -0
  51. data/test/airbrake_test.rb +26 -0
  52. data/test/hoptoad_test.rb +26 -0
  53. data/test/job_hooks_test.rb +423 -0
  54. data/test/job_plugins_test.rb +230 -0
  55. data/test/multi_queue_test.rb +95 -0
  56. data/test/plugin_test.rb +116 -0
  57. data/test/redis-test-cluster.conf +115 -0
  58. data/test/redis-test.conf +115 -0
  59. data/test/redis_queue_test.rb +133 -0
  60. data/test/resque-web_test.rb +59 -0
  61. data/test/resque_test.rb +284 -0
  62. data/test/test_helper.rb +135 -0
  63. data/test/worker_test.rb +443 -0
  64. metadata +188 -0
@@ -0,0 +1,161 @@
1
+ # Inspired by rabbitmq.rake the Redbox project at http://github.com/rick/redbox/tree/master
2
+ require 'fileutils'
3
+ require 'open-uri'
4
+ require 'pathname'
5
+
6
+ class RedisRunner
7
+ def self.redis_dir
8
+ @redis_dir ||= if ENV['PREFIX']
9
+ Pathname.new(ENV['PREFIX'])
10
+ else
11
+ Pathname.new(`which redis-server`) + '..' + '..'
12
+ end
13
+ end
14
+
15
+ def self.bin_dir
16
+ redis_dir + 'bin'
17
+ end
18
+
19
+ def self.config
20
+ @config ||= if File.exists?(redis_dir + 'etc/redis.conf')
21
+ redis_dir + 'etc/redis.conf'
22
+ else
23
+ redis_dir + '../etc/redis.conf'
24
+ end
25
+ end
26
+
27
+ def self.dtach_socket
28
+ '/tmp/redis.dtach'
29
+ end
30
+
31
+ # Just check for existance of dtach socket
32
+ def self.running?
33
+ File.exists? dtach_socket
34
+ end
35
+
36
+ def self.start
37
+ puts 'Detach with Ctrl+\ Re-attach with rake redis:attach'
38
+ sleep 1
39
+ command = "#{bin_dir}/dtach -A #{dtach_socket} #{bin_dir}/redis-server #{config}"
40
+ sh command
41
+ end
42
+
43
+ def self.attach
44
+ exec "#{bin_dir}/dtach -a #{dtach_socket}"
45
+ end
46
+
47
+ def self.stop
48
+ sh 'echo "SHUTDOWN" | nc localhost 6379'
49
+ end
50
+ end
51
+
52
+ INSTALL_DIR = ENV['INSTALL_DIR'] || '/tmp/redis'
53
+
54
+ namespace :redis do
55
+ desc 'About redis'
56
+ task :about do
57
+ puts "\nSee http://code.google.com/p/redis/ for information about redis.\n\n"
58
+ end
59
+
60
+ desc 'Start redis'
61
+ task :start do
62
+ RedisRunner.start
63
+ end
64
+
65
+ desc 'Stop redis'
66
+ task :stop do
67
+ RedisRunner.stop
68
+ end
69
+
70
+ desc 'Restart redis'
71
+ task :restart do
72
+ RedisRunner.stop
73
+ RedisRunner.start
74
+ end
75
+
76
+ desc 'Attach to redis dtach socket'
77
+ task :attach do
78
+ RedisRunner.attach
79
+ end
80
+
81
+ desc <<-DOC
82
+ Install the latest verison of Redis from Github (requires git, duh).
83
+ Use INSTALL_DIR env var like "rake redis:install INSTALL_DIR=~/tmp"
84
+ in order to get an alternate location for your install files.
85
+ DOC
86
+
87
+ task :install => [:about, :download, :make] do
88
+ bin_dir = '/usr/bin'
89
+ conf_dir = '/etc'
90
+
91
+ if ENV['PREFIX']
92
+ bin_dir = "#{ENV['PREFIX']}/bin"
93
+ sh "mkdir -p #{bin_dir}" unless File.exists?("#{bin_dir}")
94
+
95
+ conf_dir = "#{ENV['PREFIX']}/etc"
96
+ sh "mkdir -p #{conf_dir}" unless File.exists?("#{conf_dir}")
97
+ end
98
+
99
+ %w(redis-benchmark redis-cli redis-server).each do |bin|
100
+ sh "cp #{INSTALL_DIR}/src/#{bin} #{bin_dir}"
101
+ end
102
+
103
+ puts "Installed redis-benchmark, redis-cli and redis-server to #{bin_dir}"
104
+
105
+ unless File.exists?("#{conf_dir}/redis.conf")
106
+ sh "cp #{INSTALL_DIR}/redis.conf #{conf_dir}/redis.conf"
107
+ puts "Installed redis.conf to #{conf_dir} \n You should look at this file!"
108
+ end
109
+ end
110
+
111
+ task :make do
112
+ sh "cd #{INSTALL_DIR}/src && make clean"
113
+ sh "cd #{INSTALL_DIR}/src && make"
114
+ end
115
+
116
+ desc "Download package"
117
+ task :download do
118
+ sh "rm -rf #{INSTALL_DIR}/" if File.exists?("#{INSTALL_DIR}/.svn")
119
+ sh "git clone git://github.com/antirez/redis.git #{INSTALL_DIR}" unless File.exists?(INSTALL_DIR)
120
+ sh "cd #{INSTALL_DIR} && git pull" if File.exists?("#{INSTALL_DIR}/.git")
121
+ end
122
+ end
123
+
124
+ namespace :dtach do
125
+ desc 'About dtach'
126
+ task :about do
127
+ puts "\nSee http://dtach.sourceforge.net/ for information about dtach.\n\n"
128
+ end
129
+
130
+ desc 'Install dtach 0.8 from source'
131
+ task :install => [:about, :download, :make] do
132
+
133
+ bin_dir = "/usr/bin"
134
+
135
+
136
+ if ENV['PREFIX']
137
+ bin_dir = "#{ENV['PREFIX']}/bin"
138
+ sh "mkdir -p #{bin_dir}" unless File.exists?("#{bin_dir}")
139
+ end
140
+
141
+ sh "cp #{INSTALL_DIR}/dtach-0.8/dtach #{bin_dir}"
142
+ end
143
+
144
+ task :make do
145
+ sh "cd #{INSTALL_DIR}/dtach-0.8/ && ./configure && make"
146
+ end
147
+
148
+ desc "Download package"
149
+ task :download do
150
+ unless File.exists?("#{INSTALL_DIR}/dtach-0.8.tar.gz")
151
+ require 'net/http'
152
+
153
+ url = 'http://downloads.sourceforge.net/project/dtach/dtach/0.8/dtach-0.8.tar.gz'
154
+ open("#{INSTALL_DIR}/dtach-0.8.tar.gz", 'wb') do |file| file.write(open(url).read) end
155
+ end
156
+
157
+ unless File.directory?("#{INSTALL_DIR}/dtach-0.8")
158
+ sh "cd #{INSTALL_DIR} && tar xzf dtach-0.8.tar.gz"
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../../lib'
2
+ require 'resque/tasks'
@@ -0,0 +1,26 @@
1
+ require 'test_helper'
2
+
3
+ begin
4
+ require 'airbrake'
5
+ rescue LoadError
6
+ warn "Install airbrake gem to run Airbrake tests."
7
+ end
8
+
9
+ if defined? Airbrake
10
+ require 'resque/failure/airbrake'
11
+ describe "Airbrake" do
12
+ it "should be notified of an error" do
13
+ exception = StandardError.new("BOOM")
14
+ worker = Resque::Worker.new(:test)
15
+ queue = "test"
16
+ payload = {'class' => Object, 'args' => 66}
17
+
18
+ Airbrake.expects(:notify_or_ignore).with(
19
+ exception,
20
+ :parameters => {:payload_class => 'Object', :payload_args => '66'})
21
+
22
+ backend = Resque::Failure::Airbrake.new(exception, worker, queue, payload)
23
+ backend.save
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ require 'test_helper'
2
+
3
+ begin
4
+ require 'hoptoad_notifier'
5
+ rescue LoadError
6
+ warn "Install hoptoad_notifier gem to run Hoptoad tests."
7
+ end
8
+
9
+ if defined? HoptoadNotifier
10
+ require 'resque/failure/hoptoad'
11
+ describe "Hoptoad" do
12
+ it "should be notified of an error" do
13
+ exception = StandardError.new("BOOM")
14
+ worker = Resque::Worker.new(:test)
15
+ queue = "test"
16
+ payload = {'class' => Object, 'args' => 66}
17
+
18
+ HoptoadNotifier.expects(:notify_or_ignore).with(
19
+ exception,
20
+ :parameters => {:payload_class => 'Object', :payload_args => '66'})
21
+
22
+ backend = Resque::Failure::Hoptoad.new(exception, worker, queue, payload)
23
+ backend.save
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,423 @@
1
+ require 'test_helper'
2
+
3
+ describe "Resque::Job before_perform" do
4
+ include PerformJob
5
+
6
+ class ::BeforePerformJob
7
+ def self.before_perform_record_history(history)
8
+ history << :before_perform
9
+ end
10
+
11
+ def self.perform(history)
12
+ history << :perform
13
+ end
14
+ end
15
+
16
+ it "it runs before_perform before perform" do
17
+ result = perform_job(BeforePerformJob, history=[])
18
+ assert_equal true, result, "perform returned true"
19
+ assert_equal history, [:before_perform, :perform]
20
+ end
21
+
22
+ class ::BeforePerformJobFails
23
+ def self.before_perform_fail_job(history)
24
+ history << :before_perform
25
+ raise StandardError
26
+ end
27
+ def self.perform(history)
28
+ history << :perform
29
+ end
30
+ end
31
+
32
+ it "raises an error and does not perform if before_perform fails" do
33
+ history = []
34
+ assert_raises StandardError do
35
+ perform_job(BeforePerformJobFails, history)
36
+ end
37
+ assert_equal history, [:before_perform], "Only before_perform was run"
38
+ end
39
+
40
+ class ::BeforePerformJobAborts
41
+ def self.before_perform_abort(history)
42
+ history << :before_perform
43
+ raise Resque::Job::DontPerform
44
+ end
45
+ def self.perform(history)
46
+ history << :perform
47
+ end
48
+ end
49
+
50
+ it "does not perform if before_perform raises Resque::Job::DontPerform" do
51
+ result = perform_job(BeforePerformJobAborts, history=[])
52
+ assert_equal false, result, "perform returned false"
53
+ assert_equal history, [:before_perform], "Only before_perform was run"
54
+ end
55
+ end
56
+
57
+ describe "Resque::Job after_perform" do
58
+ include PerformJob
59
+
60
+ class ::AfterPerformJob
61
+ def self.perform(history)
62
+ history << :perform
63
+ end
64
+ def self.after_perform_record_history(history)
65
+ history << :after_perform
66
+ end
67
+ end
68
+
69
+ it "it runs after_perform after perform" do
70
+ result = perform_job(AfterPerformJob, history=[])
71
+ assert_equal true, result, "perform returned true"
72
+ assert_equal history, [:perform, :after_perform]
73
+ end
74
+
75
+ class ::AfterPerformJobFails
76
+ def self.perform(history)
77
+ history << :perform
78
+ end
79
+ def self.after_perform_fail_job(history)
80
+ history << :after_perform
81
+ raise StandardError
82
+ end
83
+ end
84
+
85
+ it "raises an error but has already performed if after_perform fails" do
86
+ history = []
87
+ assert_raises StandardError do
88
+ perform_job(AfterPerformJobFails, history)
89
+ end
90
+ assert_equal history, [:perform, :after_perform], "Only after_perform was run"
91
+ end
92
+ end
93
+
94
+ describe "Resque::Job around_perform" do
95
+ include PerformJob
96
+
97
+ class ::AroundPerformJob
98
+ def self.perform(history)
99
+ history << :perform
100
+ end
101
+ def self.around_perform_record_history(history)
102
+ history << :start_around_perform
103
+ yield
104
+ history << :finish_around_perform
105
+ end
106
+ end
107
+
108
+ it "it runs around_perform then yields in order to perform" do
109
+ result = perform_job(AroundPerformJob, history=[])
110
+ assert_equal true, result, "perform returned true"
111
+ assert_equal history, [:start_around_perform, :perform, :finish_around_perform]
112
+ end
113
+
114
+ class ::AroundPerformJobFailsBeforePerforming
115
+ def self.perform(history)
116
+ history << :perform
117
+ end
118
+ def self.around_perform_fail(history)
119
+ history << :start_around_perform
120
+ raise StandardError
121
+ yield
122
+ history << :finish_around_perform
123
+ end
124
+ end
125
+
126
+ it "raises an error and does not perform if around_perform fails before yielding" do
127
+ history = []
128
+ assert_raises StandardError do
129
+ perform_job(AroundPerformJobFailsBeforePerforming, history)
130
+ end
131
+ assert_equal history, [:start_around_perform], "Only part of around_perform was run"
132
+ end
133
+
134
+ class ::AroundPerformJobFailsWhilePerforming
135
+ def self.perform(history)
136
+ history << :perform
137
+ raise StandardError
138
+ end
139
+ def self.around_perform_fail_in_yield(history)
140
+ history << :start_around_perform
141
+ begin
142
+ yield
143
+ ensure
144
+ history << :ensure_around_perform
145
+ end
146
+ history << :finish_around_perform
147
+ end
148
+ end
149
+
150
+ it "raises an error but may handle exceptions if perform fails" do
151
+ history = []
152
+ assert_raises StandardError do
153
+ perform_job(AroundPerformJobFailsWhilePerforming, history)
154
+ end
155
+ assert_equal history, [:start_around_perform, :perform, :ensure_around_perform], "Only part of around_perform was run"
156
+ end
157
+
158
+ class ::AroundPerformJobDoesNotHaveToYield
159
+ def self.perform(history)
160
+ history << :perform
161
+ end
162
+ def self.around_perform_dont_yield(history)
163
+ history << :start_around_perform
164
+ history << :finish_around_perform
165
+ end
166
+ end
167
+
168
+ it "around_perform is not required to yield" do
169
+ history = []
170
+ result = perform_job(AroundPerformJobDoesNotHaveToYield, history)
171
+ assert_equal false, result, "perform returns false"
172
+ assert_equal history, [:start_around_perform, :finish_around_perform], "perform was not run"
173
+ end
174
+ end
175
+
176
+ describe "Resque::Job on_failure" do
177
+ include PerformJob
178
+
179
+ class ::FailureJobThatDoesNotFail
180
+ def self.perform(history)
181
+ history << :perform
182
+ end
183
+ def self.on_failure_record_failure(exception, history)
184
+ history << exception.message
185
+ end
186
+ end
187
+
188
+ it "it does not call on_failure if no failures occur" do
189
+ result = perform_job(FailureJobThatDoesNotFail, history=[])
190
+ assert_equal true, result, "perform returned true"
191
+ assert_equal history, [:perform]
192
+ end
193
+
194
+ class ::FailureJobThatFails
195
+ def self.perform(history)
196
+ history << :perform
197
+ raise StandardError, "oh no"
198
+ end
199
+ def self.on_failure_record_failure(exception, history)
200
+ history << exception.message
201
+ end
202
+ end
203
+
204
+ it "it calls on_failure with the exception and then re-raises the exception" do
205
+ history = []
206
+ assert_raises StandardError do
207
+ perform_job(FailureJobThatFails, history)
208
+ end
209
+ assert_equal history, [:perform, "oh no"]
210
+ end
211
+
212
+ class ::FailureJobThatFailsBadly
213
+ def self.perform(history)
214
+ history << :perform
215
+ raise SyntaxError, "oh no"
216
+ end
217
+ def self.on_failure_record_failure(exception, history)
218
+ history << exception.message
219
+ end
220
+ end
221
+
222
+ it "it calls on_failure even with bad exceptions" do
223
+ history = []
224
+ assert_raises SyntaxError do
225
+ perform_job(FailureJobThatFailsBadly, history)
226
+ end
227
+ assert_equal history, [:perform, "oh no"]
228
+ end
229
+ end
230
+
231
+ describe "Resque::Job after_enqueue" do
232
+ include PerformJob
233
+
234
+ class ::AfterEnqueueJob
235
+ @queue = :jobs
236
+ def self.after_enqueue_record_history(history)
237
+ history << :after_enqueue
238
+ end
239
+
240
+ def self.perform(history)
241
+ end
242
+ end
243
+
244
+ it "the after enqueue hook should run" do
245
+ history = []
246
+ @worker = Resque::Worker.new(:jobs)
247
+ Resque.enqueue(AfterEnqueueJob, history)
248
+ @worker.work(0)
249
+ assert_equal history, [:after_enqueue], "after_enqueue was not run"
250
+ end
251
+ end
252
+
253
+
254
+ describe "Resque::Job before_enqueue" do
255
+ include PerformJob
256
+
257
+ class ::BeforeEnqueueJob
258
+ @queue = :jobs
259
+ def self.before_enqueue_record_history(history)
260
+ history << :before_enqueue
261
+ end
262
+
263
+ def self.perform(history)
264
+ end
265
+ end
266
+
267
+ class ::BeforeEnqueueJobAbort
268
+ @queue = :jobs
269
+ def self.before_enqueue_abort(history)
270
+ false
271
+ end
272
+
273
+ def self.perform(history)
274
+ end
275
+ end
276
+
277
+ it "the before enqueue hook should run" do
278
+ history = []
279
+ @worker = Resque::Worker.new(:jobs)
280
+ assert Resque.enqueue(BeforeEnqueueJob, history)
281
+ @worker.work(0)
282
+ assert_equal history, [:before_enqueue], "before_enqueue was not run"
283
+ end
284
+
285
+ it "a before enqueue hook that returns false should prevent the job from getting queued" do
286
+ history = []
287
+ @worker = Resque::Worker.new(:jobs)
288
+ assert_nil Resque.enqueue(BeforeEnqueueJobAbort, history)
289
+ assert_equal 0, Resque.size(:jobs)
290
+ end
291
+ end
292
+
293
+ describe "Resque::Job after_dequeue" do
294
+ include PerformJob
295
+
296
+ class ::AfterDequeueJob
297
+ @queue = :jobs
298
+ def self.after_dequeue_record_history(history)
299
+ history << :after_dequeue
300
+ end
301
+
302
+ def self.perform(history)
303
+ end
304
+ end
305
+
306
+ it "the after dequeue hook should run" do
307
+ history = []
308
+ @worker = Resque::Worker.new(:jobs)
309
+ Resque.dequeue(AfterDequeueJob, history)
310
+ @worker.work(0)
311
+ assert_equal history, [:after_dequeue], "after_dequeue was not run"
312
+ end
313
+ end
314
+
315
+
316
+ describe "Resque::Job before_dequeue" do
317
+ include PerformJob
318
+
319
+ class ::BeforeDequeueJob
320
+ @queue = :jobs
321
+ def self.before_dequeue_record_history(history)
322
+ history << :before_dequeue
323
+ end
324
+
325
+ def self.perform(history)
326
+ end
327
+ end
328
+
329
+ class ::BeforeDequeueJobAbort
330
+ @queue = :jobs
331
+ def self.before_dequeue_abort(history)
332
+ false
333
+ end
334
+
335
+ def self.perform(history)
336
+ end
337
+ end
338
+
339
+ it "the before dequeue hook should run" do
340
+ history = []
341
+ @worker = Resque::Worker.new(:jobs)
342
+ Resque.dequeue(BeforeDequeueJob, history)
343
+ @worker.work(0)
344
+ assert_equal history, [:before_dequeue], "before_dequeue was not run"
345
+ end
346
+
347
+ it "a before dequeue hook that returns false should prevent the job from getting dequeued" do
348
+ history = []
349
+ assert_equal nil, Resque.dequeue(BeforeDequeueJobAbort, history)
350
+ end
351
+ end
352
+
353
+ describe "Resque::Job all hooks" do
354
+ include PerformJob
355
+
356
+ class ::VeryHookyJob
357
+ def self.before_perform_record_history(history)
358
+ history << :before_perform
359
+ end
360
+ def self.around_perform_record_history(history)
361
+ history << :start_around_perform
362
+ yield
363
+ history << :finish_around_perform
364
+ end
365
+ def self.perform(history)
366
+ history << :perform
367
+ end
368
+ def self.after_perform_record_history(history)
369
+ history << :after_perform
370
+ end
371
+ def self.on_failure_record_history(exception, history)
372
+ history << exception.message
373
+ end
374
+ end
375
+
376
+ it "the complete hook order" do
377
+ result = perform_job(VeryHookyJob, history=[])
378
+ assert_equal true, result, "perform returned true"
379
+ assert_equal history, [
380
+ :before_perform,
381
+ :start_around_perform,
382
+ :perform,
383
+ :finish_around_perform,
384
+ :after_perform
385
+ ]
386
+ end
387
+
388
+ class ::VeryHookyJobThatFails
389
+ def self.before_perform_record_history(history)
390
+ history << :before_perform
391
+ end
392
+ def self.around_perform_record_history(history)
393
+ history << :start_around_perform
394
+ yield
395
+ history << :finish_around_perform
396
+ end
397
+ def self.perform(history)
398
+ history << :perform
399
+ end
400
+ def self.after_perform_record_history(history)
401
+ history << :after_perform
402
+ raise StandardError, "oh no"
403
+ end
404
+ def self.on_failure_record_history(exception, history)
405
+ history << exception.message
406
+ end
407
+ end
408
+
409
+ it "the complete hook order with a failure at the last minute" do
410
+ history = []
411
+ assert_raises StandardError do
412
+ perform_job(VeryHookyJobThatFails, history)
413
+ end
414
+ assert_equal history, [
415
+ :before_perform,
416
+ :start_around_perform,
417
+ :perform,
418
+ :finish_around_perform,
419
+ :after_perform,
420
+ "oh no"
421
+ ]
422
+ end
423
+ end