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 367 368 369 370 371 372 373 374 375 376
  def test_should_calculate_grouped_with_longer_field
    field = "a" * Account.connection.max_identifier_length

    Account.update_all("#{field} = credit_limit")

    c = Account.group(:firm_id).sum(field)
    assert_equal 50,  c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
  end

377
  def test_should_calculate_with_invalid_field
378
    assert_equal 6, Account.calculate(:count, "*")
379
    assert_equal 6, Account.calculate(:count, :all)
380
  end
J
Jeremy Kemper 已提交
381

382
  def test_should_calculate_grouped_with_invalid_field
383
    c = Account.group("accounts.firm_id").count(:all)
384 385 386
    assert_equal 1, c[1]
    assert_equal 2, c[6]
    assert_equal 1, c[2]
387
  end
J
Jeremy Kemper 已提交
388

389
  def test_should_calculate_grouped_association_with_invalid_field
J
Jon Leighton 已提交
390
    c = Account.group(:firm).count(:all)
391 392 393 394
    assert_equal 1, c[companies(:first_firm)]
    assert_equal 2, c[companies(:rails_core)]
    assert_equal 1, c[companies(:first_client)]
  end
395

396
  def test_should_group_by_association_with_non_numeric_foreign_key
397 398
    Speedometer.create! id: "ABC"
    Minivan.create! id: "OMG", speedometer_id: "ABC"
399

400
    c = Minivan.group(:speedometer).count(:all)
401
    first_key = c.keys.first
402
    assert_equal Speedometer, first_key.class
403
    assert_equal 1, c[first_key]
404
  end
J
Jeremy Kemper 已提交
405

406
  def test_should_calculate_grouped_association_with_foreign_key_option
407
    Account.belongs_to :another_firm, class_name: "Firm", foreign_key: "firm_id"
J
Jon Leighton 已提交
408
    c = Account.group(:another_firm).count(:all)
409 410 411 412 413
    assert_equal 1, c[companies(:first_firm)]
    assert_equal 2, c[companies(:rails_core)]
    assert_equal 1, c[companies(:first_client)]
  end

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

422
  def test_should_calculate_grouped_by_function_with_table_alias
J
Jon Leighton 已提交
423
    c = Company.group("UPPER(companies.#{QUOTED_TYPE})").count(:all)
424
    assert_equal 2, c[nil]
425 426 427
    assert_equal 1, c["DEPENDENTFIRM"]
    assert_equal 5, c["CLIENT"]
    assert_equal 2, c["FIRM"]
428
  end
J
Jeremy Kemper 已提交
429

430 431 432
  def test_should_not_overshadow_enumerable_sum
    assert_equal 6, [1, 2, 3].sum(&:abs)
  end
433 434 435 436 437

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

438 439 440 441
  def test_should_sum_scoped_field_with_from
    assert_equal Club.count, Organization.clubs.count
  end

442
  def test_should_sum_scoped_field_with_conditions
443
    assert_equal 8,  companies(:rails_core).companies.where("id > 7").sum(:id)
444 445 446
  end

  def test_should_group_by_scoped_field
J
Jon Leighton 已提交
447
    c = companies(:rails_core).companies.group(:name).sum(:id)
448 449
    assert_equal 7, c["Leetsoft"]
    assert_equal 8, c["Jadedpixel"]
450 451
  end

E
Emilio Tagua 已提交
452
  def test_should_group_by_summed_field_through_association_and_having
453 454 455
    c = companies(:rails_core).companies.group(:name).having("sum(id) > 7").sum(:id)
    assert_nil      c["Leetsoft"]
    assert_equal 8, c["Jadedpixel"]
456
  end
457

458
  def test_should_count_selected_field_with_include
459 460
    assert_equal 6, Account.includes(:firm).distinct.count
    assert_equal 4, Account.includes(:firm).distinct.select(:credit_limit).count
461 462
    assert_equal 4, Account.includes(:firm).distinct.count("DISTINCT credit_limit")
    assert_equal 4, Account.includes(:firm).distinct.count("DISTINCT(credit_limit)")
463
  end
J
Jeremy Kemper 已提交
464

465 466 467 468 469 470 471
  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
472
    joined_count = Account.includes(:firm).where(companies: { name: "37signals" }).count
473 474 475
    assert_equal 1, joined_count
  end

476
  def test_should_count_scoped_select
