PageRenderTime 57ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/activerecord/test/cases/finder_test.rb

https://github.com/InBedBy11/rails
Ruby | 1052 lines | 891 code | 153 blank | 8 comment | 4 complexity | 739a9e0135e104b3393c7446c827742f MD5 | raw file
Possible License(s): ISC
  1. require "cases/helper"
  2. require 'models/post'
  3. require 'models/author'
  4. require 'models/categorization'
  5. require 'models/comment'
  6. require 'models/company'
  7. require 'models/topic'
  8. require 'models/reply'
  9. require 'models/entrant'
  10. require 'models/project'
  11. require 'models/developer'
  12. require 'models/customer'
  13. require 'models/job'
  14. class DynamicFinderMatchTest < ActiveRecord::TestCase
  15. def test_find_no_match
  16. assert_nil ActiveRecord::DynamicFinderMatch.match("not_a_finder")
  17. end
  18. def test_find_by
  19. match = ActiveRecord::DynamicFinderMatch.match("find_by_age_and_sex_and_location")
  20. assert_not_nil match
  21. assert match.finder?
  22. assert_equal :first, match.finder
  23. assert_equal %w(age sex location), match.attribute_names
  24. end
  25. def find_by_bang
  26. match = ActiveRecord::DynamicFinderMatch.match("find_by_age_and_sex_and_location!")
  27. assert_not_nil match
  28. assert match.finder?
  29. assert match.bang?
  30. assert_equal :first, match.finder
  31. assert_equal %w(age sex location), match.attribute_names
  32. end
  33. def test_find_all_by
  34. match = ActiveRecord::DynamicFinderMatch.match("find_all_by_age_and_sex_and_location")
  35. assert_not_nil match
  36. assert match.finder?
  37. assert_equal :all, match.finder
  38. assert_equal %w(age sex location), match.attribute_names
  39. end
  40. def test_find_or_initialize_by
  41. match = ActiveRecord::DynamicFinderMatch.match("find_or_initialize_by_age_and_sex_and_location")
  42. assert_not_nil match
  43. assert !match.finder?
  44. assert match.instantiator?
  45. assert_equal :first, match.finder
  46. assert_equal :new, match.instantiator
  47. assert_equal %w(age sex location), match.attribute_names
  48. end
  49. def test_find_or_create_by
  50. match = ActiveRecord::DynamicFinderMatch.match("find_or_create_by_age_and_sex_and_location")
  51. assert_not_nil match
  52. assert !match.finder?
  53. assert match.instantiator?
  54. assert_equal :first, match.finder
  55. assert_equal :create, match.instantiator
  56. assert_equal %w(age sex location), match.attribute_names
  57. end
  58. end
  59. class FinderTest < ActiveRecord::TestCase
  60. fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers, :categories, :categorizations
  61. def test_find
  62. assert_equal(topics(:first).title, Topic.find(1).title)
  63. end
  64. # find should handle strings that come from URLs
  65. # (example: Category.find(params[:id]))
  66. def test_find_with_string
  67. assert_equal(Topic.find(1).title,Topic.find("1").title)
  68. end
  69. def test_exists
  70. assert Topic.exists?(1)
  71. assert Topic.exists?("1")
  72. assert Topic.exists?(:author_name => "David")
  73. assert Topic.exists?(:author_name => "Mary", :approved => true)
  74. assert Topic.exists?(["parent_id = ?", 1])
  75. assert !Topic.exists?(45)
  76. begin
  77. assert !Topic.exists?("foo")
  78. rescue ActiveRecord::StatementInvalid
  79. # PostgreSQL complains about string comparison with integer field
  80. rescue Exception
  81. flunk
  82. end
  83. assert_raise(NoMethodError) { Topic.exists?([1,2]) }
  84. end
  85. def test_exists_returns_true_with_one_record_and_no_args
  86. assert Topic.exists?
  87. end
  88. def test_does_not_exist_with_empty_table_and_no_args_given
  89. Topic.delete_all
  90. assert !Topic.exists?
  91. end
  92. def test_exists_with_aggregate_having_three_mappings
  93. existing_address = customers(:david).address
  94. assert Customer.exists?(:address => existing_address)
  95. end
  96. def test_exists_with_aggregate_having_three_mappings_with_one_difference
  97. existing_address = customers(:david).address
  98. assert !Customer.exists?(:address =>
  99. Address.new(existing_address.street, existing_address.city, existing_address.country + "1"))
  100. assert !Customer.exists?(:address =>
  101. Address.new(existing_address.street, existing_address.city + "1", existing_address.country))
  102. assert !Customer.exists?(:address =>
  103. Address.new(existing_address.street + "1", existing_address.city, existing_address.country))
  104. end
  105. def test_exists_with_scoped_include
  106. Developer.send(:with_scope, :find => { :include => :projects, :order => "projects.name" }) do
  107. assert Developer.exists?
  108. end
  109. end
  110. def test_find_by_array_of_one_id
  111. assert_kind_of(Array, Topic.find([ 1 ]))
  112. assert_equal(1, Topic.find([ 1 ]).length)
  113. end
  114. def test_find_by_ids
  115. assert_equal 2, Topic.find(1, 2).size
  116. assert_equal topics(:second).title, Topic.find([2]).first.title
  117. end
  118. def test_find_by_ids_with_limit_and_offset
  119. assert_equal 2, Entrant.find([1,3,2], :limit => 2).size
  120. assert_equal 1, Entrant.find([1,3,2], :limit => 3, :offset => 2).size
  121. # Also test an edge case: If you have 11 results, and you set a
  122. # limit of 3 and offset of 9, then you should find that there
  123. # will be only 2 results, regardless of the limit.
  124. devs = Developer.find :all
  125. last_devs = Developer.find devs.map(&:id), :limit => 3, :offset => 9
  126. assert_equal 2, last_devs.size
  127. end
  128. def test_find_an_empty_array
  129. assert_equal [], Topic.find([])
  130. end
  131. def test_find_by_ids_missing_one
  132. assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, 2, 45) }
  133. end
  134. def test_find_all_with_limit
  135. assert_equal(2, Entrant.find(:all, :limit => 2).size)
  136. assert_equal(0, Entrant.find(:all, :limit => 0).size)
  137. end
  138. def test_find_all_with_prepared_limit_and_offset
  139. entrants = Entrant.find(:all, :order => "id ASC", :limit => 2, :offset => 1)
  140. assert_equal(2, entrants.size)
  141. assert_equal(entrants(:second).name, entrants.first.name)
  142. assert_equal 3, Entrant.count
  143. entrants = Entrant.find(:all, :order => "id ASC", :limit => 2, :offset => 2)
  144. assert_equal(1, entrants.size)
  145. assert_equal(entrants(:third).name, entrants.first.name)
  146. end
  147. def test_find_all_with_limit_and_offset_and_multiple_order_clauses
  148. first_three_posts = Post.find :all, :order => 'author_id, id', :limit => 3, :offset => 0
  149. second_three_posts = Post.find :all, :order => ' author_id,id ', :limit => 3, :offset => 3
  150. last_posts = Post.find :all, :order => ' author_id, id ', :limit => 3, :offset => 6
  151. assert_equal [[0,3],[1,1],[1,2]], first_three_posts.map { |p| [p.author_id, p.id] }
  152. assert_equal [[1,4],[1,5],[1,6]], second_three_posts.map { |p| [p.author_id, p.id] }
  153. assert_equal [[2,7]], last_posts.map { |p| [p.author_id, p.id] }
  154. end
  155. def test_find_with_group
  156. developers = Developer.find(:all, :group => "salary", :select => "salary")
  157. assert_equal 4, developers.size
  158. assert_equal 4, developers.map(&:salary).uniq.size
  159. end
  160. def test_find_with_group_and_having
  161. developers = Developer.find(:all, :group => "salary", :having => "sum(salary) > 10000", :select => "salary")
  162. assert_equal 3, developers.size
  163. assert_equal 3, developers.map(&:salary).uniq.size
  164. assert developers.all? { |developer| developer.salary > 10000 }
  165. end
  166. def test_find_with_group_and_sanitized_having
  167. developers = Developer.find(:all, :group => "salary", :having => ["sum(salary) > ?", 10000], :select => "salary")
  168. assert_equal 3, developers.size
  169. assert_equal 3, developers.map(&:salary).uniq.size
  170. assert developers.all? { |developer| developer.salary > 10000 }
  171. end
  172. def test_find_with_entire_select_statement
  173. topics = Topic.find_by_sql "SELECT * FROM topics WHERE author_name = 'Mary'"
  174. assert_equal(1, topics.size)
  175. assert_equal(topics(:second).title, topics.first.title)
  176. end
  177. def test_find_with_prepared_select_statement
  178. topics = Topic.find_by_sql ["SELECT * FROM topics WHERE author_name = ?", "Mary"]
  179. assert_equal(1, topics.size)
  180. assert_equal(topics(:second).title, topics.first.title)
  181. end
  182. def test_find_by_sql_with_sti_on_joined_table
  183. accounts = Account.find_by_sql("SELECT * FROM accounts INNER JOIN companies ON companies.id = accounts.firm_id")
  184. assert_equal [Account], accounts.collect(&:class).uniq
  185. end
  186. def test_find_first
  187. first = Topic.find(:first, :conditions => "title = 'The First Topic'")
  188. assert_equal(topics(:first).title, first.title)
  189. end
  190. def test_find_first_failing
  191. first = Topic.find(:first, :conditions => "title = 'The First Topic!'")
  192. assert_nil(first)
  193. end
  194. def test_first
  195. assert_equal topics(:second).title, Topic.where("title = 'The Second Topic of the day'").first.title
  196. end
  197. def test_first_failing
  198. assert_nil Topic.where("title = 'The Second Topic of the day!'").first
  199. end
  200. def test_unexisting_record_exception_handling
  201. assert_raise(ActiveRecord::RecordNotFound) {
  202. Topic.find(1).parent
  203. }
  204. Topic.find(2).topic
  205. end
  206. def test_find_only_some_columns
  207. topic = Topic.find(1, :select => "author_name")
  208. assert_raise(ActiveModel::MissingAttributeError) {topic.title}
  209. assert_equal "David", topic.author_name
  210. assert !topic.attribute_present?("title")
  211. #assert !topic.respond_to?("title")
  212. assert topic.attribute_present?("author_name")
  213. assert topic.respond_to?("author_name")
  214. end
  215. def test_find_on_blank_conditions
  216. [nil, " ", [], {}].each do |blank|
  217. assert_nothing_raised { Topic.find(:first, :conditions => blank) }
  218. end
  219. end
  220. def test_find_on_blank_bind_conditions
  221. [ [""], ["",{}] ].each do |blank|
  222. assert_nothing_raised { Topic.find(:first, :conditions => blank) }
  223. end
  224. end
  225. def test_find_on_array_conditions
  226. assert Topic.find(1, :conditions => ["approved = ?", false])
  227. assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => ["approved = ?", true]) }
  228. end
  229. def test_find_on_hash_conditions
  230. assert Topic.find(1, :conditions => { :approved => false })
  231. assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :approved => true }) }
  232. end
  233. def test_find_on_hash_conditions_with_explicit_table_name
  234. assert Topic.find(1, :conditions => { 'topics.approved' => false })
  235. assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { 'topics.approved' => true }) }
  236. end
  237. def test_find_on_hash_conditions_with_hashed_table_name
  238. assert Topic.find(1, :conditions => {:topics => { :approved => false }})
  239. assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => {:topics => { :approved => true }}) }
  240. end
  241. def test_find_with_hash_conditions_on_joined_table
  242. firms = Firm.joins(:account).where(:accounts => { :credit_limit => 50 })
  243. assert_equal 1, firms.size
  244. assert_equal companies(:first_firm), firms.first
  245. end
  246. def test_find_with_hash_conditions_on_joined_table_and_with_range
  247. firms = DependentFirm.all :joins => :account, :conditions => {:name => 'RailsCore', :accounts => { :credit_limit => 55..60 }}
  248. assert_equal 1, firms.size
  249. assert_equal companies(:rails_core), firms.first
  250. end
  251. def test_find_on_hash_conditions_with_explicit_table_name_and_aggregate
  252. david = customers(:david)
  253. assert Customer.find(david.id, :conditions => { 'customers.name' => david.name, :address => david.address })
  254. assert_raise(ActiveRecord::RecordNotFound) {
  255. Customer.find(david.id, :conditions => { 'customers.name' => david.name + "1", :address => david.address })
  256. }
  257. end
  258. def test_find_on_association_proxy_conditions
  259. assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10], Comment.find_all_by_post_id(authors(:david).posts).map(&:id).sort
  260. end
  261. def test_find_on_hash_conditions_with_range
  262. assert_equal [1,2], Topic.find(:all, :conditions => { :id => 1..2 }).map(&:id).sort
  263. assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :id => 2..3 }) }
  264. end
  265. def test_find_on_hash_conditions_with_end_exclusive_range
  266. assert_equal [1,2,3], Topic.find(:all, :conditions => { :id => 1..3 }).map(&:id).sort
  267. assert_equal [1,2], Topic.find(:all, :conditions => { :id => 1...3 }).map(&:id).sort
  268. assert_raise(ActiveRecord::RecordNotFound) { Topic.find(3, :conditions => { :id => 2...3 }) }
  269. end
  270. def test_find_on_hash_conditions_with_multiple_ranges
  271. assert_equal [1,2,3], Comment.find(:all, :conditions => { :id => 1..3, :post_id => 1..2 }).map(&:id).sort
  272. assert_equal [1], Comment.find(:all, :conditions => { :id => 1..1, :post_id => 1..10 }).map(&:id).sort
  273. end
  274. def test_find_on_multiple_hash_conditions
  275. assert Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => false })
  276. assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
  277. assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "HHC", :replies_count => 1, :approved => false }) }
  278. assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
  279. end
  280. def test_condition_interpolation
  281. assert_kind_of Firm, Company.find(:first, :conditions => ["name = '%s'", "37signals"])
  282. assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!"])
  283. assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!' OR 1=1"])
  284. assert_kind_of Time, Topic.find(:first, :conditions => ["id = %d", 1]).written_on
  285. end
  286. def test_condition_array_interpolation
  287. assert_kind_of Firm, Company.find(:first, :conditions => ["name = '%s'", "37signals"])
  288. assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!"])
  289. assert_nil Company.find(:first, :conditions => ["name = '%s'", "37signals!' OR 1=1"])
  290. assert_kind_of Time, Topic.find(:first, :conditions => ["id = %d", 1]).written_on
  291. end
  292. def test_condition_hash_interpolation
  293. assert_kind_of Firm, Company.find(:first, :conditions => { :name => "37signals"})
  294. assert_nil Company.find(:first, :conditions => { :name => "37signals!"})
  295. assert_kind_of Time, Topic.find(:first, :conditions => {:id => 1}).written_on
  296. end
  297. def test_hash_condition_find_malformed
  298. assert_raise(ActiveRecord::StatementInvalid) {
  299. Company.find(:first, :conditions => { :id => 2, :dhh => true })
  300. }
  301. end
  302. def test_hash_condition_find_with_escaped_characters
  303. Company.create("name" => "Ain't noth'n like' \#stuff")
  304. assert Company.find(:first, :conditions => { :name => "Ain't noth'n like' \#stuff" })
  305. end
  306. def test_hash_condition_find_with_array
  307. p1, p2 = Post.find(:all, :limit => 2, :order => 'id asc')
  308. assert_equal [p1, p2], Post.find(:all, :conditions => { :id => [p1, p2] }, :order => 'id asc')
  309. assert_equal [p1, p2], Post.find(:all, :conditions => { :id => [p1, p2.id] }, :order => 'id asc')
  310. end
  311. def test_hash_condition_find_with_nil
  312. topic = Topic.find(:first, :conditions => { :last_read => nil } )
  313. assert_not_nil topic
  314. assert_nil topic.last_read
  315. end
  316. def test_hash_condition_find_with_aggregate_having_one_mapping
  317. balance = customers(:david).balance
  318. assert_kind_of Money, balance
  319. found_customer = Customer.find(:first, :conditions => {:balance => balance})
  320. assert_equal customers(:david), found_customer
  321. end
  322. def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_aggregate
  323. gps_location = customers(:david).gps_location
  324. assert_kind_of GpsLocation, gps_location
  325. found_customer = Customer.find(:first, :conditions => {:gps_location => gps_location})
  326. assert_equal customers(:david), found_customer
  327. end
  328. def test_hash_condition_find_with_aggregate_having_one_mapping_and_key_value_being_attribute_value
  329. balance = customers(:david).balance
  330. assert_kind_of Money, balance
  331. found_customer = Customer.find(:first, :conditions => {:balance => balance.amount})
  332. assert_equal customers(:david), found_customer
  333. end
  334. def test_hash_condition_find_with_aggregate_attribute_having_same_name_as_field_and_key_value_being_attribute_value
  335. gps_location = customers(:david).gps_location
  336. assert_kind_of GpsLocation, gps_location
  337. found_customer = Customer.find(:first, :conditions => {:gps_location => gps_location.gps_location})
  338. assert_equal customers(:david), found_customer
  339. end
  340. def test_hash_condition_find_with_aggregate_having_three_mappings
  341. address = customers(:david).address
  342. assert_kind_of Address, address
  343. found_customer = Customer.find(:first, :conditions => {:address => address})
  344. assert_equal customers(:david), found_customer
  345. end
  346. def test_hash_condition_find_with_one_condition_being_aggregate_and_another_not
  347. address = customers(:david).address
  348. assert_kind_of Address, address
  349. found_customer = Customer.find(:first, :conditions => {:address => address, :name => customers(:david).name})
  350. assert_equal customers(:david), found_customer
  351. end
  352. def test_condition_utc_time_interpolation_with_default_timezone_local
  353. with_env_tz 'America/New_York' do
  354. with_active_record_default_timezone :local do
  355. topic = Topic.first
  356. assert_equal topic, Topic.find(:first, :conditions => ['written_on = ?', topic.written_on.getutc])
  357. end
  358. end
  359. end
  360. def test_hash_condition_utc_time_interpolation_with_default_timezone_local
  361. with_env_tz 'America/New_York' do
  362. with_active_record_default_timezone :local do
  363. topic = Topic.first
  364. assert_equal topic, Topic.find(:first, :conditions => {:written_on => topic.written_on.getutc})
  365. end
  366. end
  367. end
  368. def test_condition_local_time_interpolation_with_default_timezone_utc
  369. with_env_tz 'America/New_York' do
  370. with_active_record_default_timezone :utc do
  371. topic = Topic.first
  372. assert_equal topic, Topic.find(:first, :conditions => ['written_on = ?', topic.written_on.getlocal])
  373. end
  374. end
  375. end
  376. def test_hash_condition_local_time_interpolation_with_default_timezone_utc
  377. with_env_tz 'America/New_York' do
  378. with_active_record_default_timezone :utc do
  379. topic = Topic.first
  380. assert_equal topic, Topic.find(:first, :conditions => {:written_on => topic.written_on.getlocal})
  381. end
  382. end
  383. end
  384. def test_bind_variables
  385. assert_kind_of Firm, Company.find(:first, :conditions => ["name = ?", "37signals"])
  386. assert_nil Company.find(:first, :conditions => ["name = ?", "37signals!"])
  387. assert_nil Company.find(:first, :conditions => ["name = ?", "37signals!' OR 1=1"])
  388. assert_kind_of Time, Topic.find(:first, :conditions => ["id = ?", 1]).written_on
  389. assert_raise(ActiveRecord::PreparedStatementInvalid) {
  390. Company.find(:first, :conditions => ["id=? AND name = ?", 2])
  391. }
  392. assert_raise(ActiveRecord::PreparedStatementInvalid) {
  393. Company.find(:first, :conditions => ["id=?", 2, 3, 4])
  394. }
  395. end
  396. def test_bind_variables_with_quotes
  397. Company.create("name" => "37signals' go'es agains")
  398. assert Company.find(:first, :conditions => ["name = ?", "37signals' go'es agains"])
  399. end
  400. def test_named_bind_variables_with_quotes
  401. Company.create("name" => "37signals' go'es agains")
  402. assert Company.find(:first, :conditions => ["name = :name", {:name => "37signals' go'es agains"}])
  403. end
  404. def test_bind_arity
  405. assert_nothing_raised { bind '' }
  406. assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
  407. assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?' }
  408. assert_nothing_raised { bind '?', 1 }
  409. assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 }
  410. end
  411. def test_named_bind_variables
  412. assert_equal '1', bind(':a', :a => 1) # ' ruby-mode
  413. assert_equal '1 1', bind(':a :a', :a => 1) # ' ruby-mode
  414. assert_nothing_raised { bind("'+00:00'", :foo => "bar") }
  415. assert_kind_of Firm, Company.find(:first, :conditions => ["name = :name", { :name => "37signals" }])
  416. assert_nil Company.find(:first, :conditions => ["name = :name", { :name => "37signals!" }])
  417. assert_nil Company.find(:first, :conditions => ["name = :name", { :name => "37signals!' OR 1=1" }])
  418. assert_kind_of Time, Topic.find(:first, :conditions => ["id = :id", { :id => 1 }]).written_on
  419. end
  420. def test_bind_enumerable
  421. quoted_abc = %(#{ActiveRecord::Base.connection.quote('a')},#{ActiveRecord::Base.connection.quote('b')},#{ActiveRecord::Base.connection.quote('c')})
  422. assert_equal '1,2,3', bind('?', [1, 2, 3])
  423. assert_equal quoted_abc, bind('?', %w(a b c))
  424. assert_equal '1,2,3', bind(':a', :a => [1, 2, 3])
  425. assert_equal quoted_abc, bind(':a', :a => %w(a b c)) # '
  426. require 'set'
  427. assert_equal '1,2,3', bind('?', Set.new([1, 2, 3]))
  428. assert_equal quoted_abc, bind('?', Set.new(%w(a b c)))
  429. assert_equal '1,2,3', bind(':a', :a => Set.new([1, 2, 3]))
  430. assert_equal quoted_abc, bind(':a', :a => Set.new(%w(a b c))) # '
  431. end
  432. def test_bind_empty_enumerable
  433. quoted_nil = ActiveRecord::Base.connection.quote(nil)
  434. assert_equal quoted_nil, bind('?', [])
  435. assert_equal " in (#{quoted_nil})", bind(' in (?)', [])
  436. assert_equal "foo in (#{quoted_nil})", bind('foo in (?)', [])
  437. end
  438. def test_bind_empty_string
  439. quoted_empty = ActiveRecord::Base.connection.quote('')
  440. assert_equal quoted_empty, bind('?', '')
  441. end
  442. def test_bind_chars
  443. quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
  444. quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
  445. assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi")
  446. assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper")
  447. assert_equal "name=#{quoted_bambi}", bind('name=?', "Bambi".mb_chars)
  448. assert_equal "name=#{quoted_bambi_and_thumper}", bind('name=?', "Bambi\nand\nThumper".mb_chars)
  449. end
  450. def test_bind_record
  451. o = Struct.new(:quoted_id).new(1)
  452. assert_equal '1', bind('?', o)
  453. os = [o] * 3
  454. assert_equal '1,1,1', bind('?', os)
  455. end
  456. def test_named_bind_with_postgresql_type_casts
  457. l = Proc.new { bind(":a::integer '2009-01-01'::date", :a => '10') }
  458. assert_nothing_raised(&l)
  459. assert_equal "#{ActiveRecord::Base.quote_value('10')}::integer '2009-01-01'::date", l.call
  460. end
  461. def test_string_sanitation
  462. assert_not_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1")
  463. assert_equal "#{ActiveRecord::Base.connection.quoted_string_prefix}'something; select table'", ActiveRecord::Base.sanitize("something; select table")
  464. end
  465. def test_count
  466. assert_equal(0, Entrant.count(:conditions => "id > 3"))
  467. assert_equal(1, Entrant.count(:conditions => ["id > ?", 2]))
  468. assert_equal(2, Entrant.count(:conditions => ["id > ?", 1]))
  469. end
  470. def test_count_by_sql
  471. assert_equal(0, Entrant.count_by_sql("SELECT COUNT(*) FROM entrants WHERE id > 3"))
  472. assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
  473. assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
  474. end
  475. def test_find_by_one_attribute
  476. assert_equal topics(:first), Topic.find_by_title("The First Topic")
  477. assert_nil Topic.find_by_title("The First Topic!")
  478. end
  479. def test_find_by_one_attribute_bang
  480. assert_equal topics(:first), Topic.find_by_title!("The First Topic")
  481. assert_raise(ActiveRecord::RecordNotFound) { Topic.find_by_title!("The First Topic!") }
  482. end
  483. def test_find_by_one_attribute_with_order_option
  484. assert_equal accounts(:signals37), Account.find_by_credit_limit(50, :order => 'id')
  485. assert_equal accounts(:rails_core_account), Account.find_by_credit_limit(50, :order => 'id DESC')
  486. end
  487. def test_find_by_one_attribute_with_conditions
  488. assert_equal accounts(:rails_core_account), Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
  489. end
  490. def test_find_by_one_attribute_that_is_an_aggregate
  491. address = customers(:david).address
  492. assert_kind_of Address, address
  493. found_customer = Customer.find_by_address(address)
  494. assert_equal customers(:david), found_customer
  495. end
  496. def test_find_by_one_attribute_that_is_an_aggregate_with_one_attribute_difference
  497. address = customers(:david).address
  498. assert_kind_of Address, address
  499. missing_address = Address.new(address.street, address.city, address.country + "1")
  500. assert_nil Customer.find_by_address(missing_address)
  501. missing_address = Address.new(address.street, address.city + "1", address.country)
  502. assert_nil Customer.find_by_address(missing_address)
  503. missing_address = Address.new(address.street + "1", address.city, address.country)
  504. assert_nil Customer.find_by_address(missing_address)
  505. end
  506. def test_find_by_two_attributes_that_are_both_aggregates
  507. balance = customers(:david).balance
  508. address = customers(:david).address
  509. assert_kind_of Money, balance
  510. assert_kind_of Address, address
  511. found_customer = Customer.find_by_balance_and_address(balance, address)
  512. assert_equal customers(:david), found_customer
  513. end
  514. def test_find_by_two_attributes_with_one_being_an_aggregate
  515. balance = customers(:david).balance
  516. assert_kind_of Money, balance
  517. found_customer = Customer.find_by_balance_and_name(balance, customers(:david).name)
  518. assert_equal customers(:david), found_customer
  519. end
  520. def test_dynamic_finder_on_one_attribute_with_conditions_returns_same_results_after_caching
  521. # ensure this test can run independently of order
  522. class << Account; self; end.send(:remove_method, :find_by_credit_limit) if Account.public_methods.any? { |m| m.to_s == 'find_by_credit_limit' }
  523. a = Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6])
  524. assert_equal a, Account.find_by_credit_limit(50, :conditions => ['firm_id = ?', 6]) # find_by_credit_limit has been cached
  525. end
  526. def test_find_by_one_attribute_with_several_options
  527. assert_equal accounts(:unknown), Account.find_by_credit_limit(50, :order => 'id DESC', :conditions => ['id != ?', 3])
  528. end
  529. def test_find_by_one_missing_attribute
  530. assert_raise(NoMethodError) { Topic.find_by_undertitle("The First Topic!") }
  531. end
  532. def test_find_by_invalid_method_syntax
  533. assert_raise(NoMethodError) { Topic.fail_to_find_by_title("The First Topic") }
  534. assert_raise(NoMethodError) { Topic.find_by_title?("The First Topic") }
  535. assert_raise(NoMethodError) { Topic.fail_to_find_or_create_by_title("Nonexistent Title") }
  536. assert_raise(NoMethodError) { Topic.find_or_create_by_title?("Nonexistent Title") }
  537. end
  538. def test_find_by_two_attributes
  539. assert_equal topics(:first), Topic.find_by_title_and_author_name("The First Topic", "David")
  540. assert_nil Topic.find_by_title_and_author_name("The First Topic", "Mary")
  541. end
  542. def test_find_last_by_one_attribute
  543. assert_equal Topic.last, Topic.find_last_by_title(Topic.last.title)
  544. assert_nil Topic.find_last_by_title("A title with no matches")
  545. end
  546. def test_find_last_by_invalid_method_syntax
  547. assert_raise(NoMethodError) { Topic.fail_to_find_last_by_title("The First Topic") }
  548. assert_raise(NoMethodError) { Topic.find_last_by_title?("The First Topic") }
  549. end
  550. def test_find_last_by_one_attribute_with_several_options
  551. assert_equal accounts(:signals37), Account.find_last_by_credit_limit(50, :order => 'id DESC', :conditions => ['id != ?', 3])
  552. end
  553. def test_find_last_by_one_missing_attribute
  554. assert_raise(NoMethodError) { Topic.find_last_by_undertitle("The Last Topic!") }
  555. end
  556. def test_find_last_by_two_attributes
  557. topic = Topic.last
  558. assert_equal topic, Topic.find_last_by_title_and_author_name(topic.title, topic.author_name)
  559. assert_nil Topic.find_last_by_title_and_author_name(topic.title, "Anonymous")
  560. end
  561. def test_find_all_by_one_attribute
  562. topics = Topic.find_all_by_content("Have a nice day")
  563. assert_equal 2, topics.size
  564. assert topics.include?(topics(:first))
  565. assert_equal [], Topic.find_all_by_title("The First Topic!!")
  566. end
  567. def test_find_all_by_one_attribute_that_is_an_aggregate
  568. balance = customers(:david).balance
  569. assert_kind_of Money, balance
  570. found_customers = Customer.find_all_by_balance(balance)
  571. assert_equal 1, found_customers.size
  572. assert_equal customers(:david), found_customers.first
  573. end
  574. def test_find_all_by_two_attributes_that_are_both_aggregates
  575. balance = customers(:david).balance
  576. address = customers(:david).address
  577. assert_kind_of Money, balance
  578. assert_kind_of Address, address
  579. found_customers = Customer.find_all_by_balance_and_address(balance, address)
  580. assert_equal 1, found_customers.size
  581. assert_equal customers(:david), found_customers.first
  582. end
  583. def test_find_all_by_two_attributes_with_one_being_an_aggregate
  584. balance = customers(:david).balance
  585. assert_kind_of Money, balance
  586. found_customers = Customer.find_all_by_balance_and_name(balance, customers(:david).name)
  587. assert_equal 1, found_customers.size
  588. assert_equal customers(:david), found_customers.first
  589. end
  590. def test_find_all_by_one_attribute_with_options
  591. topics = Topic.find_all_by_content("Have a nice day", :order => "id DESC")
  592. assert topics(:first), topics.last
  593. topics = Topic.find_all_by_content("Have a nice day", :order => "id")
  594. assert topics(:first), topics.first
  595. end
  596. def test_find_all_by_array_attribute
  597. assert_equal 2, Topic.find_all_by_title(["The First Topic", "The Second Topic of the day"]).size
  598. end
  599. def test_find_all_by_boolean_attribute
  600. topics = Topic.find_all_by_approved(false)
  601. assert_equal 1, topics.size
  602. assert topics.include?(topics(:first))
  603. topics = Topic.find_all_by_approved(true)
  604. assert_equal 3, topics.size
  605. assert topics.include?(topics(:second))
  606. end
  607. def test_find_by_nil_attribute
  608. topic = Topic.find_by_last_read nil
  609. assert_not_nil topic
  610. assert_nil topic.last_read
  611. end
  612. def test_find_all_by_nil_attribute
  613. topics = Topic.find_all_by_last_read nil
  614. assert_equal 3, topics.size
  615. assert topics.collect(&:last_read).all?(&:nil?)
  616. end
  617. def test_find_by_nil_and_not_nil_attributes
  618. topic = Topic.find_by_last_read_and_author_name nil, "Mary"
  619. assert_equal "Mary", topic.author_name
  620. end
  621. def test_find_all_by_nil_and_not_nil_attributes
  622. topics = Topic.find_all_by_last_read_and_author_name nil, "Mary"
  623. assert_equal 1, topics.size
  624. assert_equal "Mary", topics[0].author_name
  625. end
  626. def test_find_or_create_from_one_attribute
  627. number_of_companies = Company.count
  628. sig38 = Company.find_or_create_by_name("38signals")
  629. assert_equal number_of_companies + 1, Company.count
  630. assert_equal sig38, Company.find_or_create_by_name("38signals")
  631. assert !sig38.new_record?
  632. end
  633. def test_find_or_create_from_two_attributes
  634. number_of_topics = Topic.count
  635. another = Topic.find_or_create_by_title_and_author_name("Another topic","John")
  636. assert_equal number_of_topics + 1, Topic.count
  637. assert_equal another, Topic.find_or_create_by_title_and_author_name("Another topic", "John")
  638. assert !another.new_record?
  639. end
  640. def test_find_or_create_from_two_attributes_with_one_being_an_aggregate
  641. number_of_customers = Customer.count
  642. created_customer = Customer.find_or_create_by_balance_and_name(Money.new(123), "Elizabeth")
  643. assert_equal number_of_customers + 1, Customer.count
  644. assert_equal created_customer, Customer.find_or_create_by_balance(Money.new(123), "Elizabeth")
  645. assert !created_customer.new_record?
  646. end
  647. def test_find_or_create_from_one_attribute_and_hash
  648. number_of_companies = Company.count
  649. sig38 = Company.find_or_create_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
  650. assert_equal number_of_companies + 1, Company.count
  651. assert_equal sig38, Company.find_or_create_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
  652. assert !sig38.new_record?
  653. assert_equal "38signals", sig38.name
  654. assert_equal 17, sig38.firm_id
  655. assert_equal 23, sig38.client_of
  656. end
  657. def test_find_or_create_from_one_aggregate_attribute
  658. number_of_customers = Customer.count
  659. created_customer = Customer.find_or_create_by_balance(Money.new(123))
  660. assert_equal number_of_customers + 1, Customer.count
  661. assert_equal created_customer, Customer.find_or_create_by_balance(Money.new(123))
  662. assert !created_customer.new_record?
  663. end
  664. def test_find_or_create_from_one_aggregate_attribute_and_hash
  665. number_of_customers = Customer.count
  666. balance = Money.new(123)
  667. name = "Elizabeth"
  668. created_customer = Customer.find_or_create_by_balance({:balance => balance, :name => name})
  669. assert_equal number_of_customers + 1, Customer.count
  670. assert_equal created_customer, Customer.find_or_create_by_balance({:balance => balance, :name => name})
  671. assert !created_customer.new_record?
  672. assert_equal balance, created_customer.balance
  673. assert_equal name, created_customer.name
  674. end
  675. def test_find_or_initialize_from_one_attribute
  676. sig38 = Company.find_or_initialize_by_name("38signals")
  677. assert_equal "38signals", sig38.name
  678. assert sig38.new_record?
  679. end
  680. def test_find_or_initialize_from_one_aggregate_attribute
  681. new_customer = Customer.find_or_initialize_by_balance(Money.new(123))
  682. assert_equal 123, new_customer.balance.amount
  683. assert new_customer.new_record?
  684. end
  685. def test_find_or_initialize_from_one_attribute_should_not_set_attribute_even_when_protected
  686. c = Company.find_or_initialize_by_name({:name => "Fortune 1000", :rating => 1000})
  687. assert_equal "Fortune 1000", c.name
  688. assert_not_equal 1000, c.rating
  689. assert c.valid?
  690. assert c.new_record?
  691. end
  692. def test_find_or_create_from_one_attribute_should_set_not_attribute_even_when_protected
  693. c = Company.find_or_create_by_name({:name => "Fortune 1000", :rating => 1000})
  694. assert_equal "Fortune 1000", c.name
  695. assert_not_equal 1000, c.rating
  696. assert c.valid?
  697. assert !c.new_record?
  698. end
  699. def test_find_or_initialize_from_one_attribute_should_set_attribute_even_when_protected
  700. c = Company.find_or_initialize_by_name_and_rating("Fortune 1000", 1000)
  701. assert_equal "Fortune 1000", c.name
  702. assert_equal 1000, c.rating
  703. assert c.valid?
  704. assert c.new_record?
  705. end
  706. def test_find_or_create_from_one_attribute_should_set_attribute_even_when_protected
  707. c = Company.find_or_create_by_name_and_rating("Fortune 1000", 1000)
  708. assert_equal "Fortune 1000", c.name
  709. assert_equal 1000, c.rating
  710. assert c.valid?
  711. assert !c.new_record?
  712. end
  713. def test_find_or_initialize_should_set_protected_attributes_if_given_as_block
  714. c = Company.find_or_initialize_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
  715. assert_equal "Fortune 1000", c.name
  716. assert_equal 1000.to_f, c.rating.to_f
  717. assert c.valid?
  718. assert c.new_record?
  719. end
  720. def test_find_or_create_should_set_protected_attributes_if_given_as_block
  721. c = Company.find_or_create_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
  722. assert_equal "Fortune 1000", c.name
  723. assert_equal 1000.to_f, c.rating.to_f
  724. assert c.valid?
  725. assert !c.new_record?
  726. end
  727. def test_find_or_create_should_work_with_block_on_first_call
  728. class << Company
  729. undef_method(:find_or_create_by_name) if method_defined?(:find_or_create_by_name)
  730. end
  731. c = Company.find_or_create_by_name(:name => "Fortune 1000") { |f| f.rating = 1000 }
  732. assert_equal "Fortune 1000", c.name
  733. assert_equal 1000.to_f, c.rating.to_f
  734. assert c.valid?
  735. assert !c.new_record?
  736. end
  737. def test_find_or_initialize_from_two_attributes
  738. another = Topic.find_or_initialize_by_title_and_author_name("Another topic","John")
  739. assert_equal "Another topic", another.title
  740. assert_equal "John", another.author_name
  741. assert another.new_record?
  742. end
  743. def test_find_or_initialize_from_one_aggregate_attribute_and_one_not
  744. new_customer = Customer.find_or_initialize_by_balance_and_name(Money.new(123), "Elizabeth")
  745. assert_equal 123, new_customer.balance.amount
  746. assert_equal "Elizabeth", new_customer.name
  747. assert new_customer.new_record?
  748. end
  749. def test_find_or_initialize_from_one_attribute_and_hash
  750. sig38 = Company.find_or_initialize_by_name({:name => "38signals", :firm_id => 17, :client_of => 23})
  751. assert_equal "38signals", sig38.name
  752. assert_equal 17, sig38.firm_id
  753. assert_equal 23, sig38.client_of
  754. assert sig38.new_record?
  755. end
  756. def test_find_or_initialize_from_one_aggregate_attribute_and_hash
  757. balance = Money.new(123)
  758. name = "Elizabeth"
  759. new_customer = Customer.find_or_initialize_by_balance({:balance => balance, :name => name})
  760. assert_equal balance, new_customer.balance
  761. assert_equal name, new_customer.name
  762. assert new_customer.new_record?
  763. end
  764. def test_find_with_bad_sql
  765. assert_raise(ActiveRecord::StatementInvalid) { Topic.find_by_sql "select 1 from badtable" }
  766. end
  767. def test_find_with_invalid_params
  768. assert_raise(ArgumentError) { Topic.find :first, :join => "It should be `joins'" }
  769. assert_raise(ArgumentError) { Topic.find :first, :conditions => '1 = 1', :join => "It should be `joins'" }
  770. end
  771. def test_dynamic_finder_with_invalid_params
  772. assert_raise(ArgumentError) { Topic.find_by_title 'No Title', :join => "It should be `joins'" }
  773. end
  774. def test_find_all_with_join
  775. developers_on_project_one = Developer.find(
  776. :all,
  777. :joins => 'LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id',
  778. :conditions => 'project_id=1'
  779. )
  780. assert_equal 3, developers_on_project_one.length
  781. developer_names = developers_on_project_one.map { |d| d.name }
  782. assert developer_names.include?('David')
  783. assert developer_names.include?('Jamis')
  784. end
  785. def test_joins_dont_clobber_id
  786. first = Firm.find(
  787. :first,
  788. :joins => 'INNER JOIN companies clients ON clients.firm_id = companies.id',
  789. :conditions => 'companies.id = 1'
  790. )
  791. assert_equal 1, first.id
  792. end
  793. def test_joins_with_string_array
  794. person_with_reader_and_post = Post.find(
  795. :all,
  796. :joins => [
  797. "INNER JOIN categorizations ON categorizations.post_id = posts.id",
  798. "INNER JOIN categories ON categories.id = categorizations.category_id AND categories.type = 'SpecialCategory'"
  799. ]
  800. )
  801. assert_equal 1, person_with_reader_and_post.size
  802. end
  803. def test_find_by_id_with_conditions_with_or
  804. assert_nothing_raised do
  805. Post.find([1,2,3],
  806. :conditions => "posts.id <= 3 OR posts.#{QUOTED_TYPE} = 'Post'")
  807. end
  808. end
  809. # http://dev.rubyonrails.org/ticket/6778
  810. def test_find_ignores_previously_inserted_record
  811. post = Post.create!(:title => 'test', :body => 'it out')
  812. assert_equal [], Post.find_all_by_id(nil)
  813. end
  814. def test_find_by_empty_ids
  815. assert_equal [], Post.find([])
  816. end
  817. def test_find_by_empty_in_condition
  818. assert_equal [], Post.find(:all, :conditions => ['id in (?)', []])
  819. end
  820. def test_find_by_records
  821. p1, p2 = Post.find(:all, :limit => 2, :order => 'id asc')
  822. assert_equal [p1, p2], Post.find(:all, :conditions => ['id in (?)', [p1, p2]], :order => 'id asc')
  823. assert_equal [p1, p2], Post.find(:all, :conditions => ['id in (?)', [p1, p2.id]], :order => 'id asc')
  824. end
  825. def test_select_value
  826. assert_equal "37signals", Company.connection.select_value("SELECT name FROM companies WHERE id = 1")
  827. assert_nil Company.connection.select_value("SELECT name FROM companies WHERE id = -1")
  828. # make sure we didn't break count...
  829. assert_equal 0, Company.count_by_sql("SELECT COUNT(*) FROM companies WHERE name = 'Halliburton'")
  830. assert_equal 1, Company.count_by_sql("SELECT COUNT(*) FROM companies WHERE name = '37signals'")
  831. end
  832. def test_select_values
  833. assert_equal ["1","2","3","4","5","6","7","8","9", "10"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map! { |i| i.to_s }
  834. assert_equal ["37signals","Summit","Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel", "Odegy", "Ex Nihilo Part Deux"], Company.connection.select_values("SELECT name FROM companies ORDER BY id")
  835. end
  836. def test_select_rows
  837. assert_equal(
  838. [["1", nil, nil, "37signals"],
  839. ["2", "1", "2", "Summit"],
  840. ["3", "1", "1", "Microsoft"]],
  841. 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?}})
  842. assert_equal [["1", "37signals"], ["2", "Summit"], ["3", "Microsoft"]],
  843. 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?}}
  844. end
  845. def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
  846. assert_equal 2, Post.find(:all, :include => { :authors => :author_address }, :order => ' author_addresses.id DESC ', :limit => 2).size
  847. assert_equal 3, Post.find(:all, :include => { :author => :author_address, :authors => :author_address},
  848. :order => ' author_addresses_authors.id DESC ', :limit => 3).size
  849. end
  850. def test_with_limiting_with_custom_select
  851. posts = Post.find(:all, :include => :author, :select => ' posts.*, authors.id as "author_id"', :limit => 3, :order => 'posts.id')
  852. assert_equal 3, posts.size
  853. assert_equal [0, 1, 1], posts.map(&:author_id).sort
  854. end
  855. def test_finder_with_scoped_from
  856. all_topics = Topic.find(:all)
  857. Topic.send(:with_scope, :find => { :from => 'fake_topics' }) do
  858. assert_equal all_topics, Topic.from('topics').to_a
  859. end
  860. end
  861. protected
  862. def bind(statement, *vars)
  863. if vars.first.is_a?(Hash)
  864. ActiveRecord::Base.send(:replace_named_bind_variables, statement, vars.first)
  865. else
  866. ActiveRecord::Base.send(:replace_bind_variables, statement, vars)
  867. end
  868. end
  869. def with_env_tz(new_tz = 'US/Eastern')
  870. old_tz, ENV['TZ'] = ENV['TZ'], new_tz
  871. yield
  872. ensure
  873. old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
  874. end
  875. def with_active_record_default_timezone(zone)
  876. old_zone, ActiveRecord::Base.default_timezone = ActiveRecord::Base.default_timezone, zone
  877. yield
  878. ensure
  879. ActiveRecord::Base.default_timezone = old_zone
  880. end
  881. end