resque-integration 3.4.1

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 (66) hide show
  1. checksums.yaml +7 -0
  2. data/.drone.yml +28 -0
  3. data/.gitignore +21 -0
  4. data/.rspec +3 -0
  5. data/Appraisals +27 -0
  6. data/CHANGELOG.md +311 -0
  7. data/Gemfile +4 -0
  8. data/README.md +281 -0
  9. data/Rakefile +1 -0
  10. data/app/assets/javascripts/standalone/progress_bar.js +47 -0
  11. data/app/controllers/resque/jobs_controller.rb +42 -0
  12. data/app/controllers/resque/queues/info_controller.rb +9 -0
  13. data/app/controllers/resque/queues/status_controller.rb +38 -0
  14. data/app/views/shared/job_progress_bar.html.haml +17 -0
  15. data/bin/resque-status +59 -0
  16. data/config/routes.rb +13 -0
  17. data/config.ru +9 -0
  18. data/dip.yml +44 -0
  19. data/docker-compose.development.yml +12 -0
  20. data/docker-compose.drone.yml +6 -0
  21. data/docker-compose.yml +10 -0
  22. data/lib/generators/resque/integration/install/install_generator.rb +38 -0
  23. data/lib/resque/integration/backtrace.rb +29 -0
  24. data/lib/resque/integration/configuration.rb +238 -0
  25. data/lib/resque/integration/continuous.rb +75 -0
  26. data/lib/resque/integration/engine.rb +103 -0
  27. data/lib/resque/integration/extensions/job.rb +17 -0
  28. data/lib/resque/integration/extensions/worker.rb +17 -0
  29. data/lib/resque/integration/extensions.rb +8 -0
  30. data/lib/resque/integration/failure_backends/queues_totals.rb +37 -0
  31. data/lib/resque/integration/failure_backends.rb +7 -0
  32. data/lib/resque/integration/god.erb +99 -0
  33. data/lib/resque/integration/hooks.rb +72 -0
  34. data/lib/resque/integration/logs_rotator.rb +95 -0
  35. data/lib/resque/integration/monkey_patch/verbose_formatter.rb +10 -0
  36. data/lib/resque/integration/ordered.rb +142 -0
  37. data/lib/resque/integration/priority.rb +89 -0
  38. data/lib/resque/integration/queues_info/age.rb +53 -0
  39. data/lib/resque/integration/queues_info/config.rb +96 -0
  40. data/lib/resque/integration/queues_info/size.rb +33 -0
  41. data/lib/resque/integration/queues_info.rb +55 -0
  42. data/lib/resque/integration/tasks/hooks.rake +49 -0
  43. data/lib/resque/integration/tasks/lock.rake +37 -0
  44. data/lib/resque/integration/tasks/resque.rake +101 -0
  45. data/lib/resque/integration/unique.rb +218 -0
  46. data/lib/resque/integration/version.rb +5 -0
  47. data/lib/resque/integration.rb +146 -0
  48. data/lib/resque-integration.rb +1 -0
  49. data/resque-integration.gemspec +40 -0
  50. data/spec/fixtures/resque_queues.yml +45 -0
  51. data/spec/resque/controllers/jobs_controller_spec.rb +65 -0
  52. data/spec/resque/integration/configuration_spec.rb +147 -0
  53. data/spec/resque/integration/continuous_spec.rb +122 -0
  54. data/spec/resque/integration/failure_backends/queues_totals_spec.rb +105 -0
  55. data/spec/resque/integration/ordered_spec.rb +87 -0
  56. data/spec/resque/integration/priority_spec.rb +94 -0
  57. data/spec/resque/integration/queues_info_spec.rb +402 -0
  58. data/spec/resque/integration/unique_spec.rb +184 -0
  59. data/spec/resque/integration_spec.rb +105 -0
  60. data/spec/shared/resque_inline.rb +10 -0
  61. data/spec/spec_helper.rb +28 -0
  62. data/vendor/assets/images/progressbar/white.gif +0 -0
  63. data/vendor/assets/javascripts/jquery.progressbar.js +177 -0
  64. data/vendor/assets/stylesheets/jquery.progressbar.css.erb +33 -0
  65. data/vendor/assets/stylesheets/jquery.progressbar.no_pipeline.css +33 -0
  66. metadata +402 -0
