calculations_test.rb 34.3 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
require "support/stubs/strong_parameters"
23

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

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

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

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

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

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

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

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

61 62
  def test_should_return_nil_as_average
    assert_nil NumericData.average(:bank_balance)
63
  end
64

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    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

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

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

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

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

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

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

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

G
Gannon McGibbon 已提交
222 223
    assert_match %r{accounts}i, e.sql
    assert_match "credit_limit, firm_name", e.sql
224 225
  end

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

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

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

246 247 248 249 250 251
  def test_count_with_eager_loading_and_custom_select_and_order
    posts = Post.includes(:comments).order("comments.id").select(:type)
    assert_queries(1) { assert_equal 11, posts.count }
    assert_queries(1) { assert_equal 11, posts.count(:all) }
  end

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

258 259 260 261 262 263
  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

264 265 266 267 268 269 270 271 272 273 274 275
  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

276 277 278 279 280 281 282 283 284 285 286 287
  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

288 289 290 291 292 293 294 295 296 297 298 299
  def test_distinct_joins_count_with_group_by
    expected = { nil => 4, 1 => 1, 2 => 1, 4 => 1, 5 => 1, 7 => 1 }
    assert_equal expected, Post.left_joins(:comments).group(:post_id).distinct.count(:author_id)
    assert_equal expected, Post.left_joins(:comments).group(:post_id).distinct.select(:author_id).count
    assert_equal expected, Post.left_joins(:comments).group(:post_id).count("DISTINCT posts.author_id")
    assert_equal expected, Post.left_joins(:comments).group(:post_id).select("DISTINCT posts.author_id").count

    expected = { nil => 6, 1 => 1, 2 => 1, 4 => 1, 5 => 1, 7 => 1 }
    assert_equal expected, Post.left_joins(:comments).group(:post_id).distinct.count(:all)
    assert_equal expected, Post.left_joins(:comments).group(:post_id).distinct.select(:author_id).count(:all)
  end

300 301 302 303
  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

304
  def test_should_group_by_summed_field_having_condition
305
    c = Account.group(:firm_id).having("sum(credit_limit) > 50").sum(:credit_limit)
306 307 308 309 310
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
  end

311
  def test_should_group_by_summed_field_having_condition_from_select
312
    skip unless current_adapter?(:Mysql2Adapter, :SQLite3Adapter)
313
    c = Account.select("MIN(credit_limit) AS min_credit_limit").group(:firm_id).having("min_credit_limit > 50").sum(:credit_limit)
314 315 316 317 318
    assert_nil       c[1]
    assert_equal 60, c[2]
    assert_equal 53, c[9]
  end

319
  def test_should_group_by_summed_association
J
Jon Leighton 已提交
320
    c = Account.group(:firm).sum(:credit_limit)
321 322 323 324
    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 已提交
325

326
  def test_should_sum_field_with_conditions
327
    assert_equal 105, Account.where("firm_id = 6").sum(:credit_limit)
328 329
  end

330
  def test_should_return_zero_if_sum_conditions_return_nothing
331 332
    assert_equal 0, Account.where("1 = 2").sum(:credit_limit)
    assert_equal 0, companies(:rails_core).companies.where("1 = 2").sum(:id)
333 334
  end

335
  def test_sum_should_return_valid_values_for_decimals
336
    NumericData.create(bank_balance: 19.83)
337 338 339
    assert_equal 19.83, NumericData.sum(:bank_balance)
  end

340
  def test_should_return_type_casted_values_with_group_and_expression
341
    assert_equal 0.5, Account.group(:firm_name).sum("0.01 * credit_limit")["37signals"]
342 343
  end

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

351
  def test_should_group_by_summed_field_with_conditions_and_having
352 353
    c = Account.where("firm_id > 1").group(:firm_id).
     having("sum(credit_limit) > 60").sum(:credit_limit)
354 355 356
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_nil        c[2]
357 358 359
  end

  def test_should_group_by_fields_with_table_alias
360
    c = Account.group("accounts.firm_id").sum(:credit_limit)
361 362 363
    assert_equal 50,  c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
364
  end
J
Jeremy Kemper 已提交
365

366
  def test_should_calculate_with_invalid_field
367
    assert_equal 6, Account.calculate(:count, "*")
368
    assert_equal 6, Account.calculate(:count, :all)
369
  end
J
Jeremy Kemper 已提交
370

371
  def test_should_calculate_grouped_with_invalid_field
372
    c = Account.group("accounts.firm_id").count(:all)
373 374 375
    assert_equal 1, c[1]
    assert_equal 2, c[6]
    assert_equal 1, c[2]
