calculations_test.rb 26.7 KB
Newer Older
1
require "cases/helper"
2
require "models/book"
3
require 'models/club'
J
Jeremy Kemper 已提交
4
require 'models/company'
5
require "models/contract"
6
require 'models/edge'
7
require 'models/organization'
8 9
require 'models/possession'
require 'models/topic'
10
require 'models/reply'
11 12
require 'models/minivan'
require 'models/speedometer'
A
Aaron Patterson 已提交
13
require 'models/ship_part'
14
require 'models/treasure'
15 16 17 18
require 'models/developer'
require 'models/comment'
require 'models/rating'
require 'models/post'
19

20 21
class NumericData < ActiveRecord::Base
  self.table_name = 'numeric_data'
22

23 24 25
  attribute :world_population, :integer
  attribute :my_house_population, :integer
  attribute :atoms_in_universe, :integer
26 27
end

28
class CalculationsTest < ActiveRecord::TestCase
R
Ryuta Kamizono 已提交
29
  fixtures :companies, :accounts, :topics, :speedometers, :minivans, :books
30 31

  def test_should_sum_field
32
    assert_equal 318, Account.sum(:credit_limit)
33 34
  end

35 36 37 38
  def test_should_sum_arel_attribute
    assert_equal 318, Account.sum(Account.arel_table[:credit_limit])
  end

39 40
  def test_should_average_field
    value = Account.average(:credit_limit)
41
    assert_equal 53.0, value
42 43
  end

44 45 46 47 48
  def test_should_average_arel_attribute
    value = Account.average(Account.arel_table[:credit_limit])
    assert_equal 53.0, value
  end

49 50 51 52
  def test_should_resolve_aliased_attributes
    assert_equal 318, Account.sum(:available_credit)
  end

53 54 55 56 57
  def test_should_return_decimal_average_of_integer_field
    value = Account.average(:id)
    assert_equal 3.5, value
  end

58
  def test_should_return_integer_average_if_db_returns_such
A
Aaron Patterson 已提交
59 60 61
    ShipPart.delete_all
    ShipPart.create!(:id => 3, :name => 'foo')
    value = ShipPart.average(:id)
62 63 64
    assert_equal 3, value
  end

65 66 67
  def test_should_return_nil_as_average
    assert_nil NumericData.average(:bank_balance)
  end
68

69 70 71 72
  def test_should_get_maximum_of_field
    assert_equal 60, Account.maximum(:credit_limit)
  end

73 74 75 76
  def test_should_get_maximum_of_arel_attribute
    assert_equal 60, Account.maximum(Account.arel_table[:credit_limit])
  end

77
  def test_should_get_maximum_of_field_with_include
J
Jon Leighton 已提交
78
    assert_equal 55, Account.where("companies.name != 'Summit'").references(:companies).includes(:firm).maximum(:credit_limit)
79 80
  end

81 82 83 84
  def test_should_get_maximum_of_arel_attribute_with_include
    assert_equal 55, Account.where("companies.name != 'Summit'").references(:companies).includes(:firm).maximum(Account.arel_table[:credit_limit])
  end

85 86 87 88
  def test_should_get_minimum_of_field
    assert_equal 50, Account.minimum(:credit_limit)
  end

89 90 91 92
  def test_should_get_minimum_of_arel_attribute
    assert_equal 50, Account.minimum(Account.arel_table[:credit_limit])
  end

93
  def test_should_group_by_field
J
Jon Leighton 已提交
94
    c = Account.group(:firm_id).sum(:credit_limit)
95 96 97
    [1,6,2].each do |firm_id|
      assert c.keys.include?(firm_id), "Group #{c.inspect} does not contain firm_id #{firm_id}"
    end
98
  end
99

100
  def test_should_group_by_arel_attribute
101 102 103 104
    c = Account.group(Account.arel_table[:firm_id]).sum(:credit_limit)
    [1,6,2].each do |firm_id|
      assert c.keys.include?(firm_id), "Group #{c.inspect} does not contain firm_id #{firm_id}"
    end
105 106
  end

107
  def test_should_group_by_multiple_fields
J
Jon Leighton 已提交
108
    c = Account.group('firm_id', :credit_limit).count(:all)
