calculations_test.rb 31.9 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
  def test_should_return_nil_as_average
    assert_nil NumericData.average(:bank_balance)
  end
63

64 65 66 67
  def test_should_get_maximum_of_field
    assert_equal 60, Account.maximum(:credit_limit)
  end

68 69 70 71
  def test_should_get_maximum_of_arel_attribute
    assert_equal 60, Account.maximum(Account.arel_table[:credit_limit])
  end

72
  def test_should_get_maximum_of_field_with_include
J
Jon Leighton 已提交
73
    assert_equal 55, Account.where("companies.name != 'Summit'").references(:companies).includes(:firm).maximum(:credit_limit)
74 75
  end

76 77 78 79
  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

80 81 82 83
  def test_should_get_minimum_of_field
    assert_equal 50, Account.minimum(:credit_limit)
  end

84 85 86 87
  def test_should_get_minimum_of_arel_attribute
    assert_equal 50, Account.minimum(Account.arel_table[:credit_limit])
  end

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

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

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

  def test_should_group_by_multiple_fields_having_functions
108
    c = Topic.group(:author_name, "COALESCE(type, title)").count(:all)
109 110 111 112 113
    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
114 115

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

122
  def test_should_generate_valid_sql_with_joins_and_group
123
    assert_nothing_raised do
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
      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

141
  def test_should_order_by_grouped_field
142
    c = Account.group(:firm_id).order("firm_id").sum(:credit_limit)
143
    assert_equal [1, 2, 6, 9], c.keys.compact
144 145 146
  end

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

152
  def test_should_limit_calculation
153
    c = Account.where("firm_id IS NOT NULL").group(:firm_id).order("firm_id").limit(2).sum(:credit_limit)
154 155 156 157
    assert_equal [1, 2], c.keys.compact
  end

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

163
  def test_limit_should_apply_before_count
164
    accounts = Account.limit(4)
165 166 167 168 169

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

170
  def test_limit_should_apply_before_count_arel_attribute
171
    accounts = Account.limit(4)
172 173 174 175 176 177

    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

178 179 180 181 182 183 184
  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 已提交
185 186
    return if current_adapter?(:OracleAdapter)

187
    queries = assert_sql { Account.limit(1).count }
188 189 190 191
    assert_equal 1, queries.length
    assert_match(/LIMIT/, queries.first)
  end

192 193 194
  def test_offset_is_kept
    return if current_adapter?(:OracleAdapter)

195 196
    queries = assert_sql { Account.offset(1).count }
    assert_equal 1, queries.length
197
    assert_match(/OFFSET/, queries.first)
198 199
  end

200 201 202 203
  def test_limit_with_offset_is_kept
    return if current_adapter?(:OracleAdapter)

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

216 217 218 219 220
  def test_count_on_invalid_columns_raises
    e = assert_raises(ActiveRecord::StatementInvalid) {
      Account.select("credit_limit, firm_name").count
    }

221
    assert_match %r{accounts}i, e.message
222 223 224
    assert_match "credit_limit, firm_name", e.message
  end

225 226 227 228 229 230 231
  def test_apply_distinct_in_count
    queries = assert_sql do
      Account.distinct.count
      Account.group(:firm_id).distinct.count
    end

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

239 240 241 242 243 244
  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

245 246 247 248 249 250
  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

251 252 253 254 255 256
  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

257 258 259 260 261 262 263 264 265 266 267 268
  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

269 270 271 272 273 274 275 276 277 278 279 280
  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

281 282 283 284
  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

285
  def test_should_group_by_summed_field_having_condition
286
    c = Account.group(:firm_id).having("sum(credit_limit) > 50").sum(:credit_limit)
287 288 289 290 291
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
  end

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

300
  def test_should_group_by_summed_association
J
Jon Leighton 已提交
301
    c = Account.group(:firm).sum(:credit_limit)
302 303 304 305
    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 已提交
306

307
  def test_should_sum_field_with_conditions
308
    assert_equal 105, Account.where("firm_id = 6").sum(:credit_limit)
309 310
  end

311
  def test_should_return_zero_if_sum_conditions_return_nothing
312 313
    assert_equal 0, Account.where("1 = 2").sum(:credit_limit)
    assert_equal 0, companies(:rails_core).companies.where("1 = 2").sum(:id)
314 315
  end

316
  def test_sum_should_return_valid_values_for_decimals
317
    NumericData.create(bank_balance: 19.83)
318 319 320
    assert_equal 19.83, NumericData.sum(:bank_balance)
  end

321
  def test_should_return_type_casted_values_with_group_and_expression