376
  end
J
Jeremy Kemper 已提交
377

378
  def test_should_calculate_grouped_association_with_invalid_field
J
Jon Leighton 已提交
379
    c = Account.group(:firm).count(:all)
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

385
  def test_should_group_by_association_with_non_numeric_foreign_key
386 387
    Speedometer.create! id: "ABC"
    Minivan.create! id: "OMG", speedometer_id: "ABC"
388

389
    c = Minivan.group(:speedometer).count(:all)
390
    first_key = c.keys.first
391
    assert_equal Speedometer, first_key.class
392
    assert_equal 1, c[first_key]
393
  end
J
Jeremy Kemper 已提交
394

395
  def test_should_calculate_grouped_association_with_foreign_key_option
396
    Account.belongs_to :another_firm, class_name: "Firm", foreign_key: "firm_id"
J
Jon Leighton 已提交
397
    c = Account.group(:another_firm).count(:all)
398 399 400 401 402
    assert_equal 1, c[companies(:first_firm)]
    assert_equal 2, c[companies(:rails_core)]
    assert_equal 1, c[companies(:first_client)]
  end

403
  def test_should_calculate_grouped_by_function
J
Jon Leighton 已提交
404
    c = Company.group("UPPER(#{QUOTED_TYPE})").count(:all)
405
    assert_equal 2, c[nil]
406 407 408
    assert_equal 1, c["DEPENDENTFIRM"]
    assert_equal 5, c["CLIENT"]
    assert_equal 2, c["FIRM"]
409
  end
J
Jeremy Kemper 已提交
410

411
  def test_should_calculate_grouped_by_function_with_table_alias
J
Jon Leighton 已提交
412
    c = Company.group("UPPER(companies.#{QUOTED_TYPE})").count(:all)
413
    assert_equal 2, c[nil]
414 415 416
    assert_equal 1, c["DEPENDENTFIRM"]
    assert_equal 5, c["CLIENT"]
    assert_equal 2, c["FIRM"]
417
  end
J
Jeremy Kemper 已提交
418

419 420 421
  def test_should_not_overshadow_enumerable_sum
    assert_equal 6, [1, 2, 3].sum(&:abs)
  end
422 423 424 425 426

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

427 428 429 430
  def test_should_sum_scoped_field_with_from
    assert_equal Club.count, Organization.clubs.count
  end

431
  def test_should_sum_scoped_field_with_conditions
432
    assert_equal 8,  companies(:rails_core).companies.where("id > 7").sum(:id)
433 434 435
  end

  def test_should_group_by_scoped_field
J
Jon Leighton 已提交
436
    c = companies(:rails_core).companies.group(:name).sum(:id)
437 438
    assert_equal 7, c["Leetsoft"]
    assert_equal 8, c["Jadedpixel"]
439 440
  end

E
Emilio Tagua 已提交
441
  def test_should_group_by_summed_field_through_association_and_having
442 443 444
    c = companies(:rails_core).companies.group(:name).having("sum(id) > 7").sum(:id)
    assert_nil      c["Leetsoft"]
    assert_equal 8, c["Jadedpixel"]
445
  end
446

447
  def test_should_count_selected_field_with_include
448 449
    assert_equal 6, Account.includes(:firm).distinct.count
    assert_equal 4, Account.includes(:firm).distinct.select(:credit_limit).count
450 451
    assert_equal 4, Account.includes(:firm).distinct.count("DISTINCT credit_limit")
    assert_equal 4, Account.includes(:firm).distinct.count("DISTINCT(credit_limit)")
452
  end
J
Jeremy Kemper 已提交
453

454 455 456 457 458 459 460
  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
461
    joined_count = Account.includes(:firm).where(companies: { name: "37signals" }).count
462 463 464
    assert_equal 1, joined_count
  end

465
  def test_should_count_scoped_select
466
    Account.update_all("credit_limit = NULL")
467
    assert_equal 0, Account.select("credit_limit").count
468 469 470
  end

  def test_should_count_scoped_select_with_options
471
    Account.update_all("credit_limit = NULL")
472 473
    Account.last.update_columns("credit_limit" => 49)
    Account.first.update_columns("credit_limit" => 51)
474

475
    assert_equal 1, Account.select("credit_limit").where("credit_limit >= 50").count
476 477
  end

478
  def test_should_count_manual_select_with_include
479
    assert_equal 6, Account.select("DISTINCT accounts.id").includes(:firm).count
480 481
  end