109 110 111 112 113 114 115 116 117 118
    [ [nil, 50], [1, 50], [6, 50], [6, 55], [9, 53], [2, 60] ].each { |firm_and_limit| assert c.keys.include?(firm_and_limit) }
  end

  def test_should_group_by_multiple_fields_having_functions
    c = Topic.group(:author_name, 'COALESCE(type, title)').count(:all)
    assert_equal 1, c[["Carl", "The Third Topic of the day"]]
    assert_equal 1, c[["Mary", "Reply"]]
    assert_equal 1, c[["David", "The First Topic"]]
    assert_equal 1, c[["Carl", "Reply"]]
  end
119 120

  def test_should_group_by_summed_field
J
Jon Leighton 已提交
121
    c = Account.group(:firm_id).sum(:credit_limit)
122 123 124
    assert_equal 50,   c[1]
    assert_equal 105,  c[6]
    assert_equal 60,   c[2]
125 126
  end

127
  def test_should_generate_valid_sql_with_joins_and_group
128
    assert_nothing_raised do
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
      AuditLog.joins(:developer).group(:id).count
    end
  end

  def test_should_calculate_against_given_relation
    developer = Developer.create!(name: "developer")
    developer.audit_logs.create!(message: "first log")
    developer.audit_logs.create!(message: "second log")

    c = developer.audit_logs.joins(:developer).group(:id).count

    assert_equal developer.audit_logs.count, c.size
    developer.audit_logs.each do |log|
      assert_equal 1, c[log.id]
    end
  end

146
  def test_should_order_by_grouped_field
147
    c = Account.group(:firm_id).order("firm_id").sum(:credit_limit)
148
    assert_equal [1, 2, 6, 9], c.keys.compact
149 150 151
  end

  def test_should_order_by_calculation
152
    c = Account.group(:firm_id).order("sum_credit_limit desc, firm_id").sum(:credit_limit)
153 154
    assert_equal [105, 60, 53, 50, 50], c.keys.collect { |k| c[k] }
    assert_equal [6, 2, 9, 1], c.keys.compact
155 156
  end

157
  def test_should_limit_calculation
158
    c = Account.where("firm_id IS NOT NULL").group(:firm_id).order("firm_id").limit(2).sum(:credit_limit)
159 160 161 162
    assert_equal [1, 2], c.keys.compact
  end

  def test_should_limit_calculation_with_offset
163 164
    c = Account.where("firm_id IS NOT NULL").group(:firm_id).order("firm_id").
     limit(2).offset(1).sum(:credit_limit)
165 166 167
    assert_equal [2, 6], c.keys.compact
  end

168 169 170 171 172 173 174
  def test_limit_should_apply_before_count
    accounts = Account.limit(3).where('firm_id IS NOT NULL')

    assert_equal 3, accounts.count(:firm_id)
    assert_equal 3, accounts.select(:firm_id).count
  end

175 176 177 178 179 180 181 182
  def test_limit_should_apply_before_count_arel_attribute
    accounts = Account.limit(3).where('firm_id IS NOT NULL')

    firm_id_attribute = Account.arel_table[:firm_id]
    assert_equal 3, accounts.count(firm_id_attribute)
    assert_equal 3, accounts.select(firm_id_attribute).count
  end

183 184 185 186 187 188 189
  def test_count_should_shortcut_with_limit_zero
    accounts = Account.limit(0)

    assert_no_queries { assert_equal 0, accounts.count }
  end

  def test_limit_is_kept
A
Aaron Patterson 已提交
190 191
    return if current_adapter?(:OracleAdapter)

192
    queries = assert_sql { Account.limit(1).count }
193 194 195 196
    assert_equal 1, queries.length
    assert_match(/LIMIT/, queries.first)
  end

197 198 199
  def test_offset_is_kept
    return if current_adapter?(:OracleAdapter)

200 201
    queries = assert_sql { Account.offset(1).count }
    assert_equal 1, queries.length
202
    assert_match(/OFFSET/, queries.first)
203 204
  end

205 206 207 208
  def test_limit_with_offset_is_kept
    return if current_adapter?(:OracleAdapter)

    queries = assert_sql { Account.limit(1).offset(1).count }
209
    assert_equal 1, queries.length
210 211
    assert_match(/LIMIT/, queries.first)
    assert_match(/OFFSET/, queries.first)
