finder_test.rb 47.2 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
    assert_equal false, Topic.includes(:replies).limit(0).exists?
274
    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
    assert_equal false, author.unique_categorized_posts.includes(:special_comments).limit(0).exists?
    assert_equal true, author.unique_categorized_posts.includes(:special_comments).limit(1).exists?
281 282 283 284
  end

  def test_exists_with_distinct_association_includes_limit_and_order
    author = Author.first
B
Ben Toews 已提交
285 286
    assert_equal false, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(0).exists?
    assert_equal true, author.unique_categorized_posts.includes(:special_comments).order("comments.tags_count DESC").limit(1).exists?
287 288
  end

289
  def test_exists_should_reference_correct_aliases_while_joining_tables_of_has_many_through_association
290 291
    developer = developers(:david)
    assert_not_predicate developer.ratings.includes(comment: :post).where(posts: { id: 1 }), :exists?
292 293
  end

294
  def test_exists_with_empty_table_and_no_args_given
295
    Topic.delete_all
296
    assert_equal false, Topic.exists?
297
  end
298

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

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

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

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

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

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

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

  def test_find_by_with_large_number
348
    assert_nil Topic.find_by(id: "9999999999999999999999999999999")
349 350 351
  end

  def test_find_by_id_with_large_number
352
    assert_nil Topic.find_by_id("9999999999999999999999999999999")
353 354
  end

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

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

365 366 367 368
  def test_find_an_empty_array
    assert_equal [], Topic.find([])
  end

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

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

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

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

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

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

398 399 400 401
  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
402

403 404 405 406 407 408
  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

409 410 411 412 413
  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

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

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

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

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

454 455 456 457 458 459
  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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    comments = comments.offset(1)
    assert_equal comments.limit(2).to_a.last, comments.limit(2).last
    assert_equal comments.limit(2).to_a.last(2), comments.limit(2).last(2)
    assert_equal comments.limit(2).to_a.last(3), comments.limit(2).last(3)
684 685
  end

686 687
  def test_take_and_first_and_last_with_integer_should_return_an_array
    assert_kind_of Array, Topic.take(5)
688 689 690 691
    assert_kind_of Array, Topic.first(5)
    assert_kind_of Array, Topic.last(5)
  end

D
David Heinemeier Hansson 已提交
692
  def test_unexisting_record_exception_handling
693
    assert_raise(ActiveRecord::RecordNotFound) {
D
David Heinemeier Hansson 已提交
694 695
      Topic.find(1).parent
    }
696

697
    Topic.find(2).topic
D
David Heinemeier Hansson 已提交
698
  end
699

700
  def test_find_only_some_columns
701
    topic = Topic.select("author_name").find(1)
702 703
    assert_raise(ActiveModel::MissingAttributeError) { topic.title }
    assert_raise(ActiveModel::MissingAttributeError) { topic.title? }
704
    assert_nil topic.read_attribute("title")
705 706
    assert_equal "David", topic.author_name
    assert !topic.attribute_present?("title")
707
    assert !topic.attribute_present?(:title)
708
    assert topic.attribute_present?("author_name")
709
    assert_respond_to topic, "author_name"
710
  end
J
Jeremy Kemper 已提交
711

712
  def test_find_on_array_conditions
713 714
    assert Topic.where(["approved = ?", false]).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(["approved = ?", true]).find(1) }
D
David Heinemeier Hansson 已提交
715
  end
716

717
  def test_find_on_hash_conditions
718 719
    assert Topic.where(approved: false).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(approved: true).find(1) }
720
  end
721

722
  def test_find_on_hash_conditions_with_qualified_attribute_dot_notation_string
723 724
    assert Topic.where("topics.approved" => false).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where("topics.approved" => true).find(1) }
725 726
  end

727 728 729 730 731
  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

732
  def test_find_on_hash_conditions_with_hashed_table_name
733 734
    assert Topic.where(topics: { approved: false }).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(topics: { approved: true }).find(1) }
735 736
  end

737
  def test_find_on_combined_explicit_and_hashed_table_names
738 739 740
    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) }
741 742
  end

743
  def test_find_with_hash_conditions_on_joined_table
744
    firms = Firm.joins(:account).where(accounts: { credit_limit: 50 })
745 746 747 748 749
    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
750
    firms = DependentFirm.joins(:account).where(name: "RailsCore", accounts: { credit_limit: 55..60 })
751 752 753 754
    assert_equal 1, firms.size
    assert_equal companies(:rails_core), firms.first
  end

755 756
  def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate
    david = customers(:david)
757
    assert Customer.where("customers.name" => david.name, :address => david.address).find(david.id)
758
    assert_raise(ActiveRecord::RecordNotFound) {
759
      Customer.where("customers.name" => david.name + "1", :address => david.address).find(david.id)
760 761 762
    }
  end

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

