calculations_test.rb 12.3 KB
Newer Older
1
require "cases/helper"
J
Jeremy Kemper 已提交
2 3
require 'models/company'
require 'models/topic'
4
require 'models/edge'
5 6 7

Company.has_many :accounts

8 9 10 11
class NumericData < ActiveRecord::Base
  self.table_name = 'numeric_data'
end

12
class CalculationsTest < ActiveRecord::TestCase
13 14 15
  fixtures :companies, :accounts, :topics

  def test_should_sum_field
16
    assert_equal 318, Account.sum(:credit_limit)
17 18 19 20
  end

  def test_should_average_field
    value = Account.average(:credit_limit)
21 22
    assert_kind_of BigDecimal, value
    assert_equal BigDecimal.new('53.0'), value
23 24
  end

25 26 27
  def test_should_return_nil_as_average
    assert_nil NumericData.average(:bank_balance)
  end
28 29 30 31 32
  
  def test_type_cast_calculated_value_should_convert_db_averages_of_fixnum_class_to_decimal
    assert_equal 0, NumericData.send(:type_cast_calculated_value, 0, nil, 'avg')
    assert_equal 53.0, NumericData.send(:type_cast_calculated_value, 53, nil, 'avg')
  end
33

34 35 36 37
  def test_should_get_maximum_of_field
    assert_equal 60, Account.maximum(:credit_limit)
  end

38 39 40 41 42 43 44 45 46 47
  def test_should_get_maximum_of_field_with_include
    assert_equal 50, Account.maximum(:credit_limit, :include => :firm, :conditions => "companies.name != 'Summit'")
  end

  def test_should_get_maximum_of_field_with_scoped_include
    Account.with_scope :find => { :include => :firm, :conditions => "companies.name != 'Summit'" } do
      assert_equal 50, Account.maximum(:credit_limit)
    end
  end

48 49 50 51 52 53
  def test_should_get_minimum_of_field
    assert_equal 50, Account.minimum(:credit_limit)
  end

  def test_should_group_by_field
    c = Account.sum(:credit_limit, :group => :firm_id)
54
    [1,6,2].each { |firm_id| assert c.keys.include?(firm_id) }
55 56 57 58
  end

  def test_should_group_by_summed_field
    c = Account.sum(:credit_limit, :group => :firm_id)
59 60 61
    assert_equal 50,   c[1]
    assert_equal 105,  c[6]
    assert_equal 60,   c[2]
62 63
  end

64 65
  def test_should_order_by_grouped_field
    c = Account.sum(:credit_limit, :group => :firm_id, :order => "firm_id")
66
    assert_equal [1, 2, 6, 9], c.keys.compact
67 68 69 70
  end

  def test_should_order_by_calculation
    c = Account.sum(:credit_limit, :group => :firm_id, :order => "sum_credit_limit desc, firm_id")
71 72
    assert_equal [105, 60, 53, 50, 50], c.keys.collect { |k| c[k] }
    assert_equal [6, 2, 9, 1], c.keys.compact
73 74
  end

75 76 77 78 79 80 81 82 83 84 85 86
  def test_should_limit_calculation
    c = Account.sum(:credit_limit, :conditions => "firm_id IS NOT NULL",
                    :group => :firm_id, :order => "firm_id", :limit => 2)
    assert_equal [1, 2], c.keys.compact
  end

  def test_should_limit_calculation_with_offset
    c = Account.sum(:credit_limit, :conditions => "firm_id IS NOT NULL",
                    :group => :firm_id, :order => "firm_id", :limit => 2, :offset => 1)
    assert_equal [2, 6], c.keys.compact
  end

87
  def test_should_group_by_summed_field_having_condition
J
Jeremy Kemper 已提交
88
    c = Account.sum(:credit_limit, :group => :firm_id,
89
                                   :having => 'sum(credit_limit) > 50')
90 91 92
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
93 94
  end

95 96 97 98 99 100 101 102
  def test_should_group_by_summed_field_having_sanitized_condition
    c = Account.sum(:credit_limit, :group => :firm_id,
                                   :having => ['sum(credit_limit) > ?', 50])
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
  end

103 104 105 106 107 108
  def test_should_group_by_summed_association
    c = Account.sum(:credit_limit, :group => :firm)
    assert_equal 50,   c[companies(:first_firm)]
    assert_equal 105,  c[companies(:rails_core)]
    assert_equal 60,   c[companies(:first_client)]
  end
J
Jeremy Kemper 已提交
109

110 111 112 113
  def test_should_sum_field_with_conditions
    assert_equal 105, Account.sum(:credit_limit, :conditions => 'firm_id = 6')
  end

114 115
  def test_should_return_zero_if_sum_conditions_return_nothing
    assert_equal 0, Account.sum(:credit_limit, :conditions => '1 = 2')
116
    assert_equal 0, companies(:rails_core).companies.sum(:id, :conditions => '1 = 2')
117 118
  end

