transaction snippets

How to Test Authentication With Devise+Capybara+Minitest

Tagged devise, capybara, authentication, warden, transaction, gotcha, minitest  Languages ruby

Testing authentication functionality with Capybara and Devise? See the following checklist:

* Use shared connections or disable transactional fixtures. * Set Capybara.default_host to match config.session_store.domain or you'll get "401 Unauthorized" * Name of test should end with "integration", e.g. describe "Dashboard Business integration" do * Add the following to your integration tests:

include Warden::Test::Helpers
  Warden.test_mode!

  after do
    Warden.test_reset!
  end

Full example of integration test with Devise, Capybara, and minitest:

class IntegrationSpec < MiniTest::Spec
  include Rails.application.routes.url_helpers
  include Capybara::DSL
  include Warden::Test::Helpers
  Warden.test_mode!

  before do
    @routes = Rails.application.routes
  end

  after do
    Warden.test_reset!
  end

  def sign_in(user)
    login_as(user, scope: :user)
  end

  def sign_out
    logout(:user)
  end

  def default_url_options
    Rails.configuration.action_mailer.default_url_options
  end
end

MiniTest::Spec.register_spec_type( /integration$/, IntegrationSpec )

Repeatable-read vs read-committed example in Rails / ActiveRecord

Tagged isolation, postgres, read-commited, repeatable-read, transaction, activerecord, rails  Languages ruby
ActiveRecord::Base.logger = Logger.new(STDOUT)

require "test/unit/assertions"
include Test::Unit::Assertions

class Cust1 < ActiveRecord::Base
  self.table_name = "customers"
end

class Cust2 < ActiveRecord::Base
  self.table_name = "customers"
end

# NOTE: Two separate connections
Cust1.establish_connection :development
Cust2.establish_connection :development

Cust1.find_by(username: 'z@localhost').try(:delete)

c = Cust1.create!(first_name: 'Putin', last_name: 'jebus', password_digest: 'abcdef12345', username: 'z@localhost')

case ENV['isolation']
when 'repeatable_read'
  #
  # NOTE: Repeatable-read isolation
  #
  # This level is different from Read Committed in that a query in a repeatable
  # read transaction sees a snapshot as of the start of the transaction, not as
  # of the start of the current query within the transaction. Thus, successive
  # SELECT commands within a single transaction see the same data, i.e., they do
  # not see changes made by other transactions that committed after their own
  # transaction started.
  #
  puts "Repeatable read"
  assert ActiveRecord::Base.connection.transaction_isolation_levels.include?(:repeatable_read)
  Cust1.transaction(isolation: :repeatable_read) do
    c.reload
    Cust2.find(c.id).update_attributes(first_name: 'Stalin')
    c.reload
    assert_equal 'Putin', c.first_name
  end
  c.reload
  assert_equal 'Stalin', c.first_name
else
  #
  # NOTE: Read-committed isolation
  #
   # Read Committed is the default isolation level in PostgreSQL. When a
  # transaction uses this isolation level, a SELECT query (without a FOR
  # UPDATE/SHARE clause) sees only data committed before the query began; it
  # never sees either uncommitted data or changes committed during query
  # execution by concurrent transactions. In effect, a SELECT query sees a
  # snapshot of the database as of the instant the query begins to run.
  # However, SELECT does see the effects of previous updates executed within
  # its own transaction, even though they are not yet committed. Also note that
  # two successive SELECT commands can see different data, even though they are
  # within a single transaction, if other transactions commit changes after the
  # first SELECT starts and before the second SELECT starts.
  #
  puts "Read committed"
  Cust1.transaction do
    c.reload
    Cust2.find(c.id).update_attributes(first_name: 'Stalin')
    c.reload
    assert_equal 'Stalin', c.first_name
  end
  c.reload
  assert_equal 'Stalin', c.first_name
end

See