767
  def test_find_on_hash_conditions_with_range
768
    assert_equal [1, 2], Topic.where(id: 1..2).to_a.map(&:id).sort
769
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2..3).find(1) }
770
  end
771

772
  def test_find_on_hash_conditions_with_end_exclusive_range
773 774
    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
775
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2...3).find(3) }
776 777
  end

778
  def test_find_on_hash_conditions_with_multiple_ranges
779
    assert_equal [1, 2, 3], Comment.where(id: 1..3, post_id: 1..2).to_a.map(&:id).sort
780
    assert_equal [1], Comment.where(id: 1..1, post_id: 1..10).to_a.map(&:id).sort
781
  end
782

783
  def test_find_on_hash_conditions_with_array_of_integers_and_ranges
784
    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
785 786
  end

787
  def test_find_on_hash_conditions_with_array_of_ranges
788
    assert_equal [1, 2, 6, 7, 8], Comment.where(id: [1..2, 6..8]).to_a.map(&:id).sort
789 790
  end

791
  def test_find_on_multiple_hash_conditions
792 793 794
    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) }
795
  end
796

797
  def test_condition_interpolation
798
    assert_kind_of Firm, Company.where("name = '%s'", "37signals").first
799 800 801
    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
802 803
  end

804
  def test_condition_array_interpolation
805 806 807 808
    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 已提交
809
  end
810

811
  def test_condition_hash_interpolation
812 813 814
    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
815
  end
816

817
  def test_hash_condition_find_malformed
818
    assert_raise(ActiveRecord::StatementInvalid) {
819
      Company.where(id: 2, dhh: true).first
820 821
    }
  end
822

823 824
  def test_hash_condition_find_with_escaped_characters
    Company.create("name" => "Ain't noth'n like' \#stuff")
825
    assert Company.where(name: "Ain't noth'n like' \#stuff").first
826 827 828
  end

  def test_hash_condition_find_with_array
829 830 831
    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
832 833 834
  end

  def test_hash_condition_find_with_nil
835
    topic = Topic.where(last_read: nil).first
836 837
    assert_not_nil topic
    assert_nil topic.last_read
838
  end
D
David Heinemeier Hansson 已提交
839

840 841 842
  def test_hash_condition_find_with_aggregate_having_one_mapping
    balance = customers(:david).balance
    assert_kind_of Money, balance
843
    found_customer = Customer.where(balance: balance).first
844 845 846 847 848 849
    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
850
    found_customer = Customer.where(gps_location: gps_location).first
851 852 853 854 855 856
    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
857
    found_customer = Customer.where(balance: balance.amount).first
858 859 860 861 862 863
    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
864
    found_customer = Customer.where(gps_location: gps_location.gps_location).first
865 866 867 868 869 870
    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
871
    found_customer = Customer.where(address: address).first
872 873 874 875 876 877
    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
878
    found_customer = Customer.where(address: address, name: customers(:david).name).first
879 880 881
    assert_equal customers(:david), found_customer
  end

882
  def test_condition_utc_time_interpolation_with_default_timezone_local
883
    with_env_tz "America/New_York" do
884
      with_timezone_config default: :local do
885
        topic = Topic.first
886
        assert_equal topic, Topic.where(["written_on = ?", topic.written_on.getutc]).first
887 888 889 890 891
      end
    end
  end

  def test_hash_condition_utc_time_interpolation_with_default_timezone_local
892
    with_env_tz "America/New_York" do
893
      with_timezone_config default: :local do
894
        topic = Topic.first
895
        assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first
896 897 898 899 900
      end
    end
  end

  def test_condition_local_time_interpolation_with_default_timezone_utc
901
    with_env_tz "America/New_York" do
902
      with_timezone_config default: :utc do
903
        topic = Topic.first
904
        assert_equal topic, Topic.where(["written_on = ?", topic.written_on.getlocal]).first
905 906 907 908 909
      end
    end
  end

  def test_hash_condition_local_time_interpolation_with_default_timezone_utc
910
    with_env_tz "America/New_York" do
911
      with_timezone_config default: :utc do
912
        topic = Topic.first
913
        assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first
914 915 916 917
      end
    end
  end

D
David Heinemeier Hansson 已提交
918
  def test_bind_variables
919 920 921 922
    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
923
    assert_raise(ActiveRecord::PreparedStatementInvalid) {
924
      Company.where(["id=? AND name = ?", 2]).first
D
David Heinemeier Hansson 已提交
925
    }
926
    assert_raise(ActiveRecord::PreparedStatementInvalid) {
927
      Company.where(["id=?", 2, 3, 4]).first
D
David Heinemeier Hansson 已提交
928 929
    }
  end
930

D
David Heinemeier Hansson 已提交
931
  def test_bind_variables_with_quotes