482 483 484 485 486
  def test_should_count_manual_select_with_count_all
    assert_equal 5, Account.select("DISTINCT accounts.firm_id").count(:all)
  end

  def test_should_count_with_manual_distinct_select_and_distinct
487
    assert_equal 4, Account.select("DISTINCT accounts.firm_id").distinct(true).count
488 489 490 491 492 493 494 495 496 497 498 499
  end

  def test_should_count_manual_select_with_group_with_count_all
    expected = { nil => 1, 1 => 1, 2 => 1, 6 => 2, 9 => 1 }
    actual = Account.select("DISTINCT accounts.firm_id").group("accounts.firm_id").count(:all)
    assert_equal expected, actual
  end

  def test_should_count_manual_with_count_all
    assert_equal 6, Account.count(:all)
  end

500 501 502 503 504
  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

505 506 507
  def test_count_with_column_parameter
    assert_equal 5, Account.count(:firm_id)
  end
J
Jeremy Kemper 已提交
508

509 510 511 512 513 514 515 516
  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

517 518
  def test_count_with_distinct
    assert_equal 4, Account.select(:credit_limit).distinct.count
519 520
  end

521 522 523 524
  def test_count_with_aliased_attribute
    assert_equal 6, Account.count(:available_credit)
  end

525
  def test_count_with_column_and_options_parameter
J
Jon Leighton 已提交
526
    assert_equal 2, Account.where("credit_limit = 50 AND firm_id IS NOT NULL").count(:firm_id)
527
  end
J
Jeremy Kemper 已提交
528

O
oleg dashevskii 已提交
529
  def test_should_count_field_in_joined_table
530 531
    assert_equal 5, Account.joins(:firm).count("companies.id")
    assert_equal 4, Account.joins(:firm).distinct.count("companies.id")
O
oleg dashevskii 已提交
532 533
  end

534 535 536 537 538 539 540 541 542 543
  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 已提交
544
  def test_should_count_field_in_joined_table_with_group_by
545
    c = Account.group("accounts.firm_id").joins(:firm).count("companies.id")
O
oleg dashevskii 已提交
546

547
    [1, 6, 2, 9].each { |firm_id| assert_includes c.keys, firm_id }
O
oleg dashevskii 已提交
548 549
  end

550
  def test_should_count_field_of_root_table_with_conflicting_group_by_column
551 552 553 554
    expected = { 1 => 2, 2 => 1, 4 => 5, 5 => 2, 7 => 1 }
    assert_equal expected, Post.joins(:comments).group(:post_id).count
    assert_equal expected, Post.joins(:comments).group("comments.post_id").count
    assert_equal expected, Post.joins(:comments).group(:post_id).select("DISTINCT posts.author_id").count(:all)
555 556
  end

557 558 559 560 561 562 563
  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
564

L
Lauro Caetano 已提交
565 566 567 568 569 570 571 572 573
  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
574 575 576
    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 已提交
577 578
  end

579 580 581 582
  def test_count_with_block
    assert_equal 4, Account.count { |account| account.credit_limit.modulo(10).zero? }
  end

583
  def test_should_sum_expression
584
    if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter, :OracleAdapter)
585 586
      assert_equal 636, Account.sum("2 * credit_limit")
    else
587
      assert_equal 636, Account.sum("2 * credit_limit").to_i
588
    end
589
  end
590

591
  def test_sum_expression_returns_zero_when_no_records_to_sum
592
    assert_equal 0, Account.where("1 = 2").sum("2 * credit_limit")
593 594
  end

595
  def test_count_with_from_option
596
    assert_equal Company.count(:all), Company.from("companies").count(:all)
J
Jon Leighton 已提交
597
    assert_equal Account.where("credit_limit = 50").count(:all),
598
        Account.from("accounts").where("credit_limit = 50").count(:all)
599 600
    assert_equal Company.where(type: "Firm").count(:type),
        Company.where(type: "Firm").from("companies").count(:type)
601 602 603
  end

  def test_sum_with_from_option
604
    assert_equal Account.sum(:credit_limit), Account.from("accounts").sum(:credit_limit)
J
Jon Leighton 已提交
605
    assert_equal Account.where("credit_limit > 50").sum(:credit_limit),
606
        Account.where("credit_limit > 50").from("accounts").sum(:credit_limit)
607 608 609
  end

  def test_average_with_from_option
610
    assert_equal Account.average(:credit_limit), Account.from("accounts").average(:credit_limit)
J
Jon Leighton 已提交
611
    assert_equal Account.where("credit_limit > 50").average(:credit_limit),
612
        Account.where("credit_limit > 50").from("accounts").average(:credit_limit)
613 614 615
  end

  def test_minimum_with_from_option
