calculations_test.rb 32.6 KB
Newer Older
1 2
# frozen_string_literal: true

3
require "cases/helper"
4
require "models/book"
5 6
require "models/club"
require "models/company"
7
require "models/contract"
8 9 10 11 12
require "models/edge"
require "models/organization"
require "models/possession"
require "models/topic"
require "models/reply"
13
require "models/numeric_data"
14 15 16 17 18
require "models/minivan"
require "models/speedometer"
require "models/ship_part"
require "models/treasure"
require "models/developer"
19
require "models/post"
20 21
require "models/comment"
require "models/rating"
22

23
class CalculationsTest < ActiveRecord::TestCase
24
  fixtures :companies, :accounts, :topics, :speedometers, :minivans, :books, :posts, :comments
25 26

  def test_should_sum_field
27
    assert_equal 318, Account.sum(:credit_limit)
28 29
  end

30 31 32 33
  def test_should_sum_arel_attribute
    assert_equal 318, Account.sum(Account.arel_table[:credit_limit])
  end

34 35
  def test_should_average_field
    value = Account.average(:credit_limit)
36
    assert_equal 53.0, value
37 38
  end

39 40 41 42 43
  def test_should_average_arel_attribute
    value = Account.average(Account.arel_table[:credit_limit])
    assert_equal 53.0, value
  end

44 45 46 47
  def test_should_resolve_aliased_attributes
    assert_equal 318, Account.sum(:available_credit)
  end

48 49 50 51 52
  def test_should_return_decimal_average_of_integer_field
    value = Account.average(:id)
    assert_equal 3.5, value
  end

53
  def test_should_return_integer_average_if_db_returns_such
A
Aaron Patterson 已提交
54
    ShipPart.delete_all
55
    ShipPart.create!(id: 3, name: "foo")
A
Aaron Patterson 已提交
56
    value = ShipPart.average(:id)
57 58 59
    assert_equal 3, value
  end

60 61 62 63 64 65
  def test_should_return_nil_to_d_as_average
    if nil.respond_to?(:to_d)
      assert_equal BigDecimal(0), NumericData.average(:bank_balance)
    else
      assert_nil NumericData.average(:bank_balance)
    end
66
  end
67

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

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

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

80 81 82 83
  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

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

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

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

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

106
  def test_should_group_by_multiple_fields
107
    c = Account.group("firm_id", :credit_limit).count(:all)
108
    [ [nil, 50], [1, 50], [6, 50], [6, 55], [9, 53], [2, 60] ].each { |firm_and_limit| assert_includes c.keys, firm_and_limit }
109 110 111
  end

  def test_should_group_by_multiple_fields_having_functions
112
    c = Topic.group(:author_name, "COALESCE(type, title)").count(:all)
113 114 115 116 117
    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
118 119

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

126
  def test_should_generate_valid_sql_with_joins_and_group
127
    assert_nothing_raised do
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
      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

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

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

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

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

167
  def test_limit_should_apply_before_count
168
    accounts = Account.limit(4)
169 170 171 172 173

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

174
  def test_limit_should_apply_before_count_arel_attribute
175
    accounts = Account.limit(4)
176 177 178 179 180 181

    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

182 183 184 185 186 187 188
  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 已提交
189 190
    return if current_adapter?(:OracleAdapter)

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

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

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

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

    queries = assert_sql { Account.limit(1).offset(1).count }
208
    assert_equal 1, queries.length
209 210
    assert_match(/LIMIT/, queries.first)
    assert_match(/OFFSET/, queries.first)
211 212 213 214 215 216 217 218 219
  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

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

G
Gannon McGibbon 已提交
225 226
    assert_match %r{accounts}i, e.sql
    assert_match "credit_limit, firm_name", e.sql
227 228
  end

229 230 231 232 233 234 235
  def test_apply_distinct_in_count
    queries = assert_sql do
      Account.distinct.count
      Account.group(:firm_id).distinct.count
    end

    queries.each do |query|
