<commit_before>#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#

require File.expand_path(File.dirname(__FILE__) + '/../config/canvas_rails3')
if CANVAS_RAILS2
  Spec::Example::ExampleGroupMethods.module_eval do
    def include_examples(*args)
      it_should_behave_like(*args)
    end
  end
end

unless CANVAS_RAILS2
  begin
    require RUBY_VERSION >= '2.0.0' ? 'byebug' : 'debugger'
  rescue LoadError
  end

  RSpec.configure do |c|
    c.treat_symbols_as_metadata_keys_with_true_values = true
    c.color = true

    c.around(:each) do |example|
      attempts = 0
      begin
        Timeout::timeout(180) {
          example.run
        }
        if ENV['AUTORERUN']
          e = @example.instance_variable_get('@exception')
          if !e.nil? && (attempts += 1) < 2 && !example.metadata[:no_retry]
            puts "FAILURE: #{@example.description} \n #{e}".red
            puts "RETRYING: #{@example.description}".yellow
            @example.instance_variable_set('@exception', nil)
            redo
          elsif e.nil? && attempts != 0
            puts "SUCCESS: retry passed for \n #{@example.description}".green
          end
        end
      end until true
    end
  end
end

begin
  ; require File.expand_path(File.dirname(__FILE__) + "/../parallelized_specs/lib/parallelized_specs.rb");
rescue LoadError;
end

ENV["RAILS_ENV"] = 'test'