212 213 214 215 216 217 218 219 220
  end

  def test_no_limit_no_offset
    queries = assert_sql { Account.count }
    assert_equal 1, queries.length
    assert_no_match(/LIMIT/, queries.first)
    assert_no_match(/OFFSET/, queries.first)
  end

221 222 223 224 225
  def test_count_on_invalid_columns_raises
    e = assert_raises(ActiveRecord::StatementInvalid) {
      Account.select("credit_limit, firm_name").count
    }

226
    assert_match %r{accounts}i, e.message
227 228 229
    assert_match "credit_limit, firm_name", e.message
  end

230
  def test_should_group_by_summed_field_having_condition
231
    c = Account.group(:firm_id).having('sum(credit_limit) > 50').sum(:credit_limit)
232 233 234 235 236
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
  end

237
  def test_should_group_by_summed_field_having_condition_from_select
238
    c = Account.select("MIN(credit_limit) AS min_credit_limit").group(:firm_id).having("MIN(credit_limit) > 50").sum(:credit_limit)
239 240 241 242 243
    assert_nil       c[1]
    assert_equal 60, c[2]
    assert_equal 53, c[9]
  end

244
  def test_should_group_by_summed_association
J
Jon Leighton 已提交
245
    c = Account.group(:firm).sum(:credit_limit)
246 247 248 249
    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 已提交
250

251
  def test_should_sum_field_with_conditions
J
Jon Leighton 已提交
252
    assert_equal 105, Account.where('firm_id = 6').sum(:credit_limit)
253 254
  end

255
  def test_should_return_zero_if_sum_conditions_return_nothing
J
Jon Leighton 已提交
256 257
    assert_equal 0, Account.where('1 = 2').sum(:credit_limit)
    assert_equal 0, companies(:rails_core).companies.where('1 = 2').sum(:id)
258 259
  end

260 261 262 263 264
  def test_sum_should_return_valid_values_for_decimals
    NumericData.create(:bank_balance => 19.83)
    assert_equal 19.83, NumericData.sum(:bank_balance)
  end

265 266 267 268
  def test_should_return_type_casted_values_with_group_and_expression
    assert_equal 0.5, Account.group(:firm_name).sum('0.01 * credit_limit')['37signals']
  end

269
  def test_should_group_by_summed_field_with_conditions
270
    c = Account.where('firm_id > 1').group(:firm_id).sum(:credit_limit)
271 272 273
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
274
  end
J
Jeremy Kemper 已提交
275

276
  def test_should_group_by_summed_field_with_conditions_and_having
277 278
    c = Account.where('firm_id > 1').group(:firm_id).
     having('sum(credit_limit) > 60').sum(:credit_limit)
279 280 281
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_nil        c[2]
282 283 284
  end

  def test_should_group_by_fields_with_table_alias
J
Jon Leighton 已提交
285
    c = Account.group('accounts.firm_id').sum(:credit_limit)
286 287 288
    assert_equal 50,  c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
289
  end
J
Jeremy Kemper 已提交
290

291
  def test_should_calculate_with_invalid_field
292 293
    assert_equal 6, Account.calculate(:count, '*')
    assert_equal 6, Account.calculate(:count, :all)
294
  end
J
Jeremy Kemper 已提交
295

296
  def test_should_calculate_grouped_with_invalid_field
J
Jon Leighton 已提交
297
    c = Account.group('accounts.firm_id').count(:all)
298 299 300
    assert_equal 1, c[1]
    assert_equal 2, c[6]
    assert_equal 1, c[2]
301
  end
J
Jeremy Kemper 已提交
302

303
  def test_should_calculate_grouped_association_with_invalid_field
J
Jon Leighton 已提交
304
    c = Account.group(:firm).count(:all)
305 306 307 308
    assert_equal 1, c[companies(:first_firm)]
    assert_equal 2, c[companies(:rails_core)]
    assert_equal 1, c[companies(:first_client)]
  end
309

310
  def test_should_group_by_association_with_non_numeric_foreign_key
A
Aaron Patterson 已提交
311 312
    Speedometer.create! id: 'ABC'
    Minivan.create! id: 'OMG', speedometer_id: 'ABC'
313

314
    c = Minivan.group(:speedometer).count(:all)