236 237 238
      # `table_alias_length` in `column_alias_for` would execute
      # "SHOW max_identifier_length" statement in PostgreSQL adapter.
      next if query == "SHOW max_identifier_length"
239 240 241 242
      assert_match %r{\ASELECT(?! DISTINCT) COUNT\(DISTINCT\b}, query
    end
  end

243 244 245 246 247 248
  def test_count_with_eager_loading_and_custom_order
    posts = Post.includes(:comments).order("comments.id")
    assert_queries(1) { assert_equal 11, posts.count }
    assert_queries(1) { assert_equal 11, posts.count(:all) }
  end

249 250 251 252 253 254
  def test_count_with_eager_loading_and_custom_order_and_distinct
    posts = Post.includes(:comments).order("comments.id").distinct
    assert_queries(1) { assert_equal 11, posts.count }
    assert_queries(1) { assert_equal 11, posts.count(:all) }
  end

255 256 257 258 259 260
  def test_distinct_count_all_with_custom_select_and_order
    accounts = Account.distinct.select("credit_limit % 10").order(Arel.sql("credit_limit % 10"))
    assert_queries(1) { assert_equal 3, accounts.count(:all) }
    assert_queries(1) { assert_equal 3, accounts.load.size }
  end

261 262 263 264 265 266 267 268 269 270 271 272
  def test_distinct_count_with_order_and_limit
    assert_equal 4, Account.distinct.order(:firm_id).limit(4).count
  end

  def test_distinct_count_with_order_and_offset
    assert_equal 4, Account.distinct.order(:firm_id).offset(2).count
  end

  def test_distinct_count_with_order_and_limit_and_offset
    assert_equal 4, Account.distinct.order(:firm_id).limit(4).offset(2).count
  end

273 274 275 276 277 278 279 280 281 282 283 284
  def test_distinct_joins_count_with_order_and_limit
    assert_equal 3, Account.joins(:firm).distinct.order(:firm_id).limit(3).count
  end

  def test_distinct_joins_count_with_order_and_offset
    assert_equal 3, Account.joins(:firm).distinct.order(:firm_id).offset(2).count
  end

  def test_distinct_joins_count_with_order_and_limit_and_offset
    assert_equal 3, Account.joins(:firm).distinct.order(:firm_id).limit(3).offset(2).count
  end

285 286 287 288
  def test_distinct_count_with_group_by_and_order_and_limit
    assert_equal({ 6 => 2 }, Account.group(:firm_id).distinct.order("1 DESC").limit(1).count)
  end

289
  def test_should_group_by_summed_field_having_condition
290
    c = Account.group(:firm_id).having("sum(credit_limit) > 50").sum(:credit_limit)
291 292 293 294 295
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
  end

296
  def test_should_group_by_summed_field_having_condition_from_select
297
    skip unless current_adapter?(:Mysql2Adapter, :SQLite3Adapter)
298
    c = Account.select("MIN(credit_limit) AS min_credit_limit").group(:firm_id).having("min_credit_limit > 50").sum(:credit_limit)
299 300 301 302 303
    assert_nil       c[1]
    assert_equal 60, c[2]
    assert_equal 53, c[9]
  end

304
  def test_should_group_by_summed_association
J
Jon Leighton 已提交
305
    c = Account.group(:firm).sum(:credit_limit)
306 307 308 309
    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 已提交
310

311
  def test_should_sum_field_with_conditions
312
    assert_equal 105, Account.where("firm_id = 6").sum(:credit_limit)
313 314
  end

315
  def test_should_return_zero_if_sum_conditions_return_nothing
316 317
    assert_equal 0, Account.where("1 = 2").sum(:credit_limit)
    assert_equal 0, companies(:rails_core).companies.where("1 = 2").sum(:id)
318 319
  end

320
  def test_sum_should_return_valid_values_for_decimals
321
    NumericData.create(bank_balance: 19.83)
322 323 324
    assert_equal 19.83, NumericData.sum(:bank_balance)
  end

325
  def test_should_return_type_casted_values_with_group_and_expression
326
    assert_equal 0.5, Account.group(:firm_name).sum("0.01 * credit_limit")["37signals"]
327 328
  end

329
  def test_should_group_by_summed_field_with_conditions
330
    c = Account.where("firm_id > 1").group(:firm_id).sum(:credit_limit)
331 332 333
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
334
  end
J
Jeremy Kemper 已提交
335

336
  def test_should_group_by_summed_field_with_conditions_and_having
337 338
    c = Account.where("firm_id > 1").group(:firm_id).
     having("sum(credit_limit) > 60").sum(:credit_limit)
339 340 341
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_nil        c[2]
342 343 344
  end

  def test_should_group_by_fields_with_table_alias
345
    c = Account.group("accounts.firm_id").sum(:credit_limit)
346 347 348
    assert_equal 50,  c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
349
  end
J
Jeremy Kemper 已提交
350

351
  def test_should_calculate_with_invalid_field
352
    assert_equal 6, Account.calculate(:count, "*")
353
    assert_equal 6, Account.calculate(:count, :all)
354
  end
J
Jeremy Kemper 已提交
355

356
  def test_should_calculate_grouped_with_invalid_field
357
    c = Account.group("accounts.firm_id").count(:all)
358 359 360
    assert_equal 1, c[1]
    assert_equal 2, c[6]
    assert_equal 1, c[2]
361
  end
J
Jeremy Kemper 已提交
362

363
  def test_should_calculate_grouped_association_with_invalid_field
J
Jon Leighton 已提交
364
    c = Account.group(:firm).count(:all)
365 366 367 368
    assert_equal 1, c[companies(:first_firm)]
    assert_equal 2, c[companies(:rails_core)]
    assert_equal 1, c[companies(:first_client)]
  end
369

370
  def test_should_group_by_association_with_non_numeric_foreign_key
371 372
    Speedometer.create! id: "ABC"
    Minivan.create! id: "OMG", speedometer_id: "ABC"
373

374
    c = Minivan.group(:speedometer).count(:all)
375
    first_key = c.keys.first
376
    assert_equal Speedometer, first_key.class
377
    assert_equal 1, c[first_key]
378
  end
J
Jeremy Kemper 已提交
379

380
  def test_should_calculate_grouped_association_with_foreign_key_option
381
    Account.belongs_to :another_firm, class_name: "Firm", foreign_key: "firm_id"
J
Jon Leighton 已提交
382
    c = Account.group(:another_firm).count(:all)
383 384 385 386 387
    assert_equal 1, c[companies(:first_firm)]
    assert_equal 2, c[companies(:rails_core)]
    assert_equal 1, c[companies(:first_client)]
  end

388
  def test_should_calculate_grouped_by_function
J
Jon Leighton 已提交
389
    c = Company.group("UPPER(#{QUOTED_TYPE})").count(:all)
390
    assert_equal 2, c[nil]
391 392 393
    assert_equal 1, c["DEPENDENTFIRM"]
    assert_equal 5, c["CLIENT"]
    assert_equal 2, c["FIRM"]
394
  end
J
Jeremy Kemper 已提交
395

396
  def test_should_calculate_grouped_by_function_with_table_alias
J
Jon Leighton 已提交
397
    c = Company.group("UPPER(companies.#{QUOTED_TYPE})").count(:all)
398
    assert_equal 2, c[nil]
399 400 401
    assert_equal 1, c["DEPENDENTFIRM"]
    assert_equal 5, c["CLIENT"]
    assert_equal 2, c["FIRM"]
402
  end
J
Jeremy Kemper 已提交
403

404 405 406
  def test_should_not_overshadow_enumerable_sum
    assert_equal 6, [1, 2, 3].sum(&:abs)
  end
407 408 409 410 411

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

412 413 414 415
  def test_should_sum_scoped_field_with_from
    assert_equal Club.count, Organization.clubs.count
  end

416
  def test_should_sum_scoped_field_with_conditions
417
    assert_equal 8,  companies(:rails_core).companies.where("id > 7").sum(:id)
418 419 420
  end

  def test_should_group_by_scoped_field
J
Jon Leighton 已提交
421
    c = companies(:rails_core).companies.group(:name).sum(:id)
422 423
    assert_equal 7, c["Leetsoft"]
    assert_equal 8, c["Jadedpixel"]
424 425
  end

E
Emilio Tagua 已提交
426
  def test_should_group_by_summed_field_through_association_and_having
427 428 429
    c = companies(:rails_core).companies.group(:name).having("sum(id) > 7").sum(:id)
    assert_nil      c["Leetsoft"]
    assert_equal 8, c["Jadedpixel"]
430
  end
431

432
  def test_should_count_selected_field_with_include
433 434
    assert_equal 6, Account.includes(:firm).distinct.count
    assert_equal 4, Account.includes(:firm).distinct.select(:credit_limit).count
435
  end
J
Jeremy Kemper 已提交
436

437 438 439 440 441 442 443
  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
444
    joined_count = Account.includes(:firm).where(companies: { name: "37signals" }).count
445 446 447
    assert_equal 1, joined_count
  end

448
  def test_should_count_scoped_select
449
    Account.update_all("credit_limit = NULL")
450
    assert_equal 0, Account.select("credit_limit").count
451 452 453
  end

  def test_should_count_scoped_select_with_options
454
    Account.update_all("credit_limit = NULL")
455 456
    Account.last.update_columns("credit_limit" => 49)
    Account.first.update_columns("credit_limit" => 51)
457

458
    assert_equal 1, Account.select("credit_limit").where("credit_limit >= 50").count
459 460
  end

461
  def test_should_count_manual_select_with_include
462
    assert_equal 6, Account.select("DISTINCT accounts.id").includes(:firm).count
463 464
  end

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

470 471 472
  def test_count_with_column_parameter
    assert_equal 5, Account.count(:firm_id)
  end
J
Jeremy Kemper 已提交
473

474 475 476 477 478 479 480 481
  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

482 483
  def test_count_with_distinct
    assert_equal 4, Account.select(:credit_limit).distinct.count
484 485
  end

486 487 488 489
  def test_count_with_aliased_attribute
    assert_equal 6, Account.count(:available_credit)
  end

490
  def test_count_with_column_and_options_parameter
J
Jon Leighton 已提交
491
    assert_equal 2, Account.where("credit_limit = 50 AND firm_id IS NOT NULL").count(:firm_id)
492
  end
J
Jeremy Kemper 已提交
493

O
oleg dashevskii 已提交
494
  def test_should_count_field_in_joined_table
495 496
    assert_equal 5, Account.joins(:firm).count("companies.id")
    assert_equal 4, Account.joins(:firm).distinct.count("companies.id")
O
oleg dashevskii 已提交
497 498
  end

499 500 501 502 503 504 505 506 507 508
  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 已提交
509
  def test_should_count_field_in_joined_table_with_group_by
510
    c = Account.group("accounts.firm_id").joins(:firm).count("companies.id")
O
oleg dashevskii 已提交
511

512
    [1, 6, 2, 9].each { |firm_id| assert_includes c.keys, firm_id }
O
oleg dashevskii 已提交
513 514
  end

515 516
  def test_should_count_field_of_root_table_with_conflicting_group_by_column
    assert_equal({ 1 => 1 }, Firm.joins(:accounts).group(:firm_id).count)
517
    assert_equal({ 1 => 1 }, Firm.joins(:accounts).group("accounts.firm_id").count)
518 519
  end

520 521 522 523 524 525 526
  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
527

L
Lauro Caetano 已提交
528 529 530 531 532 533 534 535 536
  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
537 538 539
    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
L
Lauro Caetano 已提交
540 541
  end

542 543 544 545
  def test_count_with_block
    assert_equal 4, Account.count { |account| account.credit_limit.modulo(10).zero? }
  end

546
  def test_should_sum_expression
547
    if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter, :OracleAdapter)
