diff --git a/Gemfile.lock b/Gemfile.lock index 5a8df78..89b73d9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,7 +6,17 @@ PATH GEM remote: https://rubygems.org/ specs: + activesupport (6.0.1) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 0.7, < 2) + minitest (~> 5.1) + tzinfo (~> 1.1) + zeitwerk (~> 2.2) + concurrent-ruby (1.1.5) diff-lcs (1.3) + i18n (1.7.0) + concurrent-ruby (~> 1.0) + minitest (5.13.0) rake (10.5.0) rspec (3.9.0) rspec-core (~> 3.9.0) @@ -21,11 +31,16 @@ GEM diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.9.0) rspec-support (3.9.0) + thread_safe (0.3.6) + tzinfo (1.2.5) + thread_safe (~> 0.1) + zeitwerk (2.2.1) PLATFORMS ruby DEPENDENCIES + activesupport rake (~> 10.0) rspec (~> 3.0) simple-redis-lock! diff --git a/spec/simple_redis_lock/redis_lock_spec.rb b/spec/simple_redis_lock/redis_lock_spec.rb new file mode 100644 index 0000000..52a5fa0 --- /dev/null +++ b/spec/simple_redis_lock/redis_lock_spec.rb @@ -0,0 +1,118 @@ +require 'spec_helper' + +RSpec.describe SimpleRedisLock::RedisLock do + let(:redis_double) { double('Redis') } + let(:redis_lock) { described_class.new(retry_count: retry_count) } + let(:retry_count) { 20 } + before { allow_any_instance_of(described_class).to receive(:redis).and_return(redis_double) } + + describe '#lock_resource' do + let(:key) { 'key' } + let(:value) { 'value' } + let(:ttl) { 2.seconds } + subject(:locked) { redis_lock.lock_resource(key, value, ttl) } + + context 'when the lock is available' do + before { allow(redis_double).to receive_messages(set: true) } + + it 'acquires the lock' do + expect(locked).to be true + end + end + + context 'when the lock is unavailable' do + it 'tries to acquire the lock based on the retry count' do + expect(redis_double).to receive(:set).exactly(retry_count).times + expect(locked).to be false + end + end + + context 'when the lock is available during retry' do + it 'tries to acquire the lock and succeeds' do + responses = [false] * 10 << true + allow(redis_double).to receive(:set).and_return(*responses) + expect(locked).to be true + end + end + end + + describe '#with_lock' do + let(:key) { 'key' } + let(:model) { double('model', foo: 'some name') } + + subject(:with_lock) do + redis_lock.with_lock(resource: key) do |locked| + model.foo if locked + end + end + + context 'when the lock is available' do + before { allow(redis_double).to receive_messages(set: true) } + + it 'yields the inner block' do + allow(redis_double).to receive_messages(eval: 1) + expect(model).to receive(:foo) + with_lock + end + + it 'unlocks the lock after completion' do + expect(redis_double).to receive_messages(eval: 0) + with_lock + end + + it 'unlocks the lock even if the block errors' do + expect(redis_double).to receive_messages(eval: 0) + allow(model).to receive(:foo).and_throw(:explosions) + + catch :explosions do + with_lock + end + end + end + + context 'when the lock is unavailable' do + before { allow(redis_double).to receive_messages(set: false) } + + it 'unlocks the lock' do + expect(redis_double).to receive_messages(eval: 1) + with_lock + end + + it 'does not yield the inner block' do + allow(redis_double).to receive_messages(eval: 1) + expect(model).not_to receive(:foo) + with_lock + end + end + end + + describe '#with_lock!' do + let(:key) { 'key' } + let(:return_value) { 'some name' } + let(:model) { double('model', foo: return_value) } + + subject(:with_lock!) do + redis_lock.with_lock!(resource: key) do + model.foo + end + end + + context 'when the lock is available' do + before { allow(redis_double).to receive_messages(set: true, eval: 1) } + + it 'yields the inner block and returns its value' do + allow(redis_double).to receive_messages(eval: 1) + expect(with_lock!).to eq return_value + end + end + + context 'when the lock is unavailable' do + before { allow(redis_double).to receive_messages(set: false, eval: 0) } + + it 'throws an exception' do + allow(redis_double).to receive_messages(eval: 1) + expect { with_lock! }.to raise_exception LockError + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5d5dc03..8a802bf 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,5 @@ require 'bundler/setup' +require 'rspec' require 'simple_redis_lock/redis_lock' RSpec.configure do |config|