B
Benjamin Fleischer 已提交
932 933
    Company.create("name" => "37signals' go'es against")
    assert Company.where(["name = ?", "37signals' go'es against"]).first
D
David Heinemeier Hansson 已提交
934 935 936
  end

  def test_named_bind_variables_with_quotes
B
Benjamin Fleischer 已提交
937 938
    Company.create("name" => "37signals' go'es against")
    assert Company.where(["name = :name", { name: "37signals' go'es against" }]).first
D
David Heinemeier Hansson 已提交
939 940 941
  end

  def test_named_bind_variables
942 943 944 945
    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 已提交
946 947 948 949 950 951 952 953 954
  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
955
    assert_equal topics(:first), Topic.find_by_title("The First Topic")
D
David Heinemeier Hansson 已提交
956 957
    assert_nil Topic.find_by_title("The First Topic!")
  end
J
Jeremy Kemper 已提交
958

959 960
  def test_find_by_one_attribute_bang
    assert_equal topics(:first), Topic.find_by_title!("The First Topic")
961 962 963
    assert_raises_with_message(ActiveRecord::RecordNotFound, "Couldn't find Topic") do
      Topic.find_by_title!("The First Topic!")
    end
964 965
  end

966
  def test_find_by_on_attribute_that_is_a_reserved_word
967
    dog_alias = "Dog"
968 969 970 971 972
    dog = Dog.create(alias: dog_alias)

    assert_equal dog, Dog.find_by_alias(dog_alias)
  end

973 974 975 976 977
  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 已提交
978
  def test_find_by_one_attribute_bang_with_blank_defined
979 980
    blank_topic = BlankTopic.create(title: "The Blank One")
    assert_equal blank_topic, BlankTopic.find_by_title!("The Blank One")
N
Nikita Afanasenko 已提交
981 982
  end

983
  def test_find_by_one_attribute_with_conditions
984
    assert_equal accounts(:rails_core_account), Account.where("firm_id = ?", 6).find_by_credit_limit(50)
985 986
  end

987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020
  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

1021 1022
  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 已提交
1023
    class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.include?(:find_by_credit_limit)
1024 1025
    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
1026 1027
  end

1028
  def test_find_by_one_attribute_with_several_options
1029
    assert_equal accounts(:unknown), Account.order("id DESC").where("id != ?", 3).find_by_credit_limit(50)
1030
  end
1031

D
David Heinemeier Hansson 已提交
1032
  def test_find_by_one_missing_attribute
1033
    assert_raise(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
D
David Heinemeier Hansson 已提交
1034
  end
1035

1036
  def test_find_by_invalid_method_syntax
1037 1038 1039 1040
    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") }
1041
  end
D
David Heinemeier Hansson 已提交
1042 1043

  def test_find_by_two_attributes
1044
    assert_equal topics(:first), Topic.find_by_title_and_author_name("The First Topic", "David")
D
David Heinemeier Hansson 已提交
1045 1046 1047
    assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
  end

1048 1049 1050 1051
  def test_find_by_two_attributes_but_passing_only_one
    assert_raise(ArgumentError) { Topic.find_by_title_and_author_name("The First Topic") }
  end

L
Lauro Caetano 已提交
1052
  def test_find_last_with_offset
1053
    devs = Developer.order("id")
L
Lauro Caetano 已提交
1054 1055 1056

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

D
David Heinemeier Hansson 已提交
1060 1061 1062 1063 1064
  def test_find_by_nil_attribute
    topic = Topic.find_by_last_read nil
    assert_not_nil topic
    assert_nil topic.last_read
  end
1065

D
David Heinemeier Hansson 已提交
1066 1067 1068 1069 1070 1071
  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
1072
    assert_raise(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
D
David Heinemeier Hansson 已提交
1073 1074
  end

1075
  def test_joins_dont_clobber_id
1076
    first = Firm.
1077 1078
      joins("INNER JOIN companies clients ON clients.firm_id = companies.id").
      where("companies.id = 1").first
1079 1080 1081
    assert_equal 1, first.id
  end

1082
  def test_joins_with_string_array
1083 1084 1085 1086
    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'"
            ])
1087 1088 1089
    assert_equal 1, person_with_reader_and_post.size
  end

1090 1091
  def test_find_by_id_with_conditions_with_or
    assert_nothing_raised do
1092
      Post.where("posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'").find([1, 2, 3])
1093 1094
    end
  end
1095

1096
  def test_find_ignores_previously_inserted_record
1097
    Post.create!(title: "test", body: "it out")
J
Jon Leighton 已提交
1098
    assert_equal [], Post.where(id: nil)
1099 1100
  end

1101 1102 1103 1104 1105
  def test_find_by_empty_ids
    assert_equal [], Post.find([])
  end

  def test_find_by_empty_in_condition