119 120 121 122 123
  def test_sum_should_return_valid_values_for_decimals
    NumericData.create(:bank_balance => 19.83)
    assert_equal 19.83, NumericData.sum(:bank_balance)
  end

124
  def test_should_group_by_summed_field_with_conditions
J
Jeremy Kemper 已提交
125
    c = Account.sum(:credit_limit, :conditions => 'firm_id > 1',
126
                                   :group => :firm_id)
127 128 129
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
130
  end
J
Jeremy Kemper 已提交
131

132
  def test_should_group_by_summed_field_with_conditions_and_having
J
Jeremy Kemper 已提交
133 134
    c = Account.sum(:credit_limit, :conditions => 'firm_id > 1',
                                   :group => :firm_id,
135
                                   :having => 'sum(credit_limit) > 60')
136 137 138
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_nil        c[2]
139 140 141 142
  end

  def test_should_group_by_fields_with_table_alias
    c = Account.sum(:credit_limit, :group => 'accounts.firm_id')
143 144 145
    assert_equal 50,  c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
146
  end
J
Jeremy Kemper 已提交
147

148
  def test_should_calculate_with_invalid_field
149 150
    assert_equal 6, Account.calculate(:count, '*')
    assert_equal 6, Account.calculate(:count, :all)
151
  end
J
Jeremy Kemper 已提交
152

153 154
  def test_should_calculate_grouped_with_invalid_field
    c = Account.count(:all, :group => 'accounts.firm_id')
155 156 157
    assert_equal 1, c[1]
    assert_equal 2, c[6]
    assert_equal 1, c[2]
158
  end
J
Jeremy Kemper 已提交
159

160 161 162 163 164 165
  def test_should_calculate_grouped_association_with_invalid_field
    c = Account.count(:all, :group => :firm)
    assert_equal 1, c[companies(:first_firm)]
    assert_equal 2, c[companies(:rails_core)]
    assert_equal 1, c[companies(:first_client)]
  end
166

167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
  def test_should_group_by_association_with_non_numeric_foreign_key
    ActiveRecord::Base.connection.expects(:select_all).returns([{"count_all" => 1, "firm_id" => "ABC"}])

    firm = mock()
    firm.expects(:id).returns("ABC")
    firm.expects(:class).returns(Firm)
    Company.expects(:find).with(["ABC"]).returns([firm])

    column = mock()
    column.expects(:name).at_least_once.returns(:firm_id)
    column.expects(:type_cast).with("ABC").returns("ABC")
    Account.expects(:columns).at_least_once.returns([column])

    c = Account.count(:all, :group => :firm)
    first_key = c.keys.first
    assert_equal Firm, first_key.class
    assert_equal 1, c[first_key]
184
  end
J
Jeremy Kemper 已提交
185

186 187 188 189 190 191 192 193
  def test_should_calculate_grouped_association_with_foreign_key_option
    Account.belongs_to :another_firm, :class_name => 'Firm', :foreign_key => 'firm_id'
    c = Account.count(:all, :group => :another_firm)
    assert_equal 1, c[companies(:first_firm)]
    assert_equal 2, c[companies(:rails_core)]
    assert_equal 1, c[companies(:first_client)]
  end

194 195 196
  def test_should_not_modify_options_when_using_includes
    options = {:conditions => 'companies.id > 1', :include => :firm}
    options_copy = options.dup
J
Jeremy Kemper 已提交
197

198 199 200
    Account.count(:all, options)
    assert_equal options_copy, options
  end
201 202

  def test_should_calculate_grouped_by_function
203
    c = Company.count(:all, :group => "UPPER(#{QUOTED_TYPE})")
204 205
    assert_equal 2, c[nil]
    assert_equal 1, c['DEPENDENTFIRM']
206
    assert_equal 3, c['CLIENT']
207 208
    assert_equal 2, c['FIRM']
  end
J
Jeremy Kemper 已提交
209

210
  def test_should_calculate_grouped_by_function_with_table_alias
211
    c = Company.count(:all, :group => "UPPER(companies.#{QUOTED_TYPE})")
212 213
    assert_equal 2, c[nil]
    assert_equal 1, c['DEPENDENTFIRM']
214
    assert_equal 3, c['CLIENT']
215
    assert_equal 2, c['FIRM']
216
  end
J
Jeremy Kemper 已提交
217

218 219 220
  def test_should_not_overshadow_enumerable_sum
    assert_equal 6, [1, 2, 3].sum(&:abs)
  end
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235

  def test_should_sum_scoped_field
    assert_equal 15, companies(:rails_core).companies.sum(:id)
  end

  def test_should_sum_scoped_field_with_conditions
    assert_equal 8,  companies(:rails_core).companies.sum(:id, :conditions => 'id > 7')
  end

  def test_should_group_by_scoped_field
    c = companies(:rails_core).companies.sum(:id, :group => :name)
    assert_equal 7, c['Leetsoft']
    assert_equal 8, c['Jadedpixel']
  end

E
Emilio Tagua 已提交
236
  def test_should_group_by_summed_field_through_association_and_having
