canvas_sync 0.21.1 → 0.22.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/lib/canvas_sync/concerns/auto_relations.rb +11 -0
  3. data/lib/canvas_sync/config.rb +3 -5
  4. data/lib/canvas_sync/generators/templates/models/rubric.rb +2 -1
  5. data/lib/canvas_sync/job_batches/batch.rb +432 -402
  6. data/lib/canvas_sync/job_batches/callback.rb +100 -114
  7. data/lib/canvas_sync/job_batches/chain_builder.rb +194 -196
  8. data/lib/canvas_sync/job_batches/{active_job.rb → compat/active_job.rb} +2 -2
  9. data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/helpers.rb +1 -1
  10. data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web.rb +3 -3
  11. data/lib/canvas_sync/job_batches/{sidekiq.rb → compat/sidekiq.rb} +35 -22
  12. data/lib/canvas_sync/job_batches/compat.rb +20 -0
  13. data/lib/canvas_sync/job_batches/context_hash.rb +124 -126
  14. data/lib/canvas_sync/job_batches/jobs/base_job.rb +2 -4
  15. data/lib/canvas_sync/job_batches/jobs/concurrent_batch_job.rb +14 -16
  16. data/lib/canvas_sync/job_batches/jobs/managed_batch_job.rb +125 -127
  17. data/lib/canvas_sync/job_batches/jobs/serial_batch_job.rb +14 -16
  18. data/lib/canvas_sync/job_batches/pool.rb +193 -195
  19. data/lib/canvas_sync/job_batches/redis_model.rb +50 -52
  20. data/lib/canvas_sync/job_batches/redis_script.rb +129 -131
  21. data/lib/canvas_sync/job_batches/status.rb +85 -87
  22. data/lib/canvas_sync/job_uniqueness/compat/active_job.rb +75 -0
  23. data/lib/canvas_sync/job_uniqueness/compat/sidekiq.rb +135 -0
  24. data/lib/canvas_sync/job_uniqueness/compat.rb +20 -0
  25. data/lib/canvas_sync/job_uniqueness/configuration.rb +25 -0
  26. data/lib/canvas_sync/job_uniqueness/job_uniqueness.rb +47 -0
  27. data/lib/canvas_sync/job_uniqueness/lock_context.rb +171 -0
  28. data/lib/canvas_sync/job_uniqueness/locksmith.rb +92 -0
  29. data/lib/canvas_sync/job_uniqueness/on_conflict/base.rb +32 -0
  30. data/lib/canvas_sync/job_uniqueness/on_conflict/log.rb +13 -0
  31. data/lib/canvas_sync/job_uniqueness/on_conflict/null_strategy.rb +9 -0
  32. data/lib/canvas_sync/job_uniqueness/on_conflict/raise.rb +11 -0
  33. data/lib/canvas_sync/job_uniqueness/on_conflict/reject.rb +21 -0
  34. data/lib/canvas_sync/job_uniqueness/on_conflict/reschedule.rb +20 -0
  35. data/lib/canvas_sync/job_uniqueness/on_conflict.rb +41 -0
  36. data/lib/canvas_sync/job_uniqueness/strategy/base.rb +104 -0
  37. data/lib/canvas_sync/job_uniqueness/strategy/until_and_while_executing.rb +35 -0
  38. data/lib/canvas_sync/job_uniqueness/strategy/until_executed.rb +20 -0
  39. data/lib/canvas_sync/job_uniqueness/strategy/until_executing.rb +20 -0
  40. data/lib/canvas_sync/job_uniqueness/strategy/until_expired.rb +16 -0
  41. data/lib/canvas_sync/job_uniqueness/strategy/while_executing.rb +26 -0
  42. data/lib/canvas_sync/job_uniqueness/strategy.rb +27 -0
  43. data/lib/canvas_sync/job_uniqueness/unique_job_common.rb +79 -0
  44. data/lib/canvas_sync/misc_helper.rb +1 -1
  45. data/lib/canvas_sync/version.rb +1 -1
  46. data/lib/canvas_sync.rb +4 -3
  47. data/spec/dummy/app/models/rubric.rb +2 -1
  48. data/spec/dummy/config/environments/test.rb +1 -1
  49. data/spec/job_batching/batch_spec.rb +49 -7
  50. data/spec/job_batching/{active_job_spec.rb → compat/active_job_spec.rb} +2 -2
  51. data/spec/job_batching/{sidekiq_spec.rb → compat/sidekiq_spec.rb} +14 -12
  52. data/spec/job_batching/flow_spec.rb +1 -1
  53. data/spec/job_batching/integration_helper.rb +1 -1
  54. data/spec/job_batching/status_spec.rb +2 -2
  55. data/spec/job_uniqueness/compat/active_job_spec.rb +49 -0
  56. data/spec/job_uniqueness/compat/sidekiq_spec.rb +68 -0
  57. data/spec/job_uniqueness/lock_context_spec.rb +95 -0
  58. data/spec/job_uniqueness/on_conflict/log_spec.rb +11 -0
  59. data/spec/job_uniqueness/on_conflict/raise_spec.rb +10 -0
  60. data/spec/job_uniqueness/on_conflict/reschedule_spec.rb +24 -0
  61. data/spec/job_uniqueness/on_conflict_spec.rb +16 -0
  62. data/spec/job_uniqueness/spec_helper.rb +14 -0
  63. data/spec/job_uniqueness/strategy/base_spec.rb +100 -0
  64. data/spec/job_uniqueness/strategy/until_and_while_executing_spec.rb +48 -0
  65. data/spec/job_uniqueness/strategy/until_executed_spec.rb +23 -0
  66. data/spec/job_uniqueness/strategy/until_executing_spec.rb +23 -0
  67. data/spec/job_uniqueness/strategy/until_expired_spec.rb +23 -0
  68. data/spec/job_uniqueness/strategy/while_executing_spec.rb +33 -0
  69. data/spec/job_uniqueness/support/lock_strategy.rb +28 -0
  70. data/spec/job_uniqueness/support/on_conflict.rb +24 -0
  71. data/spec/job_uniqueness/support/test_worker.rb +19 -0
  72. data/spec/job_uniqueness/unique_job_common_spec.rb +45 -0
  73. data/spec/spec_helper.rb +1 -1
  74. metadata +278 -204
  75. /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/batches_assets/css/styles.less +0 -0
  76. /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/batches_assets/js/batch_tree.js +0 -0
  77. /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/batches_assets/js/util.js +0 -0
  78. /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_batch_tree.erb +0 -0
  79. /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_batches_table.erb +0 -0
  80. /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_common.erb +0 -0
  81. /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_jobs_table.erb +0 -0
  82. /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/_pagination.erb +0 -0
  83. /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/batch.erb +0 -0
  84. /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/batches.erb +0 -0
  85. /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/pool.erb +0 -0
  86. /data/lib/canvas_sync/job_batches/{sidekiq → compat/sidekiq}/web/views/pools.erb +0 -0