315
    first_key = c.keys.first
316
    assert_equal Speedometer, first_key.class
317
    assert_equal 1, c[first_key]
318
  end
J
Jeremy Kemper 已提交
319

320 321
  def test_should_calculate_grouped_association_with_foreign_key_option
    Account.belongs_to :another_firm, :class_name => 'Firm', :foreign_key => 'firm_id'
J
Jon Leighton 已提交
322
    c = Account.group(:another_firm).count(:all)
323 324 325 326 327
    assert_equal 1, c[companies(:first_firm)]
    assert_equal 2, c[companies(:rails_core)]
    assert_equal 1, c[companies(:first_client)]
  end

328
  def test_should_calculate_grouped_by_function
J
Jon Leighton 已提交
329
    c = Company.group("UPPER(#{QUOTED_TYPE})").count(:all)
330 331
    assert_equal 2, c[nil]
    assert_equal 1, c['DEPENDENTFIRM']
332
    assert_equal 5, c['CLIENT']
333 334
    assert_equal 2, c['FIRM']
  end
J
Jeremy Kemper 已提交
335

336
  def test_should_calculate_grouped_by_function_with_table_alias
J
Jon Leighton 已提交
337
    c = Company.group("UPPER(companies.#{QUOTED_TYPE})").count(:all)
338 339
    assert_equal 2, c[nil]
    assert_equal 1, c['DEPENDENTFIRM']
340
    assert_equal 5, c['CLIENT']
341
    assert_equal 2, c['FIRM']
342
  end
J
Jeremy Kemper 已提交
343

344 345 346
  def test_should_not_overshadow_enumerable_sum
    assert_equal 6, [1, 2, 3].sum(&:abs)
  end
347 348 349 350 351

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

352 353 354 355
  def test_should_sum_scoped_field_with_from
    assert_equal Club.count, Organization.clubs.count
  end

356
  def test_should_sum_scoped_field_with_conditions
J
Jon Leighton 已提交
357
    assert_equal 8,  companies(:rails_core).companies.where('id > 7').sum(:id)
358 359 360
  end

  def test_should_group_by_scoped_field
J
Jon Leighton 已提交
361
    c = companies(:rails_core).companies.group(:name).sum(:id)
362 363 364 365
    assert_equal 7, c['Leetsoft']
    assert_equal 8, c['Jadedpixel']
  end

E
Emilio Tagua 已提交
366
  def test_should_group_by_summed_field_through_association_and_having
J
Jon Leighton 已提交
367
    c = companies(:rails_core).companies.group(:name).having('sum(id) > 7').sum(:id)
368 369 370
    assert_nil      c['Leetsoft']
    assert_equal 8, c['Jadedpixel']
  end
371

372
  def test_should_count_selected_field_with_include
373 374
    assert_equal 6, Account.includes(:firm).distinct.count
    assert_equal 4, Account.includes(:firm).distinct.select(:credit_limit).count
375
  end
J
Jeremy Kemper 已提交
376

377 378 379 380 381 382 383
  def test_should_not_perform_joined_include_by_default
    assert_equal Account.count, Account.includes(:firm).count
    queries = assert_sql { Account.includes(:firm).count }
    assert_no_match(/join/i, queries.last)
  end

  def test_should_perform_joined_include_when_referencing_included_tables
384
    joined_count = Account.includes(:firm).where(:companies => {:name => '37signals'}).count
385 386 387
    assert_equal 1, joined_count
  end

388
  def test_should_count_scoped_select
389
    Account.update_all("credit_limit = NULL")
390
    assert_equal 0, Account.select("credit_limit").count
391 392 393
  end

  def test_should_count_scoped_select_with_options
394
    Account.update_all("credit_limit = NULL")
395 396
    Account.last.update_columns('credit_limit' => 49)
    Account.first.update_columns('credit_limit' => 51)
397

398
    assert_equal 1, Account.select("credit_limit").where('credit_limit >= 50').count
399 400
  end

401
  def test_should_count_manual_select_with_include
402
    assert_equal 6, Account.select("DISTINCT accounts.id").includes(:firm).count
403 404
  end

405 406 407 408 409
  def test_count_selected_arel_attribute
    assert_equal 5, Account.select(Account.arel_table[:firm_id]).count
    assert_equal 4, Account.distinct.select(Account.arel_table[:firm_id]).count
  end