1106
    assert_equal [], Post.where("id in (?)", [])
1107 1108 1109
  end

  def test_find_by_records
1110 1111 1112
    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")
1113 1114
  end

1115 1116 1117 1118 1119 1120 1121 1122 1123
  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
1124 1125
    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")
1126 1127
  end

1128 1129
  def test_select_rows
    assert_equal(
1130
      [["1", "1", nil, "37signals"],
1131 1132
       ["2", "1", "2", "Summit"],
       ["3", "1", "1", "Microsoft"]],
1133
      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? } })
1134
    assert_equal [["1", "37signals"], ["2", "Summit"], ["3", "Microsoft"]],
1135
      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? } }
1136 1137
  end

1138
  def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
R
Fix...  
Ryuta Kamizono 已提交
1139 1140
    assert_equal 2, Post.includes(authors: :author_address).
      where.not(author_addresses: { id: nil }).
B
Ben Toews 已提交
1141
      order("author_addresses.id DESC").limit(2).to_a.size
1142

1143
    assert_equal 3, Post.includes(author: :author_address, authors: :author_address).
R
Fix...  
Ryuta Kamizono 已提交
1144
      where.not(author_addresses_authors: { id: nil }).
B
Ben Toews 已提交
1145
      order("author_addresses_authors.id DESC").limit(3).to_a.size
1146 1147
  end

1148
  def test_find_with_nil_inside_set_passed_for_one_attribute
1149 1150
    client_of = Company.
      where(client_of: [2, 1, nil],
1151
            name: ["37signals", "Summit", "Microsoft"]).
1152
      order("client_of DESC").
1153
      map(&:client_of)
1154

1155
    assert_includes client_of, nil
1156
    assert_equal [2, 1].sort, client_of.compact.sort
1157 1158
  end

1159
  def test_find_with_nil_inside_set_passed_for_attribute
1160 1161
    client_of = Company.
      where(client_of: [nil]).
1162
      order("client_of DESC").
1163
      map(&:client_of)
1164 1165 1166 1167

    assert_equal [], client_of.compact
  end

1168
  def test_with_limiting_with_custom_select
1169
    posts = Post.references(:authors).merge(
1170
      includes: :author, select: 'posts.*, authors.id as "author_id"',
1171
      limit: 3, order: "posts.id"
1172
    ).to_a
1173 1174
    assert_equal 3, posts.size
    assert_equal [0, 1, 1], posts.map(&:author_id).sort
1175
  end
1176

1177 1178 1179 1180 1181 1182 1183 1184 1185 1186
  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

1187
  def test_find_one_message_with_custom_primary_key
1188 1189 1190
    table_with_custom_primary_key do |model|
      model.primary_key = :name
      e = assert_raises(ActiveRecord::RecordNotFound) do
1191
        model.find "Hello World!"
1192
      end
1193
      assert_equal "Couldn't find MercedesCar with 'name'=Hello World!", e.message
1194 1195 1196 1197 1198 1199 1200
    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
1201
        model.find "Hello", "World!"
1202
      end
1203
      assert_equal "Couldn't find all MercedesCars with 'name': (Hello, World!) (found 0 results, but was looking for 2).", e.message
1204 1205 1206
    end
  end

1207 1208 1209 1210 1211 1212
  def test_find_without_primary_key
    assert_raises(ActiveRecord::UnknownPrimaryKey) do
      Matey.find(1)
    end
  end

1213
  def test_finder_with_offset_string
1214
    assert_nothing_raised { Topic.offset("3").to_a }
1215 1216
  end

1217 1218 1219 1220 1221 1222 1223 1224 1225
  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
1226
    assert_equal posts(:eager_other), Post.find_by("id = ?", posts(:eager_other).id)
1227 1228
  end

1229 1230 1231 1232
  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

1233
  test "find_by returns nil if the record is missing" do
1234
    assert_nil Post.find_by("1 = 0")
1235 1236
  end

1237 1238
  test "find_by with associations" do
    assert_equal authors(:david), Post.find_by(author: authors(:david)).author
1239
    assert_equal authors(:mary), Post.find_by(author: authors(:mary)).author
1240 1241
  end

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

1246 1247 1248 1249 1250 1251 1252 1253 1254
  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
1255
    assert_equal posts(:eager_other), Post.find_by!("id = ?", posts(:eager_other).id)
1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267
  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

1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287
  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

1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315
  test "#skip_query_cache! for #exists?" do
    Topic.cache do
      assert_queries(1) do
        Topic.exists?
        Topic.exists?
      end

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

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

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

1316
  private
1317 1318 1319
    def table_with_custom_primary_key
      yield(Class.new(Toy) do
        def self.name
1320
          "MercedesCar"
1321 1322 1323
        end
      end)
    end
1324 1325 1326 1327 1328

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