477
    Account.update_all("credit_limit = NULL")
478
    assert_equal 0, Account.select("credit_limit").count
479 480 481
  end

  def test_should_count_scoped_select_with_options
482
    Account.update_all("credit_limit = NULL")
483 484
    Account.last.update_columns("credit_limit" => 49)
    Account.first.update_columns("credit_limit" => 51)
485

486
    assert_equal 1, Account.select("credit_limit").where("credit_limit >= 50").count
487 488
  end

489
  def test_should_count_manual_select_with_include
490
    assert_equal 6, Account.select("DISTINCT accounts.id").includes(:firm).count
491 492
  end

493 494 495 496 497
  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
498
    assert_equal 4, Account.select("DISTINCT accounts.firm_id").distinct(true).count
499 500 501 502 503 504 505 506 507 508 509 510
  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

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

516 517 518
  def test_count_with_column_parameter
    assert_equal 5, Account.count(:firm_id)
  end
J
Jeremy Kemper 已提交
519

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

528 529
  def test_count_with_distinct
    assert_equal 4, Account.select(:credit_limit).distinct.count
530 531
  end

532 533 534 535
  def test_count_with_aliased_attribute
    assert_equal 6, Account.count(:available_credit)
  end

536
  def test_count_with_column_and_options_parameter
J
Jon Leighton 已提交
537
    assert_equal 2, Account.where("credit_limit = 50 AND firm_id IS NOT NULL").count(:firm_id)
538
  end
J
Jeremy Kemper 已提交
539

O
oleg dashevskii 已提交
540
  def test_should_count_field_in_joined_table
541 542
    assert_equal 5, Account.joins(:firm).count("companies.id")
    assert_equal 4, Account.joins(:firm).distinct.count("companies.id")
O
oleg dashevskii 已提交
543 544
  end

545 546 547 548 549 550 551 552 553 554
  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 已提交
555
  def test_should_count_field_in_joined_table_with_group_by
556
    c = Account.group("accounts.firm_id").joins(:firm).count("companies.id")
O
oleg dashevskii 已提交
557

558
    [1, 6, 2, 9].each { |firm_id| assert_includes c.keys, firm_id }
O
oleg dashevskii 已提交
559 560
  end

561
  def test_should_count_field_of_root_table_with_conflicting_group_by_column
562 563 564 565
    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)
566 567
  end

568 569 570 571 572 573 574
  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
575

L
Lauro Caetano 已提交
576 577 578 579 580 581 582 583 584
  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
585 586 587
    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 已提交
588 589
  end

590 591 592 593
  def test_count_with_block
    assert_equal 4, Account.count { |account| account.credit_limit.modulo(10).zero? }
  end

594
  def test_should_sum_expression
595
    if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter, :OracleAdapter)
596 597
      assert_equal 636, Account.sum("2 * credit_limit")
    else
598
      assert_equal 636, Account.sum("2 * credit_limit").to_i
599
    end
600
  end
601

602
  def test_sum_expression_returns_zero_when_no_records_to_sum
603
    assert_equal 0, Account.where("1 = 2").sum("2 * credit_limit")
604 605
  end

606
  def test_count_with_from_option
607
    assert_equal Company.count(:all), Company.from("companies").count(:all)
J
Jon Leighton 已提交
608
    assert_equal Account.where("credit_limit = 50").count(:all),
609
        Account.from("accounts").where("credit_limit = 50").count(:all)
610 611
    assert_equal Company.where(type: "Firm").count(:type),
        Company.where(type: "Firm").from("companies").count(:type)
612 613 614
  end

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

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

  def test_minimum_with_from_option
627
    assert_equal Account.minimum(:credit_limit), Account.from("accounts").minimum(:credit_limit)
J
Jon Leighton 已提交
628
    assert_equal Account.where("credit_limit > 50").minimum(:credit_limit),
629
        Account.where("credit_limit > 50").from("accounts").minimum(:credit_limit)
630 631 632
  end

  def test_maximum_with_from_option
633
    assert_equal Account.maximum(:credit_limit), Account.from("accounts").maximum(:credit_limit)
J
Jon Leighton 已提交
634
    assert_equal Account.where("credit_limit > 50").maximum(:credit_limit),
635
        Account.where("credit_limit > 50").from("accounts").maximum(:credit_limit)
636 637
  end

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

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

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