410 411 412
  def test_count_with_column_parameter
    assert_equal 5, Account.count(:firm_id)
  end
J
Jeremy Kemper 已提交
413

414 415 416 417 418 419 420 421
  def test_count_with_arel_attribute
    assert_equal 5, Account.count(Account.arel_table[:firm_id])
  end

  def test_count_with_arel_star
    assert_equal 6, Account.count(Arel.star)
  end

422 423
  def test_count_with_distinct
    assert_equal 4, Account.select(:credit_limit).distinct.count
424 425 426 427

    assert_deprecated do
      assert_equal 4, Account.select(:credit_limit).uniq.count
    end
428 429
  end

430 431 432 433
  def test_count_with_aliased_attribute
    assert_equal 6, Account.count(:available_credit)
  end

434
  def test_count_with_column_and_options_parameter
J
Jon Leighton 已提交
435
    assert_equal 2, Account.where("credit_limit = 50 AND firm_id IS NOT NULL").count(:firm_id)
436
  end
J
Jeremy Kemper 已提交
437

O
oleg dashevskii 已提交
438
  def test_should_count_field_in_joined_table
J
Jon Leighton 已提交
439
    assert_equal 5, Account.joins(:firm).count('companies.id')
440
    assert_equal 4, Account.joins(:firm).distinct.count('companies.id')
O
oleg dashevskii 已提交
441 442
  end

443 444 445 446 447 448 449 450 451 452
  def test_count_arel_attribute_in_joined_table_with
    assert_equal 5, Account.joins(:firm).count(Company.arel_table[:id])
    assert_equal 4, Account.joins(:firm).distinct.count(Company.arel_table[:id])
  end

  def test_count_selected_arel_attribute_in_joined_table
    assert_equal 5, Account.joins(:firm).select(Company.arel_table[:id]).count
    assert_equal 4, Account.joins(:firm).distinct.select(Company.arel_table[:id]).count
  end

O
oleg dashevskii 已提交
453
  def test_should_count_field_in_joined_table_with_group_by
454
    c = Account.group('accounts.firm_id').joins(:firm).count('companies.id')
O
oleg dashevskii 已提交
455 456 457 458

    [1,6,2,9].each { |firm_id| assert c.keys.include?(firm_id) }
  end

459 460 461 462 463
  def test_should_count_field_of_root_table_with_conflicting_group_by_column
    assert_equal({ 1 => 1 }, Firm.joins(:accounts).group(:firm_id).count)
    assert_equal({ 1 => 1 }, Firm.joins(:accounts).group('accounts.firm_id').count)
  end

464 465 466 467 468 469 470
  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
471

L
Lauro Caetano 已提交
472 473 474 475 476 477 478 479 480 481 482 483 484 485
  def test_count_with_order
    assert_equal 6, Account.order(:credit_limit).count
  end

  def test_count_with_reverse_order
    assert_equal 6, Account.order(:credit_limit).reverse_order.count
  end

  def test_count_with_where_and_order
    assert_equal 1, Account.where(firm_name: '37signals').count
    assert_equal 1, Account.where(firm_name: '37signals').order(:firm_name).count
    assert_equal 1, Account.where(firm_name: '37signals').order(:firm_name).reverse_order.count
  end

486 487 488 489
  def test_count_with_block
    assert_equal 4, Account.count { |account| account.credit_limit.modulo(10).zero? }
  end

490
  def test_should_sum_expression
491 492 493 494
    # Oracle adapter returns floating point value 636.0 after SUM
    if current_adapter?(:OracleAdapter)
      assert_equal 636, Account.sum("2 * credit_limit")
    else
495
      assert_equal 636, Account.sum("2 * credit_limit").to_i
496
    end
497
  end
498

499 500 501 502
  def test_sum_expression_returns_zero_when_no_records_to_sum
    assert_equal 0, Account.where('1 = 2').sum("2 * credit_limit")
  end

503
  def test_count_with_from_option
J
Jon Leighton 已提交
504 505 506 507 508
    assert_equal Company.count(:all), Company.from('companies').count(:all)
    assert_equal Account.where("credit_limit = 50").count(:all),
        Account.from('accounts').where("credit_limit = 50").count(:all)
    assert_equal Company.where(:type => "Firm").count(:type),
        Company.where(:type => "Firm").from('companies').count(:type)
