PageRenderTime 52ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/plugins/meta_where/test/test_relations.rb

https://github.com/muffs/mumbls
Ruby | 330 lines | 274 code | 56 blank | 0 comment | 4 complexity | 57681646cec2572294441d4aec43a26a MD5 | raw file
  1. require 'helper'
  2. class TestRelations < Test::Unit::TestCase
  3. context "A company relation" do
  4. setup do
  5. @r = Company.scoped
  6. end
  7. should "behave as expected with one-level hash params" do
  8. results = @r.where(:name => 'Initech')
  9. assert_equal 1, results.size
  10. assert_equal results.first, Company.find_by_name('Initech')
  11. end
  12. should "behave as expected with nested hash params" do
  13. results = @r.where(
  14. :developers => {
  15. :name => 'Peter Gibbons',
  16. :notes => {
  17. :note => 'A straight shooter with upper management written all over him.'
  18. }
  19. }
  20. )
  21. assert_raises ActiveRecord::StatementInvalid do
  22. results.all
  23. end
  24. results = results.joins(:developers => :notes)
  25. assert_equal 1, results.size
  26. assert_equal results.first, Company.find_by_name('Initech')
  27. end
  28. should "allow selection of join type in association joins" do
  29. assert_match /INNER JOIN/, @r.joins(:developers.inner).to_sql
  30. assert_match /LEFT OUTER JOIN/, @r.joins(:developers.outer).to_sql
  31. end
  32. should "only join once even if two join types are used" do
  33. assert_equal 1, @r.joins(:developers.inner, :developers.outer).to_sql.scan("JOIN").size
  34. end
  35. should "allow SQL functions via Symbol#func" do
  36. assert_equal @r.where(:name.in => ['Initech', 'Mission Data']), @r.joins(:developers).group('companies.id').having(:developers => {:count.func(:id).gt => 2}).all
  37. end
  38. should "allow SQL functions via Symbol#[]" do
  39. assert_equal @r.where(:name.in => ['Initech', 'Mission Data']), @r.joins(:developers).group('companies.id').having(:developers => {:count[:id].gt => 2}).all
  40. end
  41. should "allow SQL functions in select clause" do
  42. assert_equal [3,2,3], @r.joins(:developers).group('companies.id').select(:count[Developer.arel_table[:id]].as(:developers_count)).map {|c| c.developers_count}
  43. end
  44. should "allow operators on MetaWhere::Function objects" do
  45. assert_equal @r.where(:name.in => ['Initech', 'Mission Data']), @r.joins(:developers).group('companies.id').having(:developers => [:count[:id] > 2]).all
  46. end
  47. should "join multiple parameters to an SQL function with commas" do
  48. assert_match /concat\("companies"."id","companies"."name"\) LIKE '%blah%'/, @r.where(:concat[:id,:name].matches => '%blah%').to_sql
  49. end
  50. should "create new records with values from equality predicates" do
  51. assert_equal "New Company",
  52. @r.where(:name => 'New Company').new.name
  53. assert_equal "New Company",
  54. @r.where(:name.eq => 'New Company').new.name
  55. assert_equal "New Company",
  56. @r.where(:name.eq % 'New Company').new.name
  57. end
  58. should "create new records with values from equality predicates using last supplied predicate" do
  59. assert_equal "Newer Company",
  60. @r.where(:name => 'New Company').where(:name => 'Newer Company').new.name
  61. assert_equal "Newer Company",
  62. @r.where(:name.eq => 'New Company').where(:name.eq => 'Newer Company').new.name
  63. assert_equal "Newer Company",
  64. @r.where(:name.eq % 'New Company').where(:name.eq % 'Newer Company').new.name
  65. end
  66. should "behave as expected with SQL interpolation" do
  67. results = @r.where('name like ?', '%tech')
  68. assert_equal 1, results.size
  69. assert_equal results.first, Company.find_by_name('Initech')
  70. end
  71. should "behave as expected with mixed hash and SQL interpolation" do
  72. results = @r.where('name like ?', '%tech').where(:created_at => 100.years.ago..Time.now)
  73. assert_equal 1, results.size
  74. assert_equal results.first, Company.find_by_name('Initech')
  75. end
  76. should "behave as expected with empty arrays" do
  77. none = @r.where("3 = 1").all
  78. assert_equal none, @r.where(:name => []).all
  79. assert_equal none, @r.where(:name.in => []).all
  80. end
  81. should "allow multiple condition params in a single where" do
  82. results = @r.where(['name like ?', '%tech'], :created_at => 100.years.ago..Time.now)
  83. assert_equal 1, results.size
  84. assert_equal results.first, Company.find_by_name('Initech')
  85. end
  86. should "allow predicate method selection on hash keys" do
  87. assert_equal @r.where(:name.eq => 'Initech').all, @r.where(:name => 'Initech').all
  88. assert_equal @r.where(:name.matches => 'Mission%').all, @r.where('name LIKE ?', 'Mission%').all
  89. end
  90. should "allow operators to select predicate methods" do
  91. assert_equal @r.where(:name ^ 'Initech').all, @r.where('name != ?', 'Initech').all
  92. assert_equal @r.where(:id + [1,3]).all, @r.where('id IN (?)', [1,3]).all
  93. assert_equal @r.where(:name =~ 'Advanced%').all, @r.where('name LIKE ?', 'Advanced%').all
  94. end
  95. should "use % 'substitution' for hash key predicate methods" do
  96. assert_equal @r.where(:name.like % 'Advanced%').all, @r.where('name LIKE ?', 'Advanced%').all
  97. end
  98. should "allow | and & for compound predicates" do
  99. assert_equal @r.where(:name.like % 'Advanced%' | :name.like % 'Init%').all,
  100. @r.where('name LIKE ? OR name LIKE ?', 'Advanced%', 'Init%').all
  101. assert_equal @r.where(:name.like % 'Mission%' & :name.like % '%Data').all,
  102. @r.where('name LIKE ? AND name LIKE ?', 'Mission%', '%Data').all
  103. end
  104. should "allow nested conditions hashes to have array values" do
  105. assert_equal @r.joins(:data_types).where(:data_types => {:dec => 2..5}).all,
  106. @r.joins(:data_types).where(:data_types => [:dec >= 2, :dec <= 5]).all
  107. end
  108. should "allow nested conditions hashes to have MetaWhere::Condition values" do
  109. assert_equal @r.joins(:data_types).where(:data_types => {:dec.gt => 2}).all,
  110. @r.joins(:data_types).where(:data_types => :dec > 2).all
  111. end
  112. should "allow nested conditions hashes to have MetaWhere::And values" do
  113. assert_equal @r.joins(:data_types).where(:data_types => {:dec => 2..5}).all,
  114. @r.joins(:data_types).where(:data_types => ((:dec >= 2) & (:dec <= 5))).all
  115. end
  116. should "allow nested conditions hashes to have MetaWhere::Or values" do
  117. assert_equal @r.joins(:data_types).where(:data_types => [:dec.gteq % 2 | :bln.eq % true]).all,
  118. @r.joins(:data_types).where(:data_types => ((:dec >= 2) | (:bln >> true))).all
  119. end
  120. should "allow combinations of options that no sane developer would ever try to use" do
  121. assert_equal @r.find_all_by_name('Initech'),
  122. @r.joins(:data_types, :developers => [:projects, :notes]).
  123. where(
  124. {
  125. :data_types => [:dec > 3, {:bln.eq => true}]
  126. } &
  127. {
  128. :developers => {
  129. :name.like => 'Peter Gibbons'
  130. }
  131. } &
  132. {
  133. :developers => {
  134. :projects => {
  135. :estimated_hours.gteq => 1000
  136. },
  137. :notes => [:note.matches % '%straight shooter%']
  138. }
  139. }
  140. ).uniq
  141. end
  142. should "allow ordering by attributes in ascending order" do
  143. last_created = @r.all.sort {|a, b| a.created_at <=> b.created_at}.last
  144. assert_equal last_created, @r.order(:created_at.asc).last
  145. end
  146. should "allow ordering by attributes in descending order" do
  147. last_created = @r.all.sort {|a, b| a.created_at <=> b.created_at}.last
  148. assert_equal last_created, @r.order(:created_at.desc).first
  149. end
  150. should "allow ordering by attributes on nested associations" do
  151. highest_paying = Developer.order(:salary.desc).first.company
  152. assert_equal highest_paying, @r.joins(:developers).order(:developers => :salary.desc).first
  153. end
  154. context "with eager-loaded developers" do
  155. setup do
  156. @r = @r.includes(:developers).where(:developers => {:name => 'Ernie Miller'})
  157. end
  158. should "return the expected result" do
  159. assert_equal Company.where(:name => 'Mission Data'), @r.all
  160. end
  161. should "generate debug SQL with the joins in place" do
  162. assert_match /LEFT OUTER JOIN "developers"/, @r.debug_sql
  163. end
  164. end
  165. end
  166. context "A relation from an STI class" do
  167. setup do
  168. @r = TimeAndMaterialsProject.scoped
  169. end
  170. should "return results from the designated class only" do
  171. assert_equal 2, @r.size
  172. assert @r.all? {|r| r.is_a?(TimeAndMaterialsProject)}
  173. end
  174. should "inherit the default scope of the parent class" do
  175. assert_match /IS NOT NULL/, @r.to_sql
  176. end
  177. should "allow use of scopes in the parent class" do
  178. assert_equal 1, @r.hours_lte_100.size
  179. assert_equal 'MetaSearch Development', @r.hours_lte_100.first.name
  180. end
  181. end
  182. context "A merged relation with a different base class" do
  183. setup do
  184. @r = Developer.where(:salary.gteq % 70000) & Company.where(:name.matches % 'Initech')
  185. end
  186. should "keep the table of the second relation intact in the query" do
  187. assert_match /#{Company.quoted_table_name}."name"/, @r.to_sql
  188. end
  189. should "return expected results" do
  190. assert_equal ['Peter Gibbons', 'Michael Bolton'], @r.all.map(&:name)
  191. end
  192. end
  193. context "A merged relation with a different base class and a MetaWhere::JoinType in joins" do
  194. setup do
  195. @r = Developer.where(:salary.gteq % 70000) & Company.where(:name.matches % 'Initech').joins(:data_types.outer)
  196. end
  197. should "merge the JoinType under the association for the merged relation" do
  198. assert_match /LEFT OUTER JOIN #{DataType.quoted_table_name} ON #{DataType.quoted_table_name}."company_id" = #{Company.quoted_table_name}."id"/,
  199. @r.to_sql
  200. end
  201. end
  202. context "A merged relation with with a different base class and an alternate association" do
  203. setup do
  204. @r = Company.scoped.merge(Developer.where(:salary.gt => 70000), :slackers)
  205. end
  206. should "use the proper association" do
  207. assert_match Company.joins(:slackers).where(:slackers => {:salary.gt => 70000}).to_sql,
  208. @r.to_sql
  209. end
  210. should "return expected results" do
  211. assert_equal ['Initech', 'Advanced Optical Solutions'], @r.all.map(&:name)
  212. end
  213. end
  214. context "A Person relation" do
  215. setup do
  216. @r = Person.scoped
  217. end
  218. context "with self-referencing joins" do
  219. setup do
  220. @r = @r.where(:children => {:children => {:name => 'Jacob'}}).joins(:children => :children)
  221. end
  222. should "join the table multiple times with aliases" do
  223. assert_equal 2, @r.to_sql.scan('INNER JOIN').size
  224. assert_match /INNER JOIN "people" "children_people"/, @r.to_sql
  225. assert_match /INNER JOIN "people" "children_people_2"/, @r.to_sql
  226. end
  227. should "place the condition on the correct join" do
  228. assert_match /"children_people_2"."name" = 'Jacob'/, @r.to_sql
  229. end
  230. should "return the expected result" do
  231. assert_equal Person.where(:name => 'Abraham'), @r.all
  232. end
  233. end
  234. context "with self-referencing joins on parent and children" do
  235. setup do
  236. @r = @r.where(:children => {:children => {:parent => {:parent => {:name => 'Abraham'}}}}).
  237. joins(:children => {:children => {:parent => :parent}})
  238. end
  239. should "join the table multiple times with aliases" do
  240. assert_equal 4, @r.to_sql.scan('INNER JOIN').size
  241. assert_match /INNER JOIN "people" "children_people"/, @r.to_sql
  242. assert_match /INNER JOIN "people" "children_people_2"/, @r.to_sql
  243. assert_match /INNER JOIN "people" "parents_people"/, @r.to_sql
  244. assert_match /INNER JOIN "people" "parents_people_2"/, @r.to_sql
  245. end
  246. should "place the condition on the correct join" do
  247. assert_match /"parents_people_2"."name" = 'Abraham'/, @r.to_sql
  248. end
  249. should "return the expected result" do
  250. assert_equal Person.where(:name => 'Abraham'), @r.all
  251. end
  252. end
  253. end
  254. context "A Developer relation" do
  255. setup do
  256. @r = Developer.scoped
  257. end
  258. should "allow a hash with another relation as a value" do
  259. query = @r.where(:company_id => Company.where(:name.matches => '%i%'))
  260. assert_match /IN \(1, 2, 3\)/, query.to_sql
  261. assert_same_elements Developer.all, query.all
  262. end
  263. should "merge multiple conditions on the same column and predicate with ORs" do
  264. assert_match /"developers"."name" = 'blah' OR "developers"."name" = 'blah2'/,
  265. @r.where(:name => 'blah').where(:name => 'blah2').to_sql
  266. assert_match /"developers"."name" LIKE '%blah%' OR "developers"."name" LIKE '%blah2%'/,
  267. @r.where(:name.matches => '%blah%').where(:name.matches => '%blah2%').to_sql
  268. end
  269. should "erge multiple conditions on the same column but different predicate with ANDs" do
  270. assert_match /"developers"."name" = 'blah' AND "developers"."name" LIKE '%blah2%'/,
  271. @r.where(:name => 'blah').where(:name.matches => '%blah2%').to_sql
  272. end
  273. end
  274. end