finder_test.rb 48.1 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

R
Ryuta Kamizono 已提交
249 250 251
  # 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
252
    assert_equal true, Topic.order(:id).distinct.exists?
253 254
  end

255 256
  # Ensure +exists?+ runs without an error by excluding order value.
  def test_exists_with_order
257
    assert_equal true, Topic.order(Arel.sql("invalid sql here")).exists?
258 259
  end

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

  def test_exists_with_left_joins
B
Ben Toews 已提交
265
    assert_equal true, Topic.left_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_eager_load
B
Ben Toews 已提交
269
    assert_equal true, Topic.eager_load(:replies).where(replies_topics: { approved: true }).order("replies_topics.created_at DESC").exists?
R
Ryuta Kamizono 已提交
270 271
  end

272
  def test_exists_with_includes_limit_and_empty_result
273 274
    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? }
275 276
  end

277 278
  def test_exists_with_distinct_association_includes_and_limit
    author = Author.first
279 280 281
    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? }
282 283 284 285
  end

  def test_exists_with_distinct_association_includes_limit_and_order
    author = Author.first
286 287 288
    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? }
289 290
  end

291
  def test_exists_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association
292 293
    ratings = developers(:david).ratings.includes(comment: :post).where(posts: { id: 1 })
    assert_queries(1) { assert_not_predicate ratings.limit(1), :exists? }
294 295
  end

296
  def test_exists_with_empty_table_and_no_args_given
297
    Topic.delete_all
298
    assert_equal false, Topic.exists?
299
  end
300

301 302
  def test_exists_with_aggregate_having_three_mappings
    existing_address = customers(:david).address
303
    assert_equal true, Customer.exists?(address: existing_address)
304 305 306 307
  end

  def test_exists_with_aggregate_having_three_mappings_with_one_difference
    existing_address = customers(:david).address
R
Ryuta Kamizono 已提交
308 309 310
    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))
311 312
  end

313
  def test_exists_does_not_instantiate_records
314 315 316
    assert_not_called(Developer, :instantiate) do
      Developer.exists?
    end
317 318
  end

D
David Heinemeier Hansson 已提交
319 320 321 322
  def test_find_by_array_of_one_id
    assert_kind_of(Array, Topic.find([ 1 ]))
    assert_equal(1, Topic.find([ 1 ]).length)
  end
323

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

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

345
  def test_find_with_large_number
346
    assert_raises(ActiveRecord::RecordNotFound) { Topic.find("9999999999999999999999999999999") }
347 348 349
  end

  def test_find_by_with_large_number
350
    assert_nil Topic.find_by(id: "9999999999999999999999999999999")
351 352 353
  end

  def test_find_by_id_with_large_number
354
    assert_nil Topic.find_by_id("9999999999999999999999999999999")
355 356
  end

357
  def test_find_on_relation_with_large_number
358
    assert_nil Topic.where("1=1").find_by(id: 9999999999999999999999999999999)
359 360 361 362
  end

  def test_find_by_bang_on_relation_with_large_number
    assert_raises(ActiveRecord::RecordNotFound) do
363
      Topic.where("1=1").find_by!(id: 9999999999999999999999999999999)
364 365 366
    end
  end

367 368 369 370
  def test_find_an_empty_array
    assert_equal [], Topic.find([])
  end

371 372 373 374
  def test_find_doesnt_have_implicit_ordering
    assert_sql(/^((?!ORDER).)*$/) { Topic.find(1) }
  end

D
David Heinemeier Hansson 已提交
375
  def test_find_by_ids_missing_one
376
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
D
David Heinemeier Hansson 已提交
377
  end
378

379
  def test_find_with_group_and_sanitized_having_method
380
    developers = Developer.group(:salary).having("sum(salary) > ?", 10000).select("salary").to_a
381 382 383 384 385
    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 已提交
386 387
  def test_find_with_entire_select_statement
    topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
388

D
David Heinemeier Hansson 已提交
389
    assert_equal(1, topics.size)
390
    assert_equal(topics(:second).title, topics.first.title)
D
David Heinemeier Hansson 已提交
391
  end
392