509 510 511
  end

  def test_sum_with_from_option
J
Jon Leighton 已提交
512 513 514
    assert_equal Account.sum(:credit_limit), Account.from('accounts').sum(:credit_limit)
    assert_equal Account.where("credit_limit > 50").sum(:credit_limit),
        Account.where("credit_limit > 50").from('accounts').sum(:credit_limit)
515 516 517
  end

  def test_average_with_from_option
J
Jon Leighton 已提交
518 519 520
    assert_equal Account.average(:credit_limit), Account.from('accounts').average(:credit_limit)
    assert_equal Account.where("credit_limit > 50").average(:credit_limit),
        Account.where("credit_limit > 50").from('accounts').average(:credit_limit)
521 522 523
  end

  def test_minimum_with_from_option
J
Jon Leighton 已提交
524 525 526
    assert_equal Account.minimum(:credit_limit), Account.from('accounts').minimum(:credit_limit)
    assert_equal Account.where("credit_limit > 50").minimum(:credit_limit),
        Account.where("credit_limit > 50").from('accounts').minimum(:credit_limit)
527 528 529
  end

  def test_maximum_with_from_option
J
Jon Leighton 已提交
530 531 532
    assert_equal Account.maximum(:credit_limit), Account.from('accounts').maximum(:credit_limit)
    assert_equal Account.where("credit_limit > 50").maximum(:credit_limit),
        Account.where("credit_limit > 50").from('accounts').maximum(:credit_limit)
533 534
  end

535 536 537
  def test_maximum_with_not_auto_table_name_prefix_if_column_included
    Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])

A
Aaron Patterson 已提交
538
    assert_equal 7, Company.includes(:contracts).maximum(:developer_id)
539 540 541 542 543
  end

  def test_minimum_with_not_auto_table_name_prefix_if_column_included
    Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])

A
Aaron Patterson 已提交
544
    assert_equal 7, Company.includes(:contracts).minimum(:developer_id)
545 546 547 548 549
  end

  def test_sum_with_not_auto_table_name_prefix_if_column_included
    Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])

A
Aaron Patterson 已提交
550
    assert_equal 7, Company.includes(:contracts).sum(:developer_id)
551 552
  end

R
Ryuta Kamizono 已提交
553 554
  if current_adapter?(:Mysql2Adapter)
    def test_from_option_with_specified_index
J
Jon Leighton 已提交
555 556 557
      assert_equal Edge.count(:all), Edge.from('edges USE INDEX(unique_edge_index)').count(:all)
      assert_equal Edge.where('sink_id < 5').count(:all),
          Edge.from('edges USE INDEX(unique_edge_index)').where('sink_id < 5').count(:all)
558 559 560 561
    end
  end

  def test_from_option_with_table_different_than_class
J
Jon Leighton 已提交
562
    assert_equal Account.count(:all), Company.from('accounts').count(:all)
563
  end
564 565 566 567

  def test_distinct_is_honored_when_used_with_count_operation_after_group
    # Count the number of authors for approved topics
    approved_topics_count = Topic.group(:approved).count(:author_name)[true]
568
    assert_equal approved_topics_count, 4
569
    # Count the number of distinct authors for approved Topics
570
    distinct_authors_for_approved_count = Topic.group(:approved).distinct.count(:author_name)[true]
571
    assert_equal distinct_authors_for_approved_count, 3
572
  end
573 574

  def test_pluck
575
    assert_equal [1,2,3,4,5], Topic.order(:id).pluck(:id)
576 577
  end

578 579 580 581 582
  def test_pluck_without_column_names
    assert_equal [[1, "Firm", 1, nil, "37signals", nil, 1, nil, ""]],
      Company.order(:id).limit(1).pluck
  end

583 584 585 586 587 588 589 590
  def test_pluck_type_cast
    topic = topics(:first)
    relation = Topic.where(:id => topic.id)
    assert_equal [ topic.approved ], relation.pluck(:approved)
    assert_equal [ topic.last_read ], relation.pluck(:last_read)
    assert_equal [ topic.written_on ], relation.pluck(:written_on)
  end

