with_advisory_lock 4.6.0 → 5.0.0

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +80 -0
  3. data/.gitignore +2 -0
  4. data/.tool-versions +1 -1
  5. data/Appraisals +34 -18
  6. data/CHANGELOG.md +9 -0
  7. data/Gemfile +0 -12
  8. data/README.md +17 -6
  9. data/gemfiles/{activerecord_6.0.gemfile → activerecord_6.1.gemfile} +4 -2
  10. data/gemfiles/{activerecord_5.2.gemfile → activerecord_7.0.gemfile} +4 -2
  11. data/gemfiles/activerecord_7.1.gemfile +14 -0
  12. data/lib/with_advisory_lock/base.rb +16 -2
  13. data/lib/with_advisory_lock/concern.rb +16 -16
  14. data/lib/with_advisory_lock/database_adapter_support.rb +4 -41
  15. data/lib/with_advisory_lock/failed_to_acquire_lock.rb +7 -0
  16. data/lib/with_advisory_lock/flock.rb +4 -3
  17. data/lib/with_advisory_lock/mysql.rb +5 -5
  18. data/lib/with_advisory_lock/postgresql.rb +9 -7
  19. data/lib/with_advisory_lock/version.rb +3 -1
  20. data/lib/with_advisory_lock.rb +1 -2
  21. data/test/concern_test.rb +23 -10
  22. data/test/lock_test.rb +61 -28
  23. data/test/nesting_test.rb +14 -79
  24. data/test/options_test.rb +35 -33
  25. data/test/parallelism_test.rb +35 -37
  26. data/test/shared_test.rb +93 -90
  27. data/test/test_helper.rb +52 -0
  28. data/test/test_models.rb +9 -7
  29. data/test/thread_test.rb +23 -22
  30. data/test/transaction_test.rb +34 -36
  31. data/with_advisory_lock.gemspec +24 -23
  32. metadata +25 -41
  33. data/.travis.yml +0 -38
  34. data/gemfiles/activerecord_4.2.gemfile +0 -19
  35. data/gemfiles/activerecord_5.0.gemfile +0 -19
  36. data/gemfiles/activerecord_5.1.gemfile +0 -19
  37. data/lib/with_advisory_lock/mysql_no_nesting.rb +0 -20
  38. data/lib/with_advisory_lock/nested_advisory_lock_error.rb +0 -14
  39. data/test/database.yml +0 -17
  40. data/test/minitest_helper.rb +0 -40
  41. data/tests.sh +0 -11
data/test/lock_test.rb CHANGED
@@ -1,47 +1,80 @@
1
- require 'minitest_helper'
1
+ # frozen_string_literal: true
2
2
 
3
- describe 'class methods' do
4
- let(:lock_name) { 'test lock' }
3
+ require 'test_helper'
5
4
 