548 549
      assert_equal 636, Account.sum("2 * credit_limit")
    else
550
      assert_equal 636, Account.sum("2 * credit_limit").to_i
551
    end
552
  end
553

554
  def test_sum_expression_returns_zero_when_no_records_to_sum
555
    assert_equal 0, Account.where("1 = 2").sum("2 * credit_limit")
556 557
  end

558
  def test_count_with_from_option
559
    assert_equal Company.count(:all), Company.from("companies").count(:all)
J
Jon Leighton 已提交
560
    assert_equal Account.where("credit_limit = 50").count(:all),
561
        Account.from("accounts").where("credit_limit = 50").count(:all)
562 563
    assert_equal Company.where(type: "Firm").count(:type),
        Company.where(type: "Firm").from("companies").count(:type)
564 565 566
  end

  def test_sum_with_from_option
567
    assert_equal Account.sum(:credit_limit), Account.from("accounts").sum(:credit_limit)
J
Jon Leighton 已提交
568
    assert_equal Account.where("credit_limit > 50").sum(:credit_limit),
569
        Account.where("credit_limit > 50").from("accounts").sum(:credit_limit)
570 571 572
  end

  def test_average_with_from_option
573
    assert_equal Account.average(:credit_limit), Account.from("accounts").average(:credit_limit)
