aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/cases/transactions_test.rb
diff options
context:
space:
mode:
authorJeremy Kemper <jeremy@bitsweat.net>2008-01-18 07:30:42 +0000
committerJeremy Kemper <jeremy@bitsweat.net>2008-01-18 07:30:42 +0000
commit42b39ae3f2991692672364d7e09b1e4002e66261 (patch)
treecddaf1eb2dbf7be27430bde882432db3b1cc0407 /activerecord/test/cases/transactions_test.rb
parent105a27f39ee9dbfd7fdb2b25e5ba38b00708b66c (diff)
downloadrails-42b39ae3f2991692672364d7e09b1e4002e66261.tar.gz
rails-42b39ae3f2991692672364d7e09b1e4002e66261.tar.bz2
rails-42b39ae3f2991692672364d7e09b1e4002e66261.zip
Move tests to cases
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@8660 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'activerecord/test/cases/transactions_test.rb')
-rw-r--r--activerecord/test/cases/transactions_test.rb281
1 files changed, 281 insertions, 0 deletions
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
new file mode 100644
index 0000000000..f36ee3f4bb
--- /dev/null
+++ b/activerecord/test/cases/transactions_test.rb
@@ -0,0 +1,281 @@
+require 'abstract_unit'
+require 'fixtures/topic'
+require 'fixtures/reply'
+require 'fixtures/developer'
+
+class TransactionTest < ActiveSupport::TestCase
+ self.use_transactional_fixtures = false
+ fixtures :topics, :developers
+
+ def setup
+ @first, @second = Topic.find(1, 2).sort_by { |t| t.id }
+ end
+
+ def test_successful
+ Topic.transaction do
+ @first.approved = true
+ @second.approved = false
+ @first.save
+ @second.save
+ end
+
+ assert Topic.find(1).approved?, "First should have been approved"
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
+ end
+
+ def transaction_with_return
+ Topic.transaction do
+ @first.approved = true
+ @second.approved = false
+ @first.save
+ @second.save
+ return
+ end
+ end
+
+ def test_successful_with_return
+ class << Topic.connection
+ alias :real_commit_db_transaction :commit_db_transaction
+ def commit_db_transaction
+ $committed = true
+ real_commit_db_transaction
+ end
+ end
+
+ $committed = false
+ transaction_with_return
+ assert $committed
+
+ assert Topic.find(1).approved?, "First should have been approved"
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
+ ensure
+ class << Topic.connection
+ alias :commit_db_transaction :real_commit_db_transaction rescue nil
+ end
+ end
+
+ def test_successful_with_instance_method
+ @first.transaction do
+ @first.approved = true
+ @second.approved = false
+ @first.save
+ @second.save
+ end
+
+ assert Topic.find(1).approved?, "First should have been approved"
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
+ end
+
+ def test_failing_on_exception
+ begin
+ Topic.transaction do
+ @first.approved = true
+ @second.approved = false
+ @first.save
+ @second.save
+ raise "Bad things!"
+ end
+ rescue
+ # caught it
+ end
+
+ assert @first.approved?, "First should still be changed in the objects"
+ assert !@second.approved?, "Second should still be changed in the objects"
+
+ assert !Topic.find(1).approved?, "First shouldn't have been approved"
+ assert Topic.find(2).approved?, "Second should still be approved"
+ end
+
+
+ def test_callback_rollback_in_save
+ add_exception_raising_after_save_callback_to_topic
+
+ begin
+ @first.approved = true
+ @first.save
+ flunk
+ rescue => e
+ assert_equal "Make the transaction rollback", e.message
+ assert !Topic.find(1).approved?
+ ensure
+ remove_exception_raising_after_save_callback_to_topic
+ end
+ end
+
+ def test_callback_rollback_in_create
+ new_topic = Topic.new(
+ :title => "A new topic",
+ :author_name => "Ben",
+ :author_email_address => "ben@example.com",
+ :written_on => "2003-07-16t15:28:11.2233+01:00",
+ :last_read => "2004-04-15",
+ :bonus_time => "2005-01-30t15:28:00.00+01:00",
+ :content => "Have a nice day",
+ :approved => false)
+ new_record_snapshot = new_topic.new_record?
+ id_present = new_topic.has_attribute?(Topic.primary_key)
+ id_snapshot = new_topic.id
+
+ # Make sure the second save gets the after_create callback called.
+ 2.times do
+ begin
+ add_exception_raising_after_create_callback_to_topic
+ new_topic.approved = true
+ new_topic.save
+ flunk
+ rescue => e
+ assert_equal "Make the transaction rollback", e.message
+ assert_equal new_record_snapshot, new_topic.new_record?, "The topic should have its old new_record value"
+ assert_equal id_snapshot, new_topic.id, "The topic should have its old id"
+ assert_equal id_present, new_topic.has_attribute?(Topic.primary_key)
+ ensure
+ remove_exception_raising_after_create_callback_to_topic
+ end
+ end
+ end
+
+ def test_nested_explicit_transactions
+ Topic.transaction do
+ Topic.transaction do
+ @first.approved = true
+ @second.approved = false
+ @first.save
+ @second.save
+ end
+ end
+
+ assert Topic.find(1).approved?, "First should have been approved"
+ assert !Topic.find(2).approved?, "Second should have been unapproved"
+ end
+
+ def test_manually_rolling_back_a_transaction
+ Topic.transaction do
+ @first.approved = true
+ @second.approved = false
+ @first.save
+ @second.save
+
+ raise ActiveRecord::Rollback
+ end
+
+ assert @first.approved?, "First should still be changed in the objects"
+ assert !@second.approved?, "Second should still be changed in the objects"
+
+ assert !Topic.find(1).approved?, "First shouldn't have been approved"
+ assert Topic.find(2).approved?, "Second should still be approved"
+ end
+
+ uses_mocha 'mocking connection.commit_db_transaction' do
+ def test_rollback_when_commit_raises
+ Topic.connection.expects(:begin_db_transaction)
+ Topic.connection.expects(:commit_db_transaction).raises('OH NOES')
+ Topic.connection.expects(:rollback_db_transaction)
+
+ assert_raise RuntimeError do
+ Topic.transaction do
+ # do nothing
+ end
+ end
+ end
+ end
+
+ private
+ def add_exception_raising_after_save_callback_to_topic
+ Topic.class_eval { def after_save() raise "Make the transaction rollback" end }
+ end
+
+ def remove_exception_raising_after_save_callback_to_topic
+ Topic.class_eval { remove_method :after_save }
+ end
+
+ def add_exception_raising_after_create_callback_to_topic
+ Topic.class_eval { def after_create() raise "Make the transaction rollback" end }
+ end
+
+ def remove_exception_raising_after_create_callback_to_topic
+ Topic.class_eval { remove_method :after_create }
+ end
+end
+
+if current_adapter?(:PostgreSQLAdapter)
+ class ConcurrentTransactionTest < TransactionTest
+ def setup
+ @allow_concurrency = ActiveRecord::Base.allow_concurrency
+ ActiveRecord::Base.allow_concurrency = true
+ super
+ end
+
+ def teardown
+ super
+ ActiveRecord::Base.allow_concurrency = @allow_concurrency
+ end
+
+ # This will cause transactions to overlap and fail unless they are performed on
+ # separate database connections.
+ def test_transaction_per_thread
+ assert_nothing_raised do
+ threads = (1..3).map do
+ Thread.new do
+ Topic.transaction do
+ topic = Topic.find(1)
+ topic.approved = !topic.approved?
+ topic.save!
+ topic.approved = !topic.approved?
+ topic.save!
+ end
+ end
+ end
+
+ threads.each { |t| t.join }
+ end
+ end
+
+ # Test for dirty reads among simultaneous transactions.
+ def test_transaction_isolation__read_committed
+ # Should be invariant.
+ original_salary = Developer.find(1).salary
+ temporary_salary = 200000
+
+ assert_nothing_raised do
+ threads = (1..3).map do
+ Thread.new do
+ Developer.transaction do
+ # Expect original salary.
+ dev = Developer.find(1)
+ assert_equal original_salary, dev.salary
+
+ dev.salary = temporary_salary
+ dev.save!
+
+ # Expect temporary salary.
+ dev = Developer.find(1)
+ assert_equal temporary_salary, dev.salary
+
+ dev.salary = original_salary
+ dev.save!
+
+ # Expect original salary.
+ dev = Developer.find(1)
+ assert_equal original_salary, dev.salary
+ end
+ end
+ end
+
+ # Keep our eyes peeled.
+ threads << Thread.new do
+ 10.times do
+ sleep 0.05
+ Developer.transaction do
+ # Always expect original salary.
+ assert_equal original_salary, Developer.find(1).salary
+ end
+ end
+ end
+
+ threads.each { |t| t.join }
+ end
+
+ assert_equal original_salary, Developer.find(1).salary
+ end
+ end
+end