322
    assert_equal 0.5, Account.group(:firm_name).sum("0.01 * credit_limit")["37signals"]
323 324
  end

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

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

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

347
  def test_should_calculate_with_invalid_field
348
    assert_equal 6, Account.calculate(:count, "*")
349
    assert_equal 6, Account.calculate(:count, :all)
350
  end
J
Jeremy Kemper 已提交
351

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

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

366
  def test_should_group_by_association_with_non_numeric_foreign_key
367 368
    Speedometer.create! id: "ABC"
    Minivan.create! id: "OMG", speedometer_id: "ABC"
369

370
    c = Minivan.group(:speedometer).count(:all)
371
    first_key = c.keys.first
372
    assert_equal Speedometer, first_key.class
373
    assert_equal 1, c[first_key]
374
  end
J
Jeremy Kemper 已提交
375

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

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

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

400 401 402
  def test_should_not_overshadow_enumerable_sum
    assert_equal 6, [1, 2, 3].sum(&:abs)
  end
403 404 405 406 407

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

408 409 410 411
  def test_should_sum_scoped_field_with_from
    assert_equal Club.count, Organization.clubs.count
  end

412
  def test_should_sum_scoped_field_with_conditions
413
    assert_equal 8,  companies(:rails_core).companies.where("id > 7").sum(:id)
414 415 416
  end

  def test_should_group_by_scoped_field
J
Jon Leighton 已提交
417
    c = companies(:rails_core).companies.group(:name).sum(:id)
418 419
    assert_equal 7, c["Leetsoft"]
    assert_equal 8, c["Jadedpixel"]
420 421
  end

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

428
  def test_should_count_selected_field_with_include
429 430
    assert_equal 6, Account.includes(:firm).distinct.count
    assert_equal 4, Account.includes(:firm).distinct.select(:credit_limit).count
431
  end
J
Jeremy Kemper 已提交
432

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

444
  def test_should_count_scoped_select
445
    Account.update_all("credit_limit = NULL")
446
    assert_equal 0, Account.select("credit_limit").count
447 448 449
  end

  def test_should_count_scoped_select_with_options
450
    Account.update_all("credit_limit = NULL")
451 452
    Account.last.update_columns("credit_limit" => 49)
    Account.first.update_columns("credit_limit" => 51)
453

454
    assert_equal 1, Account.select("credit_limit").where("credit_limit >= 50").count
455 456
  end

457
  def test_should_count_manual_select_with_include
458
    assert_equal 6, Account.select("DISTINCT accounts.id").includes(:firm).count
459 460
  end

461 462 463 464 465
  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

466 467 468
  def test_count_with_column_parameter
    assert_equal 5, Account.count(:firm_id)
  end
J
Jeremy Kemper 已提交
469

470 471 472 473 474 475 476 477
  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

478 479
  def test_count_with_distinct
    assert_equal 4, Account.select(:credit_limit).distinct.count
480 481
  end

482 483 484 485
  def test_count_with_aliased_attribute
    assert_equal 6, Account.count(:available_credit)
  end

486
  def test_count_with_column_and_options_parameter
J
Jon Leighton 已提交
487
    assert_equal 2, Account.where("credit_limit = 50 AND firm_id IS NOT NULL").count(:firm_id)
488
  end
J
Jeremy Kemper 已提交
489

O
oleg dashevskii 已提交
490
  def test_should_count_field_in_joined_table
491 492
    assert_equal 5, Account.joins(:firm).count("companies.id")
    assert_equal 4, Account.joins(:firm).distinct.count("companies.id")
O
oleg dashevskii 已提交
493 494
  end

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

508
    [1, 6, 2, 9].each { |firm_id| assert_includes c.keys, firm_id }
O
oleg dashevskii 已提交
509 510
  end

511 512
  def test_should_count_field_of_root_table_with_conflicting_group_by_column
    assert_equal({ 1 => 1 }, Firm.joins(:accounts).group(:firm_id).count)
513
    assert_equal({ 1 => 1 }, Firm.joins(:accounts).group("accounts.firm_id").count)
514 515
  end

516 517 518 519 520 521 522
  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
523

L
Lauro Caetano 已提交
524 525 526 527 528 529 530 531 532
  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
533 534 535
    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 已提交
536 537
  end

538 539 540 541
  def test_count_with_block
    assert_equal 4, Account.count { |account| account.credit_limit.modulo(10).zero? }
  end

542
  def test_should_sum_expression
543
    if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter, :OracleAdapter)
544 545
      assert_equal 636, Account.sum("2 * credit_limit")
    else
546
      assert_equal 636, Account.sum("2 * credit_limit").to_i
547
    end
