finder_test.rb 50.3 KB
Newer Older
1 2
# frozen_string_literal: true

3
require "cases/helper"
4 5 6 7 8 9 10 11
require "models/post"
require "models/author"
require "models/categorization"
require "models/comment"
require "models/company"
require "models/tagging"
require "models/topic"
require "models/reply"
12
require "models/rating"
13 14 15 16 17 18 19 20 21 22
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 已提交
23

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

27
  def test_find_by_id_with_hash
28
    assert_nothing_raised do
29
      Post.find_by_id(limit: 1)
30 31 32 33
    end
  end

  def test_find_by_title_and_id_with_hash
34
    assert_nothing_raised do
35
      Post.find_by_title_and_id("foo", limit: 1)
36 37 38
    end
  end

D
David Heinemeier Hansson 已提交
39
  def test_find
40
    assert_equal(topics(:first).title, Topic.find(1).title)
D
David Heinemeier Hansson 已提交
41
  end
42

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

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

54
  def test_find_with_ids_returning_ordered
55
    records = Topic.find([4, 2, 5])
56 57 58
    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
59

60
    records = Topic.find(4, 2, 5)
61 62 63
    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
64

65
    records = Topic.find(["4", "2", "5"])
66 67 68
    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
69

70
    records = Topic.find("4", "2", "5")
71 72 73
    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
74 75 76 77
  end

  def test_find_with_ids_and_order_clause
    # The order clause takes precedence over the informed ids
78
    records = Topic.order(:author_name).find([5, 3, 1])
79 80 81
    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
82

83
    records = Topic.order(:id).find([5, 3, 1])
84 85 86
    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
87 88
  end

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

  def test_find_with_ids_and_limit
98
    records = Topic.limit(3).find([3, 2, 5, 1, 4])
99
    assert_equal 3, records.size
100 101 102
    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
103 104
  end

105 106
  def test_find_with_ids_where_and_limit
    # Please note that Topic 1 is the only not approved so
107
    # if it were among the first 3 it would raise an ActiveRecord::RecordNotFound
108
    records = Topic.where(approved: true).limit(3).find([3, 2, 5, 1, 4])
109
    assert_equal 3, records.size
110 111 112
    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
113 114
  end

115
  def test_find_with_ids_and_offset
116
    records = Topic.offset(2).find([3, 2, 5, 1, 4])
117
    assert_equal 3, records.size
118 119 120
    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
121 122
  end

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
  def test_find_with_ids_with_no_id_passed
    exception = assert_raises(ActiveRecord::RecordNotFound) { Topic.find }
    assert_equal exception.model, "Topic"
    assert_equal exception.primary_key, "id"
  end

  def test_find_with_ids_with_id_out_of_range
    exception = assert_raises(ActiveRecord::RecordNotFound) do
      Topic.find("9999999999999999999999999999999")
    end

    assert_equal exception.model, "Topic"
    assert_equal exception.primary_key, "id"
  end

138
  def test_find_passing_active_record_object_is_not_permitted
139
    assert_raises(ArgumentError) do
140 141 142 143
      Topic.find(Topic.last)
    end
  end

144
  def test_symbols_table_ref
145
    gc_disabled = GC.disable
146
    Post.where("author_id" => nil)  # warm up
147
    x = Symbol.all_symbols.count
148
    Post.where("title" => { "xxxqqqq" => "bar" })
149
    assert_equal x, Symbol.all_symbols.count
R
Ryuta Kamizono 已提交
150 151
  ensure
    GC.enable if gc_disabled == false
152 153
  end

154 155 156
  # find should handle strings that come from URLs
  # (example: Category.find(params[:id]))
  def test_find_with_string
157
    assert_equal(Topic.find(1).title, Topic.find("1").title)
158
  end
159

D
David Heinemeier Hansson 已提交
160
  def test_exists
161 162 163 164
    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")
165
    assert_equal true, Topic.exists?(author_name: "Mary", approved: true)
166
    assert_equal true, Topic.exists?(["parent_id = ?", 1])
167
    assert_equal true, Topic.exists?(id: [1, 9999])
168 169

    assert_equal false, Topic.exists?(45)
170
    assert_equal false, Topic.exists?(Topic.new.id)
171

172
    assert_raise(NoMethodError) { Topic.exists?([1, 2]) }
173 174
  end