@@ -0,0 +1,147 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Resque::Integration::Configuration do
6
+ let(:config) do
7
+ File.stub(:exists? => true)
8
+ File.stub(:read)
9
+ ERB.stub_chain(:new, :result)
10
+ YAML.stub(:load => config_yaml)
11
+ described_class.new('path/to/config.yml')
12
+ end
13
+
14
+ let(:config_yaml) { {} }
15
+
16
+ describe '#schedule_file' do
17
+ let(:config_yaml) { {'resque' => {'schedule_file' => 'schedule.yml'}} }
18
+
19
+ it { expect(config.schedule_file).to eq 'schedule.yml' }
20
+ end
21
+
22
+ describe '#log_level' do
23
+ context 'when default' do
24
+ it { expect(config.log_level).to eq 1 }
25
+ end
26
+
27
+ context 'when defined' do
28
+ let(:config_yaml) { {'resque' => {'log_level' => 2}} }
29
+
30
+ it { expect(config.log_level).to eq 2 }
31
+ end
32
+ end
33
+
34
+ describe '#resque_scheduler?' do
35
+ context 'when default' do
36
+ it { expect(config.resque_scheduler?).to be_truthy }
37
+ end
38
+
39
+ context 'when defined' do
40
+ let(:config_yaml) { {'resque' => {'scheduler' => 'no'}} }
41
+
42
+ it { expect(config.resque_scheduler?).to be_falsey }
43
+ end
44
+ end
45
+
46
+ describe '#run_at_exit_hooks?' do
47
+ context 'when default' do
48
+ it { expect(config.resque_scheduler?).to be_truthy }
49
+ end
50
+
51
+ context 'when defined' do
52
+ let(:config_yaml) { {'resque' => {'run_at_exit_hooks' => 'no'}} }
53
+
54
+ it { expect(config.run_at_exit_hooks?).to be_falsey }
55
+ end
56
+ end
57
+ end
58
+
59
+ describe Resque::Integration::Configuration::Notifier do
60
+ context 'when NilClass given as config' do
61
+ subject(:config) { described_class::new(nil) }
62
+
63
+ it { should_not be_enabled }
64
+ its(:include_payload?) { should be_truthy }
65
+ its(:to) { should be_empty }
66
+ its(:from) { should eq 'no_reply@gmail.com' }
67
+ its(:mail) { should eq :alert }
68
+ its(:mailer) { should eq 'ResqueFailedJobMailer::Mailer' }
69
+ end
70
+
71
+ context 'when Hash given as config' do
72
+ let :configuration do
73
+ {to: ['to1@mail', 'to2@mail'],
74
+ from: 'from@mail',
75
+ enabled: false,
76
+ include_payload: false,
77
+ mail: 'notify',
78
+ mailer: 'MyMailer'}
79
+ end
80
+
81
+ subject(:config) { described_class::new(configuration) }
82
+
83
+ it { should_not be_enabled }
84
+ its(:include_payload?) { should be_falsey }
85
+ its(:to) { should include 'to1@mail' }
86
+ its(:to) { should include 'to2@mail' }
87
+ its(:from) { should eq 'from@mail' }
88
+ its(:mail) { should eq :notify }
89
+ its(:mailer) { should eq 'MyMailer' }
90
+ end
91
+ end
92
+
93
+ describe Resque::Integration::Configuration::Worker do
94
+ describe '.new' do
95
+ context 'when Integer given as config' do
96
+ subject(:config) { described_class::new(:default, 2) }
97
+
98
+ its(:queue) { should eq :default }
99
+ its(:count) { should eq 2 }
100
+ end
101
+
102
+ context 'when Hash given as config' do
103
+ subject(:config) { described_class::new(:default, :count => 2) }
104
+
105
+ its(:queue) { should eq :default }
106
+ its(:count) { should eq 2 }
107
+ end
108
+ end
109
+
110
+ describe '#count' do
111
+ context 'when initialized without count paramter' do
112
+ subject { described_class::new(:default, {}) }
113
+
114
+ its(:count) { should eq 1 }
115
+ end
116
+
117
+ context 'when initialized with count <= 0' do
118
+ subject { described_class::new(:default, :count => 0) }
119
+
120
+ its(:count) { should eq 1 }
121
+ end
122
+ end
123
+
124
+ describe '#env' do
125
+ let :config do
126
+ described_class.new(:default,
127
+ :count => 2,
128
+ :jobs_per_fork => 10,
129
+ :minutes_per_fork => 5,
130
+ :shuffle => true,
131
+ :env => {:VAR => 2})
132
+ end
133
+
134
+ subject { config.env }
135
+
136
+ its([:QUEUE]) { should eq 'default' }
137
+ its([:JOBS_PER_FORK]) { should eq '10' }
138
+ its([:MINUTES_PER_FORK]) { should eq '5' }
139
+ its([:SHUFFLE]) { should eq '1' }
140
+ its([:VAR]) { should eq '2' }
141
+
142
+ context "when shuffle is disabled" do
143
+ let(:config) { described_class.new(:default, shuffle: false) }
144
+ its([:SHUFFLE]) { should be_nil }
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,122 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Resque::Integration::Continuous do
6
+ context 'when applied to non-unique job' do
7
+ class ContinuousJobTest
8
+ include Resque::Integration
9
+
10
+ queue :continuous_test
11
+ continuous
12
+
13
+ def self.perform(id)
14
+ continue(id + 1)
15
+ end
16
+ end
17
+
18
+ it 'should re-enqueue the job' do
19
+ Resque.enqueue(ContinuousJobTest, 1)
20
+
21
+ job = Resque.reserve(ContinuousJobTest.queue)
22
+ job.should be_a(Resque::Job)
23
+ job.payload['args'].should eq [1]
24
+ job.perform
25
+
26
+ job2 = Resque.reserve(ContinuousJobTest.queue)
27
+ job2.should be_a(Resque::Job)
28
+ job2.payload['args'].should eq [2]
29
+ end
30
+ end
31
+
32
+ context 'when applied to unique job' do
33
+ class ContinuousUniqueJobTest
34
+ include Resque::Integration
35
+
36
+ queue :unique_continuous_test
37
+ continuous
38
+ unique { |x, y| x }
39
+
40
+ def self.execute(x, y)
41
+ continue(x, y + 1)
42
+ end
43
+ end
44
+
45
+ it 'should re-enqueue the job regardless of any locks' do
46
+ ContinuousUniqueJobTest.enqueue(1, 1)
47
+
48
+ job = Resque.reserve(ContinuousUniqueJobTest.queue)
49
+ job.should be_a(Resque::Job)
50
+
51
+ # meta prepend meta_id arg, so we just ignore it here
52
+ job.payload['args'][1..-1].should eq [1, 1]
53
+ job.perform
54
+
55
+ ContinuousUniqueJobTest.should be_locked(1, 2)
56
+ ContinuousUniqueJobTest.should be_enqueued(1, 2)
57
+
58
+ job2 = Resque.reserve(ContinuousUniqueJobTest.queue)
59
+ job2.should be_a(Resque::Job)
60
+ job2.payload['args'][1..-1].should eq [1, 2]
61
+
62
+ # clean the queue
63
+ ContinuousUniqueJobTest.dequeue(1)
64
+ end
65
+
66
+ it 'should not finish meta' do
67
+ meta = ContinuousUniqueJobTest.enqueue(2, 1)
68
+
69
+ job = Resque.reserve(ContinuousUniqueJobTest.queue)
70
+ job.perform
71
+ meta.reload!
72
+
73
+ meta2 = ContinuousUniqueJobTest.enqueued?(2, 2)
74
+
75
+ meta2.started_at.should eq meta.started_at
76
+ meta2.enqueued_at.should eq meta.enqueued_at
77
+ meta2.data.should eq meta.data
78
+ meta2.should be_working
79
+
80
+ # clean the queue
81
+ ContinuousUniqueJobTest.dequeue(2)
82
+ end
83
+
84
+ it 'should enqueue job with the same meta_id' do
85
+ ContinuousUniqueJobTest.enqueue(3, 1)
86
+
87
+ job1 = Resque.reserve(ContinuousUniqueJobTest.queue)
88
+ meta1 = job1.payload['args'].first
89
+
90
+ job1.perform
91
+
92
+ job2 = Resque.reserve(ContinuousUniqueJobTest.queue)
93
+ meta2 = job2.payload['args'].first
94
+
95
+ meta1.should eq meta2
96
+ end
97
+ end
98
+
99
+ context 'when called without arguments' do
100
+ class ContinuousWithoutArgsJobTest
101
+ include Resque::Integration
102
+
103
+ queue :continuous_without_args_test
104
+ continuous
105
+ unique
106
+
107
+ def self.execute(x)
108
+ continue
109
+ end
110
+ end
111
+
112
+ it 'should re-enqueue job with same arguments' do
113
+ ContinuousWithoutArgsJobTest.enqueue(1)
114
+
115
+ job = Resque.reserve(ContinuousWithoutArgsJobTest.queue)
116
+ job.should be_a(Resque::Job)
117
+ job.perform
118
+
119
+ ContinuousWithoutArgsJobTest.should be_enqueued(1)
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,105 @@
1
+ require 'spec_helper'
2
+
3
+ describe Resque::Integration::FailureBackends::QueuesTotals do
4
+ let(:failure) { double('UnbelievableError') }
5
+ let(:worker) { double('Worker') }
6
+ let(:payload) { double('Payload') }
7
+
8
+ describe '#save' do
9
+ let(:queue) { 'images' }
10
+ let(:backend) { described_class.new(failure, worker, queue, payload) }
11
+
12
+ before { stub_const('Resque::Integration::FailureBackends::QueuesTotals::MAX_COUNTER_VALUE', 3) }
13
+
14
+ it 'increments failures count for specified queue' do
15
+ expect do
16
+ 2.times { backend.save }
17
+ end.to change { described_class.count(queue) }.from(0).to(2)
18
+ end
19
+
20
+ context 'when counter overflows' do
21
+ it 'resets failures count for specified queue to 1' do
22
+ expect do
23
+ 3.times { backend.save }
24
+ end.to change { described_class.count(queue) }.from(0).to(1)
25
+ end
26
+ end
27
+ end
28
+
29
+ describe '.count' do
30
+ let(:images_queue) { 'images' }
31
+ let(:products_queue) { 'products' }
32
+ let(:images_failure_backend) { described_class.new(failure, worker, images_queue, payload) }
33
+ let(:products_failure_backend) { described_class.new(failure, worker, products_queue, payload) }
34
+
35
+ before do
36
+ 2.times { images_failure_backend.save }
37
+ 3.times { products_failure_backend.save }
38
+ end
39
+
40
+ context 'with specified queue' do
41
+ it 'returns failures count for specified queue' do
42
+ expect(described_class.count(images_queue)).to eq(2)
43
+ expect(described_class.count(products_queue)).to eq(3)
44
+ end
45
+ end
46
+
47
+ context 'with queue which has no failures' do
48
+ it 'returns 0' do
49
+ expect(described_class.count('not_failed')).to eq(0)
50
+ end
51
+ end
52
+
53
+ context 'without specified queue' do
54
+ it 'returns aggregated failures count from all queues' do
55
+ expect(described_class.count).to eq(5)
56
+ end
57
+ end
58
+ end
59
+
60
+ describe '.queues' do
61
+ context 'when has failures data' do
62
+ let(:images_queue) { 'images' }
63
+ let(:products_queue) { 'products' }
64
+
65
+ before do
66
+ described_class.new(failure, worker, images_queue, payload).save
67
+ described_class.new(failure, worker, products_queue, payload).save
68
+ end
69
+
70
+ it 'returns names of failed queues' do
71
+ expect(described_class.queues).to match_array([images_queue, products_queue])
72
+ end
73
+ end
74
+
75
+ context 'when does not have failures data' do
76
+ it { expect(described_class.queues).to be_empty }
77
+ end
78
+ end
79
+
80
+ describe '.clear' do
81
+ let(:images_queue) { 'images' }
82
+ let(:products_queue) { 'products' }
83
+
84
+ before do
85
+ described_class.new(failure, worker, images_queue, payload).save
86
+ described_class.new(failure, worker, products_queue, payload).save
87
+ end
88
+
89
+ context 'with specified queue' do
90
+ it 'deletes counter data for specified queue' do
91
+ expect { described_class.clear(products_queue) }.to change { described_class.count }.from(2).to(1)
92
+ expect(described_class.count(images_queue)).to eq(1)
93
+ expect(described_class.count(products_queue)).to eq(0)
94
+ end
95
+ end
96
+
97
+ context 'without specified queue' do
98
+ it 'deletes counter data for all queues' do
99
+ expect { described_class.clear }.to change { described_class.count }.from(2).to(0)
100
+ expect(described_class.count(images_queue)).to eq(0)
101
+ expect(described_class.count(products_queue)).to eq(0)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,87 @@
1
+ require "spec_helper"
2
+
3
+ describe Resque::Integration::Ordered do
4
+ class TestJob
5
+ include Resque::Integration
6
+
7
+ unique { |company_id, param1| [company_id] }
8
+ ordered max_iterations: 2
9
+ end
10
+
11
+ class UniqueTestJob
12
+ include Resque::Integration
13
+
14
+ unique { |company_id, param1| [company_id] }
15
+ ordered max_iterations: 2, unique: ->(_company_id, param1) { [param1] }
16
+ end
17
+
18
+ it "push args to separate queue" do
19
+ ordered_meta1 = TestJob.enqueue(1, 10)
20
+ ordered_meta2 = TestJob.enqueue(1, 20)
21
+
22
+ meta_id = TestJob.meta_id(1, 10)
23
+ args_key = TestJob.ordered_queue_key(meta_id)
24
+
25
+ expect(TestJob).to be_enqueued(1)
26
+ expect(TestJob.ordered_queue_size(meta_id)).to eq 2
27
+
28
+ job_args = Resque.decode(Resque.redis.lpop(args_key))
29
+ expect(job_args[0]).to eq ordered_meta1.meta_id
30
+
31
+ job_args = Resque.decode(Resque.redis.lpop(args_key))
32
+ expect(job_args[0]).to eq ordered_meta2.meta_id
33
+ end
34
+
35
+ it "execute jobs by each args" do
36
+ TestJob.enqueue(1, 10)
37
+ TestJob.enqueue(1, 20)
38
+
39
+ expect(TestJob).to receive(:execute).with(kind_of(Resque::Plugins::Meta::Metadata), 1, 10).ordered
40
+ expect(TestJob).to receive(:execute).with(kind_of(Resque::Plugins::Meta::Metadata), 1, 20).ordered
41
+ expect(TestJob).to_not receive(:continue)
42
+
43
+ meta_id = TestJob.meta_id(1, 10)
44
+ TestJob.perform(meta_id)
45
+ expect(TestJob.ordered_queue_size(meta_id)).to eq 0
46
+ end
47
+
48
+ it "reenqueue job after max iterations reached" do
49
+ TestJob.enqueue(1, 10)
50
+ TestJob.enqueue(1, 20)
51
+ TestJob.enqueue(1, 30)
52
+ TestJob.enqueue(1, 40)
53
+
54
+ expect(TestJob).to receive(:execute).with(kind_of(Resque::Plugins::Meta::Metadata), 1, 10).ordered
55
+ expect(TestJob).to receive(:execute).with(kind_of(Resque::Plugins::Meta::Metadata), 1, 20).ordered
56
+ expect(TestJob).to_not receive(:execute).with(kind_of(Resque::Plugins::Meta::Metadata), 1, 30).ordered
57
+ expect(TestJob).to_not receive(:execute).with(kind_of(Resque::Plugins::Meta::Metadata), 1, 40).ordered
58
+ expect(TestJob).to receive(:continue)
59
+
60
+ meta_id = TestJob.meta_id(1, 10)
61
+ TestJob.perform(meta_id)
62
+ expect(TestJob.ordered_queue_size(meta_id)).to eq 2
63
+ end
64
+
65
+ context 'uniqueness' do
66
+ it 'perform with unique args only once' do
67
+ UniqueTestJob.enqueue(1, 10)
68
+ UniqueTestJob.enqueue(1, 20)
69
+ UniqueTestJob.enqueue(1, 10)
70
+
71
+ expect(UniqueTestJob).to receive(:execute).once.with(kind_of(Resque::Plugins::Meta::Metadata), 1, 10).ordered
72
+ expect(UniqueTestJob).to receive(:execute).once.with(kind_of(Resque::Plugins::Meta::Metadata), 1, 20).ordered
73
+ expect(UniqueTestJob).to_not receive(:continue)
74
+
75
+ meta_id = UniqueTestJob.meta_id(1, 10)
76
+ UniqueTestJob.perform(meta_id)
77
+ expect(UniqueTestJob.ordered_queue_size(meta_id)).to eq 0
78
+ expect(UniqueTestJob.uniqueness.size(meta_id)).to eq 0
79
+ end
80
+
81
+ it 'enqueue unique jobs with equal meta' do
82
+ meta = UniqueTestJob.enqueue(1, 10)
83
+ expect(meta.meta_id).to eq UniqueTestJob.enqueue(1, 10).meta_id
84
+ expect(meta.meta_id).to_not eq UniqueTestJob.enqueue(1, 20).meta_id
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Resque::Integration::Priority do
4
+ class JobWithPriority
5
+ include Resque::Integration
6
+
7
+ queue :foo
8
+ prioritized
9
+
10
+ def self.perform(id, params)
11
+ end
12
+ end
13
+
14
+ class UniqueJobWithPriority
15
+ include Resque::Integration
16
+
17
+ queue :foo
18
+ unique do |id, params|
19
+ [id, params["param"]]
20
+ end
21
+ prioritized
22
+
23
+ def self.execute(id, params)
24
+ end
25
+ end
26
+
27
+ describe '#enqueue' do
28
+ it 'enqueue to priority queue' do
29
+ JobWithPriority.enqueue_with_priority(:high, 1, param: 'one')
30
+
31
+ expect(Resque.size(:foo)).to eq(0)
32
+ expect(Resque.size(:foo_low)).to eq(0)
33
+ expect(Resque.size(:foo_high)).to eq(1)
34
+ end
35
+
36
+ it 'enqueue to default queue' do
37
+ JobWithPriority.enqueue(1, param: 'one')
38
+
39
+ expect(Resque.size(:foo)).to eq(1)
40
+ expect(Resque.size(:foo_low)).to eq(0)
41
+ expect(Resque.size(:foo_high)).to eq(0)
42
+ end
43
+
44
+ it 'enqueue only one job' do
45
+ meta1 = UniqueJobWithPriority.enqueue_with_priority(:high, 1, param: 'one')
46
+ meta2 = UniqueJobWithPriority.enqueue_with_priority(:high, 1, param: 'one')
47
+
48
+ expect(meta1.meta_id).to eq(meta2.meta_id)
49
+
50
+ expect(Resque.size(:foo)).to eq(0)
51
+ expect(Resque.size(:foo_low)).to eq(0)
52
+ expect(Resque.size(:foo_high)).to eq(1)
53
+ end
54
+ end
55
+
56
+ describe '#dequeue' do
57
+ it 'dequeue simple job with high priority' do
58
+ JobWithPriority.enqueue_with_priority(:high, 1, param: 'one')
59
+ JobWithPriority.enqueue_with_priority(:high, 2, param: 'two')
60
+ expect(Resque.size(:foo_high)).to eq(2)
61
+
62
+ JobWithPriority.dequeue(:high, 1, param: 'one')
63
+ expect(Resque.size(:foo_high)).to eq(1)
64
+ end
65
+
66
+ it 'dequeue unique job with high priority' do
67
+ UniqueJobWithPriority.enqueue_with_priority(:high, 1, param: 'one')
68
+ UniqueJobWithPriority.enqueue_with_priority(:high, 2, param: 'two')
69
+ expect(Resque.size(:foo_high)).to eq(2)
70
+
71
+ UniqueJobWithPriority.dequeue(:high, 1, param: 'one')
72
+ expect(Resque.size(:foo_high)).to eq(1)
73
+ end
74
+ end
75
+
76
+ describe '#perform' do
77
+ include_context 'resque inline'
78
+
79
+ it 'executes job' do
80
+ expect(JobWithPriority).to receive(:perform).with(1, 'param' => 'one')
81
+ expect(JobWithPriority).to receive(:perform).with(2, 'param' => 'two')
82
+
83
+ JobWithPriority.enqueue_with_priority(:high, 1, param: 'one')
84
+ JobWithPriority.enqueue_with_priority(:high, 2, param: 'two')
85
+ end
86
+
87
+ it 'executes job' do
88
+ expect(UniqueJobWithPriority).to receive(:execute).twice.and_call_original
89
+
90
+ UniqueJobWithPriority.enqueue_with_priority(:high, 1, param: 'one')
91
+ UniqueJobWithPriority.enqueue_with_priority(:high, 1, param: 'one')
92
+ end
93
+ end
94
+ end