548
  end
549

550
  def test_sum_expression_returns_zero_when_no_records_to_sum
551
    assert_equal 0, Account.where("1 = 2").sum("2 * credit_limit")
552 553
  end

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

  def test_sum_with_from_option
563
    assert_equal Account.sum(:credit_limit), Account.from("accounts").sum(:credit_limit)
J
Jon Leighton 已提交
564
    assert_equal Account.where("credit_limit > 50").sum(:credit_limit),
565
        Account.where("credit_limit > 50").from("accounts").sum(:credit_limit)
566 567 568
  end

  def test_average_with_from_option
569
    assert_equal Account.average(:credit_limit), Account.from("accounts").average(:credit_limit)
J
Jon Leighton 已提交
570
    assert_equal Account.where("credit_limit > 50").average(:credit_limit),
571
        Account.where("credit_limit > 50").from("accounts").average(:credit_limit)
572 573 574
  end

  def test_minimum_with_from_option
575
    assert_equal Account.minimum(:credit_limit), Account.from("accounts").minimum(:credit_limit)
J
Jon Leighton 已提交
576
    assert_equal Account.where("credit_limit > 50").minimum(:credit_limit),
577
        Account.where("credit_limit > 50").from("accounts").minimum(:credit_limit)
578 579 580
  end

  def test_maximum_with_from_option
581
    assert_equal Account.maximum(:credit_limit), Account.from("accounts").maximum(:credit_limit)
J
Jon Leighton 已提交
582
    assert_equal Account.where("credit_limit > 50").maximum(:credit_limit),
583
        Account.where("credit_limit > 50").from("accounts").maximum(:credit_limit)
584 585
  end

586
  def test_maximum_with_not_auto_table_name_prefix_if_column_included
587
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
588

A
Aaron Patterson 已提交
589
    assert_equal 7, Company.includes(:contracts).maximum(:developer_id)
590 591 592
  end

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

A
Aaron Patterson 已提交
595
    assert_equal 7, Company.includes(:contracts).minimum(:developer_id)
596 597 598
  end

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

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

R
Ryuta Kamizono 已提交
604 605
  if current_adapter?(:Mysql2Adapter)
    def test_from_option_with_specified_index
606 607 608
      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)
609 610 611 612
    end
  end

  def test_from_option_with_table_different_than_class
613
    assert_equal Account.count(:all), Company.from("accounts").count(:all)
614
  end
615 616 617 618

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

  def test_pluck
626
    assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck(:id)
627 628
  end

629
  def test_pluck_without_column_names
630 631 632 633 634
    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
635 636
  end

637 638
  def test_pluck_type_cast
    topic = topics(:first)
639
    relation = Topic.where(id: topic.id)
640 641 642 643 644
    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

645 646
  def test_pluck_and_distinct
    assert_equal [50, 53, 55, 60], Account.order(:credit_limit).distinct.pluck(:credit_limit)
647 648 649 650 651 652 653 654
  end

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

655
  def test_pluck_on_aliased_attribute
656
    assert_equal "The First Topic", Topic.order(:id).pluck(:heading).first
657 658
  end

659
  def test_pluck_with_serialization
660
    t = Topic.create!(content: { foo: :bar })
661
    assert_equal [{ foo: :bar }], Topic.where(id: t.id).pluck(:content)
662 663 664
  end

  def test_pluck_with_qualified_column_name
B
Ben Toews 已提交
665
    assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck("topics.id")
666
  end
667 668

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

673
  def test_pluck_if_table_included
674
    c = Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
675 676 677
    assert_equal [c.id], Company.includes(:contracts).where("contracts.id" => c.contracts.first).pluck(:id)
  end

678
  def test_pluck_not_auto_table_name_prefix_if_column_joined
679
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
B
Ben Toews 已提交
680
    assert_equal [7], Company.joins(:contracts).pluck(:developer_id)
681
  end
T
twinturbo 已提交
682

683
  def test_pluck_with_selection_clause
B
Ben Toews 已提交
684 685 686
    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
687 688 689 690

    # 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 已提交
691
    assert_equal [50 + 53 + 55 + 60], Account.pluck(Arel.sql("SUM(DISTINCT(credit_limit)) as credit_limit"))
692 693
  end

T
twinturbo 已提交
694
  def test_plucks_with_ids
J
Jon Leighton 已提交
695
    assert_equal Company.all.map(&:id).sort, Company.ids.sort
T
twinturbo 已提交
696
  end
697

698 699
  def test_pluck_with_includes_limit_and_empty_result
    assert_equal [], Topic.includes(:replies).limit(0).pluck(:id)
