resque-stages 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +103 -0
  5. data/.rubocop_todo.yml +34 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +6 -0
  8. data/CODE_OF_CONDUCT.md +74 -0
  9. data/Gemfile +9 -0
  10. data/Gemfile.lock +172 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +250 -0
  13. data/Rakefile +8 -0
  14. data/bin/console +16 -0
  15. data/bin/setup +8 -0
  16. data/lib/resque-stages.rb +11 -0
  17. data/lib/resque/plugins/stages.rb +110 -0
  18. data/lib/resque/plugins/stages/cleaner.rb +36 -0
  19. data/lib/resque/plugins/stages/redis_access.rb +16 -0
  20. data/lib/resque/plugins/stages/staged_group.rb +181 -0
  21. data/lib/resque/plugins/stages/staged_group_list.rb +79 -0
  22. data/lib/resque/plugins/stages/staged_group_stage.rb +275 -0
  23. data/lib/resque/plugins/stages/staged_job.rb +271 -0
  24. data/lib/resque/plugins/stages/version.rb +9 -0
  25. data/lib/resque/server/public/stages.css +56 -0
  26. data/lib/resque/server/views/_group_stages_list_pagination.erb +67 -0
  27. data/lib/resque/server/views/_group_stages_list_table.erb +25 -0
  28. data/lib/resque/server/views/_stage_job_list_pagination.erb +72 -0
  29. data/lib/resque/server/views/_stage_job_list_table.erb +46 -0
  30. data/lib/resque/server/views/_staged_group_list_pagination.erb +67 -0
  31. data/lib/resque/server/views/_staged_group_list_table.erb +44 -0
  32. data/lib/resque/server/views/group_stages_list.erb +58 -0
  33. data/lib/resque/server/views/groups.erb +40 -0
  34. data/lib/resque/server/views/job_details.erb +91 -0
  35. data/lib/resque/server/views/stage.erb +64 -0
  36. data/lib/resque/stages_server.rb +240 -0
  37. data/read_me/groups_list.png +0 -0
  38. data/read_me/job.png +0 -0
  39. data/read_me/stage.png +0 -0
  40. data/read_me/stages.png +0 -0
  41. data/resque-stages.gemspec +49 -0
  42. data/spec/rails_helper.rb +40 -0
  43. data/spec/resque/plugins/stages/cleaner_spec.rb +82 -0
  44. data/spec/resque/plugins/stages/staged_group_list_spec.rb +96 -0
  45. data/spec/resque/plugins/stages/staged_group_spec.rb +226 -0
  46. data/spec/resque/plugins/stages/staged_group_stage_spec.rb +293 -0
  47. data/spec/resque/plugins/stages/staged_job_spec.rb +324 -0
  48. data/spec/resque/plugins/stages_spec.rb +369 -0
  49. data/spec/resque/server/public/stages.css_spec.rb +18 -0
  50. data/spec/resque/server/views/group_stages_list.erb_spec.rb +67 -0
  51. data/spec/resque/server/views/groups.erb_spec.rb +81 -0
  52. data/spec/resque/server/views/job_details.erb_spec.rb +100 -0
  53. data/spec/resque/server/views/stage.erb_spec.rb +68 -0
  54. data/spec/spec_helper.rb +104 -0
  55. data/spec/support/01_utils/fake_logger.rb +7 -0
  56. data/spec/support/config/redis-auth.yml +12 -0
  57. data/spec/support/fake_logger.rb +7 -0
  58. data/spec/support/jobs/basic_job.rb +17 -0
  59. data/spec/support/jobs/compressed_job.rb +18 -0
  60. data/spec/support/jobs/retry_job.rb +21 -0
  61. data/spec/support/purge_all.rb +15 -0
  62. metadata +297 -0