J
Jon Leighton 已提交
574
    assert_equal Account.where("credit_limit > 50").average(:credit_limit),
575
        Account.where("credit_limit > 50").from("accounts").average(:credit_limit)
576 577 578
  end

  def test_minimum_with_from_option
579
    assert_equal Account.minimum(:credit_limit), Account.from("accounts").minimum(:credit_limit)
J
Jon Leighton 已提交
580
    assert_equal Account.where("credit_limit > 50").minimum(:credit_limit),
581
        Account.where("credit_limit > 50").from("accounts").minimum(:credit_limit)
582 583 584
  end

  def test_maximum_with_from_option
585
    assert_equal Account.maximum(:credit_limit), Account.from("accounts").maximum(:credit_limit)
J
Jon Leighton 已提交
586
    assert_equal Account.where("credit_limit > 50").maximum(:credit_limit),
587
        Account.where("credit_limit > 50").from("accounts").maximum(:credit_limit)
588 589
  end

590
  def test_maximum_with_not_auto_table_name_prefix_if_column_included
591
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
592

A
Aaron Patterson 已提交
593
    assert_equal 7, Company.includes(:contracts).maximum(:developer_id)
594 595 596
  end

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

A
Aaron Patterson 已提交
599
    assert_equal 7, Company.includes(:contracts).minimum(:developer_id)