616
    assert_equal Account.minimum(:credit_limit), Account.from("accounts").minimum(:credit_limit)
J
Jon Leighton 已提交
617
    assert_equal Account.where("credit_limit > 50").minimum(:credit_limit),
618
        Account.where("credit_limit > 50").from("accounts").minimum(:credit_limit)
619 620 621
  end

  def test_maximum_with_from_option
622
    assert_equal Account.maximum(:credit_limit), Account.from("accounts").maximum(:credit_limit)
J
Jon Leighton 已提交
623
    assert_equal Account.where("credit_limit > 50").maximum(:credit_limit),
624
        Account.where("credit_limit > 50").from("accounts").maximum(:credit_limit)
625 626
  end

627
  def test_maximum_with_not_auto_table_name_prefix_if_column_included
628
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
629

A
Aaron Patterson 已提交
630
    assert_equal 7, Company.includes(:contracts).maximum(:developer_id)
631 632 633
  end

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

A
Aaron Patterson 已提交
636
    assert_equal 7, Company.includes(:contracts).minimum(:developer_id)
637 638 639
  end

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

A
Aaron Patterson 已提交
642
    assert_equal 7, Company.includes(:contracts).sum(:developer_id)
643 644
  end

R
Ryuta Kamizono 已提交
645 646
  if current_adapter?(:Mysql2Adapter)
    def test_from_option_with_specified_index
647 648 649
      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)
650 651 652 653
    end
  end

  def test_from_option_with_table_different_than_class
654
    assert_equal Account.count(:all), Company.from("accounts").count(:all)
655
  end
656 657 658 659

  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]
660
    assert_equal approved_topics_count, 4
661
    # Count the number of distinct authors for approved Topics
662
    distinct_authors_for_approved_count = Topic.group(:approved).distinct.count(:author_name)[true]
663
    assert_equal distinct_authors_for_approved_count, 3
664
  end
665 666

  def test_pluck
667
    assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck(:id)
668 669
  end

670
  def test_pluck_without_column_names
671 672 673 674 675
    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
676 677
  end

678 679
  def test_pluck_type_cast
    topic = topics(:first)
680
    relation = Topic.where(id: topic.id)
681 682 683 684 685
    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

686 687 688 689 690 691 692 693 694 695 696 697
  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

698 699
  def test_pluck_and_distinct
    assert_equal [50, 53, 55, 60], Account.order(:credit_limit).distinct.pluck(:credit_limit)
700 701 702 703 704 705 706 707
  end

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

708
  def test_pluck_on_aliased_attribute
709
    assert_equal "The First Topic", Topic.order(:id).pluck(:heading).first
710 711
  end

712
  def test_pluck_with_serialization
713
    t = Topic.create!(content: { foo: :bar })
714
    assert_equal [{ foo: :bar }], Topic.where(id: t.id).pluck(:content)
715 716 717
  end

  def test_pluck_with_qualified_column_name
B
Ben Toews 已提交
718
    assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck("topics.id")
719
  end
720 721

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

726
  def test_pluck_if_table_included
727
    c = Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
728 729 730
    assert_equal [c.id], Company.includes(:contracts).where("contracts.id" => c.contracts.first).pluck(:id)
  end

731
  def test_pluck_not_auto_table_name_prefix_if_column_joined
732 733 734
    company = Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
    metadata = company.contracts.first.metadata
    assert_equal [metadata], Company.joins(:contracts).pluck(:metadata)
735
  end
T
twinturbo 已提交
736

737
  def test_pluck_with_selection_clause
B
Ben Toews 已提交
738 739 740
    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
741 742 743 744

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

T
twinturbo 已提交
748
  def test_plucks_with_ids
J
Jon Leighton 已提交
749
    assert_equal Company.all.map(&:id).sort, Company.ids.sort
T
twinturbo 已提交
750
  end
751

752 753
  def test_pluck_with_includes_limit_and_empty_result
    assert_equal [], Topic.includes(:replies).limit(0).pluck(:id)
754
    assert_equal [], Topic.includes(:replies).limit(1).where("0 = 1").pluck(:id)
755 756
  end

757 758 759 760 761
  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

762 763 764 765
  def test_pluck_with_join
    assert_equal [[2, 2], [4, 4]], Reply.includes(:topic).pluck(:id, :"topics.id")
  end

766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783
  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

784
  def test_pluck_not_auto_table_name_prefix_if_column_included
785
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
B
Ben Toews 已提交
786
    ids = Company.includes(:contracts).pluck(:developer_id)