D
David Heinemeier Hansson 已提交
393 394
  def test_find_with_prepared_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

400 401 402 403
  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
404

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

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

416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
  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
431
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
432 433 434 435
      Topic.where("title = 'This title does not exist'").take!
    end
  end

436
  def test_first
437
    assert_equal topics(:second).title, Topic.where("title = 'The Second Topic of the day'").first.title
438
  end
439

440
  def test_first_failing
441
    assert_nil Topic.where("title = 'The Second Topic of the day!'").first
442
  end
D
David Heinemeier Hansson 已提交
443

444 445 446 447 448 449 450
  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
451
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
452 453 454 455
      Topic.where("title = 'This title does not exist'").first!
    end
  end

456 457 458 459 460 461
  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

462
  def test_model_class_responds_to_first_bang
463 464
    assert Topic.first!
    Topic.delete_all
465
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
466 467 468 469
      Topic.first!
    end
  end

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

558 559
  def test_second_to_last
    assert_equal topics(:fourth).title, Topic.second_to_last.title
560 561 562

    # test with offset
    assert_equal topics(:fourth), Topic.offset(1).second_to_last
563 564
    assert_equal topics(:fourth), Topic.offset(2).second_to_last
    assert_equal topics(:fourth), Topic.offset(3).second_to_last
565 566
    assert_nil Topic.offset(4).second_to_last
    assert_nil Topic.offset(5).second_to_last
567

568
    # test with limit
569
    assert_nil Topic.limit(1).second
570
    assert_nil Topic.limit(1).second_to_last
571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
  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
589 590 591

    # test with offset
    assert_equal topics(:third), Topic.offset(1).third_to_last
592
    assert_equal topics(:third), Topic.offset(2).third_to_last
593 594 595
    assert_nil Topic.offset(3).third_to_last
    assert_nil Topic.offset(4).third_to_last
    assert_nil Topic.offset(5).third_to_last
596 597

    # test with limit
598
    assert_nil Topic.limit(1).third
599
    assert_nil Topic.limit(1).third_to_last
600
    assert_nil Topic.limit(2).third
601
    assert_nil Topic.limit(2).third_to_last
602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
  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

618 619 620 621 622 623 624
  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
625
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
626 627 628 629
      Topic.where("title = 'This title does not exist'").last!
    end
  end

630
  def test_model_class_responds_to_last_bang
631
    assert_equal topics(:fifth), Topic.last!
632
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
633 634 635 636 637
      Topic.delete_all
      Topic.last!
    end
  end

638
  def test_take_and_first_and_last_with_integer_should_use_sql_limit
639 640 641
    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 }
642 643 644 645 646 647
  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

648 649 650 651
  def test_last_with_integer_and_order_should_use_sql_limit
    relation = Topic.order("title")
    assert_queries(1) { relation.last(5) }
    assert !relation.loaded?
652 653
  end

654 655 656 657 658 659 660
  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
661
    relation = Topic.limit(10).load
662 663 664 665 666 667 668
    assert_no_queries do
      relation.last
      relation.last(2)
    end
  end

  def test_last_with_irreversible_order
669
    assert_raises(ActiveRecord::IrreversibleOrderError) do
670
      Topic.order(Arel.sql("coalesce(author_name, title)")).last
671
    end
672
  end
673 674

  def test_last_on_relation_with_limit_and_offset
675
    post = posts("sti_comments")
676 677 678 679 680 681

    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)

682 683 684 685
    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)

686 687 688 689
    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)
690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
  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)
708 709
  end

710 711
  def test_take_and_first_and_last_with_integer_should_return_an_array
    assert_kind_of Array, Topic.take(5)
712 713 714 715
    assert_kind_of Array, Topic.first(5)
    assert_kind_of Array, Topic.last(5)
  end

D
David Heinemeier Hansson 已提交
716
  def test_unexisting_record_exception_handling
717
    assert_raise(ActiveRecord::RecordNotFound) {
D
David Heinemeier Hansson 已提交
718 719
      Topic.find(1).parent
    }
720

721
    Topic.find(2).topic
D
David Heinemeier Hansson 已提交
722
  end
723

724
  def test_find_only_some_columns