600 601 602
  end

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

A
Aaron Patterson 已提交
605
    assert_equal 7, Company.includes(:contracts).sum(:developer_id)
606 607
  end

R
Ryuta Kamizono 已提交
608 609
  if current_adapter?(:Mysql2Adapter)
    def test_from_option_with_specified_index
610 611 612
      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)
613 614 615 616
    end
  end

  def test_from_option_with_table_different_than_class
617
    assert_equal Account.count(:all), Company.from("accounts").count(:all)
618
  end
619 620 621 622

  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]
623
    assert_equal approved_topics_count, 4
624
    # Count the number of distinct authors for approved Topics
625
    distinct_authors_for_approved_count = Topic.group(:approved).distinct.count(:author_name)[true]
626
    assert_equal distinct_authors_for_approved_count, 3
627
  end
628 629

  def test_pluck
630
    assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck(:id)
631 632
  end

633
  def test_pluck_without_column_names
634 635 636 637 638
    if current_adapter?(:OracleAdapter)
      assert_equal [[1, "Firm", 1, nil, "37signals", nil, 1, nil, nil]], Company.order(:id).limit(1).pluck
    else
      assert_equal [[1, "Firm", 1, nil, "37signals", nil, 1, nil, ""]], Company.order(:id).limit(1).pluck
    end