A
Aaron Patterson 已提交
647
    assert_equal 7, Company.includes(:contracts).minimum(:developer_id)
648 649 650
  end

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

A
Aaron Patterson 已提交
653
    assert_equal 7, Company.includes(:contracts).sum(:developer_id)
654 655
  end

R
Ryuta Kamizono 已提交
656 657
  if current_adapter?(:Mysql2Adapter)
    def test_from_option_with_specified_index
658 659 660
      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)
661 662 663 664
    end
  end

  def test_from_option_with_table_different_than_class
665
    assert_equal Account.count(:all), Company.from("accounts").count(:all)
666
  end
667 668 669 670

  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]
671
    assert_equal approved_topics_count, 4
672
    # Count the number of distinct authors for approved Topics
673
    distinct_authors_for_approved_count = Topic.group(:approved).distinct.count(:author_name)[true]
674
    assert_equal distinct_authors_for_approved_count, 3
675
  end
676 677

  def test_pluck
678
    assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck(:id)
679 680
  end

681
  def test_pluck_without_column_names
682 683 684 685 686
    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
687 688
  end

689 690
  def test_pluck_type_cast
    topic = topics(:first)
691
    relation = Topic.where(id: topic.id)
692 693 694 695 696
    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

697 698 699 700 701 702 703 704 705 706 707 708
  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

709 710
  def test_pluck_and_distinct
    assert_equal [50, 53, 55, 60], Account.order(:credit_limit).distinct.pluck(:credit_limit)
711 712 713 714 715 716 717 718
  end

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

719
  def test_pluck_on_aliased_attribute
720
    assert_equal "The First Topic", Topic.order(:id).pluck(:heading).first
721 722
  end

723
  def test_pluck_with_serialization
724
    t = Topic.create!(content: { foo: :bar })
725
    assert_equal [{ foo: :bar }], Topic.where(id: t.id).pluck(:content)
726 727 728
  end

  def test_pluck_with_qualified_column_name
B
Ben Toews 已提交
729
    assert_equal [1, 2, 3, 4, 5], Topic.order(:id).pluck("topics.id")
730
  end
731 732

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

737
  def test_pluck_if_table_included
738
    c = Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
739 740 741
    assert_equal [c.id], Company.includes(:contracts).where("contracts.id" => c.contracts.first).pluck(:id)
  end

742
  def test_pluck_not_auto_table_name_prefix_if_column_joined
743 744 745
    company = Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
    metadata = company.contracts.first.metadata
    assert_equal [metadata], Company.joins(:contracts).pluck(:metadata)
746
  end
T
twinturbo 已提交
747

748
  def test_pluck_with_selection_clause
B
Ben Toews 已提交
749 750 751
    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
752 753 754 755

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

T
twinturbo 已提交
759
  def test_plucks_with_ids
J
Jon Leighton 已提交
760
    assert_equal Company.all.map(&:id).sort, Company.ids.sort
T
twinturbo 已提交
761
  end
762

763 764
  def test_pluck_with_includes_limit_and_empty_result
    assert_equal [], Topic.includes(:replies).limit(0).pluck(:id)
765
    assert_equal [], Topic.includes(:replies).limit(1).where("0 = 1").pluck(:id)
766 767
  end

768 769 770 771 772
  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

773 774 775 776
  def test_pluck_with_join
    assert_equal [[2, 2], [4, 4]], Reply.includes(:topic).pluck(:id, :"topics.id")
  end

777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
  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

795
  def test_pluck_not_auto_table_name_prefix_if_column_included
796
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
B
Ben Toews 已提交
797
    ids = Company.includes(:contracts).pluck(:developer_id)
798 799 800
    assert_equal Company.count, ids.length
    assert_equal [7], ids.compact
  end
801 802 803 804

  def test_pluck_multiple_columns
    assert_equal [
      [1, "The First Topic"], [2, "The Second Topic of the day"],
805 806
      [3, "The Third Topic of the day"], [4, "The Fourth Topic of the day"],
      [5, "The Fifth Topic of the day"]
807
    ], Topic.order(:id).pluck(:id, :title)
808 809
    assert_equal [
      [1, "The First Topic", "David"], [2, "The Second Topic of the day", "Mary"],
810 811
      [3, "The Third Topic of the day", "Carl"], [4, "The Fourth Topic of the day", "Carl"],
      [5, "The Fifth Topic of the day", "Jason"]
812 813 814 815 816
    ], 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 已提交
