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

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

24 25 26 27 28 29 30 31 32 33 34 35
  def test_find_by_id_with_hash
    assert_raises(ActiveRecord::StatementInvalid) do
      Post.find_by_id(:limit => 1)
    end
  end

  def test_find_by_title_and_id_with_hash
    assert_raises(ActiveRecord::StatementInvalid) do
      Post.find_by_title_and_id('foo', :limit => 1)
    end
  end

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

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

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

51 52 53 54 55 56
  def test_find_passing_active_record_object_is_deprecated
    assert_deprecated do
      Topic.find(Topic.last)
    end
  end

57
  def test_symbols_table_ref
R
Ryuta Kamizono 已提交
58
    gc_disabled = GC.disable if RUBY_VERSION >= '2.2.0'
59
    Post.where("author_id" => nil)  # warm up
60 61 62
    x = Symbol.all_symbols.count
    Post.where("title" => {"xxxqqqq" => "bar"})
    assert_equal x, Symbol.all_symbols.count
R
Ryuta Kamizono 已提交
63 64
  ensure
    GC.enable if gc_disabled == false
65 66
  end

67 68 69 70 71
  # find should handle strings that come from URLs
  # (example: Category.find(params[:id]))
  def test_find_with_string
    assert_equal(Topic.find(1).title,Topic.find("1").title)
  end
72

D
David Heinemeier Hansson 已提交
73
  def test_exists
74 75 76 77 78 79
    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")
    assert_equal true, Topic.exists?(:author_name => "Mary", :approved => true)
    assert_equal true, Topic.exists?(["parent_id = ?", 1])
80
    assert_equal true, Topic.exists?(id: [1, 9999])
81 82

    assert_equal false, Topic.exists?(45)
83
    assert_equal false, Topic.exists?(Topic.new.id)
84

85 86 87
    assert_raise(NoMethodError) { Topic.exists?([1,2]) }
  end

88 89 90 91 92 93 94 95 96 97 98 99 100
  def test_exists_with_polymorphic_relation
    post = Post.create!(title: 'Post', body: 'default', taggings: [Tagging.new(comment: 'tagging comment')])
    relation = Post.tagged_with_comment('tagging comment')

    assert_equal true, relation.exists?(title: ['Post'])
    assert_equal true, relation.exists?(['title LIKE ?', 'Post%'])
    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

101 102 103 104 105 106
  def test_exists_passing_active_record_object_is_deprecated
    assert_deprecated do
      Topic.exists?(Topic.new)
    end
  end

107
  def test_exists_fails_when_parameter_has_invalid_type
108
    assert_raises(RangeError) do
109 110
      assert_equal false, Topic.exists?(("9"*53).to_i) # number that's bigger than int
    end
111
    assert_equal false, Topic.exists?("foo")
D
David Heinemeier Hansson 已提交
112
  end
113

114
  def test_exists_does_not_select_columns_without_alias
