finder_test.rb 40.5 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'
D
David Heinemeier Hansson 已提交
18

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

22 23 24 25 26 27 28 29 30 31 32 33
  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 已提交
34
  def test_find
35
    assert_equal(topics(:first).title, Topic.find(1).title)
D
David Heinemeier Hansson 已提交
36
  end
37

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

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

49 50 51 52 53 54
  def test_find_passing_active_record_object_is_deprecated
    assert_deprecated do
      Topic.find(Topic.last)
    end
  end

55
  def test_symbols_table_ref
56
    Post.where("author_id" => nil)  # warm up
57 58 59 60 61
    x = Symbol.all_symbols.count
    Post.where("title" => {"xxxqqqq" => "bar"})
    assert_equal x, Symbol.all_symbols.count
  end

62 63 64 65 66
  # 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
67

D
David Heinemeier Hansson 已提交
68
  def test_exists
69 70 71 72 73 74
    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])
75
    assert_equal true, Topic.exists?(id: [1, 9999])
76 77

    assert_equal false, Topic.exists?(45)
78
    assert_equal false, Topic.exists?(Topic.new.id)
79

80 81 82
    assert_raise(NoMethodError) { Topic.exists?([1,2]) }
  end

83 84 85 86 87 88 89 90 91 92 93 94 95
  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

96 97 98 99 100 101
  def test_exists_passing_active_record_object_is_deprecated
    assert_deprecated do
      Topic.exists?(Topic.new)
    end
  end

102
  def test_exists_fails_when_parameter_has_invalid_type
103
    assert_raises(RangeError) do
104 105
      assert_equal false, Topic.exists?(("9"*53).to_i) # number that's bigger than int
    end
106
    assert_equal false, Topic.exists?("foo")
D
David Heinemeier Hansson 已提交
107
  end
108

109
  def test_exists_does_not_select_columns_without_alias
