module ActiveLoaders::Test

Constants

Error

Public Instance Methods

assert_all_serializers_tested(namespace = nil) click to toggle source
# File lib/active_loaders/test.rb, line 51
def assert_all_serializers_tested(namespace = nil)
  descendants =
    ObjectSpace.each_object(Class)
    .select { |klass| klass < ActiveModel::Serializer }
    .select { |klass| (namespace.nil? && !klass.name.include?("::")) || klass.name.starts_with?("#{namespace}::") }
    .reject { |klass| Array(@active_loaders_tested_serializers).include?(klass) }

  unless descendants.empty?
    fail Error, "serializers not tested: #{descendants.map(&:name).join(", ")}"
  end
end
test_serializer_queries(serializer_klass, model_klass, ignore_columns: [], skip_columns_check: false, allow_queries_per_record: 0) click to toggle source
# File lib/active_loaders/test.rb, line 6
def test_serializer_queries(serializer_klass, model_klass, ignore_columns: [], skip_columns_check: false, allow_queries_per_record: 0)
  records = get_all_records(model_klass, serializer_klass)
  fail "Not enough records to test #{serializer_klass}. Create at least 1 #{model_klass}." unless records.size > 0

  records.each do |record|
    queries = get_executed_queries do
      serializer_klass.new(record).as_json
    end

    unless queries.size == allow_queries_per_record
      fail Error, "unexpected queries\n\nRecord:\n#{record.inspect}\n\nQueries:\n#{queries.join("\n")}"
    end
  end

  # just for good measure
  queries = get_executed_queries do
    ActiveModel::ArraySerializer.new(records, each_serializer: serializer_klass).as_json
  end
  unless queries.size == (records.size * allow_queries_per_record)
    fail Error, "unexpected queries when using ArraySerializer\n\nModel:\n#{model_klass}\n\nQueries:\n#{queries.join("\n")}"
  end

  # select values (if supported)
  # TODO: Sequel?
  unless skip_columns_check
    if defined?(ActiveRecord::Base) && model_klass.ancestors.include?(ActiveRecord::Base)
      if records.first.respond_to?(:accessed_fields)
        accessed_fields = Set.new
        records.each { |record| accessed_fields.merge(record.accessed_fields) }

        unaccessed_columns = model_klass.column_names - accessed_fields.to_a - ignore_columns.map(&:to_s)

        unless unaccessed_columns.empty?
          unaccessed_columns_str = unaccessed_columns.join(", ")
          unaccessed_columns_syms = unaccessed_columns.map { |c| ":#{c}" }.join(", ")
          all_unaccessed_columns_syms = (ignore_columns.map(&:to_s) + unaccessed_columns).map { |c| ":#{c}" }.join(", ")
          fail Error, "unnecessary select for #{model_klass} columns: #{unaccessed_columns_str}\n\nAdd to #{serializer_klass} loaders block:\n  skip_select #{unaccessed_columns_syms}\n\nOr ignore this error with:\n  test_serializer_queries(#{serializer_klass}, #{model_klass}, ignore_columns: [#{all_unaccessed_columns_syms}])\n\nOr skip this columns check entirely:\n  test_serializer_queries(#{serializer_klass}, #{model_klass}, skip_columns_check: true)"
        end
      end
    end
  end

  (@active_loaders_tested_serializers ||= Set.new).add(serializer_klass)
end

Private Instance Methods

get_all_records(model_klass, serializer_klass) click to toggle source
# File lib/active_loaders/test.rb, line 64
def get_all_records(model_klass, serializer_klass)
  if defined?(ActiveRecord::Base) && model_klass.ancestors.include?(ActiveRecord::Base)
    model_klass.for_serializer(serializer_klass).to_a
  elsif defined?(Sequel::Model) && model_klass.ancestors.include?(Sequel::Model)
    model_klass.for_serializer(serializer_klass).all
  else
    fail "Unknown model #{model_klass} of type #{model_klass.superclass}."
  end
end
get_executed_queries() { || ... } click to toggle source
# File lib/active_loaders/test.rb, line 74
def get_executed_queries
  logger_io = StringIO.new
  logger = Logger.new(logger_io)
  logger.formatter = ->(severity, datetime, progname, msg) { "#{msg}\n" }
  if defined?(ActiveRecord::Base)
    ar_old_logger = ActiveRecord::Base.logger
    ActiveRecord::Base.logger = logger
  end
  if defined?(Sequel::Model)
    Sequel::Model.db.loggers << logger
  end

  begin
    yield
  ensure
    if defined?(ActiveRecord::Base)
      ActiveRecord::Base.logger = ar_old_logger
    end
    if defined?(Sequel::Model)
      Sequel::Model.db.loggers.delete(logger)
    end
  end

  logger_io.string.lines.reject { |line| line.strip == "" }
end