639 640
  end

641 642
  def test_pluck_type_cast
    topic = topics(:first)
643
    relation = Topic.where(id: topic.id)
644 645 646 647 648
    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

649 650 651 652 653 654 655 656 657 658 659 660
  def test_pluck_with_type_cast_does_not_corrupt_the_query_cache
    topic = topics(:first)
    relation = Topic.where(id: topic.id)
    assert_queries 1 do
      Topic.cache do
        kind = relation.select(:written_on).load.first.read_attribute_before_type_cast(:written_on).class
        relation.pluck(:written_on)
        assert_kind_of kind, relation.select(:written_on).load.first.read_attribute_before_type_cast(:written_on)
      end
    end
  end

661 662
  def test_pluck_and_distinct
    assert_equal [50, 53, 55, 60], Account.order(:credit_limit).distinct.pluck(:credit_limit)
663 664 665 666 667 668 669 670
  end

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

671
  def test_pluck_on_aliased_attribute
672
    assert_equal "The First Topic", Topic.order(:id).pluck(:heading).first
673 674
  end

675
  def test_pluck_with_serialization
676
    t = Topic.create!(content: { foo: :bar })
677
    assert_equal [{ foo: :bar }], Topic.where(id: t.id).pluck(:content)
678 679 680
  end

  def test_pluck_with_qualified_column_name
B
Ben Toews 已提交
681
    assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck("topics.id")
682
  end
683 684

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

689
  def test_pluck_if_table_included
690
    c = Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
691 692 693
    assert_equal [c.id], Company.includes(:contracts).where("contracts.id" => c.contracts.first).pluck(:id)
  end

694
  def test_pluck_not_auto_table_name_prefix_if_column_joined
695
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
B
Ben Toews 已提交
696
    assert_equal [7], Company.joins(:contracts).pluck(:developer_id)
697
  end
T
twinturbo 已提交
698

699
  def test_pluck_with_selection_clause
B
Ben Toews 已提交
700 701 702
    assert_equal [50, 53, 55, 60], Account.pluck(Arel.sql("DISTINCT credit_limit")).sort
    assert_equal [50, 53, 55, 60], Account.pluck(Arel.sql("DISTINCT accounts.credit_limit")).sort
    assert_equal [50, 53, 55, 60], Account.pluck(Arel.sql("DISTINCT(credit_limit)")).sort
703 704 705 706

    # 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.
B
Ben Toews 已提交
707
    assert_equal [50 + 53 + 55 + 60], Account.pluck(Arel.sql("SUM(DISTINCT(credit_limit)) as credit_limit"))
708 709
  end

T
twinturbo 已提交
710
  def test_plucks_with_ids
J
Jon Leighton 已提交
711
    assert_equal Company.all.map(&:id).sort, Company.ids.sort
T
twinturbo 已提交
712
  end
713

714 715
  def test_pluck_with_includes_limit_and_empty_result
    assert_equal [], Topic.includes(:replies).limit(0).pluck(:id)
716
    assert_equal [], Topic.includes(:replies).limit(1).where("0 = 1").pluck(:id)
717 718
  end

719 720 721 722 723
  def test_pluck_with_includes_offset
    assert_equal [5], Topic.includes(:replies).order(:id).offset(4).pluck(:id)
    assert_equal [], Topic.includes(:replies).order(:id).offset(5).pluck(:id)
  end

724 725 726 727
  def test_pluck_with_join
    assert_equal [[2, 2], [4, 4]], Reply.includes(:topic).pluck(:id, :"topics.id")
  end