110
    assert_sql(/SELECT\W+1 AS one FROM ["`]topics["`]/i) do
111 112 113 114
      Topic.exists?
    end
  end

115
  def test_exists_returns_true_with_one_record_and_no_args
116
    assert_equal true, Topic.exists?
117
  end
118

E
Egor Lynko 已提交
119
  def test_exists_returns_false_with_false_arg
120
    assert_equal false, Topic.exists?(false)
E
Egor Lynko 已提交
121 122
  end

123 124 125
  # 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
126 127 128 129 130
    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?
131 132
  end

133 134
  # ensures +exists?+ runs valid SQL by excluding order value
  def test_exists_with_order
135
    assert_equal true, Topic.order(:id).distinct.exists?
136 137
  end

138
  def test_exists_with_includes_limit_and_empty_result
139 140
    assert_equal false, Topic.includes(:replies).limit(0).exists?
    assert_equal false, Topic.includes(:replies).limit(1).where('0 = 1').exists?
141 142
  end

143 144
  def test_exists_with_distinct_association_includes_and_limit
    author = Author.first
145 146
    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?
147 148 149 150
  end

  def test_exists_with_distinct_association_includes_limit_and_order
    author = Author.first
151 152
    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?
153 154
  end

155
  def test_exists_with_empty_table_and_no_args_given
156
    Topic.delete_all
157
    assert_equal false, Topic.exists?
158
  end
159

160 161
  def test_exists_with_aggregate_having_three_mappings
    existing_address = customers(:david).address
162
    assert_equal true, Customer.exists?(:address => existing_address)
163 164 165 166
  end

  def test_exists_with_aggregate_having_three_mappings_with_one_difference
    existing_address = customers(:david).address
167
    assert_equal false, Customer.exists?(:address =>
168
      Address.new(existing_address.street, existing_address.city, existing_address.country + "1"))
169
    assert_equal false, Customer.exists?(:address =>
170
      Address.new(existing_address.street, existing_address.city + "1", existing_address.country))
171
    assert_equal false, Customer.exists?(:address =>
172 173 174
      Address.new(existing_address.street + "1", existing_address.city, existing_address.country))
  end

175 176 177 178 179
  def test_exists_does_not_instantiate_records
    Developer.expects(:instantiate).never
    Developer.exists?
  end

D
David Heinemeier Hansson 已提交
180 181 182 183
  def test_find_by_array_of_one_id
    assert_kind_of(Array, Topic.find([ 1 ]))
    assert_equal(1, Topic.find([ 1 ]).length)
  end
184

D
David Heinemeier Hansson 已提交
185
  def test_find_by_ids
186 187 188 189 190
    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
191 192
    assert_equal 2, Entrant.limit(2).find([1,3,2]).size
    assert_equal 1, Entrant.limit(3).offset(2).find([1,3,2]).size
193 194 195 196

    # 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 已提交
197
    devs = Developer.all
198
    last_devs = Developer.limit(3).offset(9).find devs.map(&:id)
199
    assert_equal 2, last_devs.size
D
David Heinemeier Hansson 已提交
200 201
  end

202 203 204 205 206 207 208 209 210 211 212 213
  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

214 215 216 217 218 219 220 221 222 223
  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

224 225 226 227
  def test_find_an_empty_array
    assert_equal [], Topic.find([])
  end

228 229 230 231
  def test_find_doesnt_have_implicit_ordering
    assert_sql(/^((?!ORDER).)*$/) { Topic.find(1) }
  end

D
David Heinemeier Hansson 已提交
232
  def test_find_by_ids_missing_one
233
    assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
D
David Heinemeier Hansson 已提交
234
  end
235

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

D
David Heinemeier Hansson 已提交
246
    assert_equal(1, topics.size)
247
    assert_equal(topics(:second).title, topics.first.title)
D
David Heinemeier Hansson 已提交
248
  end
249

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

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

257 258 259 260
  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
261

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

282
  def test_first
283
    assert_equal topics(:second).title, Topic.where("title = 'The Second Topic of the day'").first.title
284
  end
285

286
  def test_first_failing
287
    assert_nil Topic.where("title = 'The Second Topic of the day!'").first
288
  end
D
David Heinemeier Hansson 已提交
289

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

302 303 304 305 306 307
  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

308
  def test_model_class_responds_to_first_bang
309 310
    assert Topic.first!
    Topic.delete_all
311
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
312 313 314 315
      Topic.first!
    end
  end

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

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

416
  def test_model_class_responds_to_last_bang
417
    assert_equal topics(:fifth), Topic.last!
418
    assert_raises_with_message ActiveRecord::RecordNotFound, "Couldn't find Topic" do
419 420 421 422 423
      Topic.delete_all
      Topic.last!
    end
  end

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

446 447
  def test_take_and_first_and_last_with_integer_should_return_an_array
    assert_kind_of Array, Topic.take(5)
448 449 450 451
    assert_kind_of Array, Topic.first(5)
    assert_kind_of Array, Topic.last(5)
  end

D
David Heinemeier Hansson 已提交
452
  def test_unexisting_record_exception_handling
453
    assert_raise(ActiveRecord::RecordNotFound) {
D
David Heinemeier Hansson 已提交
454 455
      Topic.find(1).parent
    }
456

457
    Topic.find(2).topic
D
David Heinemeier Hansson 已提交
458
  end
459

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

472
  def test_find_on_array_conditions
473 474
    assert Topic.where(["approved = ?", false]).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(["approved = ?", true]).find(1) }
D
David Heinemeier Hansson 已提交
475
  end
476

477
  def test_find_on_hash_conditions
478 479
    assert Topic.where(approved: false).find(1)
    assert_raise(ActiveRecord::RecordNotFound) { Topic.where(approved: true).find(1) }
480
  end
481

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

487
  def test_find_on_hash_conditions_with_hashed_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 492
  end

  def test_find_with_hash_conditions_on_joined_table
493
    firms = Firm.joins(:account).where(:accounts => { :credit_limit => 50 })
494 495 496 497 498
    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
499
    firms = DependentFirm.joins(:account).where(name: 'RailsCore', accounts: { credit_limit: 55..60 })
500 501 502 503
    assert_equal 1, firms.size
    assert_equal companies(:rails_core), firms.first
  end

504 505 506 507 508 509 510 511
  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

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

516
  def test_find_on_hash_conditions_with_range
517 518
    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) }
519
  end
520

521
  def test_find_on_hash_conditions_with_end_exclusive_range
522 523 524
    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) }
525 526
  end

527
  def test_find_on_hash_conditions_with_multiple_ranges
528 529
    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
530
  end
531

532
  def test_find_on_hash_conditions_with_array_of_integers_and_ranges
533
    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
534 535
  end

536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
  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

  def test_find_on_hash_conditions_with_nested_array_of_integers_and_ranges
    assert_deprecated do
      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
    end
  end

  def test_find_on_hash_conditions_with_array_of_integers_and_arrays
    assert_deprecated do
      assert_equal [1,2,3,5,6,7,8,9], Comment.where(id: [[1, 2], 3, 5, [6, [7], 8], 9]).to_a.map(&:id).sort
    end
  end

  def test_find_on_hash_conditions_with_nested_array_of_integers_and_ranges_and_nils
    assert_deprecated do
      assert_equal [1,3,4,5], Topic.where(parent_id: [[2..6], nil]).to_a.map(&:id).sort
    end
  end

  def test_find_on_hash_conditions_with_nested_array_of_integers_and_ranges_and_more_nils
    assert_deprecated do
      assert_equal [], Topic.where(parent_id: [[7..10, nil, [nil]], [nil]]).to_a.map(&:id).sort
    end
  end

564
  def test_find_on_multiple_hash_conditions
565 566 567 568
    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) }
569
  end
570

571
  def test_condition_interpolation
572
    assert_kind_of Firm, Company.where("name = '%s'", "37signals").first
573 574 575
    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
576 577
  end

578
  def test_condition_array_interpolation
579 580 581 582
    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 已提交
583
  end
584

585
  def test_condition_hash_interpolation
586 587 588
    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
589
  end
590

591
  def test_hash_condition_find_malformed
592
    assert_raise(ActiveRecord::StatementInvalid) {
593
      Company.where(id: 2, dhh: true).first
594 595
    }
  end
596

597 598
  def test_hash_condition_find_with_escaped_characters
    Company.create("name" => "Ain't noth'n like' \#stuff")
599
    assert Company.where(name: "Ain't noth'n like' \#stuff").first
600 601 602
  end

  def test_hash_condition_find_with_array
603 604 605
    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
606 607 608
  end

  def test_hash_condition_find_with_nil
609
    topic = Topic.where(last_read: nil).first
610 611
    assert_not_nil topic
    assert_nil topic.last_read
612
  end
D
David Heinemeier Hansson 已提交
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 643 644 645 646 647 648 649 650 651 652 653 654 655
  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

656 657
  def test_condition_utc_time_interpolation_with_default_timezone_local
    with_env_tz 'America/New_York' do
658
      with_timezone_config default: :local do
659
        topic = Topic.first
660
        assert_equal topic, Topic.where(['written_on = ?', topic.written_on.getutc]).first
661 662 663 664 665 666
      end
    end
  end

  def test_hash_condition_utc_time_interpolation_with_default_timezone_local
    with_env_tz 'America/New_York' do
667
      with_timezone_config default: :local do
668
        topic = Topic.first
669
        assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first
670 671 672 673 674 675
      end
    end
  end

  def test_condition_local_time_interpolation_with_default_timezone_utc
    with_env_tz 'America/New_York' do
676
      with_timezone_config default: :utc do
677
        topic = Topic.first
678
        assert_equal topic, Topic.where(['written_on = ?', topic.written_on.getlocal]).first
679 680 681 682 683 684
      end
    end
  end

  def test_hash_condition_local_time_interpolation_with_default_timezone_utc
    with_env_tz 'America/New_York' do
685
      with_timezone_config default: :utc do
686
        topic = Topic.first
687
        assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first
688 689 690 691
      end
    end
  end

D
David Heinemeier Hansson 已提交
692
  def test_bind_variables
693 694 695 696
    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
697
    assert_raise(ActiveRecord::PreparedStatementInvalid) {
698
      Company.where(["id=? AND name = ?", 2]).first
D
David Heinemeier Hansson 已提交
699
    }
700
    assert_raise(ActiveRecord::PreparedStatementInvalid) {
701
     Company.where(["id=?", 2, 3, 4]).first
D
David Heinemeier Hansson 已提交
702 703
    }
  end
704

D
David Heinemeier Hansson 已提交
705 706
  def test_bind_variables_with_quotes
    Company.create("name" => "37signals' go'es agains")
707
    assert Company.where(["name = ?", "37signals' go'es agains"]).first
D
David Heinemeier Hansson 已提交
708 709 710 711
  end

  def test_named_bind_variables_with_quotes
    Company.create("name" => "37signals' go'es agains")
712
    assert Company.where(["name = :name", {name: "37signals' go'es agains"}]).first
D
David Heinemeier Hansson 已提交
713 714 715 716
  end

  def test_bind_arity
    assert_nothing_raised                                 { bind '' }
717
    assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
718

719
    assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?' }
D
David Heinemeier Hansson 已提交
720
    assert_nothing_raised                                 { bind '?', 1 }
721
    assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1  }
D
David Heinemeier Hansson 已提交
722
  end
723

D
David Heinemeier Hansson 已提交
724 725 726
  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
727

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

730 731 732 733
    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 已提交
734 735
  end

736 737 738 739 740 741 742 743 744 745 746 747
  class SimpleEnumerable
    include Enumerable

    def initialize(ary)
      @ary = ary
    end

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

748
  def test_bind_enumerable
749 750
    quoted_abc = %(#{ActiveRecord::Base.connection.quote('a')},#{ActiveRecord::Base.connection.quote('b')},#{ActiveRecord::Base.connection.quote('c')})

D
David Heinemeier Hansson 已提交
751
    assert_equal '1,2,3', bind('?', [1, 2, 3])
752
    assert_equal quoted_abc, bind('?', %w(a b c))
D
David Heinemeier Hansson 已提交
753 754

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

757 758
    assert_equal '1,2,3', bind('?', SimpleEnumerable.new([1, 2, 3]))
    assert_equal quoted_abc, bind('?', SimpleEnumerable.new(%w(a b c)))
759

760 761
    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 已提交
762 763
  end

764 765 766 767 768 769 770
  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 已提交
771 772 773
  def test_bind_empty_string
    quoted_empty = ActiveRecord::Base.connection.quote('')
    assert_equal quoted_empty, bind('?', '')
774 775
  end

776 777 778 779 780
  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")
781 782
    assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi".mb_chars)
    assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper".mb_chars)
783 784
  end

785 786 787 788 789 790 791 792
  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

793 794 795
  def test_named_bind_with_postgresql_type_casts
    l = Proc.new { bind(":a::integer '2009-01-01'::date", :a => '10') }
    assert_nothing_raised(&l)
796
    assert_equal "#{ActiveRecord::Base.connection.quote('10')}::integer '2009-01-01'::date", l.call
797 798
  end

D
David Heinemeier Hansson 已提交
799
  def test_string_sanitation
800 801
    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 已提交
802 803 804 805 806 807 808 809 810
  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
811
    assert_equal topics(:first), Topic.find_by_title("The First Topic")
D
David Heinemeier Hansson 已提交
812 813
    assert_nil Topic.find_by_title("The First Topic!")
  end
J
Jeremy Kemper 已提交
814

815 816
  def test_find_by_one_attribute_bang
    assert_equal topics(:first), Topic.find_by_title!("The First Topic")
817 818 819
    assert_raises_with_message(ActiveRecord::RecordNotFound, "Couldn't find Topic") do
      Topic.find_by_title!("The First Topic!")
    end
820 821
  end

822 823 824 825 826 827 828
  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

829 830 831 832 833
  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 已提交
834
  def test_find_by_one_attribute_bang_with_blank_defined
835 836
    blank_topic = BlankTopic.create(title: "The Blank One")
    assert_equal blank_topic, BlankTopic.find_by_title!("The Blank One")
N
Nikita Afanasenko 已提交
837 838
  end

839
  def test_find_by_one_attribute_with_conditions
J
Jon Leighton 已提交
840
    assert_equal accounts(:rails_core_account), Account.where('firm_id = ?', 6).find_by_credit_limit(50)
841 842
  end

843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876
  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

877 878
  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 已提交
879
    class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.include?(:find_by_credit_limit)
J
Jon Leighton 已提交
880 881
    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
882 883
  end

884
  def test_find_by_one_attribute_with_several_options
J
Jon Leighton 已提交
885
    assert_equal accounts(:unknown), Account.order('id DESC').where('id != ?', 3).find_by_credit_limit(50)
886
  end
887

D
David Heinemeier Hansson 已提交
888
  def test_find_by_one_missing_attribute
889
    assert_raise(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
D
David Heinemeier Hansson 已提交
890
  end
891

892
  def test_find_by_invalid_method_syntax
893 894 895 896
    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") }
897
  end
D
David Heinemeier Hansson 已提交
898 899

  def test_find_by_two_attributes
900
    assert_equal topics(:first), Topic.find_by_title_and_author_name("The First Topic", "David")
D
David Heinemeier Hansson 已提交
901 902 903
    assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
  end

904 905 906 907
  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 已提交
908
  def test_find_last_with_offset
909
    devs = Developer.order('id')
L
Lauro Caetano 已提交
910 911 912 913 914 915 916

    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 已提交
917 918 919 920 921
  def test_find_by_nil_attribute
    topic = Topic.find_by_last_read nil
    assert_not_nil topic
    assert_nil topic.last_read
  end
922

D
David Heinemeier Hansson 已提交
923 924 925 926 927 928
  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
929
    assert_raise(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
D
David Heinemeier Hansson 已提交
930 931
  end

932
  def test_find_all_with_join
933 934 935
    developers_on_project_one = Developer.
      joins('LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id').
      where('project_id=1').to_a
936
    assert_equal 3, developers_on_project_one.length
937 938 939
    developer_names = developers_on_project_one.map { |d| d.name }
    assert developer_names.include?('David')
    assert developer_names.include?('Jamis')
940
  end
941

942
  def test_joins_dont_clobber_id
943 944 945
    first = Firm.
      joins('INNER JOIN companies clients ON clients.firm_id = companies.id').
      where('companies.id = 1').first
946 947 948
    assert_equal 1, first.id
  end

949
  def test_joins_with_string_array
950 951 952 953
    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'"
            ])
954 955 956
    assert_equal 1, person_with_reader_and_post.size
  end

957 958
  def test_find_by_id_with_conditions_with_or
    assert_nothing_raised do
J
Jon Leighton 已提交
959
      Post.where("posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'").find([1,2,3])
960 961
    end
  end
962

963 964
  # http://dev.rubyonrails.org/ticket/6778
  def test_find_ignores_previously_inserted_record
A
Aaron Patterson 已提交
965
    Post.create!(:title => 'test', :body => 'it out')
J
Jon Leighton 已提交
966
    assert_equal [], Post.where(id: nil)
967 968
  end

969 970 971 972 973
  def test_find_by_empty_ids
    assert_equal [], Post.find([])
  end

  def test_find_by_empty_in_condition
974
    assert_equal [], Post.where('id in (?)', [])
975 976 977
  end

  def test_find_by_records
978 979 980
    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')
981 982
  end

983 984 985 986 987 988 989 990 991
  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
992 993
    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! { |i| i.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")
994 995
  end

996 997
  def test_select_rows
    assert_equal(
998
      [["1", "1", nil, "37signals"],
999 1000
       ["2", "1", "2", "Summit"],
       ["3", "1", "1", "Microsoft"]],
1001
      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?}})
1002
    assert_equal [["1", "37signals"], ["2", "Summit"], ["3", "Microsoft"]],
1003
      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?}}
1004 1005
  end

1006
  def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
1007
    assert_equal 2, Post.includes(authors: :author_address).order('author_addresses.id DESC ').limit(2).to_a.size
1008

1009 1010
    assert_equal 3, Post.includes(author: :author_address, authors: :author_address).
                              order('author_addresses_authors.id DESC ').limit(3).to_a.size
1011 1012
  end

1013
  def test_find_with_nil_inside_set_passed_for_one_attribute
1014 1015 1016 1017 1018
    client_of = Company.
      where(client_of: [2, 1, nil],
            name: ['37signals', 'Summit', 'Microsoft']).
      order('client_of DESC').
      map { |x| x.client_of }
1019

1020 1021
    assert client_of.include?(nil)
    assert_equal [2, 1].sort, client_of.compact.sort
1022 1023
  end

1024
  def test_find_with_nil_inside_set_passed_for_attribute
1025 1026 1027 1028
    client_of = Company.
      where(client_of: [nil]).
      order('client_of DESC').
      map { |x| x.client_of }
1029 1030 1031 1032

    assert_equal [], client_of.compact
  end

1033
  def test_with_limiting_with_custom_select
1034
    posts = Post.references(:authors).merge(
1035
      :includes => :author, :select => 'posts.*, authors.id as "author_id"',
J
Jon Leighton 已提交
1036
      :limit => 3, :order => 'posts.id'
1037
    ).to_a
1038 1039
    assert_equal 3, posts.size
    assert_equal [0, 1, 1], posts.map(&:author_id).sort
1040
  end
1041

1042
  def test_find_one_message_with_custom_primary_key
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058
    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
1059 1060 1061
    end
  end

1062 1063 1064 1065 1066 1067
  def test_find_without_primary_key
    assert_raises(ActiveRecord::UnknownPrimaryKey) do
      Matey.find(1)
    end
  end

1068
  def test_finder_with_offset_string
1069
    assert_nothing_raised(ActiveRecord::StatementInvalid) { Topic.offset("3").to_a }
1070 1071
  end

1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
  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

1088 1089 1090 1091 1092
  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

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

1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118
  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

D
David Heinemeier Hansson 已提交
1119 1120 1121 1122 1123 1124 1125 1126
  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
1127 1128 1129 1130 1131 1132 1133 1134

    def table_with_custom_primary_key
      yield(Class.new(Toy) do
        def self.name
          'MercedesCar'
        end
      end)
    end
1135 1136 1137 1138 1139 1140

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