calculations_test.rb 34.5 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 143 144 145 146 147 148
  def test_should_not_use_alias_for_grouped_field
    assert_sql(/GROUP BY #{Regexp.escape(Account.connection.quote_table_name("accounts.firm_id"))}/i) do
      c = Account.group(:firm_id).order("accounts_firm_id").sum(:credit_limit)
      assert_equal [1, 2, 6, 9], c.keys.compact
    end
  end

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

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

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

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

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

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

178
  def test_limit_should_apply_before_count_arel_attribute
179
    accounts = Account.limit(4)
180 181 182 183 184 185

    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

186 187 188 189 190 191 192
  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 已提交
193 194
    return if current_adapter?(:OracleAdapter)

195
    queries = capture_sql { Account.limit(1).count }
196 197 198 199
    assert_equal 1, queries.length
    assert_match(/LIMIT/, queries.first)
  end

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

203
    queries = capture_sql { Account.offset(1).count }
204
    assert_equal 1, queries.length
205
    assert_match(/OFFSET/, queries.first)
206 207
  end

208 209 210
  def test_limit_with_offset_is_kept
    return if current_adapter?(:OracleAdapter)

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

  def test_no_limit_no_offset
218
    queries = capture_sql { Account.count }
219 220 221 222 223
    assert_equal 1, queries.length
    assert_no_match(/LIMIT/, queries.first)
    assert_no_match(/OFFSET/, queries.first)
  end

224 225 226 227 228
  def test_count_on_invalid_columns_raises
    e = assert_raises(ActiveRecord::StatementInvalid) {
      Account.select("credit_limit, firm_name").count
    }

G
Gannon McGibbon 已提交
229 230
    assert_match %r{accounts}i, e.sql
    assert_match "credit_limit, firm_name", e.sql
231 232
  end

233
  def test_apply_distinct_in_count
234
    queries = capture_sql do
235 236 237 238 239 240 241 242 243
      Account.distinct.count
      Account.group(:firm_id).distinct.count
    end

    queries.each do |query|
      assert_match %r{\ASELECT(?! DISTINCT) COUNT\(DISTINCT\b}, query
    end
  end

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

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

256 257 258 259 260 261
  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

262 263 264 265 266 267
  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

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

280 281 282 283 284 285 286 287 288 289 290 291
  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

292 293 294 295 296 297 298 299 300 301 302 303
  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

304 305 306 307
  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

308
  def test_should_group_by_summed_field_having_condition
309
    c = Account.group(:firm_id).having("sum(credit_limit) > 50").sum(:credit_limit)
310 311 312 313 314
    assert_nil        c[1]
    assert_equal 105, c[6]
    assert_equal 60,  c[2]
  end

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

323
  def test_should_group_by_summed_association
J
Jon Leighton 已提交
324
    c = Account.group(:firm).sum(:credit_limit)
325 326 327 328
    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 已提交
329

330
  def test_should_sum_field_with_conditions
331
    assert_equal 105, Account.where("firm_id = 6").sum(:credit_limit)
332 333
  end

334
  def test_should_return_zero_if_sum_conditions_return_nothing
335 336
    assert_equal 0, Account.where("1 = 2").sum(:credit_limit)
    assert_equal 0, companies(:rails_core).companies.where("1 = 2").sum(:id)
337 338
  end

339
  def test_sum_should_return_valid_values_for_decimals
340
    NumericData.create(bank_balance: 19.83)
341 342 343
    assert_equal 19.83, NumericData.sum(:bank_balance)
  end

344
  def test_should_return_type_casted_values_with_group_and_expression
345
    assert_equal 0.5, Account.group(:firm_name).sum("0.01 * credit_limit")["37signals"]
346 347
  end

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

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

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

370 371 372 373 374 375 376 377 378 379 380
  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

381
  def test_should_calculate_with_invalid_field
382
    assert_equal 6, Account.calculate(:count, "*")
383
    assert_equal 6, Account.calculate(:count, :all)
384
  end
J
Jeremy Kemper 已提交
385

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

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

400
  def test_should_group_by_association_with_non_numeric_foreign_key
401 402
    Speedometer.create! id: "ABC"
    Minivan.create! id: "OMG", speedometer_id: "ABC"
403

404
    c = Minivan.group(:speedometer).count(:all)
405
    first_key = c.keys.first
406
    assert_equal Speedometer, first_key.class
407
    assert_equal 1, c[first_key]
408
  end
J
Jeremy Kemper 已提交
409

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

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

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

434 435 436
  def test_should_not_overshadow_enumerable_sum
    assert_equal 6, [1, 2, 3].sum(&:abs)
  end
437 438 439 440 441

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

442 443 444 445
  def test_should_sum_scoped_field_with_from
    assert_equal Club.count, Organization.clubs.count
  end

446
  def test_should_sum_scoped_field_with_conditions
447
    assert_equal 8,  companies(:rails_core).companies.where("id > 7").sum(:id)
448 449 450
  end

  def test_should_group_by_scoped_field
J
Jon Leighton 已提交
451
    c = companies(:rails_core).companies.group(:name).sum(:id)
452 453
    assert_equal 7, c["Leetsoft"]
    assert_equal 8, c["Jadedpixel"]
454 455
  end

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

462
  def test_should_count_selected_field_with_include
463 464
    assert_equal 6, Account.includes(:firm).distinct.count
    assert_equal 4, Account.includes(:firm).distinct.select(:credit_limit).count
465 466
    assert_equal 4, Account.includes(:firm).distinct.count("DISTINCT credit_limit")
    assert_equal 4, Account.includes(:firm).distinct.count("DISTINCT(credit_limit)")
467
  end
J
Jeremy Kemper 已提交
468

469 470
  def test_should_not_perform_joined_include_by_default
    assert_equal Account.count, Account.includes(:firm).count
471
    queries = capture_sql { Account.includes(:firm).count }
472 473 474 475
    assert_no_match(/join/i, queries.last)
  end

  def test_should_perform_joined_include_when_referencing_included_tables
476
    joined_count = Account.includes(:firm).where(companies: { name: "37signals" }).count
477 478 479
    assert_equal 1, joined_count
  end

480
  def test_should_count_scoped_select
481
    Account.update_all("credit_limit = NULL")
482
    assert_equal 0, Account.select("credit_limit").count
483 484 485
  end

  def test_should_count_scoped_select_with_options
486
    Account.update_all("credit_limit = NULL")
487 488
    Account.last.update_columns("credit_limit" => 49)
    Account.first.update_columns("credit_limit" => 51)
489

490
    assert_equal 1, Account.select("credit_limit").where("credit_limit >= 50").count
491 492
  end

493
  def test_should_count_manual_select_with_include
494
    assert_equal 6, Account.select("DISTINCT accounts.id").includes(:firm).count
495 496
  end

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

515 516 517 518 519
  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

520 521 522
  def test_count_with_column_parameter
    assert_equal 5, Account.count(:firm_id)
  end
J
Jeremy Kemper 已提交
523

524 525 526 527 528 529 530 531
  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

532 533
  def test_count_with_distinct
    assert_equal 4, Account.select(:credit_limit).distinct.count
534 535
  end

536 537 538 539
  def test_count_with_aliased_attribute
    assert_equal 6, Account.count(:available_credit)
  end

540
  def test_count_with_column_and_options_parameter
J
Jon Leighton 已提交
541
    assert_equal 2, Account.where("credit_limit = 50 AND firm_id IS NOT NULL").count(:firm_id)
542
  end
J
Jeremy Kemper 已提交
543

O
oleg dashevskii 已提交
544
  def test_should_count_field_in_joined_table
545 546
    assert_equal 5, Account.joins(:firm).count("companies.id")
    assert_equal 4, Account.joins(:firm).distinct.count("companies.id")
O
oleg dashevskii 已提交
547 548
  end

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

562
    [1, 6, 2, 9].each { |firm_id| assert_includes c.keys, firm_id }
O
oleg dashevskii 已提交
563 564
  end

565
  def test_should_count_field_of_root_table_with_conflicting_group_by_column
566 567 568 569
    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)
570 571
  end

572 573 574 575 576 577 578
  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
579

L
Lauro Caetano 已提交
580 581 582 583 584 585 586 587 588
  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
589 590 591
    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 已提交
592 593
  end

594 595 596 597
  def test_count_with_block
    assert_equal 4, Account.count { |account| account.credit_limit.modulo(10).zero? }
  end

598
  def test_should_sum_expression
599
    assert_equal 636, Account.sum("2 * credit_limit")
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
  def test_group_by_with_order_by_virtual_count_attribute
    expected = { "SpecialPost" => 1, "StiPost" => 2 }
    actual = Post.group(:type).order(:count).limit(2).maximum(:comments_count)
    assert_equal expected, actual
  end if current_adapter?(:PostgreSQLAdapter)

783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
  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

801
  def test_pluck_not_auto_table_name_prefix_if_column_included
802
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
B
Ben Toews 已提交
803
    ids = Company.includes(:contracts).pluck(:developer_id)
804 805 806
    assert_equal Company.count, ids.length
    assert_equal [7], ids.compact
  end
807 808 809 810

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

  def test_pluck_with_multiple_columns_and_includes
827
    Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
B
Ben Toews 已提交
828
    companies_and_developers = Company.order("companies.id").includes(:contracts).pluck(:name, :developer_id)
829 830 831 832

    assert_equal Company.count, companies_and_developers.length
    assert_equal ["37signals", nil], companies_and_developers.first
    assert_equal ["test", 7], companies_and_developers.last
833
  end
834 835

  def test_pluck_with_reserved_words
836
    Possession.create!(where: "Over There")
837 838 839

    assert_equal ["Over There"], Possession.pluck(:where)
  end
840 841 842

  def test_pluck_replaces_select_clause
    taks_relation = Topic.select(:approved, :id).order(:id)
843
    assert_equal [1, 2, 3, 4, 5], taks_relation.pluck(:id)
844
    assert_equal [false, true, true, true, true], taks_relation.pluck(:approved)
845
  end
846 847 848

  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"]]
849
    actual = Topic.joins(:replies).order(:id)
B
Ben Toews 已提交
850
      .pluck("topics.title", "replies_topics.title")
851 852
    assert_equal expected, actual
  end
853 854

  def test_calculation_with_polymorphic_relation
855 856 857 858
    part = ShipPart.create!(name: "has trinket")
    part.trinkets.create!

    assert_equal part.id, ShipPart.joins(:trinkets).sum(:id)
859 860
  end

S
Sean Griffin 已提交
861 862 863 864 865 866 867
  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

868 869
  def test_pluck_loaded_relation
    companies = Company.order(:id).limit(3).load
Y
yuuji.yaginuma 已提交
870

871
    assert_queries(0) do
872
      assert_equal ["37signals", "Summit", "Microsoft"], companies.pluck(:name)
873 874 875 876 877
    end
  end

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

879
    assert_queries(0) do
880
      assert_equal [[1, "37signals"], [2, "Summit"], [3, "Microsoft"]], companies.pluck(:id, :name)
881 882 883 884
    end
  end

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

887
    assert_queries(1) do
B
Ben Toews 已提交
888
      assert_equal ["37signals", "Apex", "Ex Nihilo"], companies.pluck(Arel.sql("DISTINCT name"))
889 890 891
    end
  end

892 893
  def test_pick_one
    assert_equal "The First Topic", Topic.order(:id).pick(:heading)
894
    assert_nil Topic.none.pick(:heading)
895
    assert_nil Topic.where(id: 9999999999999999999).pick(:heading)
896 897 898 899
  end

  def test_pick_two
    assert_equal ["David", "david@loudthinking.com"], Topic.order(:id).pick(:author_name, :author_email_address)
900
    assert_nil Topic.none.pick(:author_name, :author_email_address)
901
    assert_nil Topic.where(id: 9999999999999999999).pick(:author_name, :author_email_address)
902 903
  end

Y
Yuji Hanamura 已提交
904 905 906 907 908
  def test_pick_delegate_to_all
    cool_first = minivans(:cool_first)
    assert_equal cool_first.color, Minivan.pick(:color)
  end

909 910 911 912 913 914
  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
915 916 917 918 919

  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
920 921

  def test_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association
922
    assert_nothing_raised do
923
      developer = Developer.create!(name: "developer")
924 925 926
      developer.ratings.includes(comment: :post).where(posts: { id: 1 }).count
    end
  end
927 928 929 930 931 932 933 934 935 936

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

  def test_having_with_strong_parameters
939
    params = ProtectedParams.new(credit_limit: "50")
940 941 942 943 944 945 946 947 948 949

    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
950 951

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

955 956 957
  def test_count_with_block_and_column_name_raises_an_error
    assert_raises(ArgumentError) do
      Account.count(:firm_id) { true }
958 959 960
    end
  end

961 962 963
  def test_sum_with_block_and_column_name_raises_an_error
    assert_raises(ArgumentError) do
      Account.sum(:firm_id) { 1 }
964 965
    end
  end
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 1002 1003 1004 1005 1006 1007

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