175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
  def test_exists_with_scope
    davids = Author.where(name: "David")
    assert_equal true, davids.exists?
    assert_equal true, davids.exists?(authors(:david).id)
    assert_equal false, davids.exists?(authors(:mary).id)
    assert_equal false, davids.exists?("42")
    assert_equal false, davids.exists?(42)
    assert_equal false, davids.exists?(davids.new.id)

    fake = Author.where(name: "fake author")
    assert_equal false, fake.exists?
    assert_equal false, fake.exists?(authors(:david).id)
  end

  def test_exists_uses_existing_scope
    post = authors(:david).posts.first
    authors = Author.includes(:posts).where(name: "David", posts: { id: post.id })
    assert_equal true, authors.exists?(authors(:david).id)
  end

  def test_any_with_scope_on_hash_includes
    post = authors(:david).posts.first
    categories = Categorization.includes(author: :posts).where(posts: { id: post.id })
    assert_equal true, categories.exists?
  end

201
  def test_exists_with_polymorphic_relation
202 203
    post = Post.create!(title: "Post", body: "default", taggings: [Tagging.new(comment: "tagging comment")])
    relation = Post.tagged_with_comment("tagging comment")
204

205 206
    assert_equal true, relation.exists?(title: ["Post"])
    assert_equal true, relation.exists?(["title LIKE ?", "Post%"])
207 208 209 210 211 212 213
    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

214
  def test_exists_passing_active_record_object_is_not_permitted
215
    assert_raises(ArgumentError) do
216 217 218 219
      Topic.exists?(Topic.new)
    end
  end

220
  def test_exists_returns_false_when_parameter_has_invalid_type
221
    assert_equal false, Topic.exists?("foo")
222
    assert_equal false, Topic.exists?(("9" * 53).to_i) # number that's bigger than int
D
David Heinemeier Hansson 已提交
223
  end
224

225
  def test_exists_does_not_select_columns_without_alias