115
    assert_sql(/SELECT\W+1 AS one FROM ["`]topics["`]/i) do
116 117 118 119
      Topic.exists?
    end
  end

120
  def test_exists_returns_true_with_one_record_and_no_args
121
    assert_equal true, Topic.exists?
122
  end
123

E
Egor Lynko 已提交
124
  def test_exists_returns_false_with_false_arg
125
    assert_equal false, Topic.exists?(false)
E
Egor Lynko 已提交
126 127
  end

128 129 130
  # 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
131 132 133 134 135
    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?
136 137
  end

138 139
  # ensures +exists?+ runs valid SQL by excluding order value
  def test_exists_with_order
140
    assert_equal true, Topic.order(:id).distinct.exists?
141 142
  end

143
  def test_exists_with_includes_limit_and_empty_result
144 145
    assert_equal false, Topic.includes(:replies).limit(0).exists?
    assert_equal false, Topic.includes(:replies).limit(1).where('0 = 1').exists?
146 147
  end

148 149
  def test_exists_with_distinct_association_includes_and_limit
    author = Author.first
150 151
    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?
152 153 154 155
  end

  def test_exists_with_distinct_association_includes_limit_and_order
    author = Author.first
156 157
    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?
158 159
  end

160
  def test_exists_with_empty_table_and_no_args_given
161
    Topic.delete_all
162
    assert_equal false, Topic.exists?
163
  end
164

165 166
  def test_exists_with_aggregate_having_three_mappings
    existing_address = customers(:david).address
167
    assert_equal true, Customer.exists?(:address => existing_address)
168 169 170 171
  end

  def test_exists_with_aggregate_having_three_mappings_with_one_difference
    existing_address = customers(:david).address
172
    assert_equal false, Customer.exists?(:address =>
173
      Address.new(existing_address.street, existing_address.city, existing_address.country + "1"))
174
    assert_equal false, Customer.exists?(:address =>
175
      Address.new(existing_address.street, existing_address.city + "1", existing_address.country))
176
    assert_equal false, Customer.exists?(:address =>
177 178 179
      Address.new(existing_address.street + "1", existing_address.city, existing_address.country))
  end

180 181 182 183 184
  def test_exists_does_not_instantiate_records
    Developer.expects(:instantiate).never
    Developer.exists?
  end

D
David Heinemeier Hansson 已提交
185 186 187 188
  def test_find_by_array_of_one_id
    assert_kind_of(Array, Topic.find([ 1 ]))
    assert_equal(1, Topic.find([ 1 ]).length)
  end
189

D
David Heinemeier Hansson 已提交
190
  def test_find_by_ids
191 192 193 194 195
    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
196 197
    assert_equal 2, Entrant.limit(2).find([1,3,2]).size
    assert_equal 1, Entrant.limit(3).offset(2).find([1,3,2]).size
198 199 200 201

    # 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 已提交
202
    devs = Developer.all
203
    last_devs = Developer.limit(3).offset(9).find devs.map(&:id)
204
    assert_equal 2, last_devs.size
D
David Heinemeier Hansson 已提交
205 206
  end

207 208 209 210 211 212 213 214 215 216 217 218
  def test_find_with_large_number
    assert_raises(ActiveRecord::RecordNotFound) { Topic.find('9999999999999999999999999999999') }
  end

  def test_find_by_with_large_number
    assert_nil Topic.find_by(id: '9999999999999999999999999999999')
  end

  def test_find_by_id_with_large_number
    assert_nil Topic.find_by_id('9999999999999999999999999999999')
  end

219 220 221 222 223 224 225 226 227 228
  def test_find_on_relation_with_large_number
    assert_nil Topic.where('1=1').find_by(id: 9999999999999999999999999999999)
  end

  def test_find_by_bang_on_relation_with_large_number
    assert_raises(ActiveRecord::RecordNotFound) do
      Topic.where('1=1').find_by!(id: 9999999999999999999999999999999)
    end
  end

229 230 231 232
  def test_find_an_empty_array
    assert_equal [], Topic.find([])
  end

233 234 235 236
  def test_find_doesnt_have_implicit_ordering
    assert_sql(/^((?!ORDER).)*$/) { Topic.find(1) }
  end

D
David Heinemeier Hansson 已提交
237
  def test_find_by_ids_missing_one
238
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
D
David Heinemeier Hansson 已提交
239
  end
240

241
  def test_find_with_group_and_sanitized_having_method
242
    developers = Developer.group(:salary).having("sum(salary) > ?", 10000).select('salary').to_a
243 244 245 246 247
    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 已提交
248 249
  def test_find_with_entire_select_statement
    topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
250

D
David Heinemeier Hansson 已提交
251
    assert_equal(1, topics.size)
252
    assert_equal(topics(:second).title, topics.first.title)
D
David Heinemeier Hansson 已提交
253
  end
254

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

D
David Heinemeier Hansson 已提交
258
    assert_equal(1, topics.size)
259
    assert_equal(topics(:second).title, topics.first.title)
D
David Heinemeier Hansson 已提交
260
  end
261

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

267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
  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
282
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
283 284 285 286
      Topic.where("title = 'This title does not exist'").take!
    end
  end

287
  def test_first
288
    assert_equal topics(:second).title, Topic.where("title = 'The Second Topic of the day'").first.title
289
  end
290

291
  def test_first_failing
292
    assert_nil Topic.where("title = 'The Second Topic of the day!'").first
293
  end
D
David Heinemeier Hansson 已提交
294

295 296 297 298 299 300 301
  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
302
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
303 304 305 306
      Topic.where("title = 'This title does not exist'").first!
    end
  end

307 308 309 310 311 312
  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

313
  def test_model_class_responds_to_first_bang
314 315
    assert Topic.first!
    Topic.delete_all
316
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
317 318 319 320
      Topic.first!
    end
  end

321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
  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
338
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
      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
360
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
      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
382
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
      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
404
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
405 406 407 408
      Topic.fifth!
    end
  end

409 410 411 412 413 414 415
  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
416
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
417 418 419 420
      Topic.where("title = 'This title does not exist'").last!
    end
  end

421
  def test_model_class_responds_to_last_bang
422
    assert_equal topics(:fifth), Topic.last!
423
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
424 425 426 427 428
      Topic.delete_all
      Topic.last!
    end
  end

429 430
  def test_take_and_first_and_last_with_integer_should_use_sql_limit
    assert_sql(/LIMIT 3|ROWNUM <= 3/) { Topic.take(3).entries }
431 432
    assert_sql(/LIMIT 2|ROWNUM <= 2/) { Topic.first(2).entries }
    assert_sql(/LIMIT 5|ROWNUM <= 5/) { Topic.last(5).entries }
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
  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

  def test_last_with_integer_and_order_should_not_use_sql_limit
    query = assert_sql { Topic.order("title").last(5).entries }
    assert_equal 1, query.length
    assert_no_match(/LIMIT/, query.first)
  end

  def test_last_with_integer_and_reorder_should_not_use_sql_limit
    query = assert_sql { Topic.reorder("title").last(5).entries }
    assert_equal 1, query.length
    assert_no_match(/LIMIT/, query.first)
449 450
  end

451 452
  def test_take_and_first_and_last_with_integer_should_return_an_array
    assert_kind_of Array, Topic.take(5)
453 454 455 456
    assert_kind_of Array, Topic.first(5)
    assert_kind_of Array, Topic.last(5)
  end

D
David Heinemeier Hansson 已提交
457
  def test_unexisting_record_exception_handling
458
    assert_raise(ActiveRecord::RecordNotFound) {
D
David Heinemeier Hansson 已提交
459 460
      Topic.find(1).parent
    }
461

462
    Topic.find(2).topic
D
David Heinemeier Hansson 已提交
463
  end
464

465
  def test_find_only_some_columns
466
    topic = Topic.select("author_name").find(1)
467
    assert_raise(ActiveModel::MissingAttributeError) {topic.title}
468
    assert_raise(ActiveModel::MissingAttributeError) {topic.title?}
469
    assert_nil topic.read_attribute("title")
470 471
    assert_equal "David", topic.author_name
    assert !topic.attribute_present?("title")
472
    assert !topic.attribute_present?(:title)
473
    assert topic.attribute_present?("author_name")
474
    assert_respond_to topic, "author_name"
475
  end
J
Jeremy Kemper 已提交
476

477
  def test_find_on_array_conditions
478 479
    assert Topic.where(["approved = ?", false]).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(["approved = ?", true]).find(1) }
D
David Heinemeier Hansson 已提交
480
  end
481

482
  def test_find_on_hash_conditions
483 484
    assert Topic.where(approved: false).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(approved: true).find(1) }
485
  end
486

487
  def test_find_on_hash_conditions_with_explicit_table_name
488 489
    assert Topic.where('topics.approved' => false).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where('topics.approved' => true).find(1) }
490 491
  end

492
  def test_find_on_hash_conditions_with_hashed_table_name
493 494
    assert Topic.where(topics: { approved: false }).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(topics: { approved: true }).find(1) }
495 496
  end

497 498 499 500 501 502
  def test_find_on_combined_explicit_and_hashed_table_names
    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) }
  end

503
  def test_find_with_hash_conditions_on_joined_table
504
    firms = Firm.joins(:account).where(:accounts => { :credit_limit => 50 })
505 506 507 508 509
    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
510
    firms = DependentFirm.joins(:account).where(name: 'RailsCore', accounts: { credit_limit: 55..60 })
511 512 513 514
    assert_equal 1, firms.size
    assert_equal companies(:rails_core), firms.first
  end

515 516 517 518 519 520 521 522
  def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate
    david = customers(:david)
    assert Customer.where('customers.name' => david.name, :address => david.address).find(david.id)
    assert_raise(ActiveRecord::RecordNotFound) {
      Customer.where('customers.name' => david.name + "1", :address => david.address).find(david.id)
    }
  end

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

527
  def test_find_on_hash_conditions_with_range
528 529
    assert_equal [1,2], Topic.where(id: 1..2).to_a.map(&:id).sort
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2..3).find(1) }
530
  end
531

532
  def test_find_on_hash_conditions_with_end_exclusive_range
533 534 535
    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
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(id: 2...3).find(3) }
536 537
  end

538
  def test_find_on_hash_conditions_with_multiple_ranges
539 540
    assert_equal [1,2,3], Comment.where(id: 1..3, post_id: 1..2).to_a.map(&:id).sort
    assert_equal [1], Comment.where(id: 1..1, post_id: 1..10).to_a.map(&:id).sort
541
  end
542

543
  def test_find_on_hash_conditions_with_array_of_integers_and_ranges
544
    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
545 546
  end

547 548 549 550
  def test_find_on_hash_conditions_with_array_of_ranges
    assert_equal [1,2,6,7,8], Comment.where(id: [1..2, 6..8]).to_a.map(&:id).sort
  end

551
  def test_find_on_multiple_hash_conditions
552 553 554 555
    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) }
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(author_name: "David", title: "The First Topic", replies_count: 1, approved: true).find(1) }
556
  end
557

558
  def test_condition_interpolation
559
    assert_kind_of Firm, Company.where("name = '%s'", "37signals").first
560 561 562
    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
563 564
  end

565
  def test_condition_array_interpolation
566 567 568 569
    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 已提交
570
  end
571

572
  def test_condition_hash_interpolation
573 574 575
    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
576
  end
577

578
  def test_hash_condition_find_malformed
579
    assert_raise(ActiveRecord::StatementInvalid) {
580
      Company.where(id: 2, dhh: true).first
581 582
    }
  end
583

584 585
  def test_hash_condition_find_with_escaped_characters
    Company.create("name" => "Ain't noth'n like' \#stuff")
586
    assert Company.where(name: "Ain't noth'n like' \#stuff").first
587 588 589
  end

  def test_hash_condition_find_with_array
590 591 592
    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
593 594 595
  end

  def test_hash_condition_find_with_nil
596
    topic = Topic.where(last_read: nil).first
597 598
    assert_not_nil topic
    assert_nil topic.last_read
599
  end
D
David Heinemeier Hansson 已提交
600

601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642
  def test_hash_condition_find_with_aggregate_having_one_mapping
    balance = customers(:david).balance
    assert_kind_of Money, balance
    found_customer = Customer.where(:balance => balance).first
    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
    found_customer = Customer.where(:gps_location => gps_location).first
    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
    found_customer = Customer.where(:balance => balance.amount).first
    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
    found_customer = Customer.where(:gps_location => gps_location.gps_location).first
    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
    found_customer = Customer.where(:address => address).first
    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
    found_customer = Customer.where(:address => address, :name => customers(:david).name).first
    assert_equal customers(:david), found_customer
  end

643 644
  def test_condition_utc_time_interpolation_with_default_timezone_local
    with_env_tz 'America/New_York' do
645
      with_timezone_config default: :local do
646
        topic = Topic.first
647
        assert_equal topic, Topic.where(['written_on = ?', topic.written_on.getutc]).first
648 649 650 651 652 653
      end
    end
  end

  def test_hash_condition_utc_time_interpolation_with_default_timezone_local
    with_env_tz 'America/New_York' do
654
      with_timezone_config default: :local do
655
        topic = Topic.first
656
        assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first
657 658 659 660 661 662
      end
    end
  end

  def test_condition_local_time_interpolation_with_default_timezone_utc
    with_env_tz 'America/New_York' do
663
      with_timezone_config default: :utc do
664
        topic = Topic.first
665
        assert_equal topic, Topic.where(['written_on = ?', topic.written_on.getlocal]).first
666 667 668 669 670 671
      end
    end
  end

  def test_hash_condition_local_time_interpolation_with_default_timezone_utc
    with_env_tz 'America/New_York' do
672
      with_timezone_config default: :utc do
673
        topic = Topic.first
674
        assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first
675 676 677 678
      end
    end
  end

D
David Heinemeier Hansson 已提交
679
  def test_bind_variables
680 681 682 683
    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
684
    assert_raise(ActiveRecord::PreparedStatementInvalid) {
685
      Company.where(["id=? AND name = ?", 2]).first
D
David Heinemeier Hansson 已提交
686
    }
687
    assert_raise(ActiveRecord::PreparedStatementInvalid) {
688
     Company.where(["id=?", 2, 3, 4]).first
D
David Heinemeier Hansson 已提交
689 690
    }
  end
691

D
David Heinemeier Hansson 已提交
692 693
  def test_bind_variables_with_quotes
    Company.create("name" => "37signals' go'es agains")
694
    assert Company.where(["name = ?", "37signals' go'es agains"]).first
D
David Heinemeier Hansson 已提交
695 696 697 698
  end

  def test_named_bind_variables_with_quotes
    Company.create("name" => "37signals' go'es agains")
699
    assert Company.where(["name = :name", {name: "37signals' go'es agains"}]).first
D
David Heinemeier Hansson 已提交
700 701 702 703
  end

  def test_bind_arity
    assert_nothing_raised                                 { bind '' }
704
    assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
705

706
    assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?' }
D
David Heinemeier Hansson 已提交
707
    assert_nothing_raised                                 { bind '?', 1 }
708
    assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1  }
D
David Heinemeier Hansson 已提交
709
  end
710

D
David Heinemeier Hansson 已提交
711 712 713
  def test_named_bind_variables
    assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
    assert_equal '1 1', bind(':a :a', :a => 1)  # ' ruby-mode
714

715
    assert_nothing_raised { bind("'+00:00'", :foo => "bar") }
716

717 718 719 720
    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 已提交
721 722
  end

723 724 725 726 727 728 729 730 731 732 733 734
  class SimpleEnumerable
    include Enumerable

    def initialize(ary)
      @ary = ary
    end

    def each(&b)
      @ary.each(&b)
    end
  end

735
  def test_bind_enumerable
736 737
    quoted_abc = %(#{ActiveRecord::Base.connection.quote('a')},#{ActiveRecord::Base.connection.quote('b')},#{ActiveRecord::Base.connection.quote('c')})

D
David Heinemeier Hansson 已提交
738
    assert_equal '1,2,3', bind('?', [1, 2, 3])
739
    assert_equal quoted_abc, bind('?', %w(a b c))
D
David Heinemeier Hansson 已提交
740 741

    assert_equal '1,2,3', bind(':a', :a => [1, 2, 3])
742
    assert_equal quoted_abc, bind(':a', :a => %w(a b c)) # '
743

744 745
    assert_equal '1,2,3', bind('?', SimpleEnumerable.new([1, 2, 3]))
    assert_equal quoted_abc, bind('?', SimpleEnumerable.new(%w(a b c)))
746

747 748
    assert_equal '1,2,3', bind(':a', :a => SimpleEnumerable.new([1, 2, 3]))
    assert_equal quoted_abc, bind(':a', :a => SimpleEnumerable.new(%w(a b c))) # '
D
David Heinemeier Hansson 已提交
749 750
  end

751 752 753 754 755 756 757
  def test_bind_empty_enumerable
    quoted_nil = ActiveRecord::Base.connection.quote(nil)
    assert_equal quoted_nil, bind('?', [])
    assert_equal " in (#{quoted_nil})", bind(' in (?)', [])
    assert_equal "foo in (#{quoted_nil})", bind('foo in (?)', [])
  end

J
Jeremy Kemper 已提交
758 759 760
  def test_bind_empty_string
    quoted_empty = ActiveRecord::Base.connection.quote('')
    assert_equal quoted_empty, bind('?', '')
761 762
  end

763 764 765 766 767
  def test_bind_chars
    quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
    quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
    assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi")
    assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper")
768 769
    assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi".mb_chars)
    assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper".mb_chars)
770 771
  end

772 773 774 775 776 777 778 779
  def test_bind_record
    o = Struct.new(:quoted_id).new(1)
    assert_equal '1', bind('?', o)

    os = [o] * 3
    assert_equal '1,1,1', bind('?', os)
  end

780 781 782
  def test_named_bind_with_postgresql_type_casts
    l = Proc.new { bind(":a::integer '2009-01-01'::date", :a => '10') }
    assert_nothing_raised(&l)
783
    assert_equal "#{ActiveRecord::Base.connection.quote('10')}::integer '2009-01-01'::date", l.call
784 785
  end

D
David Heinemeier Hansson 已提交
786
  def test_string_sanitation
787 788
    assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
    assert_equal "'something; select table'", ActiveRecord::Base.sanitize("something; select table")
D
David Heinemeier Hansson 已提交
789 790 791 792 793 794 795 796 797
  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
798
    assert_equal topics(:first), Topic.find_by_title("The First Topic")
D
David Heinemeier Hansson 已提交
799 800
    assert_nil Topic.find_by_title("The First Topic!")
  end
J
Jeremy Kemper 已提交
801

802 803
  def test_find_by_one_attribute_bang
    assert_equal topics(:first), Topic.find_by_title!("The First Topic")
804 805 806
    assert_raises_with_message(ActiveRecord::RecordNotFound, "Couldn't find Topic") do
      Topic.find_by_title!("The First Topic!")
    end
807 808
  end

809 810 811 812 813 814 815
  def test_find_by_on_attribute_that_is_a_reserved_word
    dog_alias = 'Dog'
    dog = Dog.create(alias: dog_alias)

    assert_equal dog, Dog.find_by_alias(dog_alias)
  end

816 817 818 819 820
  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 已提交
821
  def test_find_by_one_attribute_bang_with_blank_defined
822 823
    blank_topic = BlankTopic.create(title: "The Blank One")
    assert_equal blank_topic, BlankTopic.find_by_title!("The Blank One")
N
Nikita Afanasenko 已提交
824 825
  end

826
  def test_find_by_one_attribute_with_conditions
J
Jon Leighton 已提交
827
    assert_equal accounts(:rails_core_account), Account.where('firm_id = ?', 6).find_by_credit_limit(50)
828 829
  end

830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863
  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

864 865
  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 已提交
866
    class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.include?(:find_by_credit_limit)
J
Jon Leighton 已提交
867 868
    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
869 870
  end

871
  def test_find_by_one_attribute_with_several_options
J
Jon Leighton 已提交
872
    assert_equal accounts(:unknown), Account.order('id DESC').where('id != ?', 3).find_by_credit_limit(50)
873
  end
874

D
David Heinemeier Hansson 已提交
875
  def test_find_by_one_missing_attribute
876
    assert_raise(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
D
David Heinemeier Hansson 已提交
877
  end
878

879
  def test_find_by_invalid_method_syntax
880 881 882 883
    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") }
884
  end
D
David Heinemeier Hansson 已提交
885 886

  def test_find_by_two_attributes
887
    assert_equal topics(:first), Topic.find_by_title_and_author_name("The First Topic", "David")
D
David Heinemeier Hansson 已提交
888 889 890
    assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
  end

891 892 893 894
  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 已提交
895
  def test_find_last_with_offset
896
    devs = Developer.order('id')
L
Lauro Caetano 已提交
897 898 899 900 901 902 903

    assert_equal devs[2], Developer.offset(2).first
    assert_equal devs[-3], Developer.offset(2).last
    assert_equal devs[-3], Developer.offset(2).last
    assert_equal devs[-3], Developer.offset(2).order('id DESC').first
  end

D
David Heinemeier Hansson 已提交
904 905 906 907 908
  def test_find_by_nil_attribute
    topic = Topic.find_by_last_read nil
    assert_not_nil topic
    assert_nil topic.last_read
  end
909

D
David Heinemeier Hansson 已提交
910 911 912 913 914 915
  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
916
    assert_raise(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
D
David Heinemeier Hansson 已提交
917 918
  end

919
  def test_find_all_with_join
920 921 922
    developers_on_project_one = Developer.
      joins('LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id').
      where('project_id=1').to_a
923
    assert_equal 3, developers_on_project_one.length
924
    developer_names = developers_on_project_one.map(&:name)
925 926
    assert developer_names.include?('David')
    assert developer_names.include?('Jamis')
927
  end
928

929
  def test_joins_dont_clobber_id
930 931 932
    first = Firm.
      joins('INNER JOIN companies clients ON clients.firm_id = companies.id').
      where('companies.id = 1').first
933 934 935
    assert_equal 1, first.id
  end

936
  def test_joins_with_string_array
937 938 939 940
    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'"
            ])
941 942 943
    assert_equal 1, person_with_reader_and_post.size
  end

944 945
  def test_find_by_id_with_conditions_with_or
    assert_nothing_raised do
J
Jon Leighton 已提交
946
      Post.where("posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'").find([1,2,3])
947 948
    end
  end
949

950 951
  # http://dev.rubyonrails.org/ticket/6778
  def test_find_ignores_previously_inserted_record
A
Aaron Patterson 已提交
952
    Post.create!(:title => 'test', :body => 'it out')
J
Jon Leighton 已提交
953
    assert_equal [], Post.where(id: nil)
954 955
  end

956 957 958 959 960
  def test_find_by_empty_ids
    assert_equal [], Post.find([])
  end

  def test_find_by_empty_in_condition
961
    assert_equal [], Post.where('id in (?)', [])
962 963 964
  end

  def test_find_by_records
965 966 967
    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')
968 969
  end

970 971 972 973 974 975 976 977 978
  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
979
    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)
980
    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")
981 982
  end

983 984
  def test_select_rows
    assert_equal(
985
      [["1", "1", nil, "37signals"],
986 987
       ["2", "1", "2", "Summit"],
       ["3", "1", "1", "Microsoft"]],
988
      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?}})
989
    assert_equal [["1", "37signals"], ["2", "Summit"], ["3", "Microsoft"]],
990
      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?}}
991 992
  end

993
  def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
994
    assert_equal 2, Post.includes(authors: :author_address).order('author_addresses.id DESC ').limit(2).to_a.size
995

996 997
    assert_equal 3, Post.includes(author: :author_address, authors: :author_address).
                              order('author_addresses_authors.id DESC ').limit(3).to_a.size
998 999
  end

1000
  def test_find_with_nil_inside_set_passed_for_one_attribute
1001 1002 1003 1004
    client_of = Company.
      where(client_of: [2, 1, nil],
            name: ['37signals', 'Summit', 'Microsoft']).
      order('client_of DESC').
1005
      map(&:client_of)
1006

1007 1008
    assert client_of.include?(nil)
    assert_equal [2, 1].sort, client_of.compact.sort
1009 1010
  end

1011
  def test_find_with_nil_inside_set_passed_for_attribute
1012 1013 1014
    client_of = Company.
      where(client_of: [nil]).
      order('client_of DESC').
1015
      map(&:client_of)
1016 1017 1018 1019

    assert_equal [], client_of.compact
  end

1020
  def test_with_limiting_with_custom_select
1021
    posts = Post.references(:authors).merge(
1022
      :includes => :author, :select => 'posts.*, authors.id as "author_id"',
J
Jon Leighton 已提交
1023
      :limit => 3, :order => 'posts.id'
1024
    ).to_a
1025 1026
    assert_equal 3, posts.size
    assert_equal [0, 1, 1], posts.map(&:author_id).sort
1027
  end
1028

1029
  def test_find_one_message_with_custom_primary_key
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
    table_with_custom_primary_key do |model|
      model.primary_key = :name
      e = assert_raises(ActiveRecord::RecordNotFound) do
        model.find 'Hello World!'
      end
      assert_equal %Q{Couldn't find MercedesCar with 'name'=Hello World!}, e.message
    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
        model.find 'Hello', 'World!'
      end
      assert_equal %Q{Couldn't find all MercedesCars with 'name': (Hello, World!) (found 0 results, but was looking for 2)}, e.message
1046 1047 1048
    end
  end

1049 1050 1051 1052 1053 1054
  def test_find_without_primary_key
    assert_raises(ActiveRecord::UnknownPrimaryKey) do
      Matey.find(1)
    end
  end

1055
  def test_finder_with_offset_string
1056
    assert_nothing_raised(ActiveRecord::StatementInvalid) { Topic.offset("3").to_a }
1057 1058
  end

1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074
  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
    assert_equal posts(:eager_other), Post.find_by('id = ?', posts(:eager_other).id)
  end

  test "find_by returns nil if the record is missing" do
    assert_equal nil, Post.find_by("1 = 0")
  end

1075 1076 1077 1078 1079
  test "find_by with associations" do
    assert_equal authors(:david), Post.find_by(author: authors(:david)).author
    assert_equal authors(:mary) , Post.find_by(author: authors(:mary) ).author
  end

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

1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105
  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
    assert_equal posts(:eager_other), Post.find_by!('id = ?', posts(:eager_other).id)
  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

1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125
  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

D
David Heinemeier Hansson 已提交
1126 1127 1128 1129 1130 1131 1132 1133
  protected
    def bind(statement, *vars)
      if vars.first.is_a?(Hash)
        ActiveRecord::Base.send(:replace_named_bind_variables, statement, vars.first)
      else
        ActiveRecord::Base.send(:replace_bind_variables, statement, vars)
      end
    end
1134 1135 1136 1137 1138 1139 1140 1141

    def table_with_custom_primary_key
      yield(Class.new(Toy) do
        def self.name
          'MercedesCar'
        end
      end)
    end
1142 1143 1144 1145 1146 1147

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