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

/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb

https://github.com/ghar/rails
Ruby | 824 lines | 661 code | 147 blank | 16 comment | 1 complexity | 12f6d17bbeb400907c6a1283fe3ca504 MD5 | raw file
  1. require "cases/helper"
  2. require 'models/developer'
  3. require 'models/project'
  4. require 'models/company'
  5. require 'models/customer'
  6. require 'models/order'
  7. require 'models/categorization'
  8. require 'models/category'
  9. require 'models/post'
  10. require 'models/author'
  11. require 'models/tag'
  12. require 'models/tagging'
  13. require 'models/parrot'
  14. require 'models/pirate'
  15. require 'models/treasure'
  16. require 'models/price_estimate'
  17. require 'models/club'
  18. require 'models/member'
  19. require 'models/membership'
  20. require 'models/sponsor'
  21. require 'models/country'
  22. require 'models/treaty'
  23. require 'active_support/core_ext/string/conversions'
  24. class ProjectWithAfterCreateHook < ActiveRecord::Base
  25. set_table_name 'projects'
  26. has_and_belongs_to_many :developers,
  27. :class_name => "DeveloperForProjectWithAfterCreateHook",
  28. :join_table => "developers_projects",
  29. :foreign_key => "project_id",
  30. :association_foreign_key => "developer_id"
  31. after_create :add_david
  32. def add_david
  33. david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
  34. david.projects << self
  35. end
  36. end
  37. class DeveloperForProjectWithAfterCreateHook < ActiveRecord::Base
  38. set_table_name 'developers'
  39. has_and_belongs_to_many :projects,
  40. :class_name => "ProjectWithAfterCreateHook",
  41. :join_table => "developers_projects",
  42. :association_foreign_key => "project_id",
  43. :foreign_key => "developer_id"
  44. end
  45. class ProjectWithSymbolsForKeys < ActiveRecord::Base
  46. set_table_name 'projects'
  47. has_and_belongs_to_many :developers,
  48. :class_name => "DeveloperWithSymbolsForKeys",
  49. :join_table => :developers_projects,
  50. :foreign_key => :project_id,
  51. :association_foreign_key => "developer_id"
  52. end
  53. class DeveloperWithSymbolsForKeys < ActiveRecord::Base
  54. set_table_name 'developers'
  55. has_and_belongs_to_many :projects,
  56. :class_name => "ProjectWithSymbolsForKeys",
  57. :join_table => :developers_projects,
  58. :association_foreign_key => :project_id,
  59. :foreign_key => "developer_id"
  60. end
  61. class DeveloperWithCounterSQL < ActiveRecord::Base
  62. set_table_name 'developers'
  63. has_and_belongs_to_many :projects,
  64. :class_name => "DeveloperWithCounterSQL",
  65. :join_table => "developers_projects",
  66. :association_foreign_key => "project_id",
  67. :foreign_key => "developer_id",
  68. :counter_sql => proc { "SELECT COUNT(*) AS count_all FROM projects INNER JOIN developers_projects ON projects.id = developers_projects.project_id WHERE developers_projects.developer_id =#{id}" }
  69. end
  70. class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
  71. fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
  72. :parrots, :pirates, :treasures, :price_estimates, :tags, :taggings
  73. def setup_data_for_habtm_case
  74. ActiveRecord::Base.connection.execute('delete from countries_treaties')
  75. country = Country.new(:name => 'India')
  76. country.country_id = 'c1'
  77. country.save!
  78. treaty = Treaty.new(:name => 'peace')
  79. treaty.treaty_id = 't1'
  80. country.treaties << treaty
  81. end
  82. def test_should_property_quote_string_primary_keys
  83. setup_data_for_habtm_case
  84. con = ActiveRecord::Base.connection
  85. sql = 'select * from countries_treaties'
  86. record = con.select_rows(sql).last
  87. assert_equal 'c1', record[0]
  88. assert_equal 't1', record[1]
  89. end
  90. def test_proper_usage_of_primary_keys_and_join_table
  91. setup_data_for_habtm_case
  92. assert_equal 'country_id', Country.primary_key
  93. assert_equal 'treaty_id', Treaty.primary_key
  94. country = Country.first
  95. assert_equal 1, country.treaties.count
  96. end
  97. def test_has_and_belongs_to_many
  98. david = Developer.find(1)
  99. assert !david.projects.empty?
  100. assert_equal 2, david.projects.size
  101. active_record = Project.find(1)
  102. assert !active_record.developers.empty?
  103. assert_equal 3, active_record.developers.size
  104. assert active_record.developers.include?(david)
  105. end
  106. def test_triple_equality
  107. assert !(Array === Developer.find(1).projects)
  108. assert Developer.find(1).projects === Array
  109. end
  110. def test_adding_single
  111. jamis = Developer.find(2)
  112. jamis.projects.reload # causing the collection to load
  113. action_controller = Project.find(2)
  114. assert_equal 1, jamis.projects.size
  115. assert_equal 1, action_controller.developers.size
  116. jamis.projects << action_controller
  117. assert_equal 2, jamis.projects.size
  118. assert_equal 2, jamis.projects(true).size
  119. assert_equal 2, action_controller.developers(true).size
  120. end
  121. def test_adding_type_mismatch
  122. jamis = Developer.find(2)
  123. assert_raise(ActiveRecord::AssociationTypeMismatch) { jamis.projects << nil }
  124. assert_raise(ActiveRecord::AssociationTypeMismatch) { jamis.projects << 1 }
  125. end
  126. def test_adding_from_the_project
  127. jamis = Developer.find(2)
  128. action_controller = Project.find(2)
  129. action_controller.developers.reload
  130. assert_equal 1, jamis.projects.size
  131. assert_equal 1, action_controller.developers.size
  132. action_controller.developers << jamis
  133. assert_equal 2, jamis.projects(true).size
  134. assert_equal 2, action_controller.developers.size
  135. assert_equal 2, action_controller.developers(true).size
  136. end
  137. def test_adding_from_the_project_fixed_timestamp
  138. jamis = Developer.find(2)
  139. action_controller = Project.find(2)
  140. action_controller.developers.reload
  141. assert_equal 1, jamis.projects.size
  142. assert_equal 1, action_controller.developers.size
  143. updated_at = jamis.updated_at
  144. action_controller.developers << jamis
  145. assert_equal updated_at, jamis.updated_at
  146. assert_equal 2, jamis.projects(true).size
  147. assert_equal 2, action_controller.developers.size
  148. assert_equal 2, action_controller.developers(true).size
  149. end
  150. def test_adding_multiple
  151. aredridel = Developer.new("name" => "Aredridel")
  152. aredridel.save
  153. aredridel.projects.reload
  154. aredridel.projects.push(Project.find(1), Project.find(2))
  155. assert_equal 2, aredridel.projects.size
  156. assert_equal 2, aredridel.projects(true).size
  157. end
  158. def test_adding_a_collection
  159. aredridel = Developer.new("name" => "Aredridel")
  160. aredridel.save
  161. aredridel.projects.reload
  162. aredridel.projects.concat([Project.find(1), Project.find(2)])
  163. assert_equal 2, aredridel.projects.size
  164. assert_equal 2, aredridel.projects(true).size
  165. end
  166. def test_habtm_adding_before_save
  167. no_of_devels = Developer.count
  168. no_of_projects = Project.count
  169. aredridel = Developer.new("name" => "Aredridel")
  170. aredridel.projects.concat([Project.find(1), p = Project.new("name" => "Projekt")])
  171. assert !aredridel.persisted?
  172. assert !p.persisted?
  173. assert aredridel.save
  174. assert aredridel.persisted?
  175. assert_equal no_of_devels+1, Developer.count
  176. assert_equal no_of_projects+1, Project.count
  177. assert_equal 2, aredridel.projects.size
  178. assert_equal 2, aredridel.projects(true).size
  179. end
  180. def test_habtm_saving_multiple_relationships
  181. new_project = Project.new("name" => "Grimetime")
  182. amount_of_developers = 4
  183. developers = (0...amount_of_developers).collect {|i| Developer.create(:name => "JME #{i}") }.reverse
  184. new_project.developer_ids = [developers[0].id, developers[1].id]
  185. new_project.developers_with_callback_ids = [developers[2].id, developers[3].id]
  186. assert new_project.save
  187. new_project.reload
  188. assert_equal amount_of_developers, new_project.developers.size
  189. assert_equal developers, new_project.developers
  190. end
  191. def test_habtm_unique_order_preserved
  192. assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).non_unique_developers
  193. assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).developers
  194. end
  195. def test_build
  196. devel = Developer.find(1)
  197. proj = assert_no_queries { devel.projects.build("name" => "Projekt") }
  198. assert !devel.projects.loaded?
  199. assert_equal devel.projects.last, proj
  200. assert devel.projects.loaded?
  201. assert !proj.persisted?
  202. devel.save
  203. assert proj.persisted?
  204. assert_equal devel.projects.last, proj
  205. assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
  206. end
  207. def test_new_aliased_to_build
  208. devel = Developer.find(1)
  209. proj = assert_no_queries { devel.projects.new("name" => "Projekt") }
  210. assert !devel.projects.loaded?
  211. assert_equal devel.projects.last, proj
  212. assert devel.projects.loaded?
  213. assert !proj.persisted?
  214. devel.save
  215. assert proj.persisted?
  216. assert_equal devel.projects.last, proj
  217. assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
  218. end
  219. def test_build_by_new_record
  220. devel = Developer.new(:name => "Marcel", :salary => 75000)
  221. devel.projects.build(:name => "Make bed")
  222. proj2 = devel.projects.build(:name => "Lie in it")
  223. assert_equal devel.projects.last, proj2
  224. assert !proj2.persisted?
  225. devel.save
  226. assert devel.persisted?
  227. assert proj2.persisted?
  228. assert_equal devel.projects.last, proj2
  229. assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
  230. end
  231. def test_create
  232. devel = Developer.find(1)
  233. proj = devel.projects.create("name" => "Projekt")
  234. assert !devel.projects.loaded?
  235. assert_equal devel.projects.last, proj
  236. assert !devel.projects.loaded?
  237. assert proj.persisted?
  238. assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
  239. end
  240. def test_create_by_new_record
  241. devel = Developer.new(:name => "Marcel", :salary => 75000)
  242. devel.projects.build(:name => "Make bed")
  243. proj2 = devel.projects.build(:name => "Lie in it")
  244. assert_equal devel.projects.last, proj2
  245. assert !proj2.persisted?
  246. devel.save
  247. assert devel.persisted?
  248. assert proj2.persisted?
  249. assert_equal devel.projects.last, proj2
  250. assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
  251. end
  252. def test_creation_respects_hash_condition
  253. # in Oracle '' is saved as null therefore need to save ' ' in not null column
  254. post = categories(:general).post_with_conditions.build(:body => ' ')
  255. assert post.save
  256. assert_equal 'Yet Another Testing Title', post.title
  257. # in Oracle '' is saved as null therefore need to save ' ' in not null column
  258. another_post = categories(:general).post_with_conditions.create(:body => ' ')
  259. assert another_post.persisted?
  260. assert_equal 'Yet Another Testing Title', another_post.title
  261. end
  262. def test_uniq_after_the_fact
  263. dev = developers(:jamis)
  264. dev.projects << projects(:active_record)
  265. dev.projects << projects(:active_record)
  266. assert_equal 3, dev.projects.size
  267. assert_equal 1, dev.projects.uniq.size
  268. end
  269. def test_uniq_before_the_fact
  270. projects(:active_record).developers << developers(:jamis)
  271. projects(:active_record).developers << developers(:david)
  272. assert_equal 3, projects(:active_record, :reload).developers.size
  273. end
  274. def test_uniq_option_prevents_duplicate_push
  275. project = projects(:active_record)
  276. project.developers << developers(:jamis)
  277. project.developers << developers(:david)
  278. assert_equal 3, project.developers.size
  279. project.developers << developers(:david)
  280. project.developers << developers(:jamis)
  281. assert_equal 3, project.developers.size
  282. end
  283. def test_deleting
  284. david = Developer.find(1)
  285. active_record = Project.find(1)
  286. david.projects.reload
  287. assert_equal 2, david.projects.size
  288. assert_equal 3, active_record.developers.size
  289. david.projects.delete(active_record)
  290. assert_equal 1, david.projects.size
  291. assert_equal 1, david.projects(true).size
  292. assert_equal 2, active_record.developers(true).size
  293. end
  294. def test_deleting_array
  295. david = Developer.find(1)
  296. david.projects.reload
  297. david.projects.delete(Project.find(:all))
  298. assert_equal 0, david.projects.size
  299. assert_equal 0, david.projects(true).size
  300. end
  301. def test_deleting_with_sql
  302. david = Developer.find(1)
  303. active_record = Project.find(1)
  304. active_record.developers.reload
  305. assert_equal 3, active_record.developers_by_sql.size
  306. active_record.developers_by_sql.delete(david)
  307. assert_equal 2, active_record.developers_by_sql(true).size
  308. end
  309. def test_deleting_array_with_sql
  310. active_record = Project.find(1)
  311. active_record.developers.reload
  312. assert_equal 3, active_record.developers_by_sql.size
  313. active_record.developers_by_sql.delete(Developer.find(:all))
  314. assert_equal 0, active_record.developers_by_sql(true).size
  315. end
  316. def test_deleting_all
  317. david = Developer.find(1)
  318. david.projects.reload
  319. david.projects.clear
  320. assert_equal 0, david.projects.size
  321. assert_equal 0, david.projects(true).size
  322. end
  323. def test_removing_associations_on_destroy
  324. david = DeveloperWithBeforeDestroyRaise.find(1)
  325. assert !david.projects.empty?
  326. david.destroy
  327. assert david.projects.empty?
  328. assert DeveloperWithBeforeDestroyRaise.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1").empty?
  329. end
  330. def test_destroying
  331. david = Developer.find(1)
  332. project = Project.find(1)
  333. david.projects.reload
  334. assert_equal 2, david.projects.size
  335. assert_equal 3, project.developers.size
  336. assert_no_difference "Project.count" do
  337. david.projects.destroy(project)
  338. end
  339. join_records = Developer.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = #{david.id} AND project_id = #{project.id}")
  340. assert join_records.empty?
  341. assert_equal 1, david.reload.projects.size
  342. assert_equal 1, david.projects(true).size
  343. end
  344. def test_destroying_many
  345. david = Developer.find(1)
  346. david.projects.reload
  347. projects = Project.all
  348. assert_no_difference "Project.count" do
  349. david.projects.destroy(*projects)
  350. end
  351. join_records = Developer.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = #{david.id}")
  352. assert join_records.empty?
  353. assert_equal 0, david.reload.projects.size
  354. assert_equal 0, david.projects(true).size
  355. end
  356. def test_destroy_all
  357. david = Developer.find(1)
  358. david.projects.reload
  359. assert !david.projects.empty?
  360. assert_no_difference "Project.count" do
  361. david.projects.destroy_all
  362. end
  363. join_records = Developer.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = #{david.id}")
  364. assert join_records.empty?
  365. assert david.projects.empty?
  366. assert david.projects(true).empty?
  367. end
  368. def test_deprecated_push_with_attributes_was_removed
  369. jamis = developers(:jamis)
  370. assert_raise(NoMethodError) do
  371. jamis.projects.push_with_attributes(projects(:action_controller), :joined_on => Date.today)
  372. end
  373. end
  374. def test_associations_with_conditions
  375. assert_equal 3, projects(:active_record).developers.size
  376. assert_equal 1, projects(:active_record).developers_named_david.size
  377. assert_equal 1, projects(:active_record).developers_named_david_with_hash_conditions.size
  378. assert_equal developers(:david), projects(:active_record).developers_named_david.find(developers(:david).id)
  379. assert_equal developers(:david), projects(:active_record).developers_named_david_with_hash_conditions.find(developers(:david).id)
  380. assert_equal developers(:david), projects(:active_record).salaried_developers.find(developers(:david).id)
  381. projects(:active_record).developers_named_david.clear
  382. assert_equal 2, projects(:active_record, :reload).developers.size
  383. end
  384. def test_find_in_association
  385. # Using sql
  386. assert_equal developers(:david), projects(:active_record).developers.find(developers(:david).id), "SQL find"
  387. # Using ruby
  388. active_record = projects(:active_record)
  389. active_record.developers.reload
  390. assert_equal developers(:david), active_record.developers.find(developers(:david).id), "Ruby find"
  391. end
  392. def test_include_uses_array_include_after_loaded
  393. project = projects(:active_record)
  394. project.developers.class # force load target
  395. developer = project.developers.first
  396. assert_no_queries do
  397. assert project.developers.loaded?
  398. assert project.developers.include?(developer)
  399. end
  400. end
  401. def test_include_checks_if_record_exists_if_target_not_loaded
  402. project = projects(:active_record)
  403. developer = project.developers.first
  404. project.reload
  405. assert ! project.developers.loaded?
  406. assert_queries(1) do
  407. assert project.developers.include?(developer)
  408. end
  409. assert ! project.developers.loaded?
  410. end
  411. def test_include_returns_false_for_non_matching_record_to_verify_scoping
  412. project = projects(:active_record)
  413. developer = Developer.create :name => "Bryan", :salary => 50_000
  414. assert ! project.developers.loaded?
  415. assert ! project.developers.include?(developer)
  416. end
  417. def test_find_in_association_with_custom_finder_sql
  418. assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id), "SQL find"
  419. active_record = projects(:active_record)
  420. active_record.developers_with_finder_sql.reload
  421. assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find"
  422. end
  423. def test_find_in_association_with_custom_finder_sql_and_multiple_interpolations
  424. # interpolate once:
  425. assert_equal [developers(:david), developers(:jamis), developers(:poor_jamis)], projects(:active_record).developers_with_finder_sql, "first interpolation"
  426. # interpolate again, for a different project id
  427. assert_equal [developers(:david)], projects(:action_controller).developers_with_finder_sql, "second interpolation"
  428. end
  429. def test_find_in_association_with_custom_finder_sql_and_string_id
  430. assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find"
  431. end
  432. def test_find_with_merged_options
  433. assert_equal 1, projects(:active_record).limited_developers.size
  434. assert_equal 1, projects(:active_record).limited_developers.find(:all).size
  435. assert_equal 3, projects(:active_record).limited_developers.find(:all, :limit => nil).size
  436. end
  437. def test_dynamic_find_should_respect_association_order
  438. # Developers are ordered 'name DESC, id DESC'
  439. high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
  440. assert_equal high_id_jamis, projects(:active_record).developers.find(:first, :conditions => "name = 'Jamis'")
  441. assert_equal high_id_jamis, projects(:active_record).developers.find_by_name('Jamis')
  442. end
  443. def test_dynamic_find_all_should_respect_association_order
  444. # Developers are ordered 'name DESC, id DESC'
  445. low_id_jamis = developers(:jamis)
  446. middle_id_jamis = developers(:poor_jamis)
  447. high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
  448. assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find(:all, :conditions => "name = 'Jamis'")
  449. assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis')
  450. end
  451. def test_find_should_append_to_association_order
  452. ordered_developers = projects(:active_record).developers.order('projects.id')
  453. assert_equal ['developers.name desc, developers.id desc', 'projects.id'], ordered_developers.order_values
  454. end
  455. def test_dynamic_find_all_should_respect_association_limit
  456. assert_equal 1, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'").length
  457. assert_equal 1, projects(:active_record).limited_developers.find_all_by_name('Jamis').length
  458. end
  459. def test_dynamic_find_all_order_should_override_association_limit
  460. assert_equal 2, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'", :limit => 9_000).length
  461. assert_equal 2, projects(:active_record).limited_developers.find_all_by_name('Jamis', :limit => 9_000).length
  462. end
  463. def test_dynamic_find_all_should_respect_readonly_access
  464. projects(:active_record).readonly_developers.each { |d| assert_raise(ActiveRecord::ReadOnlyRecord) { d.save! } if d.valid?}
  465. projects(:active_record).readonly_developers.each { |d| d.readonly? }
  466. end
  467. def test_new_with_values_in_collection
  468. jamis = DeveloperForProjectWithAfterCreateHook.find_by_name('Jamis')
  469. david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
  470. project = ProjectWithAfterCreateHook.new(:name => "Cooking with Bertie")
  471. project.developers << jamis
  472. project.save!
  473. project.reload
  474. assert project.developers.include?(jamis)
  475. assert project.developers.include?(david)
  476. end
  477. def test_find_in_association_with_options
  478. developers = projects(:active_record).developers.all
  479. assert_equal 3, developers.size
  480. assert_equal developers(:poor_jamis), projects(:active_record).developers.where("salary < 10000").first
  481. end
  482. def test_replace_with_less
  483. david = developers(:david)
  484. david.projects = [projects(:action_controller)]
  485. assert david.save
  486. assert_equal 1, david.projects.length
  487. end
  488. def test_replace_with_new
  489. david = developers(:david)
  490. david.projects = [projects(:action_controller), Project.new("name" => "ActionWebSearch")]
  491. david.save
  492. assert_equal 2, david.projects.length
  493. assert !david.projects.include?(projects(:active_record))
  494. end
  495. def test_replace_on_new_object
  496. new_developer = Developer.new("name" => "Matz")
  497. new_developer.projects = [projects(:action_controller), Project.new("name" => "ActionWebSearch")]
  498. new_developer.save
  499. assert_equal 2, new_developer.projects.length
  500. end
  501. def test_consider_type
  502. developer = Developer.find(:first)
  503. special_project = SpecialProject.create("name" => "Special Project")
  504. other_project = developer.projects.first
  505. developer.special_projects << special_project
  506. developer.reload
  507. assert developer.projects.include?(special_project)
  508. assert developer.special_projects.include?(special_project)
  509. assert !developer.special_projects.include?(other_project)
  510. end
  511. def test_update_attributes_after_push_without_duplicate_join_table_rows
  512. developer = Developer.new("name" => "Kano")
  513. project = SpecialProject.create("name" => "Special Project")
  514. assert developer.save
  515. developer.projects << project
  516. developer.update_column("name", "Bruza")
  517. assert_equal 1, Developer.connection.select_value(<<-end_sql).to_i
  518. SELECT count(*) FROM developers_projects
  519. WHERE project_id = #{project.id}
  520. AND developer_id = #{developer.id}
  521. end_sql
  522. end
  523. def test_updating_attributes_on_non_rich_associations
  524. welcome = categories(:technology).posts.first
  525. welcome.title = "Something else"
  526. assert welcome.save!
  527. end
  528. def test_habtm_respects_select
  529. categories(:technology).select_testing_posts(true).each do |o|
  530. assert_respond_to o, :correctness_marker
  531. end
  532. assert_respond_to categories(:technology).select_testing_posts.find(:first), :correctness_marker
  533. end
  534. def test_join_table_alias
  535. assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL').size
  536. end
  537. def test_join_with_group
  538. group = Developer.columns.inject([]) do |g, c|
  539. g << "developers.#{c.name}"
  540. g << "developers_projects_2.#{c.name}"
  541. end
  542. Project.columns.each { |c| group << "projects.#{c.name}" }
  543. assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL', :group => group.join(",")).size
  544. end
  545. def test_find_grouped
  546. all_posts_from_category1 = Post.find(:all, :conditions => "category_id = 1", :joins => :categories)
  547. grouped_posts_of_category1 = Post.find(:all, :conditions => "category_id = 1", :group => "author_id", :select => 'count(posts.id) as posts_count', :joins => :categories)
  548. assert_equal 5, all_posts_from_category1.size
  549. assert_equal 2, grouped_posts_of_category1.size
  550. end
  551. def test_find_scoped_grouped
  552. assert_equal 5, categories(:general).posts_grouped_by_title.size
  553. assert_equal 1, categories(:technology).posts_grouped_by_title.size
  554. end
  555. def test_find_scoped_grouped_having
  556. assert_equal 2, projects(:active_record).well_payed_salary_groups.size
  557. assert projects(:active_record).well_payed_salary_groups.all? { |g| g.salary > 10000 }
  558. end
  559. def test_get_ids
  560. assert_equal projects(:active_record, :action_controller).map(&:id).sort, developers(:david).project_ids.sort
  561. assert_equal [projects(:active_record).id], developers(:jamis).project_ids
  562. end
  563. def test_get_ids_for_loaded_associations
  564. developer = developers(:david)
  565. developer.projects(true)
  566. assert_queries(0) do
  567. developer.project_ids
  568. developer.project_ids
  569. end
  570. end
  571. def test_get_ids_for_unloaded_associations_does_not_load_them
  572. developer = developers(:david)
  573. assert !developer.projects.loaded?
  574. assert_equal projects(:active_record, :action_controller).map(&:id).sort, developer.project_ids.sort
  575. assert !developer.projects.loaded?
  576. end
  577. def test_assign_ids
  578. developer = Developer.new("name" => "Joe")
  579. developer.project_ids = projects(:active_record, :action_controller).map(&:id)
  580. developer.save
  581. developer.reload
  582. assert_equal 2, developer.projects.length
  583. assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort
  584. end
  585. def test_assign_ids_ignoring_blanks
  586. developer = Developer.new("name" => "Joe")
  587. developer.project_ids = [projects(:active_record).id, nil, projects(:action_controller).id, '']
  588. developer.save
  589. developer.reload
  590. assert_equal 2, developer.projects.length
  591. assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort
  592. end
  593. def test_scoped_find_on_through_association_doesnt_return_read_only_records
  594. tag = Post.find(1).tags.find_by_name("General")
  595. assert_nothing_raised do
  596. tag.save!
  597. end
  598. end
  599. def test_has_many_through_polymorphic_has_manys_works
  600. assert_equal [10, 20].to_set, pirates(:redbeard).treasure_estimates.map(&:price).to_set
  601. end
  602. def test_symbols_as_keys
  603. developer = DeveloperWithSymbolsForKeys.new(:name => 'David')
  604. project = ProjectWithSymbolsForKeys.new(:name => 'Rails Testing')
  605. project.developers << developer
  606. project.save!
  607. assert_equal 1, project.developers.size
  608. assert_equal 1, developer.projects.size
  609. assert_equal developer, project.developers.find(:first)
  610. assert_equal project, developer.projects.find(:first)
  611. end
  612. def test_self_referential_habtm_without_foreign_key_set_should_raise_exception
  613. assert_raise(ActiveRecord::HasAndBelongsToManyAssociationForeignKeyNeeded) {
  614. Member.class_eval do
  615. has_and_belongs_to_many :friends, :class_name => "Member", :join_table => "member_friends"
  616. end
  617. }
  618. end
  619. def test_dynamic_find_should_respect_association_include
  620. # SQL error in sort clause if :include is not included
  621. # due to Unknown column 'authors.id'
  622. assert Category.find(1).posts_with_authors_sorted_by_author_id.find_by_title('Welcome to the weblog')
  623. end
  624. def test_counting_on_habtm_association_and_not_array
  625. david = Developer.find(1)
  626. # Extra parameter just to make sure we aren't falling back to
  627. # Array#count in Ruby >=1.8.7, which would raise an ArgumentError
  628. assert_nothing_raised { david.projects.count(:all, :conditions => '1=1') }
  629. end
  630. def test_count
  631. david = Developer.find(1)
  632. assert_equal 2, david.projects.count
  633. end
  634. def test_count_with_counter_sql
  635. developer = DeveloperWithCounterSQL.create(:name => 'tekin')
  636. developer.project_ids = [projects(:active_record).id]
  637. developer.save
  638. developer.reload
  639. assert_equal 1, developer.projects.count
  640. end
  641. unless current_adapter?(:PostgreSQLAdapter)
  642. def test_count_with_finder_sql
  643. assert_equal 3, projects(:active_record).developers_with_finder_sql.count
  644. assert_equal 3, projects(:active_record).developers_with_multiline_finder_sql.count
  645. end
  646. end
  647. def test_association_proxy_transaction_method_starts_transaction_in_association_class
  648. Post.expects(:transaction)
  649. Category.find(:first).posts.transaction do
  650. # nothing
  651. end
  652. end
  653. def test_caching_of_columns
  654. david = Developer.find(1)
  655. # clear cache possibly created by other tests
  656. david.projects.reset_column_information
  657. # One query for columns, one for primary key
  658. assert_queries(2) { david.projects.columns; david.projects.columns }
  659. ## and again to verify that reset_column_information clears the cache correctly
  660. david.projects.reset_column_information
  661. assert_queries(2) { david.projects.columns; david.projects.columns }
  662. end
  663. def test_attributes_are_being_set_when_initialized_from_habm_association_with_where_clause
  664. new_developer = projects(:action_controller).developers.where(:name => "Marcelo").build
  665. assert_equal new_developer.name, "Marcelo"
  666. end
  667. def test_attributes_are_being_set_when_initialized_from_habm_association_with_multiple_where_clauses
  668. new_developer = projects(:action_controller).developers.where(:name => "Marcelo").where(:salary => 90_000).build
  669. assert_equal new_developer.name, "Marcelo"
  670. assert_equal new_developer.salary, 90_000
  671. end
  672. def test_include_method_in_has_and_belongs_to_many_association_should_return_true_for_instance_added_with_build
  673. project = Project.new
  674. developer = project.developers.build
  675. assert project.developers.include?(developer)
  676. end
  677. end