591 592
  def test_pluck_and_distinct
    assert_equal [50, 53, 55, 60], Account.order(:credit_limit).distinct.pluck(:credit_limit)
593 594 595 596 597 598 599 600
  end

  def test_pluck_in_relation
    company = Company.first
    contract = company.contracts.create!
    assert_equal [contract.id], company.contracts.pluck(:id)
  end

601 602 603 604
  def test_pluck_on_aliased_attribute
    assert_equal 'The First Topic', Topic.order(:id).pluck(:heading).first
  end

605 606 607 608 609 610
  def test_pluck_with_serialization
    t = Topic.create!(:content => { :foo => :bar })
    assert_equal [{:foo => :bar}], Topic.where(:id => t.id).pluck(:content)
  end

  def test_pluck_with_qualified_column_name
611
    assert_equal [1,2,3,4,5], Topic.order(:id).pluck("topics.id")
612
  end
613 614 615 616 617 618

  def test_pluck_auto_table_name_prefix
    c = Company.create!(:name => "test", :contracts => [Contract.new])
    assert_equal [c.id], Company.joins(:contracts).pluck(:id)
  end

619 620 621 622 623
  def test_pluck_if_table_included
    c = Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
    assert_equal [c.id], Company.includes(:contracts).where("contracts.id" => c.contracts.first).pluck(:id)
  end

624
  def test_pluck_not_auto_table_name_prefix_if_column_joined
625
    Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
626
    assert_equal [7], Company.joins(:contracts).pluck(:developer_id)
627
  end
T
twinturbo 已提交
628

629 630
  def test_pluck_with_selection_clause
    assert_equal [50, 53, 55, 60], Account.pluck('DISTINCT credit_limit').sort
631 632
    assert_equal [50, 53, 55, 60], Account.pluck('DISTINCT accounts.credit_limit').sort
    assert_equal [50, 53, 55, 60], Account.pluck('DISTINCT(credit_limit)').sort
633 634 635 636 637

    # MySQL returns "SUM(DISTINCT(credit_limit))" as the column name unless
    # an alias is provided.  Without the alias, the column cannot be found
    # and properly typecast.
    assert_equal [50 + 53 + 55 + 60], Account.pluck('SUM(DISTINCT(credit_limit)) as credit_limit')
638 639
  end

T
twinturbo 已提交
640
  def test_plucks_with_ids
J
Jon Leighton 已提交
641
    assert_equal Company.all.map(&:id).sort, Company.ids.sort
T
twinturbo 已提交
642
  end
643

644 645 646 647 648
  def test_pluck_with_includes_limit_and_empty_result
    assert_equal [], Topic.includes(:replies).limit(0).pluck(:id)
    assert_equal [], Topic.includes(:replies).limit(1).where('0 = 1').pluck(:id)
  end

649 650 651 652 653 654
  def test_pluck_not_auto_table_name_prefix_if_column_included
    Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
    ids = Company.includes(:contracts).pluck(:developer_id)
    assert_equal Company.count, ids.length
    assert_equal [7], ids.compact
  end
655 656 657 658

  def test_pluck_multiple_columns
    assert_equal [
      [1, "The First Topic"], [2, "The Second Topic of the day"],
659 660
      [3, "The Third Topic of the day"], [4, "The Fourth Topic of the day"],
      [5, "The Fifth Topic of the day"]
661
    ], Topic.order(:id).pluck(:id, :title)
662 663
    assert_equal [
      [1, "The First Topic", "David"], [2, "The Second Topic of the day", "Mary"],
664 665
      [3, "The Third Topic of the day", "Carl"], [4, "The Fourth Topic of the day", "Carl"],
      [5, "The Fifth Topic of the day", "Jason"]
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
    ], Topic.order(:id).pluck(:id, :title, :author_name)
  end

  def test_pluck_with_multiple_columns_and_selection_clause
    assert_equal [[1, 50], [2, 50], [3, 50], [4, 60], [5, 55], [6, 53]],
      Account.pluck('id, credit_limit')
  end

  def test_pluck_with_multiple_columns_and_includes
    Company.create!(:name => "test", :contracts => [Contract.new(:developer_id => 7)])
    companies_and_developers = Company.order('companies.id').includes(:contracts).pluck(:name, :developer_id)

    assert_equal Company.count, companies_and_developers.length
    assert_equal ["37signals", nil], companies_and_developers.first
    assert_equal ["test", 7], companies_and_developers.last