725
    topic = Topic.select("author_name").find(1)
726 727
    assert_raise(ActiveModel::MissingAttributeError) { topic.title }
    assert_raise(ActiveModel::MissingAttributeError) { topic.title? }
728
    assert_nil topic.read_attribute("title")
729 730
    assert_equal "David", topic.author_name
    assert !topic.attribute_present?("title")
731
    assert !topic.attribute_present?(:title)
732
    assert topic.attribute_present?("author_name")
733
    assert_respond_to topic, "author_name"
734
  end
J
Jeremy Kemper 已提交
735

736
  def test_find_on_array_conditions
737 738
    assert Topic.where(["approved = ?", false]).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(["approved = ?", true]).find(1) }
D
David Heinemeier Hansson 已提交
739
  end
740

741
  def test_find_on_hash_conditions
742 743
    assert Topic.where(approved: false).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(approved: true).find(1) }
744
  end
745

746
  def test_find_on_hash_conditions_with_qualified_attribute_dot_notation_string
747 748
    assert Topic.where("topics.approved" => false).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where("topics.approved" => true).find(1) }
749 750
  end

751 752 753 754 755
  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

756
  def test_find_on_hash_conditions_with_hashed_table_name
757 758
    assert Topic.where(topics: { approved: false }).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(topics: { approved: true }).find(1) }
759 760
  end

761
  def test_find_on_combined_explicit_and_hashed_table_names
762 763 764
    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) }
765 766
  end

767
  def test_find_with_hash_conditions_on_joined_table
768
    firms = Firm.joins(:account).where(accounts: { credit_limit: 50 })
769 770 771 772 773
    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
774
    firms = DependentFirm.joins(:account).where(name: "RailsCore", accounts: { credit_limit: 55..60 })
775 776 777 778
    assert_equal 1, firms.size
    assert_equal companies(:rails_core), firms.first
  end

779 780
  def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate
    david = customers(:david)
781
    assert Customer.where("customers.name" => david.name, :address => david.address).find(david.id)
782
    assert_raise(ActiveRecord::RecordNotFound) {
783
      Customer.where("customers.name" => david.name + "1", :address => david.address).find(david.id)
784 785 786
    }
  end

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

791
  def test_find_on_hash_conditions_with_range
792
    assert_equal [1, 2], Topic.where(id: 1..2).to_a.map(&:id).sort
793
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2..3).find(1) }
794
  end
795

796
  def test_find_on_hash_conditions_with_end_exclusive_range
797 798
    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
799
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2...3).find(3) }
800 801
  end

802
  def test_find_on_hash_conditions_with_multiple_ranges
803
    assert_equal [1, 2, 3], Comment.where(id: 1..3, post_id: 1..2).to_a.map(&:id).sort
804
    assert_equal [1], Comment.where(id: 1..1, post_id: 1..10).to_a.map(&:id).sort
805
  end
806

807
  def test_find_on_hash_conditions_with_array_of_integers_and_ranges
808
    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
809 810
  end

811
  def test_find_on_hash_conditions_with_array_of_ranges
812
    assert_equal [1, 2, 6, 7, 8], Comment.where(id: [1..2, 6..8]).to_a.map(&:id).sort
813 814
  end

815
  def test_find_on_multiple_hash_conditions
816 817 818
    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) }
819
  end
820

821
  def test_condition_interpolation
822
    assert_kind_of Firm, Company.where("name = '%s'", "37signals").first
823 824 825
    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
826 827
  end

828
  def test_condition_array_interpolation
829 830 831 832
    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 已提交
833
  end
834

835
  def test_condition_hash_interpolation
836 837 838
    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
839
  end
840

841
  def test_hash_condition_find_malformed
842
    assert_raise(ActiveRecord::StatementInvalid) {
843
      Company.where(id: 2, dhh: true).first
844 845
    }
  end
846

847 848
  def test_hash_condition_find_with_escaped_characters
    Company.create("name" => "Ain't noth'n like' \#stuff")
849
    assert Company.where(name: "Ain't noth'n like' \#stuff").first
850 851 852
  end

  def test_hash_condition_find_with_array
853 854 855
    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