787 788 789
    assert_equal Company.count, ids.length
    assert_equal [7], ids.compact
  end
790 791 792 793

  def test_pluck_multiple_columns
    assert_equal [
      [1, "The First Topic"], [2, "The Second Topic of the day"],
794 795
      [3, "The Third Topic of the day"], [4, "The Fourth Topic of the day"],
      [5, "The Fifth Topic of the day"]
796
    ], Topic.order(:id).pluck(:id, :title)
797 798
    assert_equal [
      [1, "The First Topic", "David"], [2, "The Second Topic of the day", "Mary"],
799 800
      [3, "The Third Topic of the day", "Carl"], [4, "The Fourth Topic of the day", "Carl"],
      [5, "The Fifth Topic of the day", "Jason"]
801 802 803 804 805
    ], 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 已提交
806
      Account.pluck("id, credit_limit")
807 808 809
  end

  def test_pluck_with_multiple_columns_and_includes
810
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
B
Ben Toews 已提交
811
    companies_and_developers = Company.order("companies.id").includes(:contracts).pluck(:name, :developer_id)
812 813 814 815

    assert_equal Company.count, companies_and_developers.length
    assert_equal ["37signals", nil], companies_and_developers.first
    assert_equal ["test", 7], companies_and_developers.last
816
  end
817 818

  def test_pluck_with_reserved_words
819
    Possession.create!(where: "Over There")
820 821 822

    assert_equal ["Over There"], Possession.pluck(:where)
  end
823 824 825

  def test_pluck_replaces_select_clause
    taks_relation = Topic.select(:approved, :id).order(:id)
826
    assert_equal [1, 2, 3, 4, 5], taks_relation.pluck(:id)
827
    assert_equal [false, true, true, true, true], taks_relation.pluck(:approved)
828
  end
829 830 831 832

  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 已提交
833
      .pluck("topics.title", "replies_topics.title")
834 835
    assert_equal expected, actual
  end
836 837

  def test_calculation_with_polymorphic_relation
838 839 840 841
    part = ShipPart.create!(name: "has trinket")
    part.trinkets.create!

    assert_equal part.id, ShipPart.joins(:trinkets).sum(:id)
842 843
  end

S
Sean Griffin 已提交
844 845 846 847 848 849 850
  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

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

855
    assert_no_queries do
856
      assert_equal ["37signals", "Summit", "Microsoft"], companies.pluck(:name)
857 858 859 860
    end
  end

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

864
    assert_no_queries do
865
      assert_equal [[1, "37signals"], [2, "Summit"], [3, "Microsoft"]], companies.pluck(:id, :name)
866 867 868 869
    end
  end

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

873
    assert_queries 1 do
B
Ben Toews 已提交
874
      assert_equal ["37signals", "Apex", "Ex Nihilo"], companies.pluck(Arel.sql("DISTINCT name"))
875 876 877
    end
  end

878 879
  def test_pick_one
    assert_equal "The First Topic", Topic.order(:id).pick(:heading)
880
    assert_nil Topic.none.pick(:heading)
881
    assert_nil Topic.where(id: 9999999999999999999).pick(:heading)
882 883 884 885
  end

  def test_pick_two
    assert_equal ["David", "david@loudthinking.com"], Topic.order(:id).pick(:author_name, :author_email_address)
886
    assert_nil Topic.none.pick(:author_name, :author_email_address)
887
    assert_nil Topic.where(id: 9999999999999999999).pick(:author_name, :author_email_address)
888 889
  end

Y
Yuji Hanamura 已提交
890 891 892 893 894
  def test_pick_delegate_to_all
    cool_first = minivans(:cool_first)
    assert_equal cool_first.color, Minivan.pick(:color)
  end

895 896 897 898 899 900
  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
901 902 903 904 905

  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
906 907

  def test_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association
908
    assert_nothing_raised do
909
      developer = Developer.create!(name: "developer")
910 911 912
      developer.ratings.includes(comment: :post).where(posts: { id: 1 }).count
    end
  end
913 914 915 916 917 918 919 920 921 922

  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
923 924

  def test_having_with_strong_parameters
925
    params = ProtectedParams.new(credit_limit: "50")
926 927 928 929 930 931 932 933 934 935

    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
936 937

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

941 942 943
  def test_count_with_block_and_column_name_raises_an_error
    assert_raises(ArgumentError) do
      Account.count(:firm_id) { true }
944 945 946
    end
  end

947 948 949
  def test_sum_with_block_and_column_name_raises_an_error
    assert_raises(ArgumentError) do
      Account.sum(:firm_id) { 1 }
950 951
    end
  end
952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993

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