681
  end
682 683 684 685 686 687

  def test_pluck_with_reserved_words
    Possession.create!(:where => "Over There")

    assert_equal ["Over There"], Possession.pluck(:where)
  end
688 689 690

  def test_pluck_replaces_select_clause
    taks_relation = Topic.select(:approved, :id).order(:id)
691 692
    assert_equal [1,2,3,4,5], taks_relation.pluck(:id)
    assert_equal [false, true, true, true, true], taks_relation.pluck(:approved)
693
  end
694 695 696 697 698 699 700

  def test_pluck_columns_with_same_name
    expected = [["The First Topic", "The Second Topic of the day"], ["The Third Topic of the day", "The Fourth Topic of the day"]]
    actual = Topic.joins(:replies)
      .pluck('topics.title', 'replies_topics.title')
    assert_equal expected, actual
  end
701 702

  def test_calculation_with_polymorphic_relation
703 704 705 706
    part = ShipPart.create!(name: "has trinket")
    part.trinkets.create!

    assert_equal part.id, ShipPart.joins(:trinkets).sum(:id)
707 708
  end

S
Sean Griffin 已提交
709 710 711 712 713 714 715
  def test_pluck_joined_with_polymorphic_relation
    part = ShipPart.create!(name: "has trinket")
    part.trinkets.create!

    assert_equal [part.id], ShipPart.joins(:trinkets).pluck(:id)
  end

716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
  def test_pluck_loaded_relation
    companies = Company.order(:id).limit(3).load
    assert_no_queries do
      assert_equal ['37signals', 'Summit', 'Microsoft'], companies.pluck(:name)
    end
  end

  def test_pluck_loaded_relation_multiple_columns
    companies = Company.order(:id).limit(3).load
    assert_no_queries do
      assert_equal [[1, '37signals'], [2, 'Summit'], [3, 'Microsoft']], companies.pluck(:id, :name)
    end
  end

  def test_pluck_loaded_relation_sql_fragment
731
    companies = Company.order(:name).limit(3).load
732
    assert_queries 1 do
733
      assert_equal ['37signals', 'Apex', 'Ex Nihilo'], companies.pluck('DISTINCT name')
734 735 736
    end
  end

737 738 739 740 741 742
  def test_grouped_calculation_with_polymorphic_relation
    part = ShipPart.create!(name: "has trinket")
    part.trinkets.create!

    assert_equal({ "has trinket" => part.id }, ShipPart.joins(:trinkets).group("ship_parts.name").sum(:id))
  end
743 744 745 746 747

  def test_calculation_grouped_by_association_doesnt_error_when_no_records_have_association
    Client.update_all(client_of: nil)
    assert_equal({ nil => Client.count }, Client.group(:firm).count)
  end
748 749

  def test_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association
750
    assert_nothing_raised do
751 752 753 754
      developer = Developer.create!(name: 'developer')
      developer.ratings.includes(comment: :post).where(posts: { id: 1 }).count
    end
  end
755 756 757 758 759 760 761 762 763 764

  def test_sum_uses_enumerable_version_when_block_is_given
    block_called = false
    relation = Client.all.load

    assert_no_queries do
      assert_equal 0, relation.sum { block_called = true; 0 }
    end
    assert block_called
  end
765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796

  def test_having_with_strong_parameters
    protected_params = Class.new do
      attr_reader :permitted
      alias :permitted? :permitted

      def initialize(parameters)
        @parameters = parameters
        @permitted = false
      end

      def to_h
        @parameters
      end

      def permit!
        @permitted = true
        self
      end
    end

    params = protected_params.new(credit_limit: '50')

    assert_raises(ActiveModel::ForbiddenAttributesError) do
      Account.group(:id).having(params)
    end

    result = Account.group(:id).having(params.permit!)
    assert_equal 50, result[0].credit_limit
    assert_equal 50, result[1].credit_limit
    assert_equal 50, result[2].credit_limit
  end
797 798

  def test_group_by_attribute_with_custom_type
R
Ryuta Kamizono 已提交
799
    assert_equal({ "proposed" => 2, "published" => 2 }, Book.group(:status).count)
800
  end
801
end