@@ -0,0 +1,369 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ RSpec.describe Resque::Plugins::Stages do
6
+ it "has a version number" do
7
+ expect(Resque::Plugins::Stages::VERSION).not_to be nil
8
+ end
9
+
10
+ context "full resque calls" do
11
+ around(:each) do |spec_proxy|
12
+ inline = Resque.inline?
13
+
14
+ begin
15
+ Resque.inline = false
16
+ spec_proxy.call
17
+ ensure
18
+ Resque.inline = inline
19
+ end
20
+ end
21
+
22
+ context BasicJob do
23
+ let(:group) { Resque::Plugins::Stages::StagedGroup.new(SecureRandom.uuid) }
24
+ let(:stage) { group.current_stage }
25
+ let(:job) { stage.enqueue(BasicJob, *options) }
26
+ let(:load_job) { Resque::Plugins::Stages::StagedJob.new(job.job_id) }
27
+ let(:worker) { Resque::Worker.new(BasicJob.queue) }
28
+ let(:options) { ["This", 1, "is", "an" => "arglist"] }
29
+
30
+ before(:each) do
31
+ worker.register_worker
32
+
33
+ allow(BasicJob).to receive(:perform_job).and_wrap_original do |orig_perform, *args|
34
+ new_job = orig_perform.call(*args)
35
+
36
+ allow(new_job).to receive(:notify_stage).and_return nil
37
+
38
+ new_job
39
+ end
40
+ end
41
+
42
+ it "records that the job is running" do
43
+ job.enqueue_job
44
+
45
+ allow(BasicJob).to receive(:perform) do
46
+ expect(load_job.status).to eq :running
47
+ end
48
+
49
+ worker_job = worker.reserve
50
+
51
+ worker.perform worker_job
52
+ worker.done_working
53
+ end
54
+
55
+ it "records that the job succeeded" do
56
+ job.enqueue_job
57
+
58
+ worker_job = worker.reserve
59
+
60
+ worker.perform worker_job
61
+ worker.done_working
62
+
63
+ expect(load_job.status).to eq :successful
64
+ expect(FakeLogger).
65
+ to have_received(:error).with "BasicJob.perform args", { "staged_job_id" => job.job_id }, "This", 1, "is", "an" => "arglist"
66
+ expect(FakeLogger).to have_received(:error).with "BasicJob.perform job.args", "This", 1, "is", "an" => "arglist"
67
+ expect(FakeLogger).to have_received(:error).with "BasicJob.perform job_id", job.job_id
68
+ end
69
+
70
+ it "records that the job failed" do
71
+ allow(FakeLogger).to receive(:error).and_raise "This is an error"
72
+
73
+ job.enqueue_job
74
+
75
+ worker_job = worker.reserve
76
+
77
+ worker.perform worker_job
78
+ worker.done_working
79
+
80
+ expect(load_job.status).to eq :failed
81
+ expect(FakeLogger).to have_received(:error).with "BasicJob.perform job_id", job.job_id
82
+ end
83
+ end
84
+
85
+ context CompressedJob do
86
+ let(:group) { Resque::Plugins::Stages::StagedGroup.new(SecureRandom.uuid) }
87
+ let(:stage) { group.current_stage }
88
+ let(:job) { stage.enqueue(CompressedJob, *options) }
89
+ let(:load_job) { Resque::Plugins::Stages::StagedJob.new(job.job_id) }
90
+ let(:worker) { Resque::Worker.new(CompressedJob.queue) }
91
+ let(:options) { ["This", 1, "is", "an" => "arglist"] }
92
+
93
+ before(:each) do
94
+ worker.register_worker
95
+
96
+ allow(CompressedJob).to receive(:perform_job).and_wrap_original do |orig_perform, *args|
97
+ new_job = orig_perform.call(*args)
98
+
99
+ allow(new_job).to receive(:notify_stage).and_return nil
100
+
101
+ new_job
102
+ end
103
+ end
104
+
105
+ it "records that the job is running" do
106
+ job.enqueue_job
107
+
108
+ allow(CompressedJob).to receive(:perform) do
109
+ expect(load_job.status).to eq :running
110
+ end
111
+
112
+ worker_job = worker.reserve
113
+
114
+ worker.perform worker_job
115
+ worker.done_working
116
+ end
117
+
118
+ it "records that the job succeeded" do
119
+ job.enqueue_job
120
+
121
+ worker_job = worker.reserve
122
+
123
+ worker.perform worker_job
124
+ worker.done_working
125
+
126
+ expect(load_job.status).to eq :successful
127
+ expect(FakeLogger).to have_received(:error).with "CompressedJob.perform args",
128
+ { "staged_job_id" => job.job_id },
129
+ "This",
130
+ 1,
131
+ "is",
132
+ "an" => "arglist"
133
+ expect(FakeLogger).to have_received(:error).with "CompressedJob.perform job.args",
134
+ "This",
135
+ 1,
136
+ "is",
137
+ "an" => "arglist"
138
+ expect(FakeLogger).to have_received(:error).with "CompressedJob.perform job_id", job.job_id
139
+ end
140
+
141
+ it "records that the job failed" do
142
+ allow(FakeLogger).to receive(:error).and_raise "This is an error"
143
+
144
+ job.enqueue_job
145
+
146
+ worker_job = worker.reserve
147
+
148
+ worker.perform worker_job
149
+ worker.done_working
150
+
151
+ expect(load_job.status).to eq :failed
152
+ expect(FakeLogger).to have_received(:error).with "CompressedJob.perform job_id", job.job_id
153
+ end
154
+ end
155
+
156
+ context RetryJob do
157
+ let(:group) { Resque::Plugins::Stages::StagedGroup.new(SecureRandom.uuid) }
158
+ let(:stage) { group.current_stage }
159
+ let(:job) { stage.enqueue(RetryJob, *options) }
160
+ let(:load_job) { Resque::Plugins::Stages::StagedJob.new(job.job_id) }
161
+ let(:worker) { Resque::Worker.new(RetryJob.queue) }
162
+ let(:options) { ["This", 1, "is", "an" => "arglist"] }
163
+
164
+ before(:each) do
165
+ worker.register_worker
166
+
167
+ allow(RetryJob).to receive(:perform_job).and_wrap_original do |orig_perform, *args|
168
+ new_job = orig_perform.call(*args)
169
+
170
+ allow(new_job).to receive(:notify_stage).and_return nil
171
+
172
+ new_job
173
+ end
174
+ end
175
+
176
+ it "records that the job succeeded" do
177
+ job.enqueue_job
178
+
179
+ worker_job = worker.reserve
180
+
181
+ worker.perform worker_job
182
+ worker.done_working
183
+
184
+ expect(load_job.status).to eq :successful
185
+ expect(FakeLogger).
186
+ to have_received(:error).with "RetryJob.perform args", { "staged_job_id" => job.job_id }, "This", 1, "is", "an" => "arglist"
187
+ expect(FakeLogger).to have_received(:error).with "RetryJob.perform job.args", "This", 1, "is", "an" => "arglist"
188
+ expect(FakeLogger).to have_received(:error).with "RetryJob.perform job_id", job.job_id
189
+ end
190
+
191
+ it "records that the job re-queued" do
192
+ allow(FakeLogger).to receive(:error).and_raise "This is an error"
193
+
194
+ job.enqueue_job
195
+
196
+ worker_job = worker.reserve
197
+
198
+ worker.perform worker_job
199
+ worker.done_working
200
+
201
+ expect(load_job.status).to eq :pending_re_run
202
+ expect(FakeLogger).to have_received(:error).with "RetryJob.perform job_id", job.job_id
203
+ end
204
+
205
+ it "records that the job failed" do
206
+ allow(FakeLogger).to receive(:error).and_raise "This is an error"
207
+
208
+ 6.times do
209
+ Resque.enqueue(*job.enqueue_args)
210
+
211
+ worker_job = worker.reserve
212
+
213
+ worker.perform worker_job
214
+ worker.done_working
215
+ end
216
+
217
+ expect(load_job.status).to eq :failed
218
+ expect(FakeLogger).to have_received(:error).exactly(6).times.with("RetryJob.perform job_id", job.job_id)
219
+ end
220
+ end
221
+ end
222
+
223
+ context "inline" do
224
+ context RetryJob do
225
+ let(:group) { Resque::Plugins::Stages::StagedGroup.new(SecureRandom.uuid) }
226
+ let(:stage) { group.current_stage }
227
+ let(:job) { stage.enqueue(RetryJob, *options) }
228
+ let(:load_job) { Resque::Plugins::Stages::StagedJob.new(job.job_id) }
229
+ let(:options) { ["This", 1, "is", "an" => "arglist"] }
230
+
231
+ before(:each) do
232
+ allow(RetryJob).to receive(:perform_job).and_wrap_original do |orig_perform, *args|
233
+ new_job = orig_perform.call(*args)
234
+
235
+ allow(new_job).to receive(:notify_stage).and_return nil
236
+
237
+ new_job
238
+ end
239
+ end
240
+
241
+ it "records that the job succeeded" do
242
+ job.enqueue_job
243
+
244
+ expect(load_job.status).to eq :successful
245
+ expect(FakeLogger).
246
+ to have_received(:error).with "RetryJob.perform args", { "staged_job_id" => job.job_id }, "This", 1, "is", "an" => "arglist"
247
+ expect(FakeLogger).to have_received(:error).with "RetryJob.perform job.args", "This", 1, "is", "an" => "arglist"
248
+ expect(FakeLogger).to have_received(:error).with "RetryJob.perform job_id", job.job_id
249
+ end
250
+
251
+ it "records that the job failed" do
252
+ allow(FakeLogger).to receive(:error).and_raise "This is an error"
253
+
254
+ expect { job.enqueue_job }.not_to raise_error
255
+
256
+ expect(load_job.status).to eq :failed
257
+ expect(FakeLogger).to have_received(:error).with "RetryJob.perform job_id", job.job_id
258
+ end
259
+ end
260
+
261
+ context CompressedJob do
262
+ let(:group) { Resque::Plugins::Stages::StagedGroup.new(SecureRandom.uuid) }
263
+ let(:stage) { group.current_stage }
264
+ let(:job) { stage.enqueue(CompressedJob, *options) }
265
+ let(:load_job) { Resque::Plugins::Stages::StagedJob.new(job.job_id) }
266
+ let(:options) { ["This", 1, "is", "an" => "arglist"] }
267
+
268
+ before(:each) do
269
+ allow(CompressedJob).to receive(:perform_job).and_wrap_original do |orig_perform, *args|
270
+ new_job = orig_perform.call(*args)
271
+
272
+ allow(new_job).to receive(:notify_stage).and_return nil
273
+
274
+ new_job
275
+ end
276
+ end
277
+
278
+ it "records that the job succeeded" do
279
+ job.enqueue_job
280
+
281
+ expect(load_job.status).to eq :successful
282
+ expect(FakeLogger).
283
+ to have_received(:error).with "CompressedJob.perform args", { "staged_job_id" => job.job_id }, "This", 1, "is", "an" => "arglist"
284
+ expect(FakeLogger).to have_received(:error).with "CompressedJob.perform job.args", "This", 1, "is", "an" => "arglist"
285
+ expect(FakeLogger).to have_received(:error).with "CompressedJob.perform job_id", job.job_id
286
+ end
287
+
288
+ it "records that the job failed" do
289
+ allow(FakeLogger).to receive(:error).and_raise "This is an error"
290
+
291
+ expect { job.enqueue_job }.not_to raise_error
292
+
293
+ expect(load_job.status).to eq :failed
294
+ expect(FakeLogger).to have_received(:error).with "CompressedJob.perform job_id", job.job_id
295
+ end
296
+ end
297
+ end
298
+
299
+ describe "perform_job" do
300
+ let(:stage) do
301
+ instance_double(Resque::Plugins::Stages::StagedGroupStage,
302
+ add_job: nil,
303
+ remove_job: nil,
304
+ group_stage_id: SecureRandom.uuid,
305
+ status: :pending,
306
+ "status=": nil,
307
+ job_completed: nil)
308
+ end
309
+
310
+ RSpec.shared_examples("it loads from args") do
311
+ it "loads the job by ID" do
312
+ expect(perform_job).to eq load_job
313
+ expect(perform_job.args).to eq ["This", 1, "is", "an" => "arglist"]
314
+ end
315
+
316
+ it "creates a new job if the id is deleted" do
317
+ job.delete
318
+
319
+ expect(perform_job).to eq load_job
320
+ expect(perform_job.args).to eq match_args
321
+ end
322
+
323
+ it "creates a new job if the first paramater is a hash but doesn't have an ID" do
324
+ perform_args[0].delete :staged_job_id
325
+ perform_args[0]["this_is"] = "silly"
326
+
327
+ expect(perform_job.args).to eq perform_args
328
+ end
329
+
330
+ it "creates a new job if there is no id" do
331
+ if perform_args.length > 1
332
+ perform_args.shift
333
+ else
334
+ perform_args[0].delete :staged_job_id
335
+ end
336
+
337
+ expect(perform_job.args).to eq perform_args
338
+ end
339
+ end
340
+
341
+ context "BasicJob" do
342
+ let(:job) { Resque::Plugins::Stages::StagedJob.create_job(stage, BasicJob, "This", 1, :is, an: "arglist") }
343
+ let(:load_job) { Resque::Plugins::Stages::StagedJob.new(job.job_id) }
344
+ let(:perform_args) { [{ staged_job_id: job.job_id }, "This", 1, "is", "an" => "arglist"] }
345
+ let(:match_args) { ["This", 1, "is", "an" => "arglist"] }
346
+ let(:perform_job) { BasicJob.perform_job(*perform_args) }
347
+
348
+ it_behaves_like "it loads from args"
349
+ end
350
+
351
+ context "CompressedJob" do
352
+ let(:job) { Resque::Plugins::Stages::StagedJob.create_job(stage, CompressedJob, "This", 1, :is, an: "arglist") }
353
+ let(:load_job) { Resque::Plugins::Stages::StagedJob.new(job.job_id) }
354
+ let(:perform_args) do
355
+ [{ "resque_compressed" => true,
356
+ "payload" => CompressedJob.compressed_args([{ "staged_job_id" => job.job_id }, "This", 1, :is, an: "arglist"]),
357
+ staged_job_id: job.job_id }]
358
+ end
359
+ let(:match_args) do
360
+ [{ "resque_compressed" => true,
361
+ "payload" => CompressedJob.compressed_args([{ "staged_job_id" => job.job_id }, "This", 1, :is, an: "arglist"]),
362
+ staged_job_id: job.job_id }]
363
+ end
364
+ let(:perform_job) { CompressedJob.perform_job(*perform_args) }
365
+
366
+ it_behaves_like "it loads from args"
367
+ end
368
+ end
369
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ RSpec.describe "approve.css" do
6
+ include Rack::Test::Methods
7
+
8
+ def app
9
+ @app ||= Resque::Server.new
10
+ end
11
+
12
+ it "fetches the CSS file" do
13
+ get "/stages/public/stages.css"
14
+
15
+ expect(last_response).to be_ok
16
+ expect(last_response.body).to be_include(".stages_pagination_block {")
17
+ end
18
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails_helper"
4
+
5
+ RSpec.describe "groups.erb" do
6
+ let(:group) { Resque::Plugins::Stages::StagedGroup.new(SecureRandom.uuid) }
7
+ let(:numbers) { Array.new(5) { |index| index } }
8
+ let!(:stages) do
9
+ numbers.map do |number|
10
+ group.stage(number)
11
+ end
12
+ end
13
+
14
+ include Rack::Test::Methods
15
+
16
+ def app
17
+ @app ||= Resque::Server.new
18
+ end
19
+
20
+ context "actions" do
21
+ before(:each) do
22
+ allow(Resque::Plugins::Stages::StagedGroup).to receive(:new).and_return group
23
+ allow(group).to receive(:delete)
24
+ allow(group).to receive(:initiate)
25
+ end
26
+
27
+ it "should respond to /stages/initiate_group" do
28
+ post "/stages/initiate_group?group_id=#{group.group_id}"
29
+
30
+ expect(last_response).to be_redirect
31
+ expect(last_response.header["Location"]).to match(/stages$/)
32
+ expect(group).to have_received(:initiate)
33
+ end
34
+
35
+ it "should respond to /stages/delete_group" do
36
+ post "/stages/delete_group?group_id=#{group.group_id}"
37
+
38
+ expect(last_response).to be_redirect
39
+ expect(last_response.header["Location"]).to match(/stages$/)
40
+ expect(group).to have_received(:delete)
41
+ end
42
+ end
43
+
44
+ it "should respond to /stages/group_stages_list" do
45
+ get "/stages/group_stages_list?group_id=#{group.group_id}"
46
+
47
+ expect(last_response).to be_ok
48
+
49
+ expect(last_response.body).to match %r{action="/stages/delete_group\?group_id=#{group.group_id}"}
50
+ expect(last_response.body).to match %r{action="/stages/initiate_group\?group_id=#{group.group_id}"}
51
+
52
+ stages.each do |stage|
53
+ expect(last_response.body).to match %r{#{stage.number}\n +</a>}
54
+ expect(last_response.body).to match %r{/stages/stage\?#{{ group_stage_id: stage.group_stage_id }.to_param.gsub("+", "\\\\+")}}
55
+ end
56
+ end
57
+
58
+ it "pages queues" do
59
+ get "/stages/group_stages_list?group_id=#{group.group_id}&page_size=2"
60
+
61
+ expect(last_response).to be_ok
62
+
63
+ expect(last_response.body).to match(%r{href="/stages/group_stages_list?.*group_id=#{group.group_id}.*page_num=2})
64
+ expect(last_response.body).to match(%r{href="/stages/group_stages_list?.*group_id=#{group.group_id}.*page_num=3})
65
+ expect(last_response.body).not_to match(%r{href="/stages/group_stages_list?.*group_id=#{group.group_id}.*page_num=4})
66
+ end
67
+ end