728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
  def test_group_by_with_limit
    expected = { "Post" => 8, "SpecialPost" => 1 }
    actual = Post.includes(:comments).group(:type).order(:type).limit(2).count("comments.id")
    assert_equal expected, actual
  end

  def test_group_by_with_offset
    expected = { "SpecialPost" => 1, "StiPost" => 2 }
    actual = Post.includes(:comments).group(:type).order(:type).offset(1).count("comments.id")
    assert_equal expected, actual
  end

  def test_group_by_with_limit_and_offset
    expected = { "SpecialPost" => 1 }
    actual = Post.includes(:comments).group(:type).order(:type).offset(1).limit(1).count("comments.id")
    assert_equal expected, actual
  end

746
  def test_pluck_not_auto_table_name_prefix_if_column_included
747
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
B
Ben Toews 已提交
748
    ids = Company.includes(:contracts).pluck(:developer_id)
749 750 751
    assert_equal Company.count, ids.length
    assert_equal [7], ids.compact
  end
752 753 754 755

  def test_pluck_multiple_columns
    assert_equal [
      [1, "The First Topic"], [2, "The Second Topic of the day"],
756 757
      [3, "The Third Topic of the day"], [4, "The Fourth Topic of the day"],
      [5, "The Fifth Topic of the day"]
758
    ], Topic.order(:id).pluck(:id, :title)
759 760
    assert_equal [
      [1, "The First Topic", "David"], [2, "The Second Topic of the day", "Mary"],
761 762
      [3, "The Third Topic of the day", "Carl"], [4, "The Fourth Topic of the day", "Carl"],
      [5, "The Fifth Topic of the day", "Jason"]
763 764 765 766 767
    ], 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]],
B
Ben Toews 已提交
768
      Account.pluck("id, credit_limit")
769 770 771
  end

  def test_pluck_with_multiple_columns_and_includes
772
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
B
Ben Toews 已提交
773
    companies_and_developers = Company.order("companies.id").includes(:contracts).pluck(:name, :developer_id)
774 775 776 777

    assert_equal Company.count, companies_and_developers.length
    assert_equal ["37signals", nil], companies_and_developers.first
    assert_equal ["test", 7], companies_and_developers.last
778
  end
779 780

  def test_pluck_with_reserved_words
781
    Possession.create!(where: "Over There")
782 783 784

    assert_equal ["Over There"], Possession.pluck(:where)
  end
785 786 787

  def test_pluck_replaces_select_clause
    taks_relation = Topic.select(:approved, :id).order(:id)
788
    assert_equal [1, 2, 3, 4, 5], taks_relation.pluck(:id)
789
    assert_equal [false, true, true, true, true], taks_relation.pluck(:approved)
790
  end
791 792 793 794

  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)
B
Ben Toews 已提交
795
      .pluck("topics.title", "replies_topics.title")
796 797
    assert_equal expected, actual
  end
798 799

  def test_calculation_with_polymorphic_relation
800 801 802 803
    part = ShipPart.create!(name: "has trinket")
    part.trinkets.create!

    assert_equal part.id, ShipPart.joins(:trinkets).sum(:id)
804 805
  end

S
Sean Griffin 已提交
806 807 808 809 810 811 812
  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

813
  def test_pluck_loaded_relation
Y
yuuji.yaginuma 已提交
814
    Company.attribute_names # Load schema information so we don't query below
815
    companies = Company.order(:id).limit(3).load
Y
yuuji.yaginuma 已提交
816

817
    assert_no_queries do
818
      assert_equal ["37signals", "Summit", "Microsoft"], companies.pluck(:name)
819 820 821 822
    end
  end

  def test_pluck_loaded_relation_multiple_columns
Y
yuuji.yaginuma 已提交
823
    Company.attribute_names # Load schema information so we don't query below
824
    companies = Company.order(:id).limit(3).load
