finder_test.rb 45.3 KB
Newer Older
1
require "cases/helper"
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
require "models/post"
require "models/author"
require "models/categorization"
require "models/comment"
require "models/company"
require "models/tagging"
require "models/topic"
require "models/reply"
require "models/entrant"
require "models/project"
require "models/developer"
require "models/computer"
require "models/customer"
require "models/toy"
require "models/matey"
require "models/dog"
require "models/car"
require "models/tyre"
D
David Heinemeier Hansson 已提交
20

21
class FinderTest < ActiveRecord::TestCase
R
Fix...  
Ryuta Kamizono 已提交
22
  fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :author_addresses, :customers, :categories, :categorizations, :cars
D
David Heinemeier Hansson 已提交
23

24
  def test_find_by_id_with_hash
25
    assert_nothing_raised do
26
      Post.find_by_id(limit: 1)
27 28 29 30
    end
  end

  def test_find_by_title_and_id_with_hash
31
    assert_nothing_raised do
32
      Post.find_by_title_and_id("foo", limit: 1)
33 34 35
    end
  end

D
David Heinemeier Hansson 已提交
36
  def test_find
37
    assert_equal(topics(:first).title, Topic.find(1).title)
D
David Heinemeier Hansson 已提交
38
  end
39

40
  def test_find_with_proc_parameter_and_block
S
Sean Griffin 已提交
41
    exception = assert_raises(RuntimeError) do
42 43
      Topic.all.find(-> { raise "should happen" }) { |e| e.title == "non-existing-title" }
    end
S
Sean Griffin 已提交
44
    assert_equal "should happen", exception.message
45

46
    assert_nothing_raised do
47 48 49 50
      Topic.all.find(-> { raise "should not happen" }) { |e| e.title == topics(:first).title }
    end
  end

51
  def test_find_with_ids_returning_ordered
52
    records = Topic.find([4, 2, 5])
53 54 55
    assert_equal "The Fourth Topic of the day", records[0].title
    assert_equal "The Second Topic of the day", records[1].title
    assert_equal "The Fifth Topic of the day", records[2].title
56

57
    records = Topic.find(4, 2, 5)
58 59 60
    assert_equal "The Fourth Topic of the day", records[0].title
    assert_equal "The Second Topic of the day", records[1].title
    assert_equal "The Fifth Topic of the day", records[2].title
61

62
    records = Topic.find(["4", "2", "5"])
63 64 65
    assert_equal "The Fourth Topic of the day", records[0].title
    assert_equal "The Second Topic of the day", records[1].title
    assert_equal "The Fifth Topic of the day", records[2].title
66

67
    records = Topic.find("4", "2", "5")
68 69 70
    assert_equal "The Fourth Topic of the day", records[0].title
    assert_equal "The Second Topic of the day", records[1].title
    assert_equal "The Fifth Topic of the day", records[2].title
71 72 73 74
  end

  def test_find_with_ids_and_order_clause
    # The order clause takes precedence over the informed ids
75
    records = Topic.order(:author_name).find([5, 3, 1])
76 77 78
    assert_equal "The Third Topic of the day", records[0].title
    assert_equal "The First Topic",            records[1].title
    assert_equal "The Fifth Topic of the day", records[2].title
79

80
    records = Topic.order(:id).find([5, 3, 1])
81 82 83
    assert_equal "The First Topic",            records[0].title
    assert_equal "The Third Topic of the day", records[1].title
    assert_equal "The Fifth Topic of the day", records[2].title
84 85
  end

86 87
  def test_find_with_ids_with_limit_and_order_clause
    # The order clause takes precedence over the informed ids
88
    records = Topic.limit(2).order(:id).find([5, 3, 1])
89
    assert_equal 2, records.size
90 91
    assert_equal "The First Topic",            records[0].title
    assert_equal "The Third Topic of the day", records[1].title
92 93 94
  end

  def test_find_with_ids_and_limit
95
    records = Topic.limit(3).find([3, 2, 5, 1, 4])
96
    assert_equal 3, records.size
97 98 99
    assert_equal "The Third Topic of the day",  records[0].title
    assert_equal "The Second Topic of the day", records[1].title
    assert_equal "The Fifth Topic of the day",  records[2].title
100 101
  end

102 103
  def test_find_with_ids_where_and_limit
    # Please note that Topic 1 is the only not approved so
104
    # if it were among the first 3 it would raise an ActiveRecord::RecordNotFound
105
    records = Topic.where(approved: true).limit(3).find([3, 2, 5, 1, 4])
106
    assert_equal 3, records.size
107 108 109
    assert_equal "The Third Topic of the day",  records[0].title
    assert_equal "The Second Topic of the day", records[1].title
    assert_equal "The Fifth Topic of the day",  records[2].title
110 111
  end

112
  def test_find_with_ids_and_offset
113
    records = Topic.offset(2).find([3, 2, 5, 1, 4])
114
    assert_equal 3, records.size
115 116 117
    assert_equal "The Fifth Topic of the day",  records[0].title
    assert_equal "The First Topic",             records[1].title
    assert_equal "The Fourth Topic of the day", records[2].title
118 119
  end

120
  def test_find_passing_active_record_object_is_not_permitted
121
    assert_raises(ArgumentError) do
122 123 124 125
      Topic.find(Topic.last)
    end
  end

126
  def test_symbols_table_ref
127
    gc_disabled = GC.disable
128
    Post.where("author_id" => nil)  # warm up
129
    x = Symbol.all_symbols.count
130
    Post.where("title" => { "xxxqqqq" => "bar" })
131
    assert_equal x, Symbol.all_symbols.count
R
Ryuta Kamizono 已提交
132 133
  ensure
    GC.enable if gc_disabled == false
134 135
  end

136 137 138
  # find should handle strings that come from URLs
  # (example: Category.find(params[:id]))
  def test_find_with_string
139
    assert_equal(Topic.find(1).title, Topic.find("1").title)
140
  end
141

D
David Heinemeier Hansson 已提交
142
  def test_exists
143 144 145 146
    assert_equal true, Topic.exists?(1)
    assert_equal true, Topic.exists?("1")
    assert_equal true, Topic.exists?(title: "The First Topic")
    assert_equal true, Topic.exists?(heading: "The First Topic")
147
    assert_equal true, Topic.exists?(author_name: "Mary", approved: true)
148
    assert_equal true, Topic.exists?(["parent_id = ?", 1])
149
    assert_equal true, Topic.exists?(id: [1, 9999])
150 151

    assert_equal false, Topic.exists?(45)
152
    assert_equal false, Topic.exists?(Topic.new.id)
153

154
    assert_raise(NoMethodError) { Topic.exists?([1, 2]) }
