aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/cases/transaction_isolation_test.rb
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test/cases/transaction_isolation_test.rb')
-rw-r--r--activerecord/test/cases/transaction_isolation_test.rb106
1 files changed, 106 insertions, 0 deletions
diff --git a/activerecord/test/cases/transaction_isolation_test.rb b/activerecord/test/cases/transaction_isolation_test.rb
new file mode 100644
index 0000000000..eaafd13360
--- /dev/null
+++ b/activerecord/test/cases/transaction_isolation_test.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+
+unless ActiveRecord::Base.connection.supports_transaction_isolation?
+ class TransactionIsolationUnsupportedTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
+ class Tag < ActiveRecord::Base
+ end
+
+ test "setting the isolation level raises an error" do
+ assert_raises(ActiveRecord::TransactionIsolationError) do
+ Tag.transaction(isolation: :serializable) {}
+ end
+ end
+ end
+else
+ class TransactionIsolationTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
+ class Tag < ActiveRecord::Base
+ self.table_name = "tags"
+ end
+
+ class Tag2 < ActiveRecord::Base
+ self.table_name = "tags"
+ end
+
+ setup do
+ Tag.establish_connection :arunit
+ Tag2.establish_connection :arunit
+ Tag.destroy_all
+ end
+
+ # It is impossible to properly test read uncommitted. The SQL standard only
+ # specifies what must not happen at a certain level, not what must happen. At
+ # the read uncommitted level, there is nothing that must not happen.
+ if ActiveRecord::Base.connection.transaction_isolation_levels.include?(:read_uncommitted)
+ test "read uncommitted" do
+ Tag.transaction(isolation: :read_uncommitted) do
+ assert_equal 0, Tag.count
+ Tag2.create
+ assert_equal 1, Tag.count
+ end
+ end
+ end
+
+ # We are testing that a dirty read does not happen
+ test "read committed" do
+ Tag.transaction(isolation: :read_committed) do
+ assert_equal 0, Tag.count
+
+ Tag2.transaction do
+ Tag2.create
+ assert_equal 0, Tag.count
+ end
+ end
+
+ assert_equal 1, Tag.count
+ end
+
+ # We are testing that a nonrepeatable read does not happen
+ if ActiveRecord::Base.connection.transaction_isolation_levels.include?(:repeatable_read)
+ test "repeatable read" do
+ tag = Tag.create(name: "jon")
+
+ Tag.transaction(isolation: :repeatable_read) do
+ tag.reload
+ Tag2.find(tag.id).update(name: "emily")
+
+ tag.reload
+ assert_equal "jon", tag.name
+ end
+
+ tag.reload
+ assert_equal "emily", tag.name
+ end
+ end
+
+ # We are only testing that there are no errors because it's too hard to
+ # test serializable. Databases behave differently to enforce the serializability
+ # constraint.
+ test "serializable" do
+ Tag.transaction(isolation: :serializable) do
+ Tag.create
+ end
+ end
+
+ test "setting isolation when joining a transaction raises an error" do
+ Tag.transaction do
+ assert_raises(ActiveRecord::TransactionIsolationError) do
+ Tag.transaction(isolation: :serializable) {}
+ end
+ end
+ end
+
+ test "setting isolation when starting a nested transaction raises error" do
+ Tag.transaction do
+ assert_raises(ActiveRecord::TransactionIsolationError) do
+ Tag.transaction(requires_new: true, isolation: :serializable) {}
+ end
+ end
+ end
+ end
+end