with_advisory_lock 4.6.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
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