155 156
  end

157
  def test_exists_with_polymorphic_relation
158 159
    post = Post.create!(title: "Post", body: "default", taggings: [Tagging.new(comment: "tagging comment")])
    relation = Post.tagged_with_comment("tagging comment")
160

161 162
    assert_equal true, relation.exists?(title: ["Post"])
    assert_equal true, relation.exists?(["title LIKE ?", "Post%"])
163 164 165 166 167 168 169
    assert_equal true, relation.exists?
    assert_equal true, relation.exists?(post.id)
    assert_equal true, relation.exists?(post.id.to_s)

    assert_equal false, relation.exists?(false)
  end

170
  def test_exists_passing_active_record_object_is_not_permitted
171
    assert_raises(ArgumentError) do
172 173 174 175
      Topic.exists?(Topic.new)
    end
  end

176
  def test_exists_returns_false_when_parameter_has_invalid_type
177
    assert_equal false, Topic.exists?("foo")
178
    assert_equal false, Topic.exists?(("9" * 53).to_i) # number that's bigger than int
D
David Heinemeier Hansson 已提交
179
  end
180

181
  def test_exists_does_not_select_columns_without_alias
182
    assert_sql(/SELECT\W+1 AS one FROM ["`]topics["`]/i) do
183 184 185 186
      Topic.exists?
    end
  end

187
  def test_exists_returns_true_with_one_record_and_no_args
188
    assert_equal true, Topic.exists?
189
  end
190

E
Egor Lynko 已提交
191
  def test_exists_returns_false_with_false_arg
192
    assert_equal false, Topic.exists?(false)
E
Egor Lynko 已提交
193 194
  end

195 196 197
  # exists? should handle nil for id's that come from URLs and always return false
  # (example: Topic.exists?(params[:id])) where params[:id] is nil
  def test_exists_with_nil_arg
198 199 200 201 202
    assert_equal false, Topic.exists?(nil)
    assert_equal true, Topic.exists?

    assert_equal false, Topic.first.replies.exists?(nil)
    assert_equal true, Topic.first.replies.exists?
203 204
  end

R
Ryuta Kamizono 已提交
205 206 207
  # Ensure +exists?+ runs without an error by excluding distinct value.
  # See https://github.com/rails/rails/pull/26981.
  def test_exists_with_order_and_distinct
208
    assert_equal true, Topic.order(:id).distinct.exists?
209 210
  end

211 212 213 214 215
  # Ensure +exists?+ runs without an error by excluding order value.
  def test_exists_with_order
    assert_equal true, Topic.order("invalid sql here").exists?
  end

R
Ryuta Kamizono 已提交
216 217 218 219 220 221 222 223 224 225 226 227
  def test_exists_with_joins
    assert_equal true, Topic.joins(:replies).where(replies_topics: { approved: true }).order("replies_topics.created_at DESC").exists?
  end

  def test_exists_with_left_joins
    assert_equal true, Topic.left_joins(:replies).where(replies_topics: { approved: true }).order("replies_topics.created_at DESC").exists?
  end

  def test_exists_with_eager_load
    assert_equal true, Topic.eager_load(:replies).where(replies_topics: { approved: true }).order("replies_topics.created_at DESC").exists?
  end

228
  def test_exists_with_includes_limit_and_empty_result
229
    assert_equal false, Topic.includes(:replies).limit(0).exists?
230
    assert_equal false, Topic.includes(:replies).limit(1).where("0 = 1").exists?
231 232
  end

233 234
  def test_exists_with_distinct_association_includes_and_limit
    author = Author.first
235 236
    assert_equal false, author.unique_categorized_posts.includes(:special_comments).limit(0).exists?
    assert_equal true, author.unique_categorized_posts.includes(:special_comments).limit(1).exists?
237 238 239 240
  end

  def test_exists_with_distinct_association_includes_limit_and_order
    author = Author.first
241 242
    assert_equal false, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(0).exists?
    assert_equal true, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(1).exists?
243 244
  end

245
  def test_exists_with_empty_table_and_no_args_given
246
    Topic.delete_all
247
    assert_equal false, Topic.exists?
248
  end
249

250 251
  def test_exists_with_aggregate_having_three_mappings
    existing_address = customers(:david).address
252
    assert_equal true, Customer.exists?(address: existing_address)
253 254 255 256
  end

  def test_exists_with_aggregate_having_three_mappings_with_one_difference
    existing_address = customers(:david).address
R
Ryuta Kamizono 已提交
257 258 259
    assert_equal false, Customer.exists?(address: Address.new(existing_address.street, existing_address.city, existing_address.country + "1"))
    assert_equal false, Customer.exists?(address: Address.new(existing_address.street, existing_address.city + "1", existing_address.country))
    assert_equal false, Customer.exists?(address: Address.new(existing_address.street + "1", existing_address.city, existing_address.country))
260 261
  end

262
  def test_exists_does_not_instantiate_records
263 264 265
    assert_not_called(Developer, :instantiate) do
      Developer.exists?
    end
266 267
  end

D
David Heinemeier Hansson 已提交
268 269 270 271
  def test_find_by_array_of_one_id
    assert_kind_of(Array, Topic.find([ 1 ]))
    assert_equal(1, Topic.find([ 1 ]).length)
  end
272

D
David Heinemeier Hansson 已提交
273
  def test_find_by_ids
274 275 276 277 278
    assert_equal 2, Topic.find(1, 2).size
    assert_equal topics(:second).title, Topic.find([2]).first.title
  end

  def test_find_by_ids_with_limit_and_offset
279 280
    assert_equal 2, Entrant.limit(2).find([1, 3, 2]).size
    entrants = Entrant.limit(3).offset(2).find([1, 3, 2])
281
    assert_equal 1, entrants.size
282
    assert_equal "Ruby Guru", entrants.first.name
283 284 285 286

    # Also test an edge case: If you have 11 results, and you set a
    #   limit of 3 and offset of 9, then you should find that there
    #   will be only 2 results, regardless of the limit.
J
Jon Leighton 已提交
287
    devs = Developer.all
288
    last_devs = Developer.limit(3).offset(9).find devs.map(&:id)
289
    assert_equal 2, last_devs.size
290 291
    assert_equal "fixture_10", last_devs[0].name
    assert_equal "Jamis", last_devs[1].name
D
David Heinemeier Hansson 已提交
292 293
  end

294
  def test_find_with_large_number
295
    assert_raises(ActiveRecord::RecordNotFound) { Topic.find("9999999999999999999999999999999") }
296 297 298
  end

  def test_find_by_with_large_number
299
    assert_nil Topic.find_by(id: "9999999999999999999999999999999")
300 301 302
  end

  def test_find_by_id_with_large_number
303
    assert_nil Topic.find_by_id("9999999999999999999999999999999")
304 305
  end

306
  def test_find_on_relation_with_large_number
307
    assert_nil Topic.where("1=1").find_by(id: 9999999999999999999999999999999)
308 309 310 311
  end

  def test_find_by_bang_on_relation_with_large_number
    assert_raises(ActiveRecord::RecordNotFound) do
312
      Topic.where("1=1").find_by!(id: 9999999999999999999999999999999)
313 314 315
    end
  end

316 317 318 319
  def test_find_an_empty_array
    assert_equal [], Topic.find([])
  end

320 321 322 323
  def test_find_doesnt_have_implicit_ordering
    assert_sql(/^((?!ORDER).)*$/) { Topic.find(1) }
  end

D
David Heinemeier Hansson 已提交
324
  def test_find_by_ids_missing_one
325
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
D
David Heinemeier Hansson 已提交
326
  end
327

328
  def test_find_with_group_and_sanitized_having_method
329
    developers = Developer.group(:salary).having("sum(salary) > ?", 10000).select("salary").to_a
330 331 332 333 334
    assert_equal 3, developers.size
    assert_equal 3, developers.map(&:salary).uniq.size
    assert developers.all? { |developer| developer.salary > 10000 }
  end

D
David Heinemeier Hansson 已提交
335 336
  def test_find_with_entire_select_statement
    topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
337

D
David Heinemeier Hansson 已提交
338
    assert_equal(1, topics.size)
339
    assert_equal(topics(:second).title, topics.first.title)
D
David Heinemeier Hansson 已提交
340
  end
341

D
David Heinemeier Hansson 已提交
342 343
  def test_find_with_prepared_select_statement
    topics = Topic.find_by_sql ["SELECT * FROM topics WHERE author_name = ?", "Mary"]
344

D
David Heinemeier Hansson 已提交
345
    assert_equal(1, topics.size)
346
    assert_equal(topics(:second).title, topics.first.title)
D
David Heinemeier Hansson 已提交
347
  end
348

349 350 351 352
  def test_find_by_sql_with_sti_on_joined_table
    accounts = Account.find_by_sql("SELECT * FROM accounts INNER JOIN companies ON companies.id = accounts.firm_id")
    assert_equal [Account], accounts.collect(&:class).uniq
  end
353

354 355 356 357 358 359
  def test_find_by_association_subquery
    author = authors(:david)
    assert_equal author.post, Post.find_by(author: Author.where(id: author))
    assert_equal author.post, Post.find_by(author_id: Author.where(id: author))
  end

360 361 362 363 364
  def test_find_by_and_where_consistency_with_active_record_instance
    author = authors(:david)
    assert_equal Post.where(author_id: author).take, Post.find_by(author_id: author)
  end

365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
  def test_take
    assert_equal topics(:first), Topic.take
  end

  def test_take_failing
    assert_nil Topic.where("title = 'This title does not exist'").take
  end

  def test_take_bang_present
    assert_nothing_raised do
      assert_equal topics(:second), Topic.where("title = 'The Second Topic of the day'").take!
    end
  end

  def test_take_bang_missing
380
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
381 382 383 384
      Topic.where("title = 'This title does not exist'").take!
    end
  end

385
  def test_first
386
    assert_equal topics(:second).title, Topic.where("title = 'The Second Topic of the day'").first.title
387
  end
388

389
  def test_first_failing
390
    assert_nil Topic.where("title = 'The Second Topic of the day!'").first
391
  end
D
David Heinemeier Hansson 已提交
392

393 394 395 396 397 398 399
  def test_first_bang_present
    assert_nothing_raised do
      assert_equal topics(:second), Topic.where("title = 'The Second Topic of the day'").first!
    end
  end

  def test_first_bang_missing
400
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
401 402 403 404
      Topic.where("title = 'This title does not exist'").first!
    end
  end

405 406 407 408 409 410
  def test_first_have_primary_key_order_by_default
    expected = topics(:first)
    expected.touch # PostgreSQL changes the default order if no order clause is used
    assert_equal expected, Topic.first
  end

411
  def test_model_class_responds_to_first_bang
412 413
    assert Topic.first!
    Topic.delete_all
414
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
415 416 417 418
      Topic.first!
    end
  end

419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
  def test_second
    assert_equal topics(:second).title, Topic.second.title
  end

  def test_second_with_offset
    assert_equal topics(:fifth), Topic.offset(3).second
  end

  def test_second_have_primary_key_order_by_default
    expected = topics(:second)
    expected.touch # PostgreSQL changes the default order if no order clause is used
    assert_equal expected, Topic.second
  end

  def test_model_class_responds_to_second_bang
    assert Topic.second!
    Topic.delete_all
436
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
      Topic.second!
    end
  end

  def test_third
    assert_equal topics(:third).title, Topic.third.title
  end

  def test_third_with_offset
    assert_equal topics(:fifth), Topic.offset(2).third
  end

  def test_third_have_primary_key_order_by_default
    expected = topics(:third)
    expected.touch # PostgreSQL changes the default order if no order clause is used
    assert_equal expected, Topic.third
  end

  def test_model_class_responds_to_third_bang
    assert Topic.third!
    Topic.delete_all
458
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479
      Topic.third!
    end
  end

  def test_fourth
    assert_equal topics(:fourth).title, Topic.fourth.title
  end

  def test_fourth_with_offset
    assert_equal topics(:fifth), Topic.offset(1).fourth
  end

  def test_fourth_have_primary_key_order_by_default
    expected = topics(:fourth)
    expected.touch # PostgreSQL changes the default order if no order clause is used
    assert_equal expected, Topic.fourth
  end

  def test_model_class_responds_to_fourth_bang
    assert Topic.fourth!
    Topic.delete_all
480
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
      Topic.fourth!
    end
  end

  def test_fifth
    assert_equal topics(:fifth).title, Topic.fifth.title
  end

  def test_fifth_with_offset
    assert_equal topics(:fifth), Topic.offset(0).fifth
  end

  def test_fifth_have_primary_key_order_by_default
    expected = topics(:fifth)
    expected.touch # PostgreSQL changes the default order if no order clause is used
    assert_equal expected, Topic.fifth
  end

  def test_model_class_responds_to_fifth_bang
    assert Topic.fifth!
    Topic.delete_all
502
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
503 504 505 506
      Topic.fifth!
    end
  end

507 508
  def test_second_to_last
    assert_equal topics(:fourth).title, Topic.second_to_last.title
509 510 511

    # test with offset
    assert_equal topics(:fourth), Topic.offset(1).second_to_last
512 513
    assert_equal topics(:fourth), Topic.offset(2).second_to_last
    assert_equal topics(:fourth), Topic.offset(3).second_to_last
514 515
    assert_nil Topic.offset(4).second_to_last
    assert_nil Topic.offset(5).second_to_last
516 517

    #test with limit
518
    assert_nil Topic.limit(1).second
519
    assert_nil Topic.limit(1).second_to_last
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
  end

  def test_second_to_last_have_primary_key_order_by_default
    expected = topics(:fourth)
    expected.touch # PostgreSQL changes the default order if no order clause is used
    assert_equal expected, Topic.second_to_last
  end

  def test_model_class_responds_to_second_to_last_bang
    assert Topic.second_to_last!
    Topic.delete_all
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
      Topic.second_to_last!
    end
  end

  def test_third_to_last
    assert_equal topics(:third).title, Topic.third_to_last.title
538 539 540

    # test with offset
    assert_equal topics(:third), Topic.offset(1).third_to_last
541
    assert_equal topics(:third), Topic.offset(2).third_to_last
542 543 544
    assert_nil Topic.offset(3).third_to_last
    assert_nil Topic.offset(4).third_to_last
    assert_nil Topic.offset(5).third_to_last
545 546

    # test with limit
547
    assert_nil Topic.limit(1).third
548
    assert_nil Topic.limit(1).third_to_last
549
    assert_nil Topic.limit(2).third
550
    assert_nil Topic.limit(2).third_to_last
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566
  end

  def test_third_to_last_have_primary_key_order_by_default
    expected = topics(:third)
    expected.touch # PostgreSQL changes the default order if no order clause is used
    assert_equal expected, Topic.third_to_last
  end

  def test_model_class_responds_to_third_to_last_bang
    assert Topic.third_to_last!
    Topic.delete_all
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
      Topic.third_to_last!
    end
  end

567 568 569 570 571 572 573
  def test_last_bang_present
    assert_nothing_raised do
      assert_equal topics(:second), Topic.where("title = 'The Second Topic of the day'").last!
    end
  end

  def test_last_bang_missing
574
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
575 576 577 578
      Topic.where("title = 'This title does not exist'").last!
    end
  end

579
  def test_model_class_responds_to_last_bang
580
    assert_equal topics(:fifth), Topic.last!
581
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
582 583 584 585 586
      Topic.delete_all
      Topic.last!
    end
  end

587
  def test_take_and_first_and_last_with_integer_should_use_sql_limit
588 589 590
    assert_sql(/LIMIT|ROWNUM <=|FETCH FIRST/) { Topic.take(3).entries }
    assert_sql(/LIMIT|ROWNUM <=|FETCH FIRST/) { Topic.first(2).entries }
    assert_sql(/LIMIT|ROWNUM <=|FETCH FIRST/) { Topic.last(5).entries }
591 592 593 594 595 596
  end

  def test_last_with_integer_and_order_should_keep_the_order
    assert_equal Topic.order("title").to_a.last(2), Topic.order("title").last(2)
  end

597 598 599 600
  def test_last_with_integer_and_order_should_use_sql_limit
    relation = Topic.order("title")
    assert_queries(1) { relation.last(5) }
    assert !relation.loaded?
601 602
  end

603 604 605 606 607 608 609
  def test_last_with_integer_and_reorder_should_use_sql_limit
    relation = Topic.reorder("title")
    assert_queries(1) { relation.last(5) }
    assert !relation.loaded?
  end

  def test_last_on_loaded_relation_should_not_use_sql
610
    relation = Topic.limit(10).load
611 612 613 614 615 616 617
    assert_no_queries do
      relation.last
      relation.last(2)
    end
  end

  def test_last_with_irreversible_order
618
    assert_raises(ActiveRecord::IrreversibleOrderError) do
619 620
      Topic.order("coalesce(author_name, title)").last
    end
621
  end
622 623

  def test_last_on_relation_with_limit_and_offset
624
    post = posts("sti_comments")
625 626 627 628 629 630 631 632 633 634

    comments = post.comments.order(id: :asc)
    assert_equal comments.limit(2).to_a.last, comments.limit(2).last
    assert_equal comments.limit(2).to_a.last(2), comments.limit(2).last(2)
    assert_equal comments.limit(2).to_a.last(3), comments.limit(2).last(3)

    comments = comments.offset(1)
    assert_equal comments.limit(2).to_a.last, comments.limit(2).last
    assert_equal comments.limit(2).to_a.last(2), comments.limit(2).last(2)
    assert_equal comments.limit(2).to_a.last(3), comments.limit(2).last(3)
635 636
  end

637 638
  def test_take_and_first_and_last_with_integer_should_return_an_array
    assert_kind_of Array, Topic.take(5)
639 640 641 642
    assert_kind_of Array, Topic.first(5)
    assert_kind_of Array, Topic.last(5)
  end

D
David Heinemeier Hansson 已提交
643
  def test_unexisting_record_exception_handling
644
    assert_raise(ActiveRecord::RecordNotFound) {
D
David Heinemeier Hansson 已提交
645 646
      Topic.find(1).parent
    }
647

648
    Topic.find(2).topic
D
David Heinemeier Hansson 已提交
649
  end
650

651
  def test_find_only_some_columns
652
    topic = Topic.select("author_name").find(1)
653 654
    assert_raise(ActiveModel::MissingAttributeError) { topic.title }
    assert_raise(ActiveModel::MissingAttributeError) { topic.title? }
655
    assert_nil topic.read_attribute("title")
656 657
    assert_equal "David", topic.author_name
    assert !topic.attribute_present?("title")
658
    assert !topic.attribute_present?(:title)
659
    assert topic.attribute_present?("author_name")
660
    assert_respond_to topic, "author_name"
661
  end
J
Jeremy Kemper 已提交
662

663
  def test_find_on_array_conditions
664 665
    assert Topic.where(["approved = ?", false]).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(["approved = ?", true]).find(1) }
D
David Heinemeier Hansson 已提交
666
  end
667

668
  def test_find_on_hash_conditions
669 670
    assert Topic.where(approved: false).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(approved: true).find(1) }
671
  end
672

673
  def test_find_on_hash_conditions_with_qualified_attribute_dot_notation_string
674 675
    assert Topic.where("topics.approved" => false).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where("topics.approved" => true).find(1) }
676 677
  end

678 679 680 681 682
  def test_find_on_hash_conditions_with_qualified_attribute_dot_notation_symbol
    assert Topic.where('topics.approved': false).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where('topics.approved': true).find(1) }
  end

683
  def test_find_on_hash_conditions_with_hashed_table_name
684 685
    assert Topic.where(topics: { approved: false }).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(topics: { approved: true }).find(1) }
686 687
  end

688
  def test_find_on_combined_explicit_and_hashed_table_names
689 690 691
    assert Topic.where("topics.approved" => false, topics: { author_name: "David" }).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where("topics.approved" => true, topics: { author_name: "David" }).find(1) }
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where("topics.approved" => false, topics: { author_name: "Melanie" }).find(1) }
692 693
  end

694
  def test_find_with_hash_conditions_on_joined_table
695
    firms = Firm.joins(:account).where(accounts: { credit_limit: 50 })
696 697 698 699 700
    assert_equal 1, firms.size
    assert_equal companies(:first_firm), firms.first
  end

  def test_find_with_hash_conditions_on_joined_table_and_with_range
701
    firms = DependentFirm.joins(:account).where(name: "RailsCore", accounts: { credit_limit: 55..60 })
702 703 704 705
    assert_equal 1, firms.size
    assert_equal companies(:rails_core), firms.first
  end

706 707
  def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate
    david = customers(:david)
708
    assert Customer.where("customers.name" => david.name, :address => david.address).find(david.id)
709
    assert_raise(ActiveRecord::RecordNotFound) {
710
      Customer.where("customers.name" => david.name + "1", :address => david.address).find(david.id)
711 712 713
    }
  end

714
  def test_find_on_association_proxy_conditions
J
Jon Leighton 已提交
715
    assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10, 12], Comment.where(post_id: authors(:david).posts).map(&:id).sort
716 717
  end

718
  def test_find_on_hash_conditions_with_range
719
    assert_equal [1, 2], Topic.where(id: 1..2).to_a.map(&:id).sort
720
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2..3).find(1) }
721
  end
722

723
  def test_find_on_hash_conditions_with_end_exclusive_range
724 725
    assert_equal [1, 2, 3], Topic.where(id: 1..3).to_a.map(&:id).sort
    assert_equal [1, 2], Topic.where(id: 1...3).to_a.map(&:id).sort
726
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2...3).find(3) }
727 728
  end

729
  def test_find_on_hash_conditions_with_multiple_ranges
730
    assert_equal [1, 2, 3], Comment.where(id: 1..3, post_id: 1..2).to_a.map(&:id).sort
731
    assert_equal [1], Comment.where(id: 1..1, post_id: 1..10).to_a.map(&:id).sort
732
  end
733

734
  def test_find_on_hash_conditions_with_array_of_integers_and_ranges
735
    assert_equal [1, 2, 3, 5, 6, 7, 8, 9], Comment.where(id: [1..2, 3, 5, 6..8, 9]).to_a.map(&:id).sort
736 737
  end

738
  def test_find_on_hash_conditions_with_array_of_ranges
739
    assert_equal [1, 2, 6, 7, 8], Comment.where(id: [1..2, 6..8]).to_a.map(&:id).sort
740 741
  end

742
  def test_find_on_multiple_hash_conditions
743 744 745
    assert Topic.where(author_name: "David", title: "The First Topic", replies_count: 1, approved: false).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(author_name: "David", title: "The First Topic", replies_count: 1, approved: true).find(1) }
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(author_name: "David", title: "HHC", replies_count: 1, approved: false).find(1) }
746
  end
747

748
  def test_condition_interpolation
749
    assert_kind_of Firm, Company.where("name = '%s'", "37signals").first
750 751 752
    assert_nil Company.where(["name = '%s'", "37signals!"]).first
    assert_nil Company.where(["name = '%s'", "37signals!' OR 1=1"]).first
    assert_kind_of Time, Topic.where(["id = %d", 1]).first.written_on
753 754
  end

755
  def test_condition_array_interpolation
756 757 758 759
    assert_kind_of Firm, Company.where(["name = '%s'", "37signals"]).first
    assert_nil Company.where(["name = '%s'", "37signals!"]).first
    assert_nil Company.where(["name = '%s'", "37signals!' OR 1=1"]).first
    assert_kind_of Time, Topic.where(["id = %d", 1]).first.written_on
D
David Heinemeier Hansson 已提交
760
  end
761

762
  def test_condition_hash_interpolation
763 764 765
    assert_kind_of Firm, Company.where(name: "37signals").first
    assert_nil Company.where(name: "37signals!").first
    assert_kind_of Time, Topic.where(id: 1).first.written_on
766
  end
767

768
  def test_hash_condition_find_malformed
769
    assert_raise(ActiveRecord::StatementInvalid) {
770
      Company.where(id: 2, dhh: true).first
771 772
    }
  end
773

774 775
  def test_hash_condition_find_with_escaped_characters
    Company.create("name" => "Ain't noth'n like' \#stuff")
776
    assert Company.where(name: "Ain't noth'n like' \#stuff").first
777 778 779
  end

  def test_hash_condition_find_with_array
780 781 782
    p1, p2 = Post.limit(2).order("id asc").to_a
    assert_equal [p1, p2], Post.where(id: [p1, p2]).order("id asc").to_a
    assert_equal [p1, p2], Post.where(id: [p1, p2.id]).order("id asc").to_a
783 784 785
  end

  def test_hash_condition_find_with_nil
786
    topic = Topic.where(last_read: nil).first
787 788
    assert_not_nil topic
    assert_nil topic.last_read
789
  end
D
David Heinemeier Hansson 已提交
790

791 792 793
  def test_hash_condition_find_with_aggregate_having_one_mapping
    balance = customers(:david).balance
    assert_kind_of Money, balance
794
    found_customer = Customer.where(balance: balance).first
795 796 797 798 799 800
    assert_equal customers(:david), found_customer
  end

  def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_aggregate
    gps_location = customers(:david).gps_location
    assert_kind_of GpsLocation, gps_location
801
    found_customer = Customer.where(gps_location: gps_location).first
802 803 804 805 806 807
    assert_equal customers(:david), found_customer
  end

  def test_hash_condition_find_with_aggregate_having_one_mapping_and_key_value_being_attribute_value
    balance = customers(:david).balance
    assert_kind_of Money, balance
808
    found_customer = Customer.where(balance: balance.amount).first
809 810 811 812 813 814
    assert_equal customers(:david), found_customer
  end

  def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_attribute_value
    gps_location = customers(:david).gps_location
    assert_kind_of GpsLocation, gps_location
815
    found_customer = Customer.where(gps_location: gps_location.gps_location).first
816 817 818 819 820 821
    assert_equal customers(:david), found_customer
  end

  def test_hash_condition_find_with_aggregate_having_three_mappings
    address = customers(:david).address
    assert_kind_of Address, address
822
    found_customer = Customer.where(address: address).first
823 824 825 826 827 828
    assert_equal customers(:david), found_customer
  end

  def test_hash_condition_find_with_one_condition_being_aggregate_and_another_not
    address = customers(:david).address
    assert_kind_of Address, address
829
    found_customer = Customer.where(address: address, name: customers(:david).name).first
830 831 832
    assert_equal customers(:david), found_customer
  end

833
  def test_condition_utc_time_interpolation_with_default_timezone_local
834
    with_env_tz "America/New_York" do
835
      with_timezone_config default: :local do
836
        topic = Topic.first
837
        assert_equal topic, Topic.where(["written_on = ?", topic.written_on.getutc]).first
838 839 840 841 842
      end
    end
  end

  def test_hash_condition_utc_time_interpolation_with_default_timezone_local
843
    with_env_tz "America/New_York" do
844
      with_timezone_config default: :local do
845
        topic = Topic.first
846
        assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first
847 848 849 850 851
      end
    end
  end

  def test_condition_local_time_interpolation_with_default_timezone_utc
852
    with_env_tz "America/New_York" do
853
      with_timezone_config default: :utc do
854
        topic = Topic.first
855
        assert_equal topic, Topic.where(["written_on = ?", topic.written_on.getlocal]).first
856 857 858 859 860
      end
    end
  end

  def test_hash_condition_local_time_interpolation_with_default_timezone_utc
861
    with_env_tz "America/New_York" do
862
      with_timezone_config default: :utc do
863
        topic = Topic.first
864
        assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first
865 866 867 868
      end
    end
  end

D
David Heinemeier Hansson 已提交
869
  def test_bind_variables
870 871 872 873
    assert_kind_of Firm, Company.where(["name = ?", "37signals"]).first
    assert_nil Company.where(["name = ?", "37signals!"]).first
    assert_nil Company.where(["name = ?", "37signals!' OR 1=1"]).first
    assert_kind_of Time, Topic.where(["id = ?", 1]).first.written_on
874
    assert_raise(ActiveRecord::PreparedStatementInvalid) {
875
      Company.where(["id=? AND name = ?", 2]).first
D
David Heinemeier Hansson 已提交
876
    }
877
    assert_raise(ActiveRecord::PreparedStatementInvalid) {
878
      Company.where(["id=?", 2, 3, 4]).first
D
David Heinemeier Hansson 已提交
879 880
    }
  end
881

D
David Heinemeier Hansson 已提交
882
  def test_bind_variables_with_quotes
B
Benjamin Fleischer 已提交
883 884
    Company.create("name" => "37signals' go'es against")
    assert Company.where(["name = ?", "37signals' go'es against"]).first
D
David Heinemeier Hansson 已提交
885 886 887
  end

  def test_named_bind_variables_with_quotes
B
Benjamin Fleischer 已提交
888 889
    Company.create("name" => "37signals' go'es against")
    assert Company.where(["name = :name", { name: "37signals' go'es against" }]).first
D
David Heinemeier Hansson 已提交
890 891 892
  end

  def test_named_bind_variables
893 894 895 896
    assert_kind_of Firm, Company.where(["name = :name", { name: "37signals" }]).first
    assert_nil Company.where(["name = :name", { name: "37signals!" }]).first
    assert_nil Company.where(["name = :name", { name: "37signals!' OR 1=1" }]).first
    assert_kind_of Time, Topic.where(["id = :id", { id: 1 }]).first.written_on
D
David Heinemeier Hansson 已提交
897 898 899 900 901 902 903 904 905
  end

  def test_count_by_sql
    assert_equal(0, Entrant.count_by_sql("SELECT COUNT(*) FROM entrants WHERE id > 3"))
    assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
    assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
  end

  def test_find_by_one_attribute
906
    assert_equal topics(:first), Topic.find_by_title("The First Topic")
D
David Heinemeier Hansson 已提交
907 908
    assert_nil Topic.find_by_title("The First Topic!")
  end
J
Jeremy Kemper 已提交
909

910 911
  def test_find_by_one_attribute_bang
    assert_equal topics(:first), Topic.find_by_title!("The First Topic")
912 913 914
    assert_raises_with_message(ActiveRecord::RecordNotFound, "Couldn't find Topic") do
      Topic.find_by_title!("The First Topic!")
    end
915 916
  end

917
  def test_find_by_on_attribute_that_is_a_reserved_word
918
    dog_alias = "Dog"
919 920 921 922 923
    dog = Dog.create(alias: dog_alias)

    assert_equal dog, Dog.find_by_alias(dog_alias)
  end

924 925 926 927 928
  def test_find_by_one_attribute_that_is_an_alias
    assert_equal topics(:first), Topic.find_by_heading("The First Topic")
    assert_nil Topic.find_by_heading("The First Topic!")
  end

N
Nikita Afanasenko 已提交
929
  def test_find_by_one_attribute_bang_with_blank_defined
930 931
    blank_topic = BlankTopic.create(title: "The Blank One")
    assert_equal blank_topic, BlankTopic.find_by_title!("The Blank One")
N
Nikita Afanasenko 已提交
932 933
  end

934
  def test_find_by_one_attribute_with_conditions
935
    assert_equal accounts(:rails_core_account), Account.where("firm_id = ?", 6).find_by_credit_limit(50)
936 937
  end

938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
  def test_find_by_one_attribute_that_is_an_aggregate
    address = customers(:david).address
    assert_kind_of Address, address
    found_customer = Customer.find_by_address(address)
    assert_equal customers(:david), found_customer
  end

  def test_find_by_one_attribute_that_is_an_aggregate_with_one_attribute_difference
    address = customers(:david).address
    assert_kind_of Address, address
    missing_address = Address.new(address.street, address.city, address.country + "1")
    assert_nil Customer.find_by_address(missing_address)
    missing_address = Address.new(address.street, address.city + "1", address.country)
    assert_nil Customer.find_by_address(missing_address)
    missing_address = Address.new(address.street + "1", address.city, address.country)
    assert_nil Customer.find_by_address(missing_address)
  end

  def test_find_by_two_attributes_that_are_both_aggregates
    balance = customers(:david).balance
    address = customers(:david).address
    assert_kind_of Money, balance
    assert_kind_of Address, address
    found_customer = Customer.find_by_balance_and_address(balance, address)
    assert_equal customers(:david), found_customer
  end

  def test_find_by_two_attributes_with_one_being_an_aggregate
    balance = customers(:david).balance
    assert_kind_of Money, balance
    found_customer = Customer.find_by_balance_and_name(balance, customers(:david).name)
    assert_equal customers(:david), found_customer
  end

972 973
  def test_dynamic_finder_on_one_attribute_with_conditions_returns_same_results_after_caching
    # ensure this test can run independently of order
A
Akira Matsuda 已提交
974
    class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.include?(:find_by_credit_limit)
975 976
    a = Account.where("firm_id = ?", 6).find_by_credit_limit(50)
    assert_equal a, Account.where("firm_id = ?", 6).find_by_credit_limit(50) # find_by_credit_limit has been cached
977 978
  end

979
  def test_find_by_one_attribute_with_several_options
980
    assert_equal accounts(:unknown), Account.order("id DESC").where("id != ?", 3).find_by_credit_limit(50)
981
  end
982

D
David Heinemeier Hansson 已提交
983
  def test_find_by_one_missing_attribute
984
    assert_raise(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
D
David Heinemeier Hansson 已提交
985
  end
986

987
  def test_find_by_invalid_method_syntax
988 989 990 991
    assert_raise(NoMethodError) { Topic.fail_to_find_by_title("The First Topic") }
    assert_raise(NoMethodError) { Topic.find_by_title?("The First Topic") }
    assert_raise(NoMethodError) { Topic.fail_to_find_or_create_by_title("Nonexistent Title") }
    assert_raise(NoMethodError) { Topic.find_or_create_by_title?("Nonexistent Title") }
992
  end
D
David Heinemeier Hansson 已提交
993 994

  def test_find_by_two_attributes
995
    assert_equal topics(:first), Topic.find_by_title_and_author_name("The First Topic", "David")
D
David Heinemeier Hansson 已提交
996 997 998
    assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
  end

999 1000 1001 1002
  def test_find_by_two_attributes_but_passing_only_one
    assert_raise(ArgumentError) { Topic.find_by_title_and_author_name("The First Topic") }
  end

L
Lauro Caetano 已提交
1003
  def test_find_last_with_offset
1004
    devs = Developer.order("id")
L
Lauro Caetano 已提交
1005 1006 1007

    assert_equal devs[2], Developer.offset(2).first
    assert_equal devs[-3], Developer.offset(2).last
1008
    assert_equal devs[-3], Developer.offset(2).order("id DESC").first
L
Lauro Caetano 已提交
1009 1010
  end

D
David Heinemeier Hansson 已提交
1011 1012 1013 1014 1015
  def test_find_by_nil_attribute
    topic = Topic.find_by_last_read nil
    assert_not_nil topic
    assert_nil topic.last_read
  end
1016

D
David Heinemeier Hansson 已提交
1017 1018 1019 1020 1021 1022
  def test_find_by_nil_and_not_nil_attributes
    topic = Topic.find_by_last_read_and_author_name nil, "Mary"
    assert_equal "Mary", topic.author_name
  end

  def test_find_with_bad_sql
1023
    assert_raise(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
D
David Heinemeier Hansson 已提交
1024 1025
  end

1026
  def test_joins_dont_clobber_id
1027
    first = Firm.
1028 1029
      joins("INNER JOIN companies clients ON clients.firm_id = companies.id").
      where("companies.id = 1").first
1030 1031 1032
    assert_equal 1, first.id
  end

1033
  def test_joins_with_string_array
1034 1035 1036 1037
    person_with_reader_and_post = Post.
      joins(["INNER JOIN categorizations ON categorizations.post_id = posts.id",
             "INNER JOIN categories ON categories.id = categorizations.category_id AND categories.type = 'SpecialCategory'"
            ])
1038 1039 1040
    assert_equal 1, person_with_reader_and_post.size
  end

1041 1042
  def test_find_by_id_with_conditions_with_or
    assert_nothing_raised do
1043
      Post.where("posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'").find([1, 2, 3])
1044 1045
    end
  end
1046

1047
  def test_find_ignores_previously_inserted_record
1048
    Post.create!(title: "test", body: "it out")
J
Jon Leighton 已提交
1049
    assert_equal [], Post.where(id: nil)
1050 1051
  end

1052 1053 1054 1055 1056
  def test_find_by_empty_ids
    assert_equal [], Post.find([])
  end

  def test_find_by_empty_in_condition
1057
    assert_equal [], Post.where("id in (?)", [])
1058 1059 1060
  end

  def test_find_by_records
1061 1062 1063
    p1, p2 = Post.limit(2).order("id asc").to_a
    assert_equal [p1, p2], Post.where(["id in (?)", [p1, p2]]).order("id asc")
    assert_equal [p1, p2], Post.where(["id in (?)", [p1, p2.id]]).order("id asc")
1064 1065
  end

1066 1067 1068 1069 1070 1071 1072 1073 1074
  def test_select_value
    assert_equal "37signals", Company.connection.select_value("SELECT name FROM companies WHERE id = 1")
    assert_nil Company.connection.select_value("SELECT name FROM companies WHERE id = -1")
    # make sure we didn't break count...
    assert_equal 0, Company.count_by_sql("SELECT COUNT(*) FROM companies WHERE name = 'Halliburton'")
    assert_equal 1, Company.count_by_sql("SELECT COUNT(*) FROM companies WHERE name = '37signals'")
  end

  def test_select_values
1075 1076
    assert_equal ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map!(&:to_s)
    assert_equal ["37signals", "Summit", "Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel", "Odegy", "Ex Nihilo Part Deux", "Apex"], Company.connection.select_values("SELECT name FROM companies ORDER BY id")
1077 1078
  end

1079 1080
  def test_select_rows
    assert_equal(
1081
      [["1", "1", nil, "37signals"],
1082 1083
       ["2", "1", "2", "Summit"],
       ["3", "1", "1", "Microsoft"]],
1084
      Company.connection.select_rows("SELECT id, firm_id, client_of, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! { |i| i.map! { |j| j.to_s unless j.nil? } })
1085
    assert_equal [["1", "37signals"], ["2", "Summit"], ["3", "Microsoft"]],
1086
      Company.connection.select_rows("SELECT id, name FROM companies WHERE id IN (1,2,3) ORDER BY id").map! { |i| i.map! { |j| j.to_s unless j.nil? } }
1087 1088
  end

1089
  def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
R
Fix...  
Ryuta Kamizono 已提交
1090 1091
    assert_equal 2, Post.includes(authors: :author_address).
      where.not(author_addresses: { id: nil }).
1092
      order("author_addresses.id DESC").limit(2).to_a.size
1093

1094
    assert_equal 3, Post.includes(author: :author_address, authors: :author_address).
R
Fix...  
Ryuta Kamizono 已提交
1095
      where.not(author_addresses_authors: { id: nil }).
1096
      order("author_addresses_authors.id DESC").limit(3).to_a.size
1097 1098
  end

1099
  def test_find_with_nil_inside_set_passed_for_one_attribute
1100 1101
    client_of = Company.
      where(client_of: [2, 1, nil],
1102 1103
            name: ["37signals", "Summit", "Microsoft"]).
      order("client_of DESC").
1104
      map(&:client_of)
1105

1106
    assert_includes client_of, nil
1107
    assert_equal [2, 1].sort, client_of.compact.sort
1108 1109
  end

1110
  def test_find_with_nil_inside_set_passed_for_attribute
1111 1112
    client_of = Company.
      where(client_of: [nil]).
1113
      order("client_of DESC").
1114
      map(&:client_of)
1115 1116 1117 1118

    assert_equal [], client_of.compact
  end

1119
  def test_with_limiting_with_custom_select
1120
    posts = Post.references(:authors).merge(
1121 1122
      includes: :author, select: 'posts.*, authors.id as "author_id"',
      limit: 3, order: "posts.id"
1123
    ).to_a
1124 1125
    assert_equal 3, posts.size
    assert_equal [0, 1, 1], posts.map(&:author_id).sort
1126
  end
1127

1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
  def test_find_one_message_on_primary_key
    e = assert_raises(ActiveRecord::RecordNotFound) do
      Car.find(0)
    end
    assert_equal 0, e.id
    assert_equal "id", e.primary_key
    assert_equal "Car", e.model
    assert_equal "Couldn't find Car with 'id'=0", e.message
  end

1138
  def test_find_one_message_with_custom_primary_key
1139 1140 1141
    table_with_custom_primary_key do |model|
      model.primary_key = :name
      e = assert_raises(ActiveRecord::RecordNotFound) do
1142
        model.find "Hello World!"
1143
      end
1144
      assert_equal "Couldn't find MercedesCar with 'name'=Hello World!", e.message
1145 1146 1147 1148 1149 1150 1151
    end
  end

  def test_find_some_message_with_custom_primary_key
    table_with_custom_primary_key do |model|
      model.primary_key = :name
      e = assert_raises(ActiveRecord::RecordNotFound) do
1152
        model.find "Hello", "World!"
1153
      end
1154
      assert_equal "Couldn't find all MercedesCars with 'name': (Hello, World!) (found 0 results, but was looking for 2)", e.message
1155 1156 1157
    end
  end

1158 1159 1160 1161 1162 1163
  def test_find_without_primary_key
    assert_raises(ActiveRecord::UnknownPrimaryKey) do
      Matey.find(1)
    end
  end

1164
  def test_finder_with_offset_string
1165
    assert_nothing_raised { Topic.offset("3").to_a }
1166 1167
  end

1168 1169 1170 1171 1172 1173 1174 1175 1176
  test "find_by with hash conditions returns the first matching record" do
    assert_equal posts(:eager_other), Post.find_by(id: posts(:eager_other).id)
  end

  test "find_by with non-hash conditions returns the first matching record" do
    assert_equal posts(:eager_other), Post.find_by("id = #{posts(:eager_other).id}")
  end

  test "find_by with multi-arg conditions returns the first matching record" do
1177
    assert_equal posts(:eager_other), Post.find_by("id = ?", posts(:eager_other).id)
1178 1179 1180
  end

  test "find_by returns nil if the record is missing" do
1181
    assert_nil Post.find_by("1 = 0")
1182 1183
  end

1184 1185
  test "find_by with associations" do
    assert_equal authors(:david), Post.find_by(author: authors(:david)).author
1186
    assert_equal authors(:mary) , Post.find_by(author: authors(:mary)).author
1187 1188
  end

1189 1190 1191 1192
  test "find_by doesn't have implicit ordering" do
    assert_sql(/^((?!ORDER).)*$/) { Post.find_by(id: posts(:eager_other).id) }
  end

1193 1194 1195 1196 1197 1198 1199 1200 1201
  test "find_by! with hash conditions returns the first matching record" do
    assert_equal posts(:eager_other), Post.find_by!(id: posts(:eager_other).id)
  end

  test "find_by! with non-hash conditions returns the first matching record" do
    assert_equal posts(:eager_other), Post.find_by!("id = #{posts(:eager_other).id}")
  end

  test "find_by! with multi-arg conditions returns the first matching record" do
1202
    assert_equal posts(:eager_other), Post.find_by!("id = ?", posts(:eager_other).id)
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214
  end

  test "find_by! doesn't have implicit ordering" do
    assert_sql(/^((?!ORDER).)*$/) { Post.find_by!(id: posts(:eager_other).id) }
  end

  test "find_by! raises RecordNotFound if the record is missing" do
    assert_raises(ActiveRecord::RecordNotFound) do
      Post.find_by!("1 = 0")
    end
  end

1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
  test "find on a scope does not perform statement caching" do
    honda = cars(:honda)
    zyke = cars(:zyke)
    tyre = honda.tyres.create!
    tyre2 = zyke.tyres.create!

    assert_equal tyre, honda.tyres.custom_find(tyre.id)
    assert_equal tyre2, zyke.tyres.custom_find(tyre2.id)
  end

  test "find_by on a scope does not perform statement caching" do
    honda = cars(:honda)
    zyke = cars(:zyke)
    tyre = honda.tyres.create!
    tyre2 = zyke.tyres.create!

    assert_equal tyre, honda.tyres.custom_find_by(id: tyre.id)
    assert_equal tyre2, zyke.tyres.custom_find_by(id: tyre2.id)
  end

1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262
  test "#skip_query_cache! for #exists?" do
    Topic.cache do
      assert_queries(1) do
        Topic.exists?
        Topic.exists?
      end

      assert_queries(2) do
        Topic.all.skip_query_cache!.exists?
        Topic.all.skip_query_cache!.exists?
      end
    end
  end

  test "#skip_query_cache! for #exists? with a limited eager load" do
    Topic.cache do
      assert_queries(2) do
        Topic.eager_load(:replies).limit(1).exists?
        Topic.eager_load(:replies).limit(1).exists?
      end

      assert_queries(4) do
        Topic.eager_load(:replies).limit(1).skip_query_cache!.exists?
        Topic.eager_load(:replies).limit(1).skip_query_cache!.exists?
      end
    end
  end

1263
  private
1264 1265 1266
    def table_with_custom_primary_key
      yield(Class.new(Toy) do
        def self.name
1267
          "MercedesCar"
1268 1269 1270
        end
      end)
    end
1271 1272 1273 1274 1275

    def assert_raises_with_message(exception_class, message, &block)
      err = assert_raises(exception_class) { block.call }
      assert_match message, err.message
    end
D
David Heinemeier Hansson 已提交
1276
end