237 238 239 240 241
    c = companies(:rails_core).companies.sum(:id, :group => :name,
                                                  :having => 'sum(id) > 7')
    assert_nil      c['Leetsoft']
    assert_equal 8, c['Jadedpixel']
  end
242 243 244 245 246 247 248

  def test_should_reject_invalid_options
    assert_nothing_raised do
      [:count, :sum].each do |func|
        # empty options are valid
        Company.send(:validate_calculation_options, func)
        # these options are valid for all calculations
J
Jeremy Kemper 已提交
249
        [:select, :conditions, :joins, :order, :group, :having, :distinct].each do |opt|
250 251 252
          Company.send(:validate_calculation_options, func, opt => true)
        end
      end
J
Jeremy Kemper 已提交
253

254 255 256
      # :include is only valid on :count
      Company.send(:validate_calculation_options, :count, :include => true)
    end
J
Jeremy Kemper 已提交
257

258 259
    assert_raise(ArgumentError) { Company.send(:validate_calculation_options, :sum,   :foo => :bar) }
    assert_raise(ArgumentError) { Company.send(:validate_calculation_options, :count, :foo => :bar) }
260
  end
J
Jeremy Kemper 已提交
261

262
  def test_should_count_selected_field_with_include
263 264
    assert_equal 6, Account.count(:distinct => true, :include => :firm)
    assert_equal 4, Account.count(:distinct => true, :include => :firm, :select => :credit_limit)
265
  end
J
Jeremy Kemper 已提交
266

267
  def test_should_count_scoped_select
268 269
    Account.update_all("credit_limit = NULL")
    assert_equal 0, Account.scoped(:select => "credit_limit").count
270 271 272
  end

  def test_should_count_scoped_select_with_options
273 274 275 276 277
    Account.update_all("credit_limit = NULL")
    Account.last.update_attribute('credit_limit', 49)
    Account.first.update_attribute('credit_limit', 51)

    assert_equal 1, Account.scoped(:select => "credit_limit").count(:conditions => ['credit_limit >= 50'])
278 279
  end

280 281 282 283
  def test_should_count_manual_select_with_include
    assert_equal 6, Account.count(:select => "DISTINCT accounts.id", :include => :firm)
  end

284 285 286
  def test_count_with_column_parameter
    assert_equal 5, Account.count(:firm_id)
  end
J
Jeremy Kemper 已提交
287

288 289 290
  def test_count_with_column_and_options_parameter
    assert_equal 2, Account.count(:firm_id, :conditions => "credit_limit = 50")
  end
J
Jeremy Kemper 已提交
291

292 293 294 295 296 297 298
  def test_count_with_no_parameters_isnt_deprecated
    assert_not_deprecated { Account.count }
  end

  def test_count_with_too_many_parameters_raises
    assert_raise(ArgumentError) { Account.count(1, 2, 3) }
  end
299 300

  def test_should_sum_expression
301
    assert_equal '636', Account.sum("2 * credit_limit")
302
  end
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347

  def test_count_with_from_option
    assert_equal Company.count(:all), Company.count(:all, :from => 'companies')
    assert_equal Account.count(:all, :conditions => "credit_limit = 50"),
        Account.count(:all, :from => 'accounts', :conditions => "credit_limit = 50")
    assert_equal Company.count(:type, :conditions => {:type => "Firm"}),
        Company.count(:type, :conditions => {:type => "Firm"}, :from => 'companies')
  end

  def test_sum_with_from_option
    assert_equal Account.sum(:credit_limit), Account.sum(:credit_limit, :from => 'accounts')
    assert_equal Account.sum(:credit_limit, :conditions => "credit_limit > 50"),
        Account.sum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
  end

  def test_average_with_from_option
    assert_equal Account.average(:credit_limit), Account.average(:credit_limit, :from => 'accounts')
    assert_equal Account.average(:credit_limit, :conditions => "credit_limit > 50"),
        Account.average(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
  end

  def test_minimum_with_from_option
    assert_equal Account.minimum(:credit_limit), Account.minimum(:credit_limit, :from => 'accounts')
    assert_equal Account.minimum(:credit_limit, :conditions => "credit_limit > 50"),
        Account.minimum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
  end

  def test_maximum_with_from_option
    assert_equal Account.maximum(:credit_limit), Account.maximum(:credit_limit, :from => 'accounts')
    assert_equal Account.maximum(:credit_limit, :conditions => "credit_limit > 50"),
        Account.maximum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
  end

  def test_from_option_with_specified_index
    if Edge.connection.adapter_name == 'MySQL'
      assert_equal Edge.count(:all), Edge.count(:all, :from => 'edges USE INDEX(unique_edge_index)')
      assert_equal Edge.count(:all, :conditions => 'sink_id < 5'),
          Edge.count(:all, :from => 'edges USE INDEX(unique_edge_index)', :conditions => 'sink_id < 5')
    end
  end

  def test_from_option_with_table_different_than_class
    assert_equal Account.count(:all), Company.count(:all, :from => 'accounts')
  end

348
end