700
    assert_equal [], Topic.includes(:replies).limit(1).where("0 = 1").pluck(:id)
701 702
  end

703 704 705 706 707
  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

708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725
  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

726
  def test_pluck_not_auto_table_name_prefix_if_column_included
727
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
B
Ben Toews 已提交
728
    ids = Company.includes(:contracts).pluck(:developer_id)
729 730 731
    assert_equal Company.count, ids.length
    assert_equal [7], ids.compact
  end
732 733 734 735

  def test_pluck_multiple_columns
    assert_equal [
      [1, "The First Topic"], [2, "The Second Topic of the day"],
736 737
      [3, "The Third Topic of the day"], [4, "The Fourth Topic of the day"],
      [5, "The Fifth Topic of the day"]
738
    ], Topic.order(:id).pluck(:id, :title)
739 740
    assert_equal [
      [1, "The First Topic", "David"], [2, "The Second Topic of the day", "Mary"],
741 742
      [3, "The Third Topic of the day", "Carl"], [4, "The Fourth Topic of the day", "Carl"],
      [5, "The Fifth Topic of the day", "Jason"]
743 744 745 746 747
    ], 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 已提交
748
      Account.pluck("id, credit_limit")
749 750 751
  end

  def test_pluck_with_multiple_columns_and_includes
752
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
B
Ben Toews 已提交
753
    companies_and_developers = Company.order("companies.id").includes(:contracts).pluck(:name, :developer_id)
754 755 756 757

    assert_equal Company.count, companies_and_developers.length
    assert_equal ["37signals", nil], companies_and_developers.first
    assert_equal ["test", 7], companies_and_developers.last
758
  end
759 760

  def test_pluck_with_reserved_words
761
    Possession.create!(where: "Over There")
762 763 764

    assert_equal ["Over There"], Possession.pluck(:where)
  end
765 766 767

  def test_pluck_replaces_select_clause
    taks_relation = Topic.select(:approved, :id).order(:id)
768
    assert_equal [1, 2, 3, 4, 5], taks_relation.pluck(:id)
769
    assert_equal [false, true, true, true, true], taks_relation.pluck(:approved)
770
  end
771 772 773 774

  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 已提交
775
      .pluck("topics.title", "replies_topics.title")
776 777
    assert_equal expected, actual
  end
778 779

  def test_calculation_with_polymorphic_relation
780 781 782 783
    part = ShipPart.create!(name: "has trinket")
    part.trinkets.create!

    assert_equal part.id, ShipPart.joins(:trinkets).sum(:id)
784 785
  end

S
Sean Griffin 已提交
786 787 788 789 790 791 792
  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

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

797
    assert_no_queries do
798
      assert_equal ["37signals", "Summit", "Microsoft"], companies.pluck(:name)
799 800 801 802
    end
  end

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

806
    assert_no_queries do
807
      assert_equal [[1, "37signals"], [2, "Summit"], [3, "Microsoft"]], companies.pluck(:id, :name)
808 809 810 811
    end
  end

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

815
    assert_queries 1 do
B
Ben Toews 已提交
816
      assert_equal ["37signals", "Apex", "Ex Nihilo"], companies.pluck(Arel.sql("DISTINCT name"))
817 818 819
    end
  end

820 821
  def test_pick_one
    assert_equal "The First Topic", Topic.order(:id).pick(:heading)
822
    assert_nil Topic.none.pick(:heading)
823 824 825 826 827
    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)
828
    assert_nil Topic.none.pick(:author_name, :author_email_address)
829 830 831
    assert_nil Topic.where("1=0").pick(:author_name, :author_email_address)
  end

Y
Yuji Hanamura 已提交
832 833 834 835 836
  def test_pick_delegate_to_all
    cool_first = minivans(:cool_first)
    assert_equal cool_first.color, Minivan.pick(:color)
  end

837 838 839 840 841 842
  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
843 844 845 846 847

  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
848 849

  def test_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association
850
    assert_nothing_raised do
851
      developer = Developer.create!(name: "developer")
852 853 854
      developer.ratings.includes(comment: :post).where(posts: { id: 1 }).count
    end
  end
855 856 857 858 859 860 861 862 863 864

  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
865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885

  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

886
    params = protected_params.new(credit_limit: "50")
887 888 889 890 891 892 893 894 895 896

    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
897 898

  def test_group_by_attribute_with_custom_type
R
Ryuta Kamizono 已提交
899
    assert_equal({ "proposed" => 2, "published" => 2 }, Book.group(:status).count)
900
  end
901 902 903 904 905 906 907 908 909 910 911 912

  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
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954

  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
955
end