6
- describe '.current_advisory_lock' do
7
- it 'returns nil outside an advisory lock request' do
8
- Tag.current_advisory_lock.must_be_nil
5
+ class LockTest < GemTestCase
6
+ setup do
7
+ @lock_name = 'test lock'
8
+ @return_val = 1900
9
+ end
10
+
11
+ test 'returns nil outside an advisory lock request' do
12
+ assert_nil(Tag.current_advisory_lock)
13
+ end
14
+
15
+ test 'returns the name of the last lock acquired' do
16
+ Tag.with_advisory_lock(@lock_name) do
17
+ assert_match(/#{@lock_name}/, Tag.current_advisory_lock)
9
18
  end
19
+ end
10
20
 
11
- it 'returns the name of the last lock acquired' do
12
- Tag.with_advisory_lock(lock_name) do
13
- # The lock name may have a prefix if WITH_ADVISORY_LOCK_PREFIX env is set
14
- Tag.current_advisory_lock.must_match(/#{lock_name}/)
15
- end
21
+ test 'can obtain a lock with a name that attempts to disrupt a SQL comment' do
22
+ dangerous_lock_name = 'test */ lock /*'
23
+ Tag.with_advisory_lock(dangerous_lock_name) do
24
+ assert_match(/#{Regexp.escape(dangerous_lock_name)}/, Tag.current_advisory_lock)
16
25
  end
26
+ end
17
27
 
18
- it 'can obtain a lock with a name that attempts to disrupt a SQL comment' do
19
- dangerous_lock_name = 'test */ lock /*'
20
- Tag.with_advisory_lock(dangerous_lock_name) do
21
- Tag.current_advisory_lock.must_match(/#{Regexp.escape(dangerous_lock_name)}/)
22
- end
28
+ test 'returns false for an unacquired lock' do
29
+ refute(Tag.advisory_lock_exists?(@lock_name))
30
+ end
23
31
 
32
+ test 'returns true for an acquired lock' do
33
+ Tag.with_advisory_lock(@lock_name) do
34
+ assert(Tag.advisory_lock_exists?(@lock_name))
24
35
  end
25
36
  end
26
37
 
27
- describe '.advisory_lock_exists?' do
28
- it 'returns false for an unacquired lock' do
29
- Tag.advisory_lock_exists?(lock_name).must_be_false
38
+ test 'returns block return value if lock successful' do
39
+ assert_equal(@return_val, Tag.with_advisory_lock!(@lock_name) { @return_val })
40
+ end
41
+
42
+ test 'returns false on lock acquisition failure' do
43
+ thread_with_lock = Thread.new do
44
+ Tag.with_advisory_lock(@lock_name, timeout_seconds: 0) do
45
+ @locked_elsewhere = true
46
+ loop { sleep 0.01 }
47
+ end
30
48
  end
31
49
 
32
- it 'returns the name of the last lock acquired' do
33
- Tag.with_advisory_lock(lock_name) do
34
- Tag.advisory_lock_exists?(lock_name).must_be_true
50
+ sleep 0.01 until @locked_elsewhere
51
+ assert_not(Tag.with_advisory_lock(@lock_name, timeout_seconds: 0) { @return_val })
52
+
53
+ thread_with_lock.kill
54
+ end
55
+
56
+ test 'raises an error on lock acquisition failure' do
57
+ thread_with_lock = Thread.new do
58
+ Tag.with_advisory_lock(@lock_name, timeout_seconds: 0) do
59
+ @locked_elsewhere = true
60
+ loop { sleep 0.01 }
35
61
  end
36
62
  end
63
+
64
+ sleep 0.01 until @locked_elsewhere
65
+ assert_raises(WithAdvisoryLock::FailedToAcquireLock) do
66
+ Tag.with_advisory_lock!(@lock_name, timeout_seconds: 0) { @return_val }
67
+ end
68
+
69
+ thread_with_lock.kill
37
70
  end
38
71
 
39
- describe 'zero timeout_seconds' do
40
- it 'attempts the lock exactly once with no timeout' do
41
- expected = SecureRandom.base64
42
- Tag.with_advisory_lock(lock_name, 0) do
43
- expected
44
- end.must_equal expected
72
+ test 'attempts the lock exactly once with no timeout' do
73
+ expected = SecureRandom.base64
74
+ actual = Tag.with_advisory_lock(@lock_name, 0) do
75
+ expected
45
76
  end
77
+
78
+ assert_equal(expected, actual)
46
79
  end
47
80
  end
data/test/nesting_test.rb CHANGED
@@ -1,93 +1,28 @@
1
- require 'minitest_helper'
1
+ # frozen_string_literal: true
2
2
 
3
- describe "lock nesting" do
4
- # This simplifies what we expect from the lock name:
5
- before :each do
3
+ require 'test_helper'
4
+
5
+ class LockNestingTest < GemTestCase
6
+ setup do
6
7
  @prior_prefix = ENV['WITH_ADVISORY_LOCK_PREFIX']
7
8
  ENV['WITH_ADVISORY_LOCK_PREFIX'] = nil
8
9
  end
9
10
 
10
- after :each do
11
+ teardown do
11
12
  ENV['WITH_ADVISORY_LOCK_PREFIX'] = @prior_prefix
12
13
  end
13
14
 
14
- it "doesn't request the same lock twice" do
15
+ test "doesn't request the same lock twice" do
15
16
  impl = WithAdvisoryLock::Base.new(nil, nil, nil)
16
- impl.lock_stack.must_be_empty
17
- Tag.with_advisory_lock("first") do
18
- impl.lock_stack.map(&:name).must_equal %w(first)
17
+ assert_empty(impl.lock_stack)
18
+ Tag.with_advisory_lock('first') do
19
+ assert_equal(%w[first], impl.lock_stack.map(&:name))
19
20
  # Even MySQL should be OK with this:
20
- Tag.with_advisory_lock("first") do
21
- impl.lock_stack.map(&:name).must_equal %w(first)
22
- end
23
- impl.lock_stack.map(&:name).must_equal %w(first)
24
- end
25
- impl.lock_stack.must_be_empty
26
- end
27
-
28
- it "raises errors with MySQL < 5.7.5 when acquiring nested lock" do
29
- skip unless env_db == :mysql && ENV['MYSQL_VERSION'] != '5.7'
30
- exc = proc {
31
- Tag.with_advisory_lock("first") do
32
- Tag.with_advisory_lock("second") do
33
- end
34
- end
35
- }.must_raise WithAdvisoryLock::NestedAdvisoryLockError
36
- exc.lock_stack.map(&:name).must_equal %w(first)
37
- end
38
-
39
- it "does not raise errors with MySQL < 5.7.5 when acquiring nested error force enabled" do
40
- skip unless env_db == :mysql && ENV['MYSQL_VERSION'] != '5.7'
41
- impl = WithAdvisoryLock::Base.new(nil, nil, nil)
42
- impl.lock_stack.must_be_empty
43
- Tag.with_advisory_lock("first", force_nested_lock_support: true) do
44
- impl.lock_stack.map(&:name).must_equal %w(first)
45
- Tag.with_advisory_lock("second", force_nested_lock_support: true) do
46
- impl.lock_stack.map(&:name).must_equal %w(first second)
47
- Tag.with_advisory_lock("first", force_nested_lock_support: true) do
48
- # Shouldn't ask for another lock:
49
- impl.lock_stack.map(&:name).must_equal %w(first second)
50
- Tag.with_advisory_lock("second", force_nested_lock_support: true) do
51
- # Shouldn't ask for another lock:
52
- impl.lock_stack.map(&:name).must_equal %w(first second)
53
- end
54
- end
21
+ Tag.with_advisory_lock('first') do
22
+ assert_equal(%w[first], impl.lock_stack.map(&:name))
55
23
  end
56
- impl.lock_stack.map(&:name).must_equal %w(first)
24
+ assert_equal(%w[first], impl.lock_stack.map(&:name))
57
25
  end
58
- impl.lock_stack.must_be_empty
59
- end
60
-
61
- it "supports nested advisory locks with !MySQL 5.6" do
62
- skip if env_db == :mysql && ENV['MYSQL_VERSION'] != '5.7'
63
- impl = WithAdvisoryLock::Base.new(nil, nil, nil)
64
- impl.lock_stack.must_be_empty
65
- Tag.with_advisory_lock("first") do
66
- impl.lock_stack.map(&:name).must_equal %w(first)
67
- Tag.with_advisory_lock("second") do
68
- impl.lock_stack.map(&:name).must_equal %w(first second)
69
- Tag.with_advisory_lock("first") do
70
- # Shouldn't ask for another lock:
71
- impl.lock_stack.map(&:name).must_equal %w(first second)
72
- Tag.with_advisory_lock("second") do
73
- # Shouldn't ask for another lock:
74
- impl.lock_stack.map(&:name).must_equal %w(first second)
75
- end
76
- end
77
- end
78
- impl.lock_stack.map(&:name).must_equal %w(first)
79
- end
80
- impl.lock_stack.must_be_empty
81
- end
82
-
83
- it "raises with !MySQL 5.6 and nested error force disabled" do
84
- skip unless env_db == :mysql && ENV['MYSQL_VERSION'] != '5.7'
85
- exc = proc {
86
- Tag.with_advisory_lock("first", force_nested_lock_support: false) do
87
- Tag.with_advisory_lock("second", force_nested_lock_support: false) do
88
- end
89
- end
90
- }.must_raise WithAdvisoryLock::NestedAdvisoryLockError
91
- exc.lock_stack.map(&:name).must_equal %w(first)
26
+ assert_empty(impl.lock_stack)
92
27
  end
93
28
  end
data/test/options_test.rb CHANGED
@@ -1,64 +1,66 @@
1
- require 'minitest_helper'
1
+ # frozen_string_literal: true
2
2
 
3
- describe 'options parsing' do
3
+ require 'test_helper'
4
+
5
+ class OptionsParsingTest < GemTestCase
4
6
  def parse_options(options)
5
7
  WithAdvisoryLock::Base.new(mock, mock, options)
6
8
  end
7
9
 
8
- specify 'defaults (empty hash)' do
10
+ test 'defaults (empty hash)' do
9
11
  impl = parse_options({})
10
- impl.timeout_seconds.must_be_nil
11
- impl.shared.must_equal false
12
- impl.transaction.must_equal false
12
+ assert_nil(impl.timeout_seconds)
13
+ assert_not(impl.shared)
14
+ assert_not(impl.transaction)
13
15
  end
14
16
 
15
- specify 'nil sets timeout to nil' do
17
+ test 'nil sets timeout to nil' do
16
18
  impl = parse_options(nil)
17
- impl.timeout_seconds.must_be_nil
18
- impl.shared.must_equal false
19
- impl.transaction.must_equal false
19
+ assert_nil(impl.timeout_seconds)
20
+ assert_not(impl.shared)
21
+ assert_not(impl.transaction)
20
22
  end
21
23
 
22
- specify 'integer sets timeout to value' do
24
+ test 'integer sets timeout to value' do
23
25
  impl = parse_options(42)
24
- impl.timeout_seconds.must_equal 42
25
- impl.shared.must_equal false
26
- impl.transaction.must_equal false
26
+ assert_equal(42, impl.timeout_seconds)
27
+ assert_not(impl.shared)
28
+ assert_not(impl.transaction)
27
29
  end
28
30
 
29
- specify 'hash with invalid key errors' do
30
- proc {
31
+ test 'hash with invalid key errors' do
32
+ assert_raises(ArgumentError) do
31
33
  parse_options(foo: 42)
32
- }.must_raise ArgumentError
34
+ end
33
35
  end
34
36
 
35
- specify 'hash with timeout_seconds sets timeout to value' do
37
+ test 'hash with timeout_seconds sets timeout to value' do
36
38
  impl = parse_options(timeout_seconds: 123)
37
- impl.timeout_seconds.must_equal 123
38
- impl.shared.must_equal false
39
- impl.transaction.must_equal false
39
+ assert_equal(123, impl.timeout_seconds)
40
+ assert_not(impl.shared)
41
+ assert_not(impl.transaction)
40
42
  end
41
43
 
42
- specify 'hash with shared option sets shared to true' do
44
+ test 'hash with shared option sets shared to true' do
43
45
  impl = parse_options(shared: true)
44
- impl.timeout_seconds.must_be_nil
45
- impl.shared.must_equal true
46
- impl.transaction.must_equal false
46
+ assert_nil(impl.timeout_seconds)
47
+ assert(impl.shared)
48
+ assert_not(impl.transaction)
47
49
  end
48
50
 
49
- specify 'hash with transaction option set transaction to true' do
51
+ test 'hash with transaction option set transaction to true' do
50
52
  impl = parse_options(transaction: true)
51
- impl.timeout_seconds.must_be_nil
52
- impl.shared.must_equal false
53
- impl.transaction.must_equal true
53
+ assert_nil(impl.timeout_seconds)
54
+ assert_not(impl.shared)
55
+ assert(impl.transaction)
54
56
  end
55
57
 
56
- specify 'hash with multiple keys sets options' do
58
+ test 'hash with multiple keys sets options' do
57
59
  foo = mock
58
60
  bar = mock
59
61
  impl = parse_options(timeout_seconds: foo, shared: bar)
60
- impl.timeout_seconds.must_equal foo
61
- impl.shared.must_equal bar
62
- impl.transaction.must_equal false
62
+ assert_equal(foo, impl.timeout_seconds)
63
+ assert_equal(bar, impl.shared)
64
+ assert_not(impl.transaction)
63
65
  end
64
66
  end
@@ -1,35 +1,37 @@
1
- require 'minitest_helper'
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
2
4
  require 'forwardable'
3
5
 
4
- describe 'parallelism' do
5
- class FindOrCreateWorker
6
- extend Forwardable
7
- def_delegators :@thread, :join, :wakeup, :status, :to_s
6
+ class FindOrCreateWorker
7
+ extend Forwardable
8
+ def_delegators :@thread, :join, :wakeup, :status, :to_s
8
9
 
9
- def initialize(name, use_advisory_lock)
10
- @name = name
11
- @use_advisory_lock = use_advisory_lock
12
- @thread = Thread.new { work_later }
13
- end
10
+ def initialize(name, use_advisory_lock)
11
+ @name = name
12
+ @use_advisory_lock = use_advisory_lock
13
+ @thread = Thread.new { work_later }
14
+ end
14
15
 
15
- def work_later
16
- sleep
17
- ActiveRecord::Base.connection_pool.with_connection do
18
- if @use_advisory_lock
19
- Tag.with_advisory_lock(@name) { work }
20
- else
21
- work
22
- end
16
+ def work_later
17
+ sleep
18
+ ActiveRecord::Base.connection_pool.with_connection do
19
+ if @use_advisory_lock
20
+ Tag.with_advisory_lock(@name) { work }
21
+ else
22
+ work
23
23
  end
24
24
  end
25
+ end
25
26
 
26
- def work
27
- Tag.transaction do
28
- Tag.where(name: @name).first_or_create
29
- end
27
+ def work
28
+ Tag.transaction do
29
+ Tag.where(name: @name).first_or_create
30
30
  end
31
31
  end
32
+ end
32
33
 
34
+ class ParallelismTest < GemTestCase
33
35
  def run_workers
34
36
  @names = @iterations.times.map { |iter| "iteration ##{iter}" }
35
37
  @names.each do |name|
@@ -37,9 +39,7 @@ describe 'parallelism' do
37
39
  FindOrCreateWorker.new(name, @use_advisory_lock)
38
40
  end
39
41
  # Wait for all the threads to get ready:
40
- until workers.all? { |ea| ea.status == 'sleep' }
41
- sleep(0.1)
42
- end
42
+ sleep(0.1) until workers.all? { |ea| ea.status == 'sleep' }
43
43
  # OK, GO!
44
44
  workers.each(&:wakeup)
45
45
  # Then wait for them to finish:
@@ -49,29 +49,27 @@ describe 'parallelism' do
49
49
  ActiveRecord::Base.connection_pool.connection
50
50
  end
51
51
 
52
- before :each do
52
+ setup do
53
53
  ActiveRecord::Base.connection.reconnect!
54
54
  @workers = 10
55
55
  end
56
56
 
57
- # < SQLite, understandably, throws "The database file is locked (database is locked)"
58
-
59
- it 'creates multiple duplicate rows without advisory locks' do
60
- skip if env_db == :sqlite
57
+ test 'creates multiple duplicate rows without advisory locks' do
58
+ skip if %i[sqlite3 jdbcsqlite3].include?(env_db)
61
59
  @use_advisory_lock = false
62
60
  @iterations = 1
63
61
  run_workers
64
- Tag.all.size.must_be :>, @iterations # <- any duplicated rows will make me happy.
65
- TagAudit.all.size.must_be :>, @iterations # <- any duplicated rows will make me happy.
66
- Label.all.size.must_be :>, @iterations # <- any duplicated rows will make me happy.
62
+ assert_operator(Tag.all.size, :>, @iterations) # <- any duplicated rows will make me happy.
63
+ assert_operator(TagAudit.all.size, :>, @iterations) # <- any duplicated rows will make me happy.
64
+ assert_operator(Label.all.size, :>, @iterations) # <- any duplicated rows will make me happy.
67
65
  end
68
66
 
69
- it "doesn't create multiple duplicate rows with advisory locks" do
67
+ test "doesn't create multiple duplicate rows with advisory locks" do
70
68
  @use_advisory_lock = true
71
69
  @iterations = 10
72
70
  run_workers
73
- Tag.all.size.must_equal @iterations # <- any duplicated rows will NOT make me happy.
74
- TagAudit.all.size.must_equal @iterations # <- any duplicated rows will NOT make me happy.
75
- Label.all.size.must_equal @iterations # <- any duplicated rows will NOT make me happy.
71
+ assert_equal(@iterations, Tag.all.size) # <- any duplicated rows will NOT make me happy.
72
+ assert_equal(@iterations, TagAudit.all.size) # <- any duplicated rows will NOT make me happy.
73
+ assert_equal(@iterations, Label.all.size) # <- any duplicated rows will NOT make me happy.
76
74
  end
77
75
  end
data/test/shared_test.rb CHANGED
@@ -1,131 +1,134 @@
1
- require 'minitest_helper'
1
+ # frozen_string_literal: true
2
2
 
3
- describe 'shared locks' do
4
- def supported?
5
- env_db != :mysql
6
- end
7
-
8
- class SharedTestWorker
9
- def initialize(shared)
10
- @shared = shared
3
+ require 'test_helper'
4
+ class SharedTestWorker
5
+ def initialize(shared)
6
+ @shared = shared
11
7
 
12
- @locked = nil
13
- @cleanup = false
14
- @thread = Thread.new { work }
15
- end
8
+ @locked = nil
9
+ @cleanup = false
10
+ @thread = Thread.new { work }
11
+ end
16
12
 
17
- def locked?
18
- sleep 0.01 while @locked.nil? && @thread.alive?
19
- @locked
20
- end
13
+ def locked?
14
+ sleep 0.01 while @locked.nil? && @thread.alive?
15
+ @locked
16
+ end
21
17
 
22
- def cleanup!
23
- @cleanup = true
24
- @thread.join
25
- raise if @thread.status.nil?
26
- end
18
+ def cleanup!
19
+ @cleanup = true
20
+ @thread.join
21
+ raise if @thread.status.nil?
22
+ end
27
23
 
28
- private
24
+ private
29
25
 
30
- def work
31
- ActiveRecord::Base.connection_pool.with_connection do
32
- Tag.with_advisory_lock('test', timeout_seconds: 0, shared: @shared) do
33
- @locked = true
34
- sleep 0.01 until @cleanup
35
- end
36
- @locked = false
26
+ def work
27
+ ActiveRecord::Base.connection_pool.with_connection do
28
+ Tag.with_advisory_lock('test', timeout_seconds: 0, shared: @shared) do
29
+ @locked = true
37
30
  sleep 0.01 until @cleanup
38
31
  end
32
+ @locked = false
33
+ sleep 0.01 until @cleanup
39
34
  end
40
35
  end
36
+ end
37
+
38
+ class SharedLocksTest < GemTestCase
39
+ def supported?
40
+ %i[trilogy mysql2 jdbcmysql].exclude?(env_db)
41
+ end
41
42
 
42
- it 'does not allow two exclusive locks' do
43
+ test 'does not allow two exclusive locks' do
43
44
  one = SharedTestWorker.new(false)
44
- one.locked?.must_equal true
45
+ assert_predicate(one, :locked?)
45
46
 
46
47
  two = SharedTestWorker.new(false)
47
- two.locked?.must_equal false
48
+ refute(two.locked?)
48
49
 
49
50
  one.cleanup!
50
51
  two.cleanup!
51
52
  end
53
+ end
52
54
 
53
- describe 'not supported' do
54
- before do
55
- skip if supported?
56
- end
55
+ class NotSupportedEnvironmentTest < SharedLocksTest
56
+ setup do
57
+ skip if supported?
58
+ end
57
59
 
58
- it 'raises an error when attempting to use a shared lock' do
59
- one = SharedTestWorker.new(true)
60
- one.locked?.must_be_nil
61
- exception = proc {
62
- one.cleanup!
63
- }.must_raise ArgumentError
64
- exception.message.must_include 'not supported'
60
+ test 'raises an error when attempting to use a shared lock' do
61
+ one = SharedTestWorker.new(true)
62
+ assert_nil(one.locked?)
63
+
64
+ exception = assert_raises(ArgumentError) do
65
+ one.cleanup!
65
66
  end
67
+
68
+ assert_match(/#{Regexp.escape('not supported')}/, exception.message)
66
69
  end
70
+ end
67
71
 
68
- describe 'supported' do
69
- before do
70
- skip unless supported?
71
- end
72
+ class SupportedEnvironmentTest < SharedLocksTest
73
+ setup do
74
+ skip unless supported?
75
+ end
72
76
 
73
- it 'does allow two shared locks' do
74
- one = SharedTestWorker.new(true)
75
- one.locked?.must_equal true
77
+ test 'does allow two shared locks' do
78
+ one = SharedTestWorker.new(true)
79
+ assert_predicate(one, :locked?)
76
80
 
77
- two = SharedTestWorker.new(true)
78
- two.locked?.must_equal true
81
+ two = SharedTestWorker.new(true)
82
+ assert_predicate(two, :locked?)
79
83
 
80
- one.cleanup!
81
- two.cleanup!
82
- end
84
+ one.cleanup!
85
+ two.cleanup!
86
+ end
83
87
 
84
- it 'does not allow exclusive lock with shared lock' do
85
- one = SharedTestWorker.new(true)
86
- one.locked?.must_equal true
88
+ test 'does not allow exclusive lock with shared lock' do
89
+ one = SharedTestWorker.new(true)
90
+ assert_predicate(one, :locked?)
87
91
 
88
- two = SharedTestWorker.new(false)
89
- two.locked?.must_equal false
92
+ two = SharedTestWorker.new(false)
93
+ refute(two.locked?)
90
94
 
91
- three = SharedTestWorker.new(true)
92
- three.locked?.must_equal true
95
+ three = SharedTestWorker.new(true)
96
+ assert_predicate(three, :locked?)
93
97
 
94
- one.cleanup!
95
- two.cleanup!
96
- three.cleanup!
97
- end
98
+ one.cleanup!
99
+ two.cleanup!
100
+ three.cleanup!
101
+ end
98
102
 
99
- it 'does not allow shared lock with exclusive lock' do
100
- one = SharedTestWorker.new(false)
101
- one.locked?.must_equal true
103
+ test 'does not allow shared lock with exclusive lock' do
104
+ one = SharedTestWorker.new(false)
105
+ assert_predicate(one, :locked?)
102
106
 
103
- two = SharedTestWorker.new(true)
104
- two.locked?.must_equal false
107
+ two = SharedTestWorker.new(true)
108
+ refute(two.locked?)
105
109
 
106
- one.cleanup!
107
- two.cleanup!
108
- end
110
+ one.cleanup!
111
+ two.cleanup!
112
+ end
109
113
 
110
- describe 'PostgreSQL' do
111
- before do
112
- skip unless env_db == :postgresql
113
- end
114
+ class PostgreSQLTest < SupportedEnvironmentTest
115
+ setup do
116
+ skip unless env_db == :postgresql
117
+ end
114
118
 
115
- def pg_lock_modes
116
- ActiveRecord::Base.connection.select_values("SELECT mode FROM pg_locks WHERE locktype = 'advisory';")
117
- end
119
+ def pg_lock_modes
120
+ ActiveRecord::Base.connection.select_values("SELECT mode FROM pg_locks WHERE locktype = 'advisory';")
121
+ end
118
122
 
119
- it 'allows shared lock to be upgraded to an exclusive lock' do
120
- pg_lock_modes.must_equal %w[]
121
- Tag.with_advisory_lock 'test', shared: true do
122
- pg_lock_modes.must_equal %w[ShareLock]
123
- Tag.with_advisory_lock 'test', shared: false do
124
- pg_lock_modes.must_equal %w[ShareLock ExclusiveLock]
125
- end
123
+ test 'allows shared lock to be upgraded to an exclusive lock' do
124
+ assert_empty(pg_lock_modes)
125
+ Tag.with_advisory_lock 'test', shared: true do
126
+ assert_equal(%w[ShareLock], pg_lock_modes)
127
+ Tag.with_advisory_lock 'test', shared: false do
128
+ assert_equal(%w[ShareLock ExclusiveLock], pg_lock_modes)
126
129
  end
127
- pg_lock_modes.must_equal %w[]
128
130
  end
131
+ assert_empty(pg_lock_modes)
129
132
  end
130
133
  end
131
134
  end