856 857 858
  end

  def test_hash_condition_find_with_nil
859
    topic = Topic.where(last_read: nil).first
860 861
    assert_not_nil topic
    assert_nil topic.last_read
862
  end
D
David Heinemeier Hansson 已提交
863

864 865 866
  def test_hash_condition_find_with_aggregate_having_one_mapping
    balance = customers(:david).balance
    assert_kind_of Money, balance
867
    found_customer = Customer.where(balance: balance).first
868 869 870 871 872 873
    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
874
    found_customer = Customer.where(gps_location: gps_location).first
875 876 877 878 879 880
    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
881
    found_customer = Customer.where(balance: balance.amount).first
882 883 884 885 886 887
    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
888
    found_customer = Customer.where(gps_location: gps_location.gps_location).first
889 890 891 892 893 894
    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
895
    found_customer = Customer.where(address: address).first
896 897 898 899 900 901
    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
902
    found_customer = Customer.where(address: address, name: customers(:david).name).first
903 904 905
    assert_equal customers(:david), found_customer
  end

906
  def test_condition_utc_time_interpolation_with_default_timezone_local
907
    with_env_tz "America/New_York" do
908
      with_timezone_config default: :local do
909
        topic = Topic.first
910
        assert_equal topic, Topic.where(["written_on = ?", topic.written_on.getutc]).first
911 912 913 914 915
      end
    end
  end

  def test_hash_condition_utc_time_interpolation_with_default_timezone_local
916
    with_env_tz "America/New_York" do
917
      with_timezone_config default: :local do
918
        topic = Topic.first
919
        assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first
920 921 922 923 924
      end
    end
  end

  def test_condition_local_time_interpolation_with_default_timezone_utc
925
    with_env_tz "America/New_York" do
926
      with_timezone_config default: :utc do
927
        topic = Topic.first
928
        assert_equal topic, Topic.where(["written_on = ?", topic.written_on.getlocal]).first
929 930 931 932 933
      end
    end
  end

  def test_hash_condition_local_time_interpolation_with_default_timezone_utc
934
    with_env_tz "America/New_York" do
935
      with_timezone_config default: :utc do
936
        topic = Topic.first
937
        assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first
938 939 940 941
      end
    end
  end

D
David Heinemeier Hansson 已提交
942
  def test_bind_variables
943 944 945 946
    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
947
    assert_raise(ActiveRecord::PreparedStatementInvalid) {
948
      Company.where(["id=? AND name = ?", 2]).first
D
David Heinemeier Hansson 已提交
949
    }
950
    assert_raise(ActiveRecord::PreparedStatementInvalid) {
951
      Company.where(["id=?", 2, 3, 4]).first
D
David Heinemeier Hansson 已提交
952 953
    }
  end
954

D
David Heinemeier Hansson 已提交
955
  def test_bind_variables_with_quotes
B
Benjamin Fleischer 已提交
956 957
    Company.create("name" => "37signals' go'es against")
    assert Company.where(["name = ?", "37signals' go'es against"]).first
D
David Heinemeier Hansson 已提交
958 959 960
  end

  def test_named_bind_variables_with_quotes
B
Benjamin Fleischer 已提交
961 962
    Company.create("name" => "37signals' go'es against")
    assert Company.where(["name = :name", { name: "37signals' go'es against" }]).first
D
David Heinemeier Hansson 已提交
963 964 965
  end

  def test_named_bind_variables
966 967 968 969
    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 已提交
970 971 972 973 974 975 976 977 978
  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
979
    assert_equal topics(:first), Topic.find_by_title("The First Topic")
D
David Heinemeier Hansson 已提交
980 981
    assert_nil Topic.find_by_title("The First Topic!")
  end
J
Jeremy Kemper 已提交
982

983 984
  def test_find_by_one_attribute_bang
    assert_equal topics(:first), Topic.find_by_title!("The First Topic")
985 986 987
    assert_raises_with_message(ActiveRecord::RecordNotFound, "Couldn't find Topic") do
      Topic.find_by_title!("The First Topic!")
    end
988 989
  end

990
  def test_find_by_on_attribute_that_is_a_reserved_word