817
      Account.pluck("id, credit_limit")
818 819 820
  end

  def test_pluck_with_multiple_columns_and_includes
821
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
B
Ben Toews 已提交
822
    companies_and_developers = Company.order("companies.id").includes(:contracts).pluck(:name, :developer_id)
823 824 825 826

    assert_equal Company.count, companies_and_developers.length
    assert_equal ["37signals", nil], companies_and_developers.first
    assert_equal ["test", 7], companies_and_developers.last
827
  end
828 829

  def test_pluck_with_reserved_words
830
    Possession.create!(where: "Over There")
831 832 833

    assert_equal ["Over There"], Possession.pluck(:where)
  end
834 835 836

  def test_pluck_replaces_select_clause
    taks_relation = Topic.select(:approved, :id).order(:id)
837
    assert_equal [1, 2, 3, 4, 5], taks_relation.pluck(:id)
838
    assert_equal [false, true, true, true, true], taks_relation.pluck(:approved)
839
  end
840 841 842 843

  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 已提交
844
      .pluck("topics.title", "replies_topics.title")
845 846
    assert_equal expected, actual
  end
847 848

  def test_calculation_with_polymorphic_relation
849 850 851 852
    part = ShipPart.create!(name: "has trinket")
    part.trinkets.create!

    assert_equal part.id, ShipPart.joins(:trinkets).sum(:id)
853 854
  end

S
Sean Griffin 已提交
855 856 857 858 859 860 861
  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

862 863
  def test_pluck_loaded_relation
    companies = Company.order(:id).limit(3).load
Y
yuuji.yaginuma 已提交
864

865
    assert_queries(0) do
866
      assert_equal ["37signals", "Summit", "Microsoft"], companies.pluck(:name)
867 868 869 870 871
    end
  end

  def test_pluck_loaded_relation_multiple_columns
    companies = Company.order(:id).limit(3).load
Y
yuuji.yaginuma 已提交
872

873
    assert_queries(0) do
874
      assert_equal [[1, "37signals"], [2, "Summit"], [3, "Microsoft"]], companies.pluck(:id, :name)
875 876 877 878
    end
  end

  def test_pluck_loaded_relation_sql_fragment
879
    companies = Company.order(:name).limit(3).load
Y
yuuji.yaginuma 已提交
880

881
    assert_queries(1) do
B
Ben Toews 已提交
882
      assert_equal ["37signals", "Apex", "Ex Nihilo"], companies.pluck(Arel.sql("DISTINCT name"))
883 884 885
    end
  end

886 887
  def test_pick_one
    assert_equal "The First Topic", Topic.order(:id).pick(:heading)
888
    assert_nil Topic.none.pick(:heading)
889
    assert_nil Topic.where(id: 9999999999999999999).pick(:heading)
890 891 892 893
  end

  def test_pick_two
    assert_equal ["David", "david@loudthinking.com"], Topic.order(:id).pick(:author_name, :author_email_address)
894
    assert_nil Topic.none.pick(:author_name, :author_email_address)
895
    assert_nil Topic.where(id: 9999999999999999999).pick(:author_name, :author_email_address)
896 897
  end

Y
Yuji Hanamura 已提交
898 899 900 901 902
  def test_pick_delegate_to_all
    cool_first = minivans(:cool_first)
    assert_equal cool_first.color, Minivan.pick(:color)
  end

903 904 905 906 907 908
  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
909 910 911 912 913

  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
914 915

  def test_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association
916
    assert_nothing_raised do
917
      developer = Developer.create!(name: "developer")
918 919 920
      developer.ratings.includes(comment: :post).where(posts: { id: 1 }).count
    end
  end
921 922 923 924 925 926 927 928 929 930

  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
931 932

  def test_having_with_strong_parameters
933
    params = ProtectedParams.new(credit_limit: "50")
934 935 936 937 938 939 940 941 942 943

    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
944 945

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

949 950 951
  def test_count_with_block_and_column_name_raises_an_error
    assert_raises(ArgumentError) do
      Account.count(:firm_id) { true }
952 953 954
    end
  end

955 956 957
  def test_sum_with_block_and_column_name_raises_an_error
    assert_raises(ArgumentError) do
      Account.sum(:firm_id) { 1 }
958 959
    end
  end
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 994 995 996 997 998 999 1000 1001

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