require File.expand_path('../../config/environment', __FILE__) unless defined?(Rails)
if CANVAS_RAILS2
  require 'spec'
  # require 'spec/autorun'
  require 'spec/rails'

  # integration specs were renamed to request specs in rspec 2
  def describe_with_rspec2_types(*args, &block)
    unless args.last.is_a?(Hash)
      args << {}
    end
    raise "please use type: :request instead of type: :integration for rspec 2 compatibility" if args.last[:type] == :integration
    if args.last[:type] == :request
      args.last[:type] = :integration
    end
    args.last[:location] ||= caller(0)[1]
    describe_without_rspec2_types(*args, &block)
  end

  alias :describe_without_rspec2_types :describe
  alias :describe :describe_with_rspec2_types

  ActionController::TestProcess.module_eval do
    def process_with_use_route_shim(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
      if parameters.is_a?(Hash)
        parameters.delete(:use_route)
      end
      process_without_use_route_shim(action, parameters, session, flash, http_method)
    end

    alias_method_chain :process, :use_route_shim
  end

  Spec::Rails::Example::ViewExampleGroup.class_eval do
    alias :view :template

    def content_for(name)
      response.capture(name)
    end
  end
else
  require 'rspec/rails'

  ActionView::TestCase::TestController.view_paths = ApplicationController.view_paths

  module RSpec::Rails
    module ViewExampleGroup
      module ExampleMethods
        # normally in rspec 2, assigns returns a newly constructed hash
        # which means that 'assigns[:key] = value' in view specs does nothing
        def assigns
          @assigns ||= super
        end

        alias :view_assigns :assigns

        delegate :content_for, :to => :view

        def render_with_helpers(*args)
          controller_class = ("#{@controller.controller_path.camelize}Controller".constantize rescue nil) || ApplicationController

          controller_class.instance_variable_set(:@js_env, nil)
          # this extends the controller's helper methods to the view
          # however, these methods are delegated to the test controller
          view.singleton_class.class_eval do
            include controller_class._helpers unless included_modules.include?(controller_class._helpers)
          end

          # so create a "real_controller"
          # and delegate the helper methods to it
          @controller.singleton_class.class_eval do
            attr_accessor :real_controller

            controller_class._helper_methods.each do |helper|
              delegate helper, :to => :real_controller
            end
          end

          real_controller = controller_class.new
          real_controller.instance_variable_set(:@_request, @controller.request)
          @controller.real_controller = real_controller

          # just calling "render 'path/to/view'" by default looks for a partial
          if args.first && args.first.is_a?(String)
            file = args.shift
            args = [{:template => file}] + args
          end
          render_without_helpers(*args)
        end

        alias_method_chain :render, :helpers
      end
    end

    module Matchers
      class HaveTag
        include ActionDispatch::Assertions::SelectorAssertions
        include Test::Unit::Assertions

        def initialize(expected)
          @expected = expected
        end

        def matches?(html, &block)
          @selected = [HTML::Document.new(html).root]
          assert_select(*@expected, &block)
          return !@failed
        end

        def assert(val, msg=nil)
          unless !!val
            @msg = msg
            @failed = true
          end
        end

        def failure_message_for_should
          @msg
        end

        def failure_message_for_should_not
          @msg
        end
      end

      def have_tag(*args)
        HaveTag.new(args)
      end
    end
  end
end
require 'action_controller_test_process'
require File.expand_path(File.dirname(__FILE__) + '/mocha_rspec_adapter')
require File.expand_path(File.dirname(__FILE__) + '/mocha_extensions')
require File.expand_path(File.dirname(__FILE__) + '/ams_spec_helper')

require 'i18n_tasks'
require 'handlebars_tasks'

# if mocha was initialized before rails (say by another spec), CollectionProxy would have
# undef_method'd them; we need to restore them
unless CANVAS_RAILS2
  Mocha::ObjectMethods.instance_methods.each do |m|
    ActiveRecord::Associations::CollectionProxy.class_eval <<-RUBY
      def #{m}; end
      remove_method #{m.inspect}
    RUBY
  end
end

Dir.glob("#{File.dirname(__FILE__).gsub(/\\/, "/")}/factories/*.rb").each { |file| require file }

def pend_with_bullet
  if defined?(Bullet) && Bullet.enable?
    pending ('PENDING: Bullet')
  end
end

def require_webmock
  # pull in webmock for selected tests, but leave it disabled by default.
  # funky require order is to skip typhoeus because of an incompatibility
  # see: https://github.com/typhoeus/typhoeus/issues/196
  require 'webmock/util/version_checker'
  require 'webmock/http_lib_adapters/http_lib_adapter_registry'
  require 'webmock/http_lib_adapters/http_lib_adapter'
  require 'webmock/http_lib_adapters/typhoeus_hydra_adapter'
  WebMock::HttpLibAdapterRegistry.instance.http_lib_adapters.delete :typhoeus
  require 'webmock/rspec'
end

# rspec aliases :describe to :context in a way that it's pretty much defined
# globally on every object. :context is already heavily used in our application,
# so we remove rspec's definition. This does not prevent 'context' from being
# used within a 'describe' block.

if defined?(Spec::DSL::Main)
  module Spec::DSL::Main
    remove_method :context if respond_to? :context
  end
end

def truncate_table(model)
  case model.connection.adapter_name
    when "SQLite"
      model.delete_all
      begin
        model.connection.execute("delete from sqlite_sequence where name='#{model.connection.quote_table_name(model.table_name)}';")
        model.connection.execute("insert into sqlite_sequence (name, seq) values ('#{model.connection.quote_table_name(model.table_name)}', #{rand(100)});")
      rescue
      end
    when "PostgreSQL"
      begin
        old_proc = model.connection.raw_connection.set_notice_processor {}
        model.connection.execute("TRUNCATE TABLE #{model.connection.quote_table_name(model.table_name)} CASCADE")
      ensure
        model.connection.raw_connection.set_notice_processor(&old_proc)
      end
    else
      model.connection.execute("SET FOREIGN_KEY_CHECKS=0")
      model.connection.execute("TRUNCATE TABLE #{model.connection.quote_table_name(model.table_name)}")
      model.connection.execute("SET FOREIGN_KEY_CHECKS=1")
  end
end

def truncate_all_tables
  models_by_connection = ActiveRecord::Base.all_models.group_by { |m| m.connection }
  models_by_connection.each do |connection, models|
    if connection.adapter_name == "PostgreSQL"
      table_names = connection.tables & models.map(&:table_name)
      connection.execute("TRUNCATE TABLE #{table_names.map { |t| connection.quote_table_name(t) }.join(',')}")
    else
      table_names = connection.tables
      models.each { |model| truncate_table(model) if table_names.include?(model.table_name) }
    end
  end
end

# wipe out the test db, in case some non-transactional tests crapped out before
# cleaning up after themselves
truncate_all_tables

# Make AR not puke if MySQL auto-commits the transaction
module MysqlOutsideTransaction
  def outside_transaction?
    # MySQL ignores creation of savepoints outside of a transaction; so if we can create one
    # and then can't release it because it doesn't exist, we're not in a transaction
    execute('SAVEPOINT outside_transaction')
    !!execute('RELEASE SAVEPOINT outside_transaction') rescue true
  end
end

module ActiveRecord::ConnectionAdapters
  if defined?(MysqlAdapter)
    MysqlAdapter.send(:include, MysqlOutsideTransaction)
  end
  if defined?(Mysql2Adapter)
    Mysql2Adapter.send(:include, MysqlOutsideTransaction)
  end
end

# Be sure to actually test serializing things to non-existent caches,
# but give Mocks a pass, since they won't exist in dev/prod
Mocha::Mock.class_eval do
  def marshal_dump
    nil
  end

  def marshal_load(data)
    raise "Mocks aren't really serializeable!"
  end

  def to_yaml(opts = {})
    YAML.quick_emit(self.object_id, opts) do |out|
      out.scalar(nil, 'null')
    end
  end

  def respond_to_with_marshalling?(symbol, include_private = false)
    return true if [:marshal_dump, :marshal_load].include?(symbol)
    respond_to_without_marshalling?(symbol, include_private)
  end

  alias_method_chain :respond_to?, :marshalling
end

[ActiveSupport::Cache::MemoryStore, (CANVAS_RAILS2 ? NilStore : ActiveSupport::Cache::NullStore)].each do |store|
  store.class_eval do
    def write_with_serialization_check(name, value, options = nil)
      Marshal.dump(value)
      write_without_serialization_check(name, value, options)
    end

    alias_method_chain :write, :serialization_check
  end
end

unless CANVAS_RAILS2
  ActiveSupport::Cache::NullStore.class_eval do
    def fetch_with_serialization_check(name, options = {}, &block)
      result = fetch_without_serialization_check(name, options, &block)
      Marshal.dump(result) if result
      result
    end

    alias_method_chain :fetch, :serialization_check
  end
end

matchers_module = (CANVAS_RAILS2 ? Spec::Matchers : RSpec::Matchers)
matchers_module.define :encompass do |expected|
  match do |actual|
    if expected.is_a?(Array) && actual.is_a?(Array)
      expected.size == actual.size && expected.zip(actual).all? { |e, a| a.slice(*e.keys) == e }
    elsif expected.is_a?(Hash) && actual.is_a?(Hash)
      actual.slice(*expected.keys) == expected
    else
      false
    end
  end
end

matchers_module.define :match_ignoring_whitespace do |expected|
  def whitespaceless(str)
    str.gsub(/\s+/, '')
  end

  match do |actual|
    whitespaceless(actual) == whitespaceless(expected)
  end
end

module Helpers
  def message(opts={})
    m = Message.new
    m.to = opts[:to] || 'some_user'
    m.from = opts[:from] || 'some_other_user'
    m.subject = opts[:subject] || 'a message for you'
    m.body = opts[:body] || 'nice body'
    m.sent_at = opts[:sent_at] || 5.days.ago
    m.workflow_state = opts[:workflow_state] || 'sent'
    m.user_id = opts[:user_id] || opts[:user].try(:id)
    m.path_type = opts[:path_type] || 'email'
    m.root_account_id = opts[:account_id] || Account.default.id
    m.save!
    m
  end
end

# Make sure extensions will work with dynamically created shards
if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' &&
    ActiveRecord::Base.connection.schema_search_path == 'public'
  %w{pg_trgm pg_collkey}.each do |extension|
    current_schema = ActiveRecord::Base.connection.select_value("SELECT nspname FROM pg_extension INNER JOIN pg_namespace ON extnamespace=pg_namespace.oid WHERE extname='#{extension}'")
    if current_schema && current_schema == 'public'
      ActiveRecord::Base.connection.execute("ALTER EXTENSION #{extension} SET SCHEMA pg_catalog") rescue nil
    end
  end
end

(CANVAS_RAILS2 ? Spec::Runner : RSpec).configure do |config|
  # If you're not using ActiveRecord you should remove these
  # lines, delete config/database.yml and disable :active_record
  # in your config/boot.rb
  config.use_transactional_fixtures = true
  config.use_instantiated_fixtures = false
  config.fixture_path = Rails.root+'spec/fixtures/'

  config.include Helpers

  if CANVAS_RAILS2
    require 'spec/onceler_noop'
    config.include Onceler::Noop
  else
    config.include Onceler::BasicHelpers

    # rspec 2+ only runs global before(:all)'s before the top-level
    # groups, not before each nested one. so we need to reset some
    # things to play nicely with its caching
    Onceler.configure do |c|
      c.before :record do
        Account.clear_special_account_cache!
        AdheresToPolicy::Cache.clear
      end
    end

    Onceler.instance_eval do
      # since once-ler creates potentially multiple levels of transaction
      # nesting, we need a way to know the base level so we can compare it
      # to AR::Conn#open_transactions. that will tell us if something is
      # "committed" or not (from the perspective of the spec)
      def base_transactions
        # if not recording, it's presumed we're in a spec, in which case
        # transactional fixtures add one more level
        open_transactions + (recording? ? 0 : 1)
      end
    end
  end

  Notification.after_create { Notification.reset_cache! }
  config.before :all do
    # so before(:all)'s don't get confused
    Account.clear_special_account_cache!
    AdheresToPolicy::Cache.clear

    # allow tests to still run in non-draft state even though it's hard-coded on
    Feature.definitions["draft_state"].send(:instance_variable_set, '@state', 'allowed')
  end

  def delete_fixtures!
    # noop for now, needed for plugin spec tweaks. implementation coming
    # in g/24755
  end

  # UTC for tests, cuz it's easier :P
  Account.time_zone_attribute_defaults[:default_time_zone] = 'UTC'

  config.before :each do
    I18n.locale = :en
    Time.zone = 'UTC'
    Account.clear_special_account_cache!
    AdheresToPolicy::Cache.clear
    Setting.reset_cache!
    ConfigFile.unstub
    HostUrl.reset_cache!
    Notification.reset_cache!
    ActiveRecord::Base.reset_any_instantiation!
    Attachment.clear_cached_mime_ids
    RoleOverride.clear_cached_contexts
    Delayed::Job.redis.flushdb if Delayed::Job == Delayed::Backend::Redis::Job
    Rails::logger.try(:info, "Running #{self.class.description} #{@method_name}")
    Attachment.domain_namespace = nil
  end

  # flush redis before the first spec, and before each spec that comes after
  # one that used redis
  class << Canvas
    attr_accessor :redis_used

    def redis_with_track_usage(*a, &b)
      self.redis_used = true
      redis_without_track_usage(*a, &b)
    end

    alias_method_chain :redis, :track_usage
    Canvas.redis_used = true
  end
  config.before :each do
    if Canvas.redis_enabled? && Canvas.redis_used
      Canvas.redis.flushdb rescue nil<commit_msg>reset cached access tokens in api specs<commit_after>#
# Copyright (C) 2011 Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#

require File.expand_path(File.dirname(__FILE__) + '/../config/canvas_rails3')
if CANVAS_RAILS2
  Spec::Example::ExampleGroupMethods.module_eval do
    def include_examples(*args)
      it_should_behave_like(*args)
    end
  end
end

unless CANVAS_RAILS2
  begin
    require RUBY_VERSION >= '2.0.0' ? 'byebug' : 'debugger'
  rescue LoadError
  end

  RSpec.configure do |c|
    c.treat_symbols_as_metadata_keys_with_true_values = true
    c.color = true

    c.around(:each) do |example|
      attempts = 0
      begin
        Timeout::timeout(180) {
          example.run
        }
        if ENV['AUTORERUN']
          e = @example.instance_variable_get('@exception')
          if !e.nil? && (attempts += 1) < 2 && !example.metadata[:no_retry]
            puts "FAILURE: #{@example.description} \n #{e}".red
            puts "RETRYING: #{@example.description}".yellow
            @example.instance_variable_set('@exception', nil)
            redo
          elsif e.nil? && attempts != 0
            puts "SUCCESS: retry passed for \n #{@example.description}".green
          end
        end
      end until true
    end
  end
end

begin
  ; require File.expand_path(File.dirname(__FILE__) + "/../parallelized_specs/lib/parallelized_specs.rb");
rescue LoadError;
end

ENV["RAILS_ENV"] = 'test'

require File.expand_path('../../config/environment', __FILE__) unless defined?(Rails)
if CANVAS_RAILS2
  require 'spec'
  # require 'spec/autorun'
  require 'spec/rails'

  # integration specs were renamed to request specs in rspec 2
  def describe_with_rspec2_types(*args, &block)
    unless args.last.is_a?(Hash)
      args << {}
    end
    raise "please use type: :request instead of type: :integration for rspec 2 compatibility" if args.last[:type] == :integration
    if args.last[:type] == :request
      args.last[:type] = :integration
    end
    args.last[:location] ||= caller(0)[1]
    describe_without_rspec2_types(*args, &block)
  end

  alias :describe_without_rspec2_types :describe
  alias :describe :describe_with_rspec2_types

  ActionController::TestProcess.module_eval do
    def process_with_use_route_shim(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
      if parameters.is_a?(Hash)
        parameters.delete(:use_route)
      end
      process_without_use_route_shim(action, parameters, session, flash, http_method)
    end

    alias_method_chain :process, :use_route_shim
  end

  Spec::Rails::Example::ViewExampleGroup.class_eval do
    alias :view :template

    def content_for(name)
      response.capture(name)
    end
  end
else
  require 'rspec/rails'

  ActionView::TestCase::TestController.view_paths = ApplicationController.view_paths

  module RSpec::Rails
    module ViewExampleGroup
      module ExampleMethods
        # normally in rspec 2, assigns returns a newly constructed hash
        # which means that 'assigns[:key] = value' in view specs does nothing
        def assigns
          @assigns ||= super
        end

        alias :view_assigns :assigns

        delegate :content_for, :to => :view

        def render_with_helpers(*args)
          controller_class = ("#{@controller.controller_path.camelize}Controller".constantize rescue nil) || ApplicationController

          controller_class.instance_variable_set(:@js_env, nil)
          # this extends the controller's helper methods to the view
          # however, these methods are delegated to the test controller
          view.singleton_class.class_eval do
            include controller_class._helpers unless included_modules.include?(controller_class._helpers)
          end

          # so create a "real_controller"
          # and delegate the helper methods to it
          @controller.singleton_class.class_eval do
            attr_accessor :real_controller

            controller_class._helper_methods.each do |helper|
              delegate helper, :to => :real_controller
            end
          end

          real_controller = controller_class.new
          real_controller.instance_variable_set(:@_request, @controller.request)
          @controller.real_controller = real_controller

          # just calling "render 'path/to/view'" by default looks for a partial
          if args.first && args.first.is_a?(String)
            file = args.shift
            args = [{:template => file}] + args
          end
          render_without_helpers(*args)
        end

        alias_method_chain :render, :helpers
      end
    end

    module Matchers
      class HaveTag
        include ActionDispatch::Assertions::SelectorAssertions
        include Test::Unit::Assertions

        def initialize(expected)
          @expected = expected
        end

        def matches?(html, &block)
          @selected = [HTML::Document.new(html).root]
          assert_select(*@expected, &block)
          return !@failed
        end

        def assert(val, msg=nil)
          unless !!val
            @msg = msg
            @failed = true
          end
        end

        def failure_message_for_should
          @msg
        end

        def failure_message_for_should_not
          @msg
        end
      end

      def have_tag(*args)
        HaveTag.new(args)
      end
    end
  end
end
require 'action_controller_test_process'
require File.expand_path(File.dirname(__FILE__) + '/mocha_rspec_adapter')
require File.expand_path(File.dirname(__FILE__) + '/mocha_extensions')
require File.expand_path(File.dirname(__FILE__) + '/ams_spec_helper')

require 'i18n_tasks'
require 'handlebars_tasks'

# if mocha was initialized before rails (say by another spec), CollectionProxy would have
# undef_method'd them; we need to restore them
unless CANVAS_RAILS2
  Mocha::ObjectMethods.instance_methods.each do |m|
    ActiveRecord::Associations::CollectionProxy.class_eval <<-RUBY
      def #{m}; end
      remove_method #{m.inspect}
    RUBY
  end
end

Dir.glob("#{File.dirname(__FILE__).gsub(/\\/, "/")}/factories/*.rb").each { |file| require file }

def pend_with_bullet
  if defined?(Bullet) && Bullet.enable?
    pending ('PENDING: Bullet')
  end
end

def require_webmock
  # pull in webmock for selected tests, but leave it disabled by default.
  # funky require order is to skip typhoeus because of an incompatibility
  # see: https://github.com/typhoeus/typhoeus/issues/196
  require 'webmock/util/version_checker'
  require 'webmock/http_lib_adapters/http_lib_adapter_registry'
  require 'webmock/http_lib_adapters/http_lib_adapter'
  require 'webmock/http_lib_adapters/typhoeus_hydra_adapter'
  WebMock::HttpLibAdapterRegistry.instance.http_lib_adapters.delete :typhoeus
  require 'webmock/rspec'
end

# rspec aliases :describe to :context in a way that it's pretty much defined
# globally on every object. :context is already heavily used in our application,
# so we remove rspec's definition. This does not prevent 'context' from being
# used within a 'describe' block.

if defined?(Spec::DSL::Main)
  module Spec::DSL::Main
    remove_method :context if respond_to? :context
  end
end

def truncate_table(model)
  case model.connection.adapter_name
    when "SQLite"
      model.delete_all
      begin
        model.connection.execute("delete from sqlite_sequence where name='#{model.connection.quote_table_name(model.table_name)}';")
        model.connection.execute("insert into sqlite_sequence (name, seq) values ('#{model.connection.quote_table_name(model.table_name)}', #{rand(100)});")
      rescue
      end
    when "PostgreSQL"
      begin
        old_proc = model.connection.raw_connection.set_notice_processor {}
        model.connection.execute("TRUNCATE TABLE #{model.connection.quote_table_name(model.table_name)} CASCADE")
      ensure
        model.connection.raw_connection.set_notice_processor(&old_proc)
      end
    else
      model.connection.execute("SET FOREIGN_KEY_CHECKS=0")
      model.connection.execute("TRUNCATE TABLE #{model.connection.quote_table_name(model.table_name)}")
      model.connection.execute("SET FOREIGN_KEY_CHECKS=1")
  end
end

def truncate_all_tables
  models_by_connection = ActiveRecord::Base.all_models.group_by { |m| m.connection }
  models_by_connection.each do |connection, models|
    if connection.adapter_name == "PostgreSQL"
      table_names = connection.tables & models.map(&:table_name)
      connection.execute("TRUNCATE TABLE #{table_names.map { |t| connection.quote_table_name(t) }.join(',')}")
    else
      table_names = connection.tables
      models.each { |model| truncate_table(model) if table_names.include?(model.table_name) }
    end
  end
end

# wipe out the test db, in case some non-transactional tests crapped out before
# cleaning up after themselves
truncate_all_tables

# Make AR not puke if MySQL auto-commits the transaction
module MysqlOutsideTransaction
  def outside_transaction?
    # MySQL ignores creation of savepoints outside of a transaction; so if we can create one
    # and then can't release it because it doesn't exist, we're not in a transaction
    execute('SAVEPOINT outside_transaction')
    !!execute('RELEASE SAVEPOINT outside_transaction') rescue true
  end
end

module ActiveRecord::ConnectionAdapters
  if defined?(MysqlAdapter)
    MysqlAdapter.send(:include, MysqlOutsideTransaction)
  end
  if defined?(Mysql2Adapter)
    Mysql2Adapter.send(:include, MysqlOutsideTransaction)
  end
end

# Be sure to actually test serializing things to non-existent caches,
# but give Mocks a pass, since they won't exist in dev/prod
Mocha::Mock.class_eval do
  def marshal_dump
    nil
  end

  def marshal_load(data)
    raise "Mocks aren't really serializeable!"
  end

  def to_yaml(opts = {})
    YAML.quick_emit(self.object_id, opts) do |out|
      out.scalar(nil, 'null')
    end
  end

  def respond_to_with_marshalling?(symbol, include_private = false)
    return true if [:marshal_dump, :marshal_load].include?(symbol)
    respond_to_without_marshalling?(symbol, include_private)
  end

  alias_method_chain :respond_to?, :marshalling
end

[ActiveSupport::Cache::MemoryStore, (CANVAS_RAILS2 ? NilStore : ActiveSupport::Cache::NullStore)].each do |store|
  store.class_eval do
    def write_with_serialization_check(name, value, options = nil)
      Marshal.dump(value)
      write_without_serialization_check(name, value, options)
    end

    alias_method_chain :write, :serialization_check
  end
end

unless CANVAS_RAILS2
  ActiveSupport::Cache::NullStore.class_eval do
    def fetch_with_serialization_check(name, options = {}, &block)
      result = fetch_without_serialization_check(name, options, &block)
      Marshal.dump(result) if result
      result
    end

    alias_method_chain :fetch, :serialization_check
  end
end

matchers_module = (CANVAS_RAILS2 ? Spec::Matchers : RSpec::Matchers)
matchers_module.define :encompass do |expected|
  match do |actual|
    if expected.is_a?(Array) && actual.is_a?(Array)
      expected.size == actual.size && expected.zip(actual).all? { |e, a| a.slice(*e.keys) == e }
    elsif expected.is_a?(Hash) && actual.is_a?(Hash)
      actual.slice(*expected.keys) == expected
    else
      false
    end
  end
end

matchers_module.define :match_ignoring_whitespace do |expected|
  def whitespaceless(str)
    str.gsub(/\s+/, '')
  end

  match do |actual|
    whitespaceless(actual) == whitespaceless(expected)
  end
end

module Helpers
  def message(opts={})
    m = Message.new
    m.to = opts[:to] || 'some_user'
    m.from = opts[:from] || 'some_other_user'
    m.subject = opts[:subject] || 'a message for you'
    m.body = opts[:body] || 'nice body'
    m.sent_at = opts[:sent_at] || 5.days.ago
    m.workflow_state = opts[:workflow_state] || 'sent'
    m.user_id = opts[:user_id] || opts[:user].try(:id)
    m.path_type = opts[:path_type] || 'email'
    m.root_account_id = opts[:account_id] || Account.default.id
    m.save!
    m
  end
end

# Make sure extensions will work with dynamically created shards
if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' &&
    ActiveRecord::Base.connection.schema_search_path == 'public'
  %w{pg_trgm pg_collkey}.each do |extension|
    current_schema = ActiveRecord::Base.connection.select_value("SELECT nspname FROM pg_extension INNER JOIN pg_namespace ON extnamespace=pg_namespace.oid WHERE extname='#{extension}'")
    if current_schema && current_schema == 'public'
      ActiveRecord::Base.connection.execute("ALTER EXTENSION #{extension} SET SCHEMA pg_catalog") rescue nil
    end
  end
end

(CANVAS_RAILS2 ? Spec::Runner : RSpec).configure do |config|
  # If you're not using ActiveRecord you should remove these
  # lines, delete config/database.yml and disable :active_record
  # in your config/boot.rb
  config.use_transactional_fixtures = true
  config.use_instantiated_fixtures = false
  config.fixture_path = Rails.root+'spec/fixtures/'

  config.include Helpers

  if CANVAS_RAILS2
    require 'spec/onceler_noop'
    config.include Onceler::Noop
  else
    config.include Onceler::BasicHelpers

    # rspec 2+ only runs global before(:all)'s before the top-level
    # groups, not before each nested one. so we need to reset some
    # things to play nicely with its caching
    Onceler.configure do |c|
      c.before :record do
        Account.clear_special_account_cache!
        AdheresToPolicy::Cache.clear
      end
    end

    Onceler.instance_eval do
      # since once-ler creates potentially multiple levels of transaction
      # nesting, we need a way to know the base level so we can compare it
      # to AR::Conn#open_transactions. that will tell us if something is
      # "committed" or not (from the perspective of the spec)
      def base_transactions
        # if not recording, it's presumed we're in a spec, in which case
        # transactional fixtures add one more level
        open_transactions + (recording? ? 0 : 1)
      end
    end
  end

  Notification.after_create { Notification.reset_cache! }
  config.before :all do
    # so before(:all)'s don't get confused
    Account.clear_special_account_cache!
    AdheresToPolicy::Cache.clear

    # allow tests to still run in non-draft state even though it's hard-coded on
    Feature.definitions["draft_state"].send(:instance_variable_set, '@state', 'allowed')
  end

  def delete_fixtures!
    # noop for now, needed for plugin spec tweaks. implementation coming
    # in g/24755
  end

  # UTC for tests, cuz it's easier :P
  Account.time_zone_attribute_defaults[:default_time_zone] = 'UTC'

  config.before :each do
    I18n.locale = :en
    Time.zone = 'UTC'
    Account.clear_special_account_cache!
    AdheresToPolicy::Cache.clear
    Setting.reset_cache!
    ConfigFile.unstub
    HostUrl.reset_cache!
    Notification.reset_cache!
    ActiveRecord::Base.reset_any_instantiation!
    Attachment.clear_cached_mime_ids
    RoleOverride.clear_cached_contexts
    Delayed::Job.redis.flushdb if Delayed::Job == Delayed::Backend::Redis::Job
    Rails::logger.try(:info, "Running #{self.class.description} #{@method_name}")
    Attachment.domain_namespace = nil
    $spec_api_tokens = {}
  end

  # flush redis before the first spec, and before each spec that comes after
  # one that used redis
  class << Canvas
    attr_accessor :redis_used

    def redis_with_track_usage(*a, &b)
      self.redis_used = true
      redis_without_track_usage(*a, &b)
    end

    alias_method_chain :redis, :track_usage
    Canvas.redis_used = true
  end
  config.before :each do
    if Canvas.redis_enabled? && Canvas.redis_used
      Canvas.redis.flushdb rescue nil
{'fraction_non_alphanumeric': 0.09311515899969126, 'fraction_numerical': 0.0035196048163013277, 'mean_word_length': 3.470190449903395, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 13, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5858659', 'n_tokens_mistral': 10038, 'n_tokens_neox': 9766, 'n_words': 2612}