991
    dog_alias = "Dog"
992 993 994 995 996
    dog = Dog.create(alias: dog_alias)

    assert_equal dog, Dog.find_by_alias(dog_alias)
  end

997 998 999 1000 1001
  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 已提交
1002
  def test_find_by_one_attribute_bang_with_blank_defined
1003 1004
    blank_topic = BlankTopic.create(title: "The Blank One")
    assert_equal blank_topic, BlankTopic.find_by_title!("The Blank One")
N
Nikita Afanasenko 已提交
1005 1006
  end

1007
  def test_find_by_one_attribute_with_conditions
1008
    assert_equal accounts(:rails_core_account), Account.where("firm_id = ?", 6).find_by_credit_limit(50)
1009 1010
  end

1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
  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

1045 1046
  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 已提交
1047
    class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.include?(:find_by_credit_limit)
1048 1049
    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
1050 1051
  end

1052
  def test_find_by_one_attribute_with_several_options
1053
    assert_equal accounts(:unknown), Account.order("id DESC").where("id != ?", 3).find_by_credit_limit(50)
1054
  end
1055

D
David Heinemeier Hansson 已提交
1056
  def test_find_by_one_missing_attribute
1057
    assert_raise(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
D
David Heinemeier Hansson 已提交
1058
  end
1059

1060
  def test_find_by_invalid_method_syntax
1061 1062 1063 1064
    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") }
1065
  end
D
David Heinemeier Hansson 已提交
1066 1067

  def test_find_by_two_attributes
1068
    assert_equal topics(:first), Topic.find_by_title_and_author_name("The First Topic", "David")
D
David Heinemeier Hansson 已提交
1069 1070 1071
    assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
  end

1072 1073 1074 1075
  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 已提交
1076 1077 1078 1079 1080
  def test_find_by_nil_attribute
    topic = Topic.find_by_last_read nil
    assert_not_nil topic
    assert_nil topic.last_read
  end
1081

D
David Heinemeier Hansson 已提交
1082 1083 1084 1085 1086 1087
  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
1088
    assert_raise(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
D
David Heinemeier Hansson 已提交
1089 1090
  end

1091
  def test_joins_dont_clobber_id
1092
    first = Firm.
1093 1094
      joins("INNER JOIN companies clients ON clients.firm_id = companies.id").
      where("companies.id = 1").first
1095 1096 1097
    assert_equal 1, first.id
  end

1098
  def test_joins_with_string_array
1099 1100 1101 1102
    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'"
            ])
1103 1104 1105
    assert_equal 1, person_with_reader_and_post.size
  end

1106 1107
  def test_find_by_id_with_conditions_with_or
    assert_nothing_raised do
1108
      Post.where("posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'").find([1, 2, 3])
1109 1110
    end
  end
1111

1112
  def test_find_ignores_previously_inserted_record
1113
    Post.create!(title: "test", body: "it out")
J
Jon Leighton 已提交
1114
    assert_equal [], Post.where(id: nil)
1115 1116
  end

1117 1118 1119 1120 1121
  def test_find_by_empty_ids
    assert_equal [], Post.find([])
  end

  def test_find_by_empty_in_condition
1122
    assert_equal [], Post.where("id in (?)", [])
1123 1124 1125
  end

  def test_find_by_records
1126 1127 1128
    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")
1129 1130
  end

1131 1132 1133 1134 1135 1136 1137 1138 1139
  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
1140 1141
    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")
1142 1143
  end

1144 1145
  def test_select_rows
    assert_equal(
1146
      [["1", "1", nil, "37signals"],
1147 1148
       ["2", "1", "2", "Summit"],
       ["3", "1", "1", "Microsoft"]],
1149
      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? } })
1150
    assert_equal [["1", "37signals"], ["2", "Summit"], ["3", "Microsoft"]],
1151
      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? } }
1152 1153
  end

1154
  def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
R
Fix...  
Ryuta Kamizono 已提交
1155 1156
    assert_equal 2, Post.includes(authors: :author_address).
      where.not(author_addresses: { id: nil }).
B
Ben Toews 已提交
1157
      order("author_addresses.id DESC").limit(2).to_a.size
