finder_test.rb 41.1 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'
20
require 'models/electron'
D
David Heinemeier Hansson 已提交
21

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

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

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

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

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

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

68 69 70 71 72
  # 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
73

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

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

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

89 90 91 92 93 94 95 96 97 98 99 100 101
  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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

208 209 210 211 212 213 214 215 216 217 218 219
  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

220 221 222 223 224 225 226 227 228 229
  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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

498 499 500 501 502 503
  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

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

516 517 518 519 520 521 522 523
  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

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

528
  def test_find_on_hash_conditions_with_range
529 530
    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) }
531
  end
532

533
  def test_find_on_hash_conditions_with_end_exclusive_range
534 535 536
    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) }
537 538
  end

539
  def test_find_on_hash_conditions_with_multiple_ranges
540 541
    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
542
  end
543

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

548 549 550 551
  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

552
  def test_find_on_multiple_hash_conditions
553 554 555 556
    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) }
557
  end
558

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

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

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

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

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

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

  def test_hash_condition_find_with_nil
597
    topic = Topic.where(last_read: nil).first
598 599
    assert_not_nil topic
    assert_nil topic.last_read
600
  end
D
David Heinemeier Hansson 已提交
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 643
  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

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

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

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

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

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

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

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

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

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

D
David Heinemeier Hansson 已提交
712 713 714
  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
715

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

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

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

    def initialize(ary)
      @ary = ary
    end

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

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

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

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

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

748 749
    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 已提交
750 751
  end

752 753 754 755 756 757 758
  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 已提交
759 760 761
  def test_bind_empty_string
    quoted_empty = ActiveRecord::Base.connection.quote('')
    assert_equal quoted_empty, bind('?', '')
762 763
  end

764 765 766 767 768
  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")
769 770
    assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi".mb_chars)
    assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper".mb_chars)
771 772
  end

773 774 775 776 777 778 779 780
  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

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

D
David Heinemeier Hansson 已提交
787
  def test_string_sanitation
788 789
    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 已提交
790 791 792 793 794 795 796 797 798
  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
799
    assert_equal topics(:first), Topic.find_by_title("The First Topic")
D
David Heinemeier Hansson 已提交
800 801
    assert_nil Topic.find_by_title("The First Topic!")
  end
J
Jeremy Kemper 已提交
802

803 804 805 806 807 808 809 810 811 812 813 814
  def test_find_by_does_not_overwrite_method_on_class
    Electron.create(name: 'First Electron')

    assert_equal 0, Electron.times_called_find_by_name

    Electron.find_by_name('First Electron')
    assert_equal 1, Electron.times_called_find_by_name

    Electron.find_by_name('Some Other Electron')
    assert_equal 2, Electron.times_called_find_by_name
  end

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
    developer_names = developers_on_project_one.map(&:name)
938 939
    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
  def test_find_ignores_previously_inserted_record
A
Aaron Patterson 已提交
964
    Post.create!(:title => 'test', :body => 'it out')
J
Jon Leighton 已提交
965
    assert_equal [], Post.where(id: nil)
966 967
  end

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

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

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

982 983 984 985 986 987 988 989 990
  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
991
    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)
992
    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")
993 994
  end

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

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

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

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

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

1023
  def test_find_with_nil_inside_set_passed_for_attribute
1024 1025 1026
    client_of = Company.
      where(client_of: [nil]).
      order('client_of DESC').
1027
      map(&:client_of)
1028 1029 1030 1031

    assert_equal [], client_of.compact
  end

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

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

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

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

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

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

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

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

1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137
  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 已提交
1138 1139 1140 1141 1142 1143 1144 1145
  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
1146 1147 1148 1149 1150 1151 1152 1153

    def table_with_custom_primary_key
      yield(Class.new(Toy) do
        def self.name
          'MercedesCar'
        end
      end)
    end
1154 1155 1156 1157 1158 1159

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