diff --git a/lib/rspec/sidekiq/matchers/be_unique.rb b/lib/rspec/sidekiq/matchers/be_unique.rb index 97bc292..96f1c25 100644 --- a/lib/rspec/sidekiq/matchers/be_unique.rb +++ b/lib/rspec/sidekiq/matchers/be_unique.rb @@ -25,6 +25,9 @@ def failure_message if !interval_matches? && @expected_interval "expected #{@klass} to be unique for #{@expected_interval} seconds, "\ "but its interval was #{actual_interval} seconds" + elsif !expiration_matches? + "expected #{@klass} to be unique until #{@expected_expiration}, "\ + "but its unique_until was #{actual_expiration || 'not specified'}" else "expected #{@klass} to be unique in the queue" end @@ -33,7 +36,7 @@ def failure_message def matches?(job) @klass = job.is_a?(Class) ? job : job.class @actual = @klass.get_sidekiq_options[unique_key] - !!(value_matches? && interval_matches?) + !!(value_matches? && interval_matches? && expiration_matches?) end def for(interval) @@ -41,6 +44,11 @@ def for(interval) self end + def until(expiration) + @expected_expiration = expiration + self + end + def interval_specified? @expected_interval end @@ -49,6 +57,10 @@ def interval_matches? !interval_specified? || actual_interval == @expected_interval end + def expiration_matches? + @expected_expiration.nil? || actual_expiration == @expected_expiration + end + def failure_message_when_negated "expected #{@klass} to not be unique in the queue" end @@ -59,6 +71,10 @@ def actual_interval @klass.get_sidekiq_options['unique_job_expiration'] end + def actual_expiration + fail 'until is not supported for SidekiqUniqueJobs' + end + def value_matches? [true, :all].include?(@actual) end @@ -73,6 +89,10 @@ def actual_interval @actual end + def actual_expiration + @klass.get_sidekiq_options['unique_until'] + end + def value_matches? @actual && @actual > 0 end diff --git a/spec/rspec/sidekiq/matchers/be_unique_spec.rb b/spec/rspec/sidekiq/matchers/be_unique_spec.rb index b998d32..72f83d5 100644 --- a/spec/rspec/sidekiq/matchers/be_unique_spec.rb +++ b/spec/rspec/sidekiq/matchers/be_unique_spec.rb @@ -53,6 +53,57 @@ include_context 'a unique worker' end + context '.until' do + let(:module_constant) { "Sidekiq::Enterprise" } + let(:expiration) { :success } + let(:interval) { 3.hours } + let(:sidekiq_options) { { unique_for: interval, unique_until: expiration } } + let(:worker) do + options = sidekiq_options + Class.new do + include ::Sidekiq::Worker + sidekiq_options options + def perform; end + end + end + + before { stub_const(module_constant, true) } + + subject do + stub_const('MuhWorker', worker) + MuhWorker + end + + it { should be_unique.for(interval).until(:success) } + + context 'errors' do + subject { expect(super()).to be_unique.until(:started) } + + context 'when there is a mismatch' do + it do + expect { subject }.to raise_error RSpec::Expectations::ExpectationNotMetError, + 'expected MuhWorker to be unique until started, but its unique_until was success' + end + end + + context 'when not specified' do + let(:expiration) { nil } + + it do + expect { subject }.to raise_error RSpec::Expectations::ExpectationNotMetError, + 'expected MuhWorker to be unique until started, but its unique_until was not specified' + end + end + + context 'when unique_until is not supported' do + let(:module_constant) { "SidekiqUniqueJobs" } + let(:sidekiq_options) { { unique: interval } } + + it { expect { subject }.to raise_error 'until is not supported for SidekiqUniqueJobs' } + end + end + end + context 'a sidekiq-unique-jobs scheduled worker' do let(:module_constant) { "SidekiqUniqueJobs" } before { @worker = create_worker unique: :all }