canvas_sync 0.21.1 → 0.22.0.beta1

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 (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 }