Commit 9a2bf96c authored by Mike Kozono's avatar Mike Kozono

Refactor: Reenqueuer shared examples

- Allow the user to specify args in the `perform` call
- For simplicity, require `subject` to be an instance of "worker class"
- For simplicity, require the rate limited method to be `#perform`
parent 2c0cdc7f
......@@ -13,7 +13,7 @@
# - `#lease_timeout`
#
# The worker spec should include `it_behaves_like 'reenqueuer'` and
# `it_behaves_like 'it is rate limited to 1 call per'`.
# `it_behaves_like '#perform is rate limited to 1 call per'`.
#
# Optionally override `#minimum_duration` to adjust the rate limit.
#
......
......@@ -9,9 +9,10 @@ RSpec.describe Geo::Secondary::RegistryConsistencyWorker, :geo do
let_it_be(:primary) { create(:geo_node, :primary) }
let_it_be(:secondary) { create(:geo_node) }
let(:worker_class) { described_class }
let(:batch_size) { described_class::BATCH_SIZE }
subject(:job) { described_class.new }
before do
stub_current_geo_node(secondary)
stub_registry_replication_config(enabled: true)
......@@ -31,9 +32,7 @@ RSpec.describe Geo::Secondary::RegistryConsistencyWorker, :geo do
allow(subject).to receive(:sleep) # faster tests
end
it_behaves_like 'it is rate limited to 1 call per', 5.seconds do
let(:rate_limited_method) { subject.perform }
end
it_behaves_like '#perform is rate limited to 1 call per', 5.seconds
context 'when RegistryConsistencyService#execute returns true at least once' do
before do
......
# frozen_string_literal: true
# Expects `worker_class` to be defined
# Expects `subject` to be a job/worker instance
RSpec.shared_examples 'reenqueuer' do
subject(:job) { worker_class.new }
before do
allow(job).to receive(:sleep) # faster tests
allow(subject).to receive(:sleep) # faster tests
end
it 'implements lease_timeout' do
expect(job.lease_timeout).to be_a(ActiveSupport::Duration)
expect(subject.lease_timeout).to be_a(ActiveSupport::Duration)
end
describe '#perform' do
it 'tries to obtain a lease' do
expect_to_obtain_exclusive_lease(job.lease_key)
expect_to_obtain_exclusive_lease(subject.lease_key)
job.perform
subject.perform
end
end
end
# Example usage:
#
# it_behaves_like 'it is rate limited to 1 call per', 5.seconds do
# subject { described_class.new }
# let(:rate_limited_method) { subject.perform }
# end
#
RSpec.shared_examples 'it is rate limited to 1 call per' do |minimum_duration|
# Expects `subject` to be a job/worker instance
RSpec.shared_examples '#perform is rate limited to 1 call per' do |minimum_duration|
before do
# Allow Timecop freeze and travel without the block form
Timecop.safe_mode = false
Timecop.freeze
time_travel_during_rate_limited_method(actual_duration)
time_travel_during_perform(actual_duration)
end
after do
......@@ -48,7 +40,7 @@ RSpec.shared_examples 'it is rate limited to 1 call per' do |minimum_duration|
it 'sleeps exactly the minimum duration' do
expect(subject).to receive(:sleep).with(a_value_within(0.01).of(minimum_duration))
rate_limited_method
subject.perform
end
end
......@@ -58,7 +50,7 @@ RSpec.shared_examples 'it is rate limited to 1 call per' do |minimum_duration|
it 'sleeps 90% of minimum duration' do
expect(subject).to receive(:sleep).with(a_value_within(0.01).of(0.9 * minimum_duration))
rate_limited_method
subject.perform
end
end
......@@ -68,7 +60,7 @@ RSpec.shared_examples 'it is rate limited to 1 call per' do |minimum_duration|
it 'sleeps 10% of minimum duration' do
expect(subject).to receive(:sleep).with(a_value_within(0.01).of(0.1 * minimum_duration))
rate_limited_method
subject.perform
end
end
......@@ -78,7 +70,7 @@ RSpec.shared_examples 'it is rate limited to 1 call per' do |minimum_duration|
it 'does not sleep' do
expect(subject).not_to receive(:sleep)
rate_limited_method
subject.perform
end
end
......@@ -88,7 +80,7 @@ RSpec.shared_examples 'it is rate limited to 1 call per' do |minimum_duration|
it 'does not sleep' do
expect(subject).not_to receive(:sleep)
rate_limited_method
subject.perform
end
end
......@@ -98,11 +90,11 @@ RSpec.shared_examples 'it is rate limited to 1 call per' do |minimum_duration|
it 'does not sleep' do
expect(subject).not_to receive(:sleep)
rate_limited_method
subject.perform
end
end
def time_travel_during_rate_limited_method(actual_duration)
def time_travel_during_perform(actual_duration)
# Save the original implementation of ensure_minimum_duration
original_ensure_minimum_duration = subject.method(:ensure_minimum_duration)
......
......@@ -40,9 +40,7 @@ RSpec.describe Reenqueuer do
it_behaves_like 'reenqueuer'
it_behaves_like 'it is rate limited to 1 call per', 5.seconds do
let(:rate_limited_method) { subject.perform }
end
it_behaves_like '#perform is rate limited to 1 call per', 5.seconds
it 'disables Sidekiq retries' do
expect(job.sidekiq_options_hash).to include('retry' => false)
......@@ -98,7 +96,7 @@ RSpec.describe Reenqueuer::ReenqueuerSleeper do
Class.new do
include Reenqueuer::ReenqueuerSleeper
def rate_limited_method
def perform
ensure_minimum_duration(11.seconds) do
# do work
end
......@@ -108,12 +106,11 @@ RSpec.describe Reenqueuer::ReenqueuerSleeper do
subject(:dummy) { dummy_class.new }
# Test that rate_limited_method is rate limited by ensure_minimum_duration
it_behaves_like 'it is rate limited to 1 call per', 11.seconds do
let(:rate_limited_method) { dummy.rate_limited_method }
end
# Slightly higher-level test of ensure_minimum_duration since we conveniently
# already have this shared example anyway.
it_behaves_like '#perform is rate limited to 1 call per', 11.seconds
# Test ensure_minimum_duration more directly
# Unit test ensure_minimum_duration
describe '#ensure_minimum_duration' do
around do |example|
# Allow Timecop.travel without the block form
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment