require 'assert' require 'mr/query' require 'ardb/relation_spy' require 'mr/fake_record' require 'mr/model' class MR::Query class UnitTests < Assert::Context desc "MR::Query" setup do @relation = FakeTestRecord.scoped @relation.results = [ FakeTestRecord.new(:id => 1), FakeTestRecord.new(:id => 2) ] @model_class = FakeTestModel @count_relation_built_with = nil @count_relation = nil @count_called = false Assert.stub(CountRelation, :new) do |*args| @count_relation_built_with = args @count_relation = Assert.stub_send(CountRelation, :new, *args) Assert.stub(@count_relation, :count) do @count_called = true Assert.stub_send(@count_relation, :count) end @count_relation end @query = MR::Query.new(FakeTestModel, @relation) end subject{ @query } should have_readers :model_class, :relation should have_imeths :results, :results!, :first, :first!, :count, :count! should "know its results" do results = subject.results assert_equal @relation.results.map{ |r| @model_class.new(r) }, results assert_same results, subject.results end should "allow re-caching its results" do results = subject.results assert_same results, subject.results new_results = subject.results! assert_not_same results, new_results assert_same new_results, subject.results assert_not_same new_results, subject.results! end should "know its first result" do result = subject.first assert_equal @model_class.new(@relation.results.first), result assert_same result, subject.first end should "return `nil` for its first result if there are no results" do @relation.results = [] assert_nil subject.first end should "allow re-caching its first result" do result = subject.first assert_same result, subject.first new_result = subject.first! assert_not_same result, new_result assert_same new_result, subject.first assert_not_same new_result, subject.first! end should "know how to run a count query to get its count" do assert_equal 2, subject.count assert_equal [@relation], @count_relation_built_with assert_true @count_called # test that it caches the count, a count relation isn't rebuilt and count # isn't called @count_relation_built_with = nil @count_called = false assert_equal 2, subject.count assert_nil @count_relation_built_with assert_false @count_called end should "allow re-caching its count" do subject.count assert_equal [@relation], @count_relation_built_with @count_relation_built_with = nil @count_called = false subject.count! assert_nil @count_relation_built_with assert_true @count_called end should "know how to build a paged query" do paged_query = subject.paged(1, 10) assert_instance_of MR::PagedQuery, paged_query assert_equal 1, paged_query.page_num assert_equal 10, paged_query.page_size end end class PagedQueryTests < UnitTests desc "MR::PagedQuery" setup do @unpaged_relation = @relation.dup @paged_query_class = MR::PagedQuery @paged_query = @paged_query_class.new(@query, 1, 1) end subject{ @paged_query } should have_readers :page_num, :page_size, :page_offset should have_imeths :total_count, :total_count! should have_imeths :has_next_page?, :is_last_page? should "be an mr query" do assert_kind_of MR::Query, subject end should "default its page number and page size" do paged_query = @paged_query_class.new(@query) assert_equal 1, paged_query.page_num assert_equal 25, paged_query.page_size end should "not allow invalid page numbers or page size values" do paged_query = @paged_query_class.new(@query, -1, -10) assert_equal 1, paged_query.page_num assert_equal 25, paged_query.page_size paged_query = @paged_query_class.new(@query, 'a', 10.4) # 'a'.to_i is 0, thus it forces it to 1 assert_equal 1, paged_query.page_num # 10.4.to_i is 10, which is valid assert_equal 10.4.to_i, paged_query.page_size end should "correctly calculate limits and offsets" do paged_query = @paged_query_class.new(@query, 1, 10) assert_equal 0, @relation.offset_value assert_equal 10, @relation.limit_value paged_query = @paged_query_class.new(@query, 5, 7) assert_equal 28, @relation.offset_value assert_equal 7, @relation.limit_value end should "know its paged results" do exp = @relation.results[0, 1].map{ |r| @model_class.new(r) } assert_equal exp, subject.results end should "know how to run a paged count query to get its count" do assert_equal 1, subject.count assert_equal [@relation], @count_relation_built_with assert_true @count_called end should "know how to run an unpaged count query to get its total count" do assert_equal 2, subject.total_count assert_equal [@unpaged_relation], @count_relation_built_with assert_true @count_called # test that it caches the total count, a count relation isn't rebuilt and # count isn't called @count_relation_built_with = nil @count_called = false assert_equal 2, subject.total_count assert_nil @count_relation_built_with assert_false @count_called end should "allow re-caching its total count" do subject.total_count assert_equal [@unpaged_relation], @count_relation_built_with @count_relation_built_with = nil @count_called = false subject.total_count! assert_nil @count_relation_built_with assert_true @count_called end should "know if it has a next page or is the last page" do page_num = Factory.integer(10) page_size = Factory.integer(10) paged_query = @paged_query_class.new(@query, page_num, page_size) page_end = paged_query.page_offset + page_size total_count = Factory.integer(page_size - 1) + page_end Assert.stub(paged_query, :total_count){ total_count } assert_true paged_query.has_next_page? assert_false paged_query.is_last_page? paged_query = @paged_query_class.new(@query, page_num, page_size) total_count = Factory.integer(page_size - 1) + paged_query.page_offset Assert.stub(paged_query, :total_count){ total_count } assert_false paged_query.has_next_page? assert_true paged_query.is_last_page? paged_query = @paged_query_class.new(@query, page_num, page_size) Assert.stub(paged_query, :total_count){ page_end } assert_false paged_query.has_next_page? assert_true paged_query.is_last_page? end end class CountRelationTests < UnitTests desc "CountRelation" setup do @relation.select('some_table.some_column') @relation.where('some_table.some_column = ?', 1) @relation.order('some_table.some_column DESC') end subject{ CountRelation } should "return the relation with emptied select and order values when " \ "the original relation was not grouped" do count_relation = subject.new(@relation) select_expressions = count_relation.applied.select{ |e| e.type == :select } assert_true select_expressions.empty? order_expressions = count_relation.applied.select{ |e| e.type == :order } assert_true order_expressions.empty? # still has the where expression where_expressions = count_relation.applied.select{ |e| e.type == :where } assert_equal 1, where_expressions.size end should "return a new relation for counting a subquery of the original " \ "relation with emptied select and order values when " \ "the original relation was grouped" do @relation.group_values = [ 'table.id' ] count_relation = subject.new(@relation) assert_not_equal count_relation.applied, @relation.applied assert_equal 1, count_relation.applied.size from_expression = count_relation.applied.first assert_equal :from, from_expression.type exp_relation = @relation.except(:select, :order).select('1') exp = "(#{exp_relation.to_sql}) AS grouped_records" assert_equal [exp], from_expression.args end end class RelationSpy < Ardb::RelationSpy attr_reader :klass attr_accessor :group_values def initialize(klass, *args) super(*args) @klass = klass @group_values = [] end # this is just a random readable string, it looks like: # select(id).where(some_id, 1) def to_sql @applied.reverse.map{ |e| "#{e.type}(#{e.args.join(", ")})" }.join('.') end end class FakeTestRecord include MR::FakeRecord def self.scoped RelationSpy.new(self) end end class FakeTestModel include MR::Model record_class FakeTestRecord end end