Y
yuuji.yaginuma 已提交
825

826
    assert_no_queries do
827
      assert_equal [[1, "37signals"], [2, "Summit"], [3, "Microsoft"]], companies.pluck(:id, :name)
828 829 830 831
    end
  end

  def test_pluck_loaded_relation_sql_fragment
Y
yuuji.yaginuma 已提交
832
    Company.attribute_names # Load schema information so we don't query below
833
    companies = Company.order(:name).limit(3).load
Y
yuuji.yaginuma 已提交
834

835
    assert_queries 1 do
B
Ben Toews 已提交
836
      assert_equal ["37signals", "Apex", "Ex Nihilo"], companies.pluck(Arel.sql("DISTINCT name"))
837 838 839
    end
  end

840 841
  def test_pick_one
    assert_equal "The First Topic", Topic.order(:id).pick(:heading)
842
    assert_nil Topic.none.pick(:heading)
843 844 845 846 847
    assert_nil Topic.where("1=0").pick(:heading)
  end

  def test_pick_two
    assert_equal ["David", "david@loudthinking.com"], Topic.order(:id).pick(:author_name, :author_email_address)
848
    assert_nil Topic.none.pick(:author_name, :author_email_address)
849 850 851
    assert_nil Topic.where("1=0").pick(:author_name, :author_email_address)
  end

Y
Yuji Hanamura 已提交
852 853 854 855 856
  def test_pick_delegate_to_all
    cool_first = minivans(:cool_first)
    assert_equal cool_first.color, Minivan.pick(:color)
  end

857 858 859 860 861 862
  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
863 864 865 866 867

  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
868 869

  def test_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association
870
    assert_nothing_raised do
871
      developer = Developer.create!(name: "developer")
872 873 874
      developer.ratings.includes(comment: :post).where(posts: { id: 1 }).count
    end
  end
875 876 877 878 879 880 881 882 883 884

  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
885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905

  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

906
    params = protected_params.new(credit_limit: "50")
907 908 909 910 911 912 913 914 915 916

    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
917 918

  def test_group_by_attribute_with_custom_type
R
Ryuta Kamizono 已提交
919
    assert_equal({ "proposed" => 2, "published" => 2 }, Book.group(:status).count)
920
  end
921 922 923 924 925 926 927 928 929 930 931 932

  def test_deprecate_count_with_block_and_column_name
    assert_deprecated do
      assert_equal 6, Account.count(:firm_id) { true }
    end
  end

  def test_deprecate_sum_with_block_and_column_name
    assert_deprecated do
      assert_equal 6, Account.sum(:firm_id) { 1 }
    end
  end
933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974

  test "#skip_query_cache! for #pluck" do
    Account.cache do
      assert_queries(1) do
        Account.pluck(:credit_limit)
        Account.pluck(:credit_limit)
      end

      assert_queries(2) do
        Account.all.skip_query_cache!.pluck(:credit_limit)
        Account.all.skip_query_cache!.pluck(:credit_limit)
      end
    end
  end

  test "#skip_query_cache! for a simple calculation" do
    Account.cache do
      assert_queries(1) do
        Account.calculate(:sum, :credit_limit)
        Account.calculate(:sum, :credit_limit)
      end

      assert_queries(2) do
        Account.all.skip_query_cache!.calculate(:sum, :credit_limit)
        Account.all.skip_query_cache!.calculate(:sum, :credit_limit)
      end
    end
  end

  test "#skip_query_cache! for a grouped calculation" do
    Account.cache do
      assert_queries(1) do
        Account.group(:firm_id).calculate(:sum, :credit_limit)
        Account.group(:firm_id).calculate(:sum, :credit_limit)
      end

      assert_queries(2) do
        Account.all.skip_query_cache!.group(:firm_id).calculate(:sum, :credit_limit)
        Account.all.skip_query_cache!.group(:firm_id).calculate(:sum, :credit_limit)
      end
    end
  end
975
end