module Switchman::RSpecHelper

including this module in your specs will give you several shards to work with during specs:

* Shard.default - the test database itself
* @shard1 - a shard using the same connection as Shard.default
* @shard2 - a shard using a dedicated connection

Public Class Methods

included(klass) click to toggle source
# File lib/switchman/r_spec_helper.rb, line 20
def self.included(klass)
  # our before handlers have already been configured from a parent group; don't add them again
  parent_group = klass.parent_groups[1]
  return if parent_group && included_in?(parent_group)

  # set up sharding schemas/dbs before the root group runs, so that
  # they persist across transactional groups (e.g. once-ler)
  root_group = klass.parent_groups.last
  root_group.prepend_before(:all) do |group|
    next if @@shard1
    next if @@sharding_failed

    # if we aren't actually going to run a sharding group/example,
    # don't set it up after all
    groups = group.class.descendant_filtered_examples.map(&:example_group).uniq
    next unless groups.any? { |descendant_group| RSpecHelper.included_in?(descendant_group) }

    puts 'Setting up sharding for all specs...'
    Shard.delete_all
    Switchman.cache.delete('default_shard')

    @@shard1, @@shard2 = TestHelper.recreate_persistent_test_shards
    @@default_shard = Shard.default
    if @@shard1.is_a?(Shard)
      @@keep_the_shards = true
      @@shard3 = nil
    else # @@shard1.is_a?(DatabaseServer)
      begin
        @@shard1 = @@shard1.create_new_shard
        @@shard2 = @@shard2.create_new_shard
      rescue => e
        warn 'Sharding setup FAILED!:'
        while e
          warn "\n#{e}\n"
          warn e.backtrace
          e = e.respond_to?(:cause) ? e.cause : nil
        end
        @@sharding_failed = true
        @@shard1&.drop_database rescue nil
        @@shard2&.drop_database rescue nil
        @@shard1 = @@shard2 = nil
        Shard.delete_all
        Shard.default(reload: true)
        next
      end
    end
    # we'll re-persist in the group's `before :all`; we don't want them to exist
    # in the db before then
    Shard.delete_all
    Switchman.cache.delete('default_shard')
    Shard.default(reload: true)
    puts 'Done!'

    at_exit do
      # preserve rspec's exit status
      status = $!.is_a?(::SystemExit) ? $!.status : nil
      puts 'Tearing down sharding for all specs'
      @@shard1.database_server.destroy unless @@shard1.database_server == Shard.default.database_server
      unless @@keep_the_shards
        @@shard1.drop_database
        @@shard1.destroy
        @@shard2.drop_database
        @@shard2.destroy
      end
      @@shard2.database_server.destroy
      exit status if status
    end
  end

  klass.before(:all) do
    next if @@sharding_failed

    dup = @@default_shard.dup
    dup.id = @@default_shard.id
    dup.save!
    Switchman.cache.delete('default_shard')
    Shard.default(reload: true)
    dup = @@shard1.dup
    dup.id = @@shard1.id
    dup.save!
    dup = @@shard2.dup
    dup.id = @@shard2.id
    dup.save!
    @shard1, @shard2 = @@shard1, @@shard2
  end

  klass.before do
    raise 'Sharding did not set up correctly' if @@sharding_failed

    Shard.clear_cache
    if use_transactional_tests
      Shard.default(reload: true)
      @shard1 = Shard.find(@shard1.id)
      @shard2 = Shard.find(@shard2.id)
      shards = [@shard2]
      shards << @shard1 unless @shard1.database_server == Shard.default.database_server
      shards.each do |shard|
        shard.activate do
          ::ActiveRecord::Base.connection.begin_transaction joinable: false
        end
      end
    end
  end

  klass.after do
    next if @@sharding_failed

    if use_transactional_tests
      shards = [@shard2]
      shards << @shard1 unless @shard1.database_server == Shard.default.database_server
      shards.each do |shard|
        shard.activate do
          ::ActiveRecord::Base.connection.rollback_transaction if ::ActiveRecord::Base.connection.transaction_open?
        end
      end
    end
    # clean up after specs
    DatabaseServer.all.each do |ds|
      if ds.fake? && ds != @shard2.database_server
        ds.shards.delete_all unless use_transactional_tests
        ds.destroy
      end
    end
  end

  klass.after(:all) do
    Shard.connection.update("TRUNCATE #{Shard.quoted_table_name} CASCADE")
    Switchman.cache.delete('default_shard')
    Shard.default(reload: true)
  end
end
included_in?(klass) click to toggle source
# File lib/switchman/r_spec_helper.rb, line 16
def self.included_in?(klass)
  klass.parent_groups.any? { |group| group.included_modules.include?(self) }
end