226
    assert_sql(/SELECT\W+1 AS one FROM ["`]topics["`]/i) do
227 228 229 230
      Topic.exists?
    end
  end

231
  def test_exists_returns_true_with_one_record_and_no_args
232
    assert_equal true, Topic.exists?
233
  end
234

E
Egor Lynko 已提交
235
  def test_exists_returns_false_with_false_arg
236
    assert_equal false, Topic.exists?(false)
E
Egor Lynko 已提交
237 238
  end

239 240 241
  # 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
242 243 244 245 246
    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?
247 248
  end

249 250 251 252
  def test_exists_with_empty_hash_arg
    assert_equal true, Topic.exists?({})
  end

R
Ryuta Kamizono 已提交
253 254 255
  # 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
256
    assert_equal true, Topic.order(:id).distinct.exists?
257 258
  end

259 260
  # Ensure +exists?+ runs without an error by excluding order value.
  def test_exists_with_order
261
    assert_equal true, Topic.order(Arel.sql("invalid sql here")).exists?
262 263
  end

R
Ryuta Kamizono 已提交
264
  def test_exists_with_joins
B
Ben Toews 已提交
265
    assert_equal true, Topic.joins(:replies).where(replies_topics: { approved: true }).order("replies_topics.created_at DESC").exists?
R
Ryuta Kamizono 已提交
266 267 268
  end

  def test_exists_with_left_joins
B
Ben Toews 已提交
269
    assert_equal true, Topic.left_joins(:replies).where(replies_topics: { approved: true }).order("replies_topics.created_at DESC").exists?
R
Ryuta Kamizono 已提交
270 271 272
  end

  def test_exists_with_eager_load
B
Ben Toews 已提交
273
    assert_equal true, Topic.eager_load(:replies).where(replies_topics: { approved: true }).order("replies_topics.created_at DESC").exists?
R
Ryuta Kamizono 已提交
274 275
  end

276
  def test_exists_with_includes_limit_and_empty_result
277 278
    assert_no_queries { assert_equal false, Topic.includes(:replies).limit(0).exists? }
    assert_queries(1) { assert_equal false, Topic.includes(:replies).limit(1).where("0 = 1").exists? }
279 280
  end

281 282
  def test_exists_with_distinct_association_includes_and_limit
    author = Author.first
283 284 285
    unique_categorized_posts = author.unique_categorized_posts.includes(:special_comments)
    assert_no_queries { assert_equal false, unique_categorized_posts.limit(0).exists? }
    assert_queries(1) { assert_equal true, unique_categorized_posts.limit(1).exists? }
286 287 288 289
  end

  def test_exists_with_distinct_association_includes_limit_and_order
    author = Author.first
290 291 292
    unique_categorized_posts = author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC")
    assert_no_queries { assert_equal false, unique_categorized_posts.limit(0).exists? }
    assert_queries(1) { assert_equal true, unique_categorized_posts.limit(1).exists? }
293 294
  end

295
  def test_exists_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association
296 297
    ratings = developers(:david).ratings.includes(comment: :post).where(posts: { id: 1 })
    assert_queries(1) { assert_not_predicate ratings.limit(1), :exists? }
298 299
  end

300
  def test_exists_with_empty_table_and_no_args_given
301
    Topic.delete_all
302
    assert_equal false, Topic.exists?
303
  end
304

305 306
  def test_exists_with_aggregate_having_three_mappings
    existing_address = customers(:david).address
307
    assert_equal true, Customer.exists?(address: existing_address)
308 309 310 311
  end

  def test_exists_with_aggregate_having_three_mappings_with_one_difference
    existing_address = customers(:david).address
R
Ryuta Kamizono 已提交
312 313 314
    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))
315 316
  end

317
  def test_exists_does_not_instantiate_records
318 319 320
    assert_not_called(Developer, :instantiate) do
      Developer.exists?
    end
321 322
  end

D
David Heinemeier Hansson 已提交
323 324 325 326
  def test_find_by_array_of_one_id
    assert_kind_of(Array, Topic.find([ 1 ]))
    assert_equal(1, Topic.find([ 1 ]).length)
  end
327

D
David Heinemeier Hansson 已提交
328
  def test_find_by_ids
329 330 331 332 333
    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
334 335
    assert_equal 2, Entrant.limit(2).find([1, 3, 2]).size
    entrants = Entrant.limit(3).offset(2).find([1, 3, 2])
336
    assert_equal 1, entrants.size
337
    assert_equal "Ruby Guru", entrants.first.name
338 339 340 341

    # 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 已提交
342
    devs = Developer.all
343
    last_devs = Developer.limit(3).offset(9).find devs.map(&:id)
344
    assert_equal 2, last_devs.size
345 346
    assert_equal "fixture_10", last_devs[0].name
    assert_equal "Jamis", last_devs[1].name
D
David Heinemeier Hansson 已提交
347 348
  end

349
  def test_find_with_large_number
350
    assert_raises(ActiveRecord::RecordNotFound) { Topic.find("9999999999999999999999999999999") }
351 352 353
  end

  def test_find_by_with_large_number
354
    assert_nil Topic.find_by(id: "9999999999999999999999999999999")
355 356 357
  end

  def test_find_by_id_with_large_number
358
    assert_nil Topic.find_by_id("9999999999999999999999999999999")
359 360
  end

361
  def test_find_on_relation_with_large_number
362
    assert_nil Topic.where("1=1").find_by(id: 9999999999999999999999999999999)
363 364 365 366
  end

  def test_find_by_bang_on_relation_with_large_number
    assert_raises(ActiveRecord::RecordNotFound) do
367
      Topic.where("1=1").find_by!(id: 9999999999999999999999999999999)
368 369 370
    end
  end

371
  def test_find_an_empty_array
372 373 374 375
    empty_array = []
    result = Topic.find(empty_array)
    assert_equal [], result
    assert_not_same empty_array, result
376 377
  end

378 379 380 381
  def test_find_doesnt_have_implicit_ordering
    assert_sql(/^((?!ORDER).)*$/) { Topic.find(1) }
  end

D
David Heinemeier Hansson 已提交
382
  def test_find_by_ids_missing_one
383
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
D
David Heinemeier Hansson 已提交
384
  end
385

386
  def test_find_with_group_and_sanitized_having_method
387
    developers = Developer.group(:salary).having("sum(salary) > ?", 10000).select("salary").to_a
388 389 390 391 392
    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 已提交
393 394
  def test_find_with_entire_select_statement
    topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
395

D
David Heinemeier Hansson 已提交
396
    assert_equal(1, topics.size)
397
    assert_equal(topics(:second).title, topics.first.title)
D
David Heinemeier Hansson 已提交
398
  end
399

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

D
David Heinemeier Hansson 已提交
403
    assert_equal(1, topics.size)
404
    assert_equal(topics(:second).title, topics.first.title)
D
David Heinemeier Hansson 已提交
405
  end
406

407 408 409 410
  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
411

412 413 414 415 416 417
  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

418 419 420 421 422
  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

423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
  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
438
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
439 440 441 442
      Topic.where("title = 'This title does not exist'").take!
    end
  end

443
  def test_first
444
    assert_equal topics(:second).title, Topic.where("title = 'The Second Topic of the day'").first.title
445
  end
446

447
  def test_first_failing
448
    assert_nil Topic.where("title = 'The Second Topic of the day!'").first
449
  end
D
David Heinemeier Hansson 已提交
450

451 452 453 454 455 456 457
  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
458
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
459 460 461 462
      Topic.where("title = 'This title does not exist'").first!
    end
  end

463 464 465 466 467 468
  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

469
  def test_model_class_responds_to_first_bang
470 471
    assert Topic.first!
    Topic.delete_all
472
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
473 474 475 476
      Topic.first!
    end
  end

477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
  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
494
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
      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
516
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
      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
538
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
      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
560
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
561 562 563 564
      Topic.fifth!
    end
  end

565 566
  def test_second_to_last
    assert_equal topics(:fourth).title, Topic.second_to_last.title
567 568 569

    # test with offset
    assert_equal topics(:fourth), Topic.offset(1).second_to_last
570 571
    assert_equal topics(:fourth), Topic.offset(2).second_to_last
    assert_equal topics(:fourth), Topic.offset(3).second_to_last
572 573
    assert_nil Topic.offset(4).second_to_last
    assert_nil Topic.offset(5).second_to_last
574

575
    # test with limit
576
    assert_nil Topic.limit(1).second
577
    assert_nil Topic.limit(1).second_to_last
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
  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
596 597 598

    # test with offset
    assert_equal topics(:third), Topic.offset(1).third_to_last
599
    assert_equal topics(:third), Topic.offset(2).third_to_last
600 601 602
    assert_nil Topic.offset(3).third_to_last
    assert_nil Topic.offset(4).third_to_last
    assert_nil Topic.offset(5).third_to_last
603 604

    # test with limit
605
    assert_nil Topic.limit(1).third
606
    assert_nil Topic.limit(1).third_to_last
607
    assert_nil Topic.limit(2).third
608
    assert_nil Topic.limit(2).third_to_last
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
  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

625 626 627 628 629 630 631
  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
632
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
633 634 635 636
      Topic.where("title = 'This title does not exist'").last!
    end
  end

637
  def test_model_class_responds_to_last_bang
638
    assert_equal topics(:fifth), Topic.last!
639
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
640 641 642 643 644
      Topic.delete_all
      Topic.last!
    end
  end

645
  def test_take_and_first_and_last_with_integer_should_use_sql_limit
646 647 648
    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 }
649 650 651 652 653 654
  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

655 656 657
  def test_last_with_integer_and_order_should_use_sql_limit
    relation = Topic.order("title")
    assert_queries(1) { relation.last(5) }
658
    assert_not_predicate relation, :loaded?
659 660
  end

661 662 663
  def test_last_with_integer_and_reorder_should_use_sql_limit
    relation = Topic.reorder("title")
    assert_queries(1) { relation.last(5) }
664
    assert_not_predicate relation, :loaded?
665 666 667
  end

  def test_last_on_loaded_relation_should_not_use_sql
668
    relation = Topic.limit(10).load
669 670 671 672 673 674 675
    assert_no_queries do
      relation.last
      relation.last(2)
    end
  end

  def test_last_with_irreversible_order
676
    assert_raises(ActiveRecord::IrreversibleOrderError) do
677
      Topic.order(Arel.sql("coalesce(author_name, title)")).last
678
    end
679
  end
680 681

  def test_last_on_relation_with_limit_and_offset
682
    post = posts("sti_comments")
683 684 685 686 687 688

    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)

689 690 691 692
    assert_equal comments.offset(2).to_a.last, comments.offset(2).last
    assert_equal comments.offset(2).to_a.last(2), comments.offset(2).last(2)
    assert_equal comments.offset(2).to_a.last(3), comments.offset(2).last(3)

693 694 695 696
    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)
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
  end

  def test_first_on_relation_with_limit_and_offset
    post = posts("sti_comments")

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

    assert_equal comments.offset(2).to_a.first, comments.offset(2).first
    assert_equal comments.offset(2).to_a.first(2), comments.offset(2).first(2)
    assert_equal comments.offset(2).to_a.first(3), comments.offset(2).first(3)

    comments = comments.offset(1)
    assert_equal comments.limit(2).to_a.first, comments.limit(2).first
    assert_equal comments.limit(2).to_a.first(2), comments.limit(2).first(2)
    assert_equal comments.limit(2).to_a.first(3), comments.limit(2).first(3)
715 716
  end

717 718
  def test_take_and_first_and_last_with_integer_should_return_an_array
    assert_kind_of Array, Topic.take(5)
719 720 721 722
    assert_kind_of Array, Topic.first(5)
    assert_kind_of Array, Topic.last(5)
  end

D
David Heinemeier Hansson 已提交
723
  def test_unexisting_record_exception_handling
724
    assert_raise(ActiveRecord::RecordNotFound) {
D
David Heinemeier Hansson 已提交
725 726
      Topic.find(1).parent
    }
727

728
    Topic.find(2).topic
D
David Heinemeier Hansson 已提交
729
  end
730

731
  def test_find_only_some_columns
732
    topic = Topic.select("author_name").find(1)
733 734
    assert_raise(ActiveModel::MissingAttributeError) { topic.title }
    assert_raise(ActiveModel::MissingAttributeError) { topic.title? }
735
    assert_nil topic.read_attribute("title")
736 737
    assert_equal "David", topic.author_name
    assert !topic.attribute_present?("title")
738
    assert !topic.attribute_present?(:title)
739
    assert topic.attribute_present?("author_name")
740
    assert_respond_to topic, "author_name"
741
  end
J
Jeremy Kemper 已提交
742

743
  def test_find_on_array_conditions
744 745
    assert Topic.where(["approved = ?", false]).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(["approved = ?", true]).find(1) }
D
David Heinemeier Hansson 已提交
746
  end
747

748
  def test_find_on_hash_conditions
749 750
    assert Topic.where(approved: false).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(approved: true).find(1) }
751
  end
752

753
  def test_find_on_hash_conditions_with_qualified_attribute_dot_notation_string
754 755
    assert Topic.where("topics.approved" => false).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where("topics.approved" => true).find(1) }
756 757
  end

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

763
  def test_find_on_hash_conditions_with_hashed_table_name
764 765
    assert Topic.where(topics: { approved: false }).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(topics: { approved: true }).find(1) }
766 767
  end

768
  def test_find_on_combined_explicit_and_hashed_table_names
769 770 771
    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) }
772 773
  end

774
  def test_find_with_hash_conditions_on_joined_table
775
    firms = Firm.joins(:account).where(accounts: { credit_limit: 50 })
776 777 778 779 780
    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
781
    firms = DependentFirm.joins(:account).where(name: "RailsCore", accounts: { credit_limit: 55..60 })
782 783 784 785
    assert_equal 1, firms.size
    assert_equal companies(:rails_core), firms.first
  end

786 787
  def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate
    david = customers(:david)
788
    assert Customer.where("customers.name" => david.name, :address => david.address).find(david.id)
789
    assert_raise(ActiveRecord::RecordNotFound) {
790
      Customer.where("customers.name" => david.name + "1", :address => david.address).find(david.id)
791 792 793
    }
  end

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

798
  def test_find_on_hash_conditions_with_range
799
    assert_equal [1, 2], Topic.where(id: 1..2).to_a.map(&:id).sort
800
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2..3).find(1) }
801
  end
802

803
  def test_find_on_hash_conditions_with_end_exclusive_range
804 805
    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
806
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2...3).find(3) }
807 808
  end

809
  def test_find_on_hash_conditions_with_multiple_ranges
810
    assert_equal [1, 2, 3], Comment.where(id: 1..3, post_id: 1..2).to_a.map(&:id).sort
811
    assert_equal [1], Comment.where(id: 1..1, post_id: 1..10).to_a.map(&:id).sort
812
  end
813

814
  def test_find_on_hash_conditions_with_array_of_integers_and_ranges
815
    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
816 817
  end

818
  def test_find_on_hash_conditions_with_array_of_ranges
819
    assert_equal [1, 2, 6, 7, 8], Comment.where(id: [1..2, 6..8]).to_a.map(&:id).sort
820 821
  end

822
  def test_find_on_multiple_hash_conditions
823 824 825
    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) }
826
  end
827

828
  def test_condition_interpolation
829
    assert_kind_of Firm, Company.where("name = '%s'", "37signals").first
830 831 832
    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
833 834
  end

835
  def test_condition_array_interpolation
836 837 838 839
    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 已提交
840
  end
841

842
  def test_condition_hash_interpolation
843 844 845
    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
846
  end
847

848
  def test_hash_condition_find_malformed
849
    assert_raise(ActiveRecord::StatementInvalid) {
850
      Company.where(id: 2, dhh: true).first
851 852
    }
  end
853

854 855
  def test_hash_condition_find_with_escaped_characters
    Company.create("name" => "Ain't noth'n like' \#stuff")
856
    assert Company.where(name: "Ain't noth'n like' \#stuff").first
857 858 859
  end

  def test_hash_condition_find_with_array
860 861 862
    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
863 864 865
  end

  def test_hash_condition_find_with_nil
866
    topic = Topic.where(last_read: nil).first
867 868
    assert_not_nil topic
    assert_nil topic.last_read
869
  end
D
David Heinemeier Hansson 已提交
870

871 872 873
  def test_hash_condition_find_with_aggregate_having_one_mapping
    balance = customers(:david).balance
    assert_kind_of Money, balance
874
    found_customer = Customer.where(balance: balance).first
875 876 877
    assert_equal customers(:david), found_customer
  end

878
  def test_hash_condition_find_with_aggregate_having_three_mappings_array
879 880
    david_address = customers(:david).address
    zaphod_address = customers(:zaphod).address
881
    barney_address = customers(:barney).address
882 883
    assert_kind_of Address, david_address
    assert_kind_of Address, zaphod_address
884 885
    found_customers = Customer.where(address: [david_address, zaphod_address, barney_address])
    assert_equal [customers(:david), customers(:zaphod), customers(:barney)], found_customers.sort_by(&:id)
886 887 888 889 890 891 892 893
  end

  def test_hash_condition_find_with_aggregate_having_one_mapping_array
    david_balance = customers(:david).balance
    zaphod_balance = customers(:zaphod).balance
    assert_kind_of Money, david_balance
    assert_kind_of Money, zaphod_balance
    found_customers = Customer.where(balance: [david_balance, zaphod_balance])
894
    assert_equal [customers(:david), customers(:zaphod)], found_customers.sort_by(&:id)
895
    assert_equal Customer.where(balance: [david_balance.amount, zaphod_balance.amount]).to_sql, found_customers.to_sql
896 897
  end

898 899 900
  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
901
    found_customer = Customer.where(gps_location: gps_location).first
902 903 904 905 906 907
    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
908
    found_customer = Customer.where(balance: balance.amount).first
909 910 911 912 913 914
    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
915
    found_customer = Customer.where(gps_location: gps_location.gps_location).first
916 917 918 919 920 921
    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
922
    found_customer = Customer.where(address: address).first
923 924 925 926 927 928
    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
929
    found_customer = Customer.where(address: address, name: customers(:david).name).first
930 931 932
    assert_equal customers(:david), found_customer
  end

933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950
  def test_hash_condition_find_nil_with_aggregate_having_one_mapping
    assert_nil customers(:zaphod).gps_location
    found_customer = Customer.where(gps_location: nil, name: customers(:zaphod).name).first
    assert_equal customers(:zaphod), found_customer
  end

  def test_hash_condition_find_nil_with_aggregate_having_multiple_mappings
    customers(:david).update(address: nil)
    assert_nil customers(:david).address_street
    assert_nil customers(:david).address_city
    found_customer = Customer.where(address: nil, name: customers(:david).name).first
    assert_equal customers(:david), found_customer
  end

  def test_hash_condition_find_empty_array_with_aggregate_having_multiple_mappings
    assert_nil Customer.where(address: []).first
  end

951
  def test_condition_utc_time_interpolation_with_default_timezone_local
952
    with_env_tz "America/New_York" do
953
      with_timezone_config default: :local do
954
        topic = Topic.first
955
        assert_equal topic, Topic.where(["written_on = ?", topic.written_on.getutc]).first
956 957 958 959 960
      end
    end
  end

  def test_hash_condition_utc_time_interpolation_with_default_timezone_local
961
    with_env_tz "America/New_York" do
962
      with_timezone_config default: :local do
963
        topic = Topic.first
964
        assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first
965 966 967 968 969
      end
    end
  end

  def test_condition_local_time_interpolation_with_default_timezone_utc
970
    with_env_tz "America/New_York" do
971
      with_timezone_config default: :utc do
972
        topic = Topic.first
973
        assert_equal topic, Topic.where(["written_on = ?", topic.written_on.getlocal]).first
974 975 976 977 978
      end
    end
  end

  def test_hash_condition_local_time_interpolation_with_default_timezone_utc
979
    with_env_tz "America/New_York" do
980
      with_timezone_config default: :utc do
981
        topic = Topic.first
982
        assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first
983 984 985 986
      end
    end
  end

D
David Heinemeier Hansson 已提交
987
  def test_bind_variables
988 989 990 991
    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
992
    assert_raise(ActiveRecord::PreparedStatementInvalid) {
993
      Company.where(["id=? AND name = ?", 2]).first
D
David Heinemeier Hansson 已提交
994
    }
995
    assert_raise(ActiveRecord::PreparedStatementInvalid) {
996
      Company.where(["id=?", 2, 3, 4]).first
D
David Heinemeier Hansson 已提交
997 998
    }
  end
999

D
David Heinemeier Hansson 已提交
1000
  def test_bind_variables_with_quotes
B
Benjamin Fleischer 已提交
1001 1002
    Company.create("name" => "37signals' go'es against")
    assert Company.where(["name = ?", "37signals' go'es against"]).first
D
David Heinemeier Hansson 已提交
1003 1004 1005
  end

  def test_named_bind_variables_with_quotes
B
Benjamin Fleischer 已提交
1006 1007
    Company.create("name" => "37signals' go'es against")
    assert Company.where(["name = :name", { name: "37signals' go'es against" }]).first
D
David Heinemeier Hansson 已提交
1008 1009 1010
  end

  def test_named_bind_variables
1011 1012 1013 1014
    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 已提交
1015 1016 1017 1018 1019 1020 1021 1022 1023
  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
1024
    assert_equal topics(:first), Topic.find_by_title("The First Topic")
D
David Heinemeier Hansson 已提交
1025 1026
    assert_nil Topic.find_by_title("The First Topic!")
  end
J
Jeremy Kemper 已提交
1027

1028 1029
  def test_find_by_one_attribute_bang
    assert_equal topics(:first), Topic.find_by_title!("The First Topic")
1030 1031 1032
    assert_raises_with_message(ActiveRecord::RecordNotFound, "Couldn't find Topic") do
      Topic.find_by_title!("The First Topic!")
    end
1033 1034
  end

1035
  def test_find_by_on_attribute_that_is_a_reserved_word
1036
    dog_alias = "Dog"
1037 1038 1039 1040 1041
    dog = Dog.create(alias: dog_alias)

    assert_equal dog, Dog.find_by_alias(dog_alias)
  end

1042 1043 1044 1045 1046
  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 已提交
1047
  def test_find_by_one_attribute_bang_with_blank_defined
1048 1049
    blank_topic = BlankTopic.create(title: "The Blank One")
    assert_equal blank_topic, BlankTopic.find_by_title!("The Blank One")
N
Nikita Afanasenko 已提交
1050 1051
  end

1052
  def test_find_by_one_attribute_with_conditions
1053
    assert_equal accounts(:rails_core_account), Account.where("firm_id = ?", 6).find_by_credit_limit(50)
1054 1055
  end

1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089
  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

1090 1091
  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 已提交
1092
    class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.include?(:find_by_credit_limit)
1093 1094
    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
1095 1096
  end

1097
  def test_find_by_one_attribute_with_several_options
1098
    assert_equal accounts(:unknown), Account.order("id DESC").where("id != ?", 3).find_by_credit_limit(50)
1099
  end
1100

D
David Heinemeier Hansson 已提交
1101
  def test_find_by_one_missing_attribute
1102
    assert_raise(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
D
David Heinemeier Hansson 已提交
1103
  end
1104

1105
  def test_find_by_invalid_method_syntax
1106 1107 1108 1109
    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") }
1110
  end
D
David Heinemeier Hansson 已提交
1111 1112

  def test_find_by_two_attributes
1113
    assert_equal topics(:first), Topic.find_by_title_and_author_name("The First Topic", "David")
D
David Heinemeier Hansson 已提交
1114 1115 1116
    assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
  end

1117 1118 1119 1120
  def test_find_by_two_attributes_but_passing_only_one
    assert_raise(ArgumentError) { Topic.find_by_title_and_author_name("The First Topic") }
  end

D
David Heinemeier Hansson 已提交
1121 1122 1123 1124 1125
  def test_find_by_nil_attribute
    topic = Topic.find_by_last_read nil
    assert_not_nil topic
    assert_nil topic.last_read
  end
1126

D
David Heinemeier Hansson 已提交
1127 1128 1129 1130 1131 1132
  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
1133
    assert_raise(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
D
David Heinemeier Hansson 已提交
1134 1135
  end

1136
  def test_joins_dont_clobber_id
1137
    first = Firm.
1138 1139
      joins("INNER JOIN companies clients ON clients.firm_id = companies.id").
      where("companies.id = 1").first
1140 1141 1142
    assert_equal 1, first.id
  end

1143
  def test_joins_with_string_array
1144 1145 1146 1147
    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'"
            ])
1148 1149 1150
    assert_equal 1, person_with_reader_and_post.size
  end

1151 1152
  def test_find_by_id_with_conditions_with_or
    assert_nothing_raised do
1153
      Post.where("posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'").find([1, 2, 3])
1154 1155
    end
  end
1156

1157
  def test_find_ignores_previously_inserted_record
1158
    Post.create!(title: "test", body: "it out")
J
Jon Leighton 已提交
1159
    assert_equal [], Post.where(id: nil)
1160 1161
  end

1162 1163 1164 1165 1166
  def test_find_by_empty_ids
    assert_equal [], Post.find([])
  end

  def test_find_by_empty_in_condition
1167
    assert_equal [], Post.where("id in (?)", [])
1168 1169 1170
  end

  def test_find_by_records
1171 1172 1173
    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")
1174 1175
  end

1176 1177 1178 1179 1180 1181 1182 1183 1184
  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
1185 1186
    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")
1187 1188
  end

1189 1190
  def test_select_rows
    assert_equal(
1191
      [["1", "1", nil, "37signals"],
1192 1193
       ["2", "1", "2", "Summit"],
       ["3", "1", "1", "Microsoft"]],
1194
      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? } })
1195
    assert_equal [["1", "37signals"], ["2", "Summit"], ["3", "Microsoft"]],
1196
      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? } }
1197 1198
  end

1199
  def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
R
Fix...  
Ryuta Kamizono 已提交
1200 1201
    assert_equal 2, Post.includes(authors: :author_address).
      where.not(author_addresses: { id: nil }).
B
Ben Toews 已提交
1202
      order("author_addresses.id DESC").limit(2).to_a.size
1203

1204
    assert_equal 3, Post.includes(author: :author_address, authors: :author_address).
R
Fix...  
Ryuta Kamizono 已提交
1205
      where.not(author_addresses_authors: { id: nil }).
B
Ben Toews 已提交
1206
      order("author_addresses_authors.id DESC").limit(3).to_a.size
1207 1208
  end

1209 1210 1211 1212 1213
  def test_find_with_eager_loading_collection_and_ordering_by_collection_primary_key
    assert_equal Post.first, Post.eager_load(comments: :ratings).
      order("posts.id, ratings.id, comments.id").first
  end

1214
  def test_find_with_nil_inside_set_passed_for_one_attribute
1215 1216
    client_of = Company.
      where(client_of: [2, 1, nil],
1217
            name: ["37signals", "Summit", "Microsoft"]).
1218
      order("client_of DESC").
1219
      map(&:client_of)
1220

1221
    assert_includes client_of, nil
1222
    assert_equal [2, 1].sort, client_of.compact.sort
1223 1224
  end

1225
  def test_find_with_nil_inside_set_passed_for_attribute
1226 1227
    client_of = Company.
      where(client_of: [nil]).
1228
      order("client_of DESC").
1229
      map(&:client_of)
1230 1231 1232 1233

    assert_equal [], client_of.compact
  end

1234
  def test_with_limiting_with_custom_select
1235
    posts = Post.references(:authors).merge(
1236
      includes: :author, select: 'posts.*, authors.id as "author_id"',
1237
      limit: 3, order: "posts.id"
1238
    ).to_a
1239 1240
    assert_equal 3, posts.size
    assert_equal [0, 1, 1], posts.map(&:author_id).sort
1241
  end
1242

1243 1244 1245 1246 1247 1248 1249 1250 1251 1252
  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

1253
  def test_find_one_message_with_custom_primary_key
1254 1255 1256
    table_with_custom_primary_key do |model|
      model.primary_key = :name
      e = assert_raises(ActiveRecord::RecordNotFound) do
1257
        model.find "Hello World!"
1258
      end
1259
      assert_equal "Couldn't find MercedesCar with 'name'=Hello World!", e.message
1260 1261 1262 1263 1264 1265 1266
    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
1267
        model.find "Hello", "World!"
1268
      end
1269
      assert_equal "Couldn't find all MercedesCars with 'name': (Hello, World!) (found 0 results, but was looking for 2).", e.message
1270 1271 1272
    end
  end

1273 1274 1275 1276 1277 1278
  def test_find_without_primary_key
    assert_raises(ActiveRecord::UnknownPrimaryKey) do
      Matey.find(1)
    end
  end

1279
  def test_finder_with_offset_string
1280
    assert_nothing_raised { Topic.offset("3").to_a }
1281 1282
  end

1283 1284 1285 1286 1287 1288 1289 1290 1291
  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
1292
    assert_equal posts(:eager_other), Post.find_by("id = ?", posts(:eager_other).id)
1293 1294
  end

1295 1296 1297 1298
  test "find_by with range conditions returns the first matching record" do
    assert_equal posts(:eager_other), Post.find_by(id: posts(:eager_other).id...posts(:misc_by_bob).id)
  end

1299
  test "find_by returns nil if the record is missing" do
1300
    assert_nil Post.find_by("1 = 0")
1301 1302
  end

1303 1304
  test "find_by with associations" do
    assert_equal authors(:david), Post.find_by(author: authors(:david)).author
1305
    assert_equal authors(:mary), Post.find_by(author: authors(:mary)).author
1306 1307
  end

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

1312 1313 1314 1315 1316 1317 1318 1319 1320
  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
1321
    assert_equal posts(:eager_other), Post.find_by!("id = ?", posts(:eager_other).id)
1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333
  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

1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353
  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

1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369
  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
1370
      assert_queries(1) do
1371 1372 1373 1374
        Topic.eager_load(:replies).limit(1).exists?
        Topic.eager_load(:replies).limit(1).exists?
      end

1375
      assert_queries(2) do
1376 1377 1378 1379 1380 1381
        Topic.eager_load(:replies).limit(1).skip_query_cache!.exists?
        Topic.eager_load(:replies).limit(1).skip_query_cache!.exists?
      end
    end
  end

1382
  private
1383 1384 1385
    def table_with_custom_primary_key
      yield(Class.new(Toy) do
        def self.name
1386
          "MercedesCar"
1387 1388 1389
        end
      end)
    end
1390 1391 1392 1393 1394

    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 已提交
1395
end