1158

1159
    assert_equal 3, Post.includes(author: :author_address, authors: :author_address).
R
Fix...  
Ryuta Kamizono 已提交
1160
      where.not(author_addresses_authors: { id: nil }).
B
Ben Toews 已提交
1161
      order("author_addresses_authors.id DESC").limit(3).to_a.size
1162 1163
  end

1164
  def test_find_with_nil_inside_set_passed_for_one_attribute
1165 1166
    client_of = Company.
      where(client_of: [2, 1, nil],
1167
            name: ["37signals", "Summit", "Microsoft"]).
1168
      order("client_of DESC").
1169
      map(&:client_of)
1170

1171
    assert_includes client_of, nil
1172
    assert_equal [2, 1].sort, client_of.compact.sort
1173 1174
  end

1175
  def test_find_with_nil_inside_set_passed_for_attribute
1176 1177
    client_of = Company.
      where(client_of: [nil]).
1178
      order("client_of DESC").
1179
      map(&:client_of)
1180 1181 1182 1183

    assert_equal [], client_of.compact
  end

1184
  def test_with_limiting_with_custom_select
1185
    posts = Post.references(:authors).merge(
1186
      includes: :author, select: 'posts.*, authors.id as "author_id"',
1187
      limit: 3, order: "posts.id"
1188
    ).to_a
1189 1190
    assert_equal 3, posts.size
    assert_equal [0, 1, 1], posts.map(&:author_id).sort
1191
  end
1192

1193 1194 1195 1196 1197 1198 1199 1200 1201 1202
  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

1203
  def test_find_one_message_with_custom_primary_key
1204 1205 1206
    table_with_custom_primary_key do |model|
      model.primary_key = :name
      e = assert_raises(ActiveRecord::RecordNotFound) do
1207
        model.find "Hello World!"
1208
      end
1209
      assert_equal "Couldn't find MercedesCar with 'name'=Hello World!", e.message
1210 1211 1212 1213 1214 1215 1216
    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
1217
        model.find "Hello", "World!"
1218
      end
1219
      assert_equal "Couldn't find all MercedesCars with 'name': (Hello, World!) (found 0 results, but was looking for 2).", e.message
1220 1221 1222
    end
  end

1223 1224 1225 1226 1227 1228
  def test_find_without_primary_key
    assert_raises(ActiveRecord::UnknownPrimaryKey) do
      Matey.find(1)
    end
  end

1229
  def test_finder_with_offset_string
1230
    assert_nothing_raised { Topic.offset("3").to_a }
1231 1232
  end

1233 1234 1235 1236 1237 1238 1239 1240 1241
  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
1242
    assert_equal posts(:eager_other), Post.find_by("id = ?", posts(:eager_other).id)
1243 1244
  end

1245 1246 1247 1248
  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

1249
  test "find_by returns nil if the record is missing" do
1250
    assert_nil Post.find_by("1 = 0")
1251 1252
  end

1253 1254
  test "find_by with associations" do
    assert_equal authors(:david), Post.find_by(author: authors(:david)).author
1255
    assert_equal authors(:mary), Post.find_by(author: authors(:mary)).author
1256 1257
  end

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

1262 1263 1264 1265 1266 1267 1268 1269 1270
  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
1271
    assert_equal posts(:eager_other), Post.find_by!("id = ?", posts(:eager_other).id)
1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283
  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

1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303
  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

1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319
  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
1320
      assert_queries(1) do
1321 1322 1323 1324
        Topic.eager_load(:replies).limit(1).exists?
        Topic.eager_load(:replies).limit(1).exists?
      end

1325
      assert_queries(2) do
1326 1327 1328 1329 1330 1331
        Topic.eager_load(:replies).limit(1).skip_query_cache!.exists?
        Topic.eager_load(:replies).limit(1).skip_query_cache!.exists?
      end
    end
  end

1332
  private
1333 1334 1335
    def table_with_custom_primary_key
      yield(Class.new(Toy) do
        def self.name
1336
          "MercedesCar"
1337 1338 1339
        end
      end)
    end
1340 1341 1342 1343 1344

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