@@ -0,0 +1,68 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobUniqueness::Compat::Sidekiq do
4
+ let(:config) { defined?(Sidekiq::Config) ? double(Sidekiq::Config) : class_double(Sidekiq) }
5
+ let(:client_middleware) { double(Sidekiq::Middleware::Chain) }
6
+
7
+ context 'client' do
8
+ it 'adds client middleware' do
9
+ allow(Sidekiq).to receive(:configure_client).and_yield(config)
10
+ expect(config).to receive(:client_middleware).and_yield(client_middleware)
11
+ expect(client_middleware).to receive(:insert_before).with(anything, CanvasSync::JobUniqueness::Compat::Sidekiq::ClientMiddleware)
12
+ CanvasSync::JobUniqueness::Compat::Sidekiq.configure
13
+ end
14
+ end
15
+
16
+ context 'server' do
17
+ let(:server_middleware) { double(Sidekiq::Middleware::Chain) }
18
+
19
+ it 'adds client and server middleware' do
20
+ allow(Sidekiq).to receive(:configure_server).and_yield(config)
21
+ expect(config).to receive(:client_middleware).and_yield(client_middleware)
22
+ expect(config).to receive(:server_middleware).and_yield(server_middleware)
23
+ expect(client_middleware).to receive(:insert_before).with(anything, CanvasSync::JobUniqueness::Compat::Sidekiq::ClientMiddleware)
24
+ expect(server_middleware).to receive(:insert_after).with(anything, CanvasSync::JobUniqueness::Compat::Sidekiq::ServerMiddleware)
25
+ CanvasSync::JobUniqueness::Compat::Sidekiq.configure
26
+ end
27
+ end
28
+
29
+ context 'Job Extension' do
30
+ let!(:dummy_worker) do
31
+ Class.new do
32
+ include Sidekiq::Worker
33
+ end
34
+ end
35
+
36
+ it 'includes UniqueJobExtension' do
37
+ expect(dummy_worker < CanvasSync::JobUniqueness::Compat::Sidekiq::WorkerExtension).to be true
38
+ end
39
+
40
+ it 'has the ensure_uniqueness method' do
41
+ expect(dummy_worker.method(:ensure_uniqueness)).to be_present
42
+ end
43
+ end
44
+
45
+ context 'Job' do
46
+ include_context 'with TestWorker'
47
+
48
+ it 'runs as expected' do
49
+ strategy = CanvasSync::JobUniqueness::Strategy::UntilExecuted.new(nil)
50
+ allow_any_instance_of(CanvasSync::JobUniqueness::LockContext).to receive(:lock_strategy) do |lock_context|
51
+ strategy.instance_variable_set(:@lock_context, lock_context)
52
+ strategy
53
+ end
54
+ allow(CanvasSync::JobUniqueness::Strategy::UntilExecuted).to receive(:new).and_return(strategy)
55
+
56
+ expect(strategy).to receive(:on_enqueue).and_call_original
57
+ expect(strategy).to receive(:on_perform).and_call_original
58
+ expect(strategy).to receive(:batch_callback).with(:complete, anything).and_call_original
59
+ expect(strategy).to receive(:batch_callback).with(:success, anything).and_call_original
60
+
61
+ expect_any_instance_of(TestWorker).to receive(:perform)
62
+
63
+ Sidekiq::Testing.inline! do
64
+ worker.perform_async
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,95 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobUniqueness::LockContext do
4
+ let!(:worker) do
5
+ Class.new do
6
+ include Sidekiq::Worker
7
+
8
+ ensure_uniqueness(
9
+ strategy: :until_executed,
10
+ )
11
+
12
+ def perform; end
13
+ end
14
+ end
15
+
16
+ before(:each) { stub_const('TestWorker', worker) }
17
+
18
+ let(:lock_context) { described_class.new(context_data) }
19
+ let(:context_data) { { queue: 'default', job_clazz: 'TestWorker', args: [1,2,3], kwargs: { foo: 'bar' } } }
20
+
21
+ describe '#base_key' do
22
+ let(:scope) { :per_queue }
23
+ before(:each) do
24
+ TestWorker.unique_job_options[:scope] = scope
25
+ end
26
+
27
+ context 'when scope is a Proc' do
28
+ let(:scope) { ->(queue:) { "blob" } }
29
+
30
+ it 'returns the base key' do
31
+ expect(lock_context.base_key).to match /^uniquejob:blob:.*/
32
+ end
33
+ end
34
+
35
+ context 'when scope is :global' do
36
+ let(:scope) { :global }
37
+
38
+ it 'returns the base key' do
39
+ expect(lock_context.base_key).to match /^uniquejob:TestWorker:.*/
40
+ end
41
+ end
42
+
43
+ context 'when scope is :per_queue' do
44
+ let(:scope) { :per_queue }
45
+
46
+ it 'returns the base key' do
47
+ expect(lock_context.base_key).to match /^uniquejob:TestWorker:default:.*/
48
+ end
49
+ end
50
+
51
+ context 'when scope is not a Proc, :global, or :per_queue' do
52
+ let(:scope) { 'foo' }
53
+
54
+ it 'returns the base key' do
55
+ expect(lock_context.base_key).to match /^uniquejob:foo:.*/
56
+ end
57
+ end
58
+ end
59
+
60
+ describe '#call_conflict_strategy' do
61
+ before(:each) do
62
+ worker.unique_job_options[:on_conflict] = :raise
63
+ end
64
+
65
+ it 'invokes the conflict strategy' do
66
+ expect_any_instance_of(CanvasSync::JobUniqueness::OnConflict::Raise).to receive(:call)
67
+ lock_context.send(:call_conflict_strategy, :perform)
68
+ end
69
+ end
70
+
71
+ describe '#conflict_strategy_for' do
72
+ before(:each) do
73
+ worker.unique_job_options[:on_conflict] = :raise
74
+ end
75
+
76
+ it 'resolves a united conflict strategy' do
77
+ expect(lock_context.send(:conflict_strategy_for, :perform)).to be_a CanvasSync::JobUniqueness::OnConflict::Raise
78
+ end
79
+
80
+ it 'resolves enqueue strategy' do
81
+ worker.unique_job_options[:on_conflict] = { enqueue: :log, perform: :raise }
82
+ expect(lock_context.send(:conflict_strategy_for, :enqueue)).to be_a CanvasSync::JobUniqueness::OnConflict::Log
83
+ end
84
+
85
+ it 'resolves perform strategy' do
86
+ worker.unique_job_options[:on_conflict] = { enqueue: :log, perform: :reject }
87
+ expect(lock_context.send(:conflict_strategy_for, :perform)).to be_a CanvasSync::JobUniqueness::OnConflict::Reject
88
+ end
89
+
90
+ it 'resolves null strategy' do
91
+ worker.unique_job_options[:on_conflict] = { enqueue: :log }
92
+ expect(lock_context.send(:conflict_strategy_for, :perform)).to be_a CanvasSync::JobUniqueness::OnConflict::NullStrategy
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,11 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobUniqueness::OnConflict::Log do
4
+ include_context "on_conflict specs"
5
+ include_examples "OnConflict is compatible with", %i[until_executing while_executing until_executed until_and_while_executing until_expired]
6
+
7
+ it "logs" do
8
+ expect(CanvasSync::JobUniqueness.logger).to receive(:info)
9
+ on_conflict.call
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobUniqueness::OnConflict::Raise do
4
+ include_context "on_conflict specs"
5
+ include_examples "OnConflict is compatible with", %i[until_executing while_executing until_executed until_and_while_executing until_expired]
6
+
7
+ it "raises an error" do
8
+ expect { on_conflict.call }.to raise_error(CanvasSync::JobUniqueness::Conflict)
9
+ end
10
+ end
@@ -0,0 +1,24 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobUniqueness::OnConflict::Reschedule do
4
+ include_context "on_conflict specs"
5
+ include_examples "OnConflict is compatible with", %i[while_executing]
6
+
7
+ it "calls reenqueue" do
8
+ expect(lock_context).to receive(:reenqueue) do |*args, **kwargs|
9
+ expect(Thread.current[:unique_jobs_previous_jid]).to be_present
10
+ end
11
+ on_conflict.call
12
+ end
13
+
14
+ it "reschedules successfully" do
15
+ TestWorker.unique_job_options.merge!({ strategy: :until_and_while_executing, on_conflict: { enqueue: :raise, perform: :reschedule }})
16
+
17
+ lock_context.handle_lifecycle!(:enqueue) {}
18
+ lock_context.handle_lifecycle!(:perform) {}
19
+
20
+ lock_context2 = CanvasSync::JobUniqueness::Compat::Sidekiq::SidekiqLockContext.new({ jid: 'j2', queue: 'default', job_clazz: 'TestWorker'}, job_instance: {})
21
+ lock_context2.handle_lifecycle!(:enqueue) {}
22
+ lock_context2.handle_lifecycle!(:perform) {}
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobUniqueness::OnConflict do
4
+
5
+ describe ".validate!" do
6
+ it "passes a valid pair" do
7
+ described_class.validate!(:reject, :while_executing)
8
+ end
9
+
10
+ it "raises an error if the strategy is not valid for the given action" do
11
+ expect do
12
+ described_class.validate!(:reject, :until_executed)
13
+ end.to raise_error(ArgumentError)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ require_relative "../spec_helper"
2
+ require "canvas_sync/job_uniqueness/job_uniqueness"
3
+
4
+ Dir[File.join(File.dirname(__FILE__), "./support/**/*.rb")].sort.each { |f| require f }
5
+
6
+ Sidekiq::Testing.server_middleware do |chain|
7
+ chain.insert_before CanvasSync::JobBatches::Compat::Sidekiq::ServerMiddleware, CanvasSync::JobUniqueness::Compat::Sidekiq::ServerMiddleware
8
+ end
9
+
10
+ RSpec.configure do |config|
11
+ config.before(:each) do
12
+ CanvasSync.redis.flushdb
13
+ end
14
+ end
@@ -0,0 +1,100 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobUniqueness::Strategy::Base do
4
+ include_context 'with TestWorker'
5
+
6
+ let!(:jid) { worker.perform_async }
7
+ let(:context) { CanvasSync::JobUniqueness::LockContext.new({ jid: jid, queue: 'default', job_clazz: 'TestWorker' }) }
8
+ let(:strategy) { CanvasSync::JobUniqueness::Strategy::UntilExecuted.new(context) }
9
+
10
+ describe '#wrap_in_batch' do
11
+ it 'does not wrap if re-enqueuing' do
12
+ Thread.current[:unique_jobs_previous_jid] = "j100"
13
+ expect(CanvasSync::JobBatches::Batch).not_to receive(:new)
14
+ strategy.send(:wrap_in_batch, &->{ })
15
+ ensure
16
+ Thread.current[:unique_jobs_previous_jid] = nil
17
+ end
18
+
19
+ context 'when the job fails' do
20
+ before(:each) do
21
+ TestWorker.unique_job_options[:strategy] = :until_executed
22
+ allow_any_instance_of(worker).to receive(:perform) do |w|
23
+ @bid = w.bid
24
+ raise 'error'
25
+ end
26
+ end
27
+
28
+ context 'unlock_on_failure: :any' do
29
+ before(:each) { TestWorker.unique_job_options[:unlock_on_failure] = :any }
30
+
31
+ it 'unlocks on any failure' do
32
+ expect(strategy).to be_locked
33
+ expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
34
+ Sidekiq::Worker.drain_all
35
+ expect(strategy).not_to be_locked
36
+ end
37
+ end
38
+
39
+ context 'unlock_on_failure: :death' do
40
+ before(:each) { TestWorker.unique_job_options[:unlock_on_failure] = :death }
41
+
42
+ it 'does not unlock on any failure' do
43
+ expect(strategy).to be_locked
44
+ expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
45
+ Sidekiq::Worker.drain_all
46
+ expect(strategy).to be_locked
47
+ end
48
+
49
+ it 'it unlocks on death' do
50
+ expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
51
+ Sidekiq::Worker.drain_all
52
+ expect(strategy).to be_locked
53
+ CanvasSync::JobBatches::Batch.process_dead_job(@bid, jid)
54
+ Sidekiq::Worker.drain_all
55
+ expect(strategy).not_to be_locked
56
+ end
57
+ end
58
+
59
+ context 'unlock_on_failure: :stagnant' do
60
+ before(:each) { TestWorker.unique_job_options[:unlock_on_failure] = :stagnant }
61
+
62
+ it 'does not unlock on any failure' do
63
+ expect(strategy).to be_locked
64
+ expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
65
+ Sidekiq::Worker.drain_all
66
+ expect(strategy).to be_locked
67
+ end
68
+
69
+ it 'it unlocks on stagnation' do
70
+ expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
71
+ Sidekiq::Worker.drain_all
72
+ expect(strategy).to be_locked
73
+ CanvasSync::JobBatches::Batch.process_dead_job(@bid, jid)
74
+ Sidekiq::Worker.drain_all
75
+ expect(strategy).not_to be_locked
76
+ end
77
+ end
78
+
79
+ context 'unlock_on_failure: :expire' do
80
+ before(:each) { TestWorker.unique_job_options[:unlock_on_failure] = :expire }
81
+
82
+ it 'does not unlock on any failure' do
83
+ expect(strategy).to be_locked
84
+ expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
85
+ Sidekiq::Worker.drain_all
86
+ expect(strategy).to be_locked
87
+ end
88
+
89
+ it 'does not unlock on death' do
90
+ expect{ Sidekiq::Worker.drain_all }.to raise_error('error')
91
+ Sidekiq::Worker.drain_all
92
+ expect(strategy).to be_locked
93
+ CanvasSync::JobBatches::Batch.process_dead_job(@bid, jid)
94
+ Sidekiq::Worker.drain_all
95
+ expect(strategy).to be_locked
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,48 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobUniqueness::Strategy::UntilAndWhileExecuting do
4
+ include_context 'lock strategy specs', strategy: :until_and_while_executing
5
+
6
+ include_examples "a lock implementation"
7
+
8
+ describe "#on_enqueue" do
9
+ it "locks the queue" do
10
+ process_one.on_enqueue {}
11
+ expect(process_one).to be_locked
12
+ end
13
+
14
+ it "does not lock the runtime lock" do
15
+ process_one.on_enqueue {}
16
+ expect(process_one.send(:runtime_lock)).to_not be_locked
17
+ end
18
+ end
19
+
20
+ describe "#on_perform" do
21
+ it "unlocks the queue" do
22
+ process_one.on_perform do
23
+ expect(process_one).to_not be_locked
24
+ end
25
+ end
26
+
27
+ it "locks the runtime lock" do
28
+ process_one.on_perform do
29
+ expect(process_one.send(:runtime_lock)).to be_locked
30
+ end
31
+ end
32
+
33
+ it "unlocks the runtime lock after processing" do
34
+ process_one.on_perform do
35
+ process_one.send(:runtime_lock).batch_callback(:success, nil)
36
+ expect(process_one.send(:runtime_lock)).to_not be_locked
37
+ end
38
+ end
39
+
40
+ it "prevents other processes from executing" do
41
+ process_one.on_perform do
42
+ expect do
43
+ process_two.on_perform { }
44
+ end.to raise_error(CanvasSync::JobUniqueness::CouldNotLockError)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,23 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobUniqueness::Strategy::UntilExecuted do
4
+ include_context 'lock strategy specs', strategy: :until_executed
5
+
6
+ include_examples "a lock implementation"
7
+
8
+ describe "#on_enqueue" do
9
+ it "locks the queue" do
10
+ process_one.on_enqueue {}
11
+ expect(process_one).to be_locked
12
+ end
13
+ end
14
+
15
+ describe "#on_perform" do
16
+ it "unlocks after processing" do
17
+ process_one.on_enqueue {}
18
+ process_one.on_perform {}
19
+ process_one.batch_callback(:success, nil)
20
+ expect(process_one).to_not be_locked
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobUniqueness::Strategy::UntilExecuting do
4
+ include_context 'lock strategy specs', strategy: :until_executing
5
+
6
+ include_examples "a lock implementation"
7
+
8
+ describe "#on_enqueue" do
9
+ it "locks the queue" do
10
+ process_one.on_enqueue {}
11
+ expect(process_one).to be_locked
12
+ end
13
+ end
14
+
15
+ describe "#on_perform" do
16
+ it "unlocks before processing" do
17
+ process_one.on_enqueue {}
18
+ process_one.on_perform do
19
+ expect(process_one).to_not be_locked
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobUniqueness::Strategy::UntilExpired do
4
+ include_context 'lock strategy specs', strategy: :until_expired
5
+
6
+ # include_examples "a lock implementation"
7
+
8
+ describe "#on_enqueue" do
9
+ it "locks the queue" do
10
+ process_one.on_enqueue {}
11
+ expect(process_one).to be_locked
12
+ end
13
+ end
14
+
15
+ describe "#on_perform" do
16
+ it "does not unlock after processing" do
17
+ process_one.on_enqueue {}
18
+ process_one.on_perform {}
19
+ process_one.batch_callback(:success, nil)
20
+ expect(process_one).to be_locked
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobUniqueness::Strategy::WhileExecuting do
4
+ include_context 'lock strategy specs', strategy: :while_executing
5
+
6
+ include_examples "a lock implementation"
7
+
8
+ describe "#on_enqueue" do
9
+ it "does not lock jobs" do
10
+ expect(process_one.on_enqueue {})
11
+ expect(process_one).not_to be_locked
12
+
13
+ expect(process_two.on_enqueue {})
14
+ expect(process_two).not_to be_locked
15
+ end
16
+ end
17
+
18
+ describe "#on_perform" do
19
+ it "locks the process" do
20
+ process_one.on_perform do
21
+ expect(process_one).to be_locked
22
+ end
23
+ end
24
+
25
+ it "prevents other processes from executing" do
26
+ process_one.on_perform do
27
+ expect do
28
+ process_two.on_perform { }
29
+ end.to raise_error(CanvasSync::JobUniqueness::CouldNotLockError)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.shared_context "lock strategy specs" do |strategy:|
4
+ include_context 'with TestWorker'
5
+
6
+ before(:each) { TestWorker.unique_job_options[:strategy] = strategy }
7
+
8
+ let(:context1) { CanvasSync::JobUniqueness::LockContext.new({ jid: 'j1', queue: 'default', job_clazz: 'TestWorker'}) }
9
+ let(:context2) { CanvasSync::JobUniqueness::LockContext.new({ jid: 'j2', queue: 'default', job_clazz: 'TestWorker'}) }
10
+
11
+ let(:process_one) { described_class.new(context1) }
12
+ let(:process_two) { described_class.new(context2) }
13
+ end
14
+
15
+ RSpec.shared_examples "a lock implementation" do
16
+ it "is unlocked when done" do
17
+ process_one.on_enqueue {}
18
+ did_lock = process_one.locked?
19
+
20
+ process_one.on_perform {}
21
+ did_lock ||= process_one.locked?
22
+
23
+ expect(did_lock).to be_truthy
24
+
25
+ process_one.batch_callback(:success, nil)
26
+ expect(process_one).not_to be_locked
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+
2
+ RSpec.shared_context "on_conflict specs" do
3
+ include_context 'with TestWorker'
4
+ let(:lock_context) { CanvasSync::JobUniqueness::LockContext.new({ jid: 'j1', queue: 'default', job_clazz: 'TestWorker'}) }
5
+ let(:on_conflict) { described_class.new(lock_context)}
6
+ end
7
+
8
+ RSpec.shared_examples "OnConflict is compatible with" do |lock_strategies|
9
+ it "is compatible with the expected Lock Strategies" do
10
+ lock_strategies.each do |lock_strategy|
11
+ CanvasSync::JobUniqueness::OnConflict.validate!(described_class, lock_strategy)
12
+ end
13
+ end
14
+ end
15
+
16
+ RSpec.shared_examples "OnConflict is incompatible with" do |lock_strategies|
17
+ it "is not compatible with the expected Lock Strategies" do
18
+ lock_strategies.each do |lock_strategy|
19
+ expect do
20
+ CanvasSync::JobUniqueness::OnConflict.validate!(described_class, lock_strategy)
21
+ end.to raise_error(ArgumentError)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.shared_context "with TestWorker" do
4
+ let!(:worker) do
5
+ Class.new do
6
+ include Sidekiq::Worker
7
+
8
+ ensure_uniqueness(
9
+ strategy: :until_executed,
10
+ )
11
+
12
+ def perform; end
13
+ end
14
+ end
15
+
16
+ before(:each) { stub_const('TestWorker', worker) }
17
+
18
+ let(:lock_context) { CanvasSync::JobUniqueness::LockContext.new({ jid: 'j1', queue: 'default', job_clazz: 'TestWorker'}) }
19
+ end
@@ -0,0 +1,45 @@
1
+ require 'job_uniqueness/spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobUniqueness::UniqueJobCommon do
4
+ describe ".unlock!" do
5
+ include_context "with TestWorker"
6
+
7
+ it "unlocks the lock with a specific JID" do
8
+ jid = TestWorker.perform_async
9
+ lctx = CanvasSync::JobUniqueness::LockContext.new({ jid: jid, queue: 'default', job_clazz: 'TestWorker'})
10
+ lstrat = lctx.lock_strategy
11
+ lsmith = lstrat.send(:locksmith)
12
+ expect(lsmith.locked?).to be_truthy
13
+
14
+ TestWorker.unlock!(jid, queue: 'default')
15
+ expect(lsmith.locked?).to be_falsey
16
+ end
17
+
18
+ it "unlocks the lock without a specific JID" do
19
+ jid = TestWorker.perform_async
20
+ lctx = CanvasSync::JobUniqueness::LockContext.new({ jid: jid, queue: 'default', job_clazz: 'TestWorker'})
21
+ lstrat = lctx.lock_strategy
22
+ lsmith = lstrat.send(:locksmith)
23
+ expect(lsmith.locked?).to be_truthy
24
+
25
+ TestWorker.unlock!(queue: 'default')
26
+ expect(lsmith.locked?).to be_falsey
27
+ end
28
+
29
+ it "unlocks the lock matching parameters" do
30
+ args = [1, 2, 3]
31
+ jid = TestWorker.perform_async(*args)
32
+
33
+ lctx = CanvasSync::JobUniqueness::LockContext.new({ jid: jid, queue: 'default', job_clazz: 'TestWorker', args: args})
34
+ lstrat = lctx.lock_strategy
35
+ lsmith = lstrat.send(:locksmith)
36
+ expect(lsmith.locked?).to be_truthy
37
+
38
+ TestWorker.unlock!(queue: 'default')
39
+ expect(lsmith.locked?).to be_truthy
40
+
41
+ TestWorker.unlock!(queue: 'default', args: args)
42
+ expect(lsmith.locked?).to be_falsey
43
+ end
44
+ end
45
+ end
data/spec/spec_helper.rb CHANGED
@@ -19,7 +19,7 @@ require 'with_model'
19
19
  require 'sidekiq/testing'
20
20
  Sidekiq::Testing.fake!
21
21
  Sidekiq::Testing.server_middleware do |chain|
22
- chain.add CanvasSync::JobBatches::Sidekiq::ServerMiddleware
22
+ chain.add CanvasSync::JobBatches::Compat::Sidekiq::ServerMiddleware
23
23
  end
24
24
 
25
25
  Dir[File.dirname(__FILE__) + "/job_batching/support/**/*.rb"].each {|f| require f }