spec/mongoid-locker_spec.rb in mongoid-locker-0.2.1 vs spec/mongoid-locker_spec.rb in mongoid-locker-0.3.0

- old
+ new

@@ -1,196 +1,255 @@ require File.join(File.dirname(__FILE__), 'spec_helper') describe Mongoid::Locker do - def remove_class klass + def remove_class(klass) Object.send :remove_const, klass.to_s.to_sym end before do # recreate the class for each spec class User include Mongoid::Document include Mongoid::Locker - field :account_balance, :type => Integer # easier to test than Float + field :account_balance, type: Integer # easier to test than Float end - @user = User.create! :account_balance => 20 + @user = User.create! account_balance: 20 end after do User.delete_all remove_class User end - - describe "#locked?" do + describe '#locked?' do it "shouldn't be locked when created" do - @user.locked?.should be_false + expect(@user.locked?).to be false end - it "should be true when locked" do + it 'should be true when locked' do @user.with_lock do - @user.locked?.should be_true + expect(@user.locked?).to be true end end - it "should respect the expiration" do + it 'should respect the expiration' do User.timeout_lock_after 1 @user.with_lock do sleep 2 - @user.locked?.should be_false + expect(@user.locked?).to be false end end - it "should be true for a different instance" do + it 'should be true for a different instance' do @user.with_lock do - User.first.locked?.should be_true + expect(User.first.locked?).to be true end end end - describe "#has_lock?" do + describe '#has_lock?' do it "shouldn't be has_lock when created" do - @user.has_lock?.should be_false + expect(@user.has_lock?).to be false end - it "should be true when has_lock" do + it 'should be true when has_lock' do @user.with_lock do - @user.has_lock?.should be_true + expect(@user.has_lock?).to be true end end - it "should respect the expiration" do + it 'should respect the expiration' do User.timeout_lock_after 1 @user.with_lock do sleep 2 - @user.has_lock?.should be_false + expect(@user.has_lock?).to be false end end - it "should be false for a different instance" do + it 'should be false for a different instance' do @user.with_lock do - User.first.has_lock?.should be_false + expect(User.first.has_lock?).to be false end end end - describe "#with_lock" do - it "should lock and unlock the user" do + describe '#with_lock' do + it 'should lock and unlock the user' do @user.with_lock do - @user.should be_locked - User.first.should be_locked + expect(@user).to be_locked + expect(User.first).to be_locked end - @user.should_not be_locked - @user.reload.should_not be_locked + expect(@user).to_not be_locked + expect(@user.reload).to_not be_locked end it "shouldn't save the full document" do @user.with_lock do @user.account_balance = 10 end - @user.account_balance.should eq(10) - User.first.account_balance.should eq(20) + expect(@user.account_balance).to eq(10) + expect(User.first.account_balance).to eq(20) end - it "should handle errors gracefully" do + it 'should handle errors gracefully' do expect { @user.with_lock do - raise "booyah!" + fail 'booyah!' end }.to raise_error - @user.reload.should_not be_locked + expect(@user.reload).to_not be_locked end - it "should complain if trying to lock locked doc" do + it 'should complain if trying to lock locked doc' do @user.with_lock do user_dup = User.first expect { user_dup.with_lock do fail "shouldn't get the lock" end - }.to raise_error(Mongoid::LockError) + }.to raise_error(Mongoid::Locker::LockError) end end - it "should handle recursive calls" do + it 'should handle recursive calls' do @user.with_lock do @user.with_lock do @user.account_balance = 10 end end - @user.account_balance.should eq(10) + expect(@user.account_balance).to eq(10) end - it "should wait until the lock times out, if desired" do + it 'should wait until the lock times out, if desired' do User.timeout_lock_after 1 @user.with_lock do user_dup = User.first - user_dup.with_lock :wait => true do + user_dup.with_lock wait: true do user_dup.account_balance = 10 user_dup.save! end end - @user.reload.account_balance.should eq(10) + expect(@user.reload.account_balance).to eq(10) end - it "should override the default timeout" do + it 'should, by default, reload the row after acquiring the lock' do + expect(@user).to receive(:reload) + @user.with_lock do + # no-op + end + end + + it 'should allow override of the default reload behavior' do + expect(@user).to_not receive(:reload) + @user.with_lock reload: false do + # no-op + end + end + + it 'should, by default, not retry' do + expect(@user).to receive(:acquire_lock).once.and_return(true) + @user.with_lock do + user_dup = User.first + + user_dup.with_lock do + # no-op + end + end + end + + it 'should retry the number of times given, if desired' do + allow(@user).to receive(:acquire_lock).and_return(false) + allow(Mongoid::Locker::Wrapper).to receive(:locked_until).and_return(Time.now) + + expect(@user).to receive(:acquire_lock).exactly(6).times + expect { + @user.with_lock retries: 5 do + # no-op + end + }.to raise_error(Mongoid::Locker::LockError) + end + + it 'should, by default, when retrying, sleep until the lock expires' do + allow(@user).to receive(:acquire_lock).and_return(false) + allow(Mongoid::Locker::Wrapper).to receive(:locked_until).and_return(Time.now + 5.seconds) + allow(@user).to receive(:sleep) { |time| expect(time).to be_within(0.1).of(5) } + + expect { + @user.with_lock retries: 1 do + # no-op + end + }.to raise_error(Mongoid::Locker::LockError) + end + + it 'should sleep for the time given, if desired' do + allow(@user).to receive(:acquire_lock).and_return(false) + allow(@user).to receive(:sleep) { |time| expect(time).to be_within(0.1).of(3) } + + expect { + @user.with_lock(retries: 1, retry_sleep: 3) do + # no-op + end + }.to raise_error(Mongoid::Locker::LockError) + end + + it 'should override the default timeout' do User.timeout_lock_after 1 expiration = (Time.now + 3).to_i - @user.with_lock :timeout => 3 do - @user.locked_until.to_i.should eq(expiration) + @user.with_lock timeout: 3 do + expect(@user.locked_until.to_i).to eq(expiration) end end - it "should reload the document if it needs to wait for a lock" do + it 'should reload the document if it needs to wait for a lock' do User.timeout_lock_after 1 @user.with_lock do user_dup = User.first @user.account_balance = 10 @user.save! - user_dup.account_balance.should eq(20) - user_dup.with_lock :wait => true do - user_dup.account_balance.should eq(10) + expect(user_dup.account_balance).to eq(20) + user_dup.with_lock wait: true do + expect(user_dup.account_balance).to eq(10) end end end - it "should succeed for subclasses" do + it 'should succeed for subclasses' do class Admin < User end admin = Admin.create! admin.with_lock do - admin.should be_locked - Admin.first.should be_locked + expect(admin).to be_locked + expect(Admin.first).to be_locked end - admin.should_not be_locked - admin.reload.should_not be_locked + expect(admin).to_not be_locked + expect(admin.reload).to_not be_locked remove_class Admin end end - describe ".timeout_lock_after" do - it "should ignore the lock if it has timed out" do + describe '.timeout_lock_after' do + it 'should ignore the lock if it has timed out' do User.timeout_lock_after 1 @user.with_lock do user_dup = User.first sleep 2 @@ -199,43 +258,43 @@ user_dup.account_balance = 10 user_dup.save! end end - @user.reload.account_balance.should eq(10) + expect(@user.reload.account_balance).to eq(10) end - it "should be independent for different classes" do + it 'should be independent for different classes' do class Account include Mongoid::Document include Mongoid::Locker end User.timeout_lock_after 1 Account.timeout_lock_after 2 - User.lock_timeout.should eq(1) + expect(User.lock_timeout).to eq(1) remove_class Account end end - describe ".locked" do - it "should return the locked documents" do - user2 = User.create! + describe '.locked' do + it 'should return the locked documents' do + User.create! @user.with_lock do - User.locked.to_a.should eq([@user]) + expect(User.locked.to_a).to eq([@user]) end end end - describe ".unlocked" do - it "should return the unlocked documents" do + describe '.unlocked' do + it 'should return the unlocked documents' do user2 = User.create! @user.with_lock do - User.unlocked.to_a.should eq([user2]) + expect(User.unlocked.to_a).to eq([user2]) end end end end