You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

query_test.rb 50KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263
  1. # Redmine - project management software
  2. # Copyright (C) 2006-2013 Jean-Philippe Lang
  3. #
  4. # This program is free software; you can redistribute it and/or
  5. # modify it under the terms of the GNU General Public License
  6. # as published by the Free Software Foundation; either version 2
  7. # of the License, or (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. require File.expand_path('../../test_helper', __FILE__)
  18. class QueryTest < ActiveSupport::TestCase
  19. include Redmine::I18n
  20. fixtures :projects, :enabled_modules, :users, :members,
  21. :member_roles, :roles, :trackers, :issue_statuses,
  22. :issue_categories, :enumerations, :issues,
  23. :watchers, :custom_fields, :custom_values, :versions,
  24. :queries,
  25. :projects_trackers,
  26. :custom_fields_trackers
  27. def test_available_filters_should_be_ordered
  28. query = IssueQuery.new
  29. assert_equal 0, query.available_filters.keys.index('status_id')
  30. end
  31. def test_custom_fields_for_all_projects_should_be_available_in_global_queries
  32. query = IssueQuery.new(:project => nil, :name => '_')
  33. assert query.available_filters.has_key?('cf_1')
  34. assert !query.available_filters.has_key?('cf_3')
  35. end
  36. def test_system_shared_versions_should_be_available_in_global_queries
  37. Version.find(2).update_attribute :sharing, 'system'
  38. query = IssueQuery.new(:project => nil, :name => '_')
  39. assert query.available_filters.has_key?('fixed_version_id')
  40. assert query.available_filters['fixed_version_id'][:values].detect {|v| v.last == '2'}
  41. end
  42. def test_project_filter_in_global_queries
  43. query = IssueQuery.new(:project => nil, :name => '_')
  44. project_filter = query.available_filters["project_id"]
  45. assert_not_nil project_filter
  46. project_ids = project_filter[:values].map{|p| p[1]}
  47. assert project_ids.include?("1") #public project
  48. assert !project_ids.include?("2") #private project user cannot see
  49. end
  50. def find_issues_with_query(query)
  51. Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
  52. query.statement
  53. ).all
  54. end
  55. def assert_find_issues_with_query_is_successful(query)
  56. assert_nothing_raised do
  57. find_issues_with_query(query)
  58. end
  59. end
  60. def assert_query_statement_includes(query, condition)
  61. assert_include condition, query.statement
  62. end
  63. def assert_query_result(expected, query)
  64. assert_nothing_raised do
  65. assert_equal expected.map(&:id).sort, query.issues.map(&:id).sort
  66. assert_equal expected.size, query.issue_count
  67. end
  68. end
  69. def test_query_should_allow_shared_versions_for_a_project_query
  70. subproject_version = Version.find(4)
  71. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  72. query.add_filter('fixed_version_id', '=', [subproject_version.id.to_s])
  73. assert query.statement.include?("#{Issue.table_name}.fixed_version_id IN ('4')")
  74. end
  75. def test_query_with_multiple_custom_fields
  76. query = IssueQuery.find(1)
  77. assert query.valid?
  78. assert query.statement.include?("#{CustomValue.table_name}.value IN ('MySQL')")
  79. issues = find_issues_with_query(query)
  80. assert_equal 1, issues.length
  81. assert_equal Issue.find(3), issues.first
  82. end
  83. def test_operator_none
  84. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  85. query.add_filter('fixed_version_id', '!*', [''])
  86. query.add_filter('cf_1', '!*', [''])
  87. assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NULL")
  88. assert query.statement.include?("#{CustomValue.table_name}.value IS NULL OR #{CustomValue.table_name}.value = ''")
  89. find_issues_with_query(query)
  90. end
  91. def test_operator_none_for_integer
  92. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  93. query.add_filter('estimated_hours', '!*', [''])
  94. issues = find_issues_with_query(query)
  95. assert !issues.empty?
  96. assert issues.all? {|i| !i.estimated_hours}
  97. end
  98. def test_operator_none_for_date
  99. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  100. query.add_filter('start_date', '!*', [''])
  101. issues = find_issues_with_query(query)
  102. assert !issues.empty?
  103. assert issues.all? {|i| i.start_date.nil?}
  104. end
  105. def test_operator_none_for_string_custom_field
  106. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  107. query.add_filter('cf_2', '!*', [''])
  108. assert query.has_filter?('cf_2')
  109. issues = find_issues_with_query(query)
  110. assert !issues.empty?
  111. assert issues.all? {|i| i.custom_field_value(2).blank?}
  112. end
  113. def test_operator_all
  114. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  115. query.add_filter('fixed_version_id', '*', [''])
  116. query.add_filter('cf_1', '*', [''])
  117. assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NOT NULL")
  118. assert query.statement.include?("#{CustomValue.table_name}.value IS NOT NULL AND #{CustomValue.table_name}.value <> ''")
  119. find_issues_with_query(query)
  120. end
  121. def test_operator_all_for_date
  122. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  123. query.add_filter('start_date', '*', [''])
  124. issues = find_issues_with_query(query)
  125. assert !issues.empty?
  126. assert issues.all? {|i| i.start_date.present?}
  127. end
  128. def test_operator_all_for_string_custom_field
  129. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  130. query.add_filter('cf_2', '*', [''])
  131. assert query.has_filter?('cf_2')
  132. issues = find_issues_with_query(query)
  133. assert !issues.empty?
  134. assert issues.all? {|i| i.custom_field_value(2).present?}
  135. end
  136. def test_numeric_filter_should_not_accept_non_numeric_values
  137. query = IssueQuery.new(:name => '_')
  138. query.add_filter('estimated_hours', '=', ['a'])
  139. assert query.has_filter?('estimated_hours')
  140. assert !query.valid?
  141. end
  142. def test_operator_is_on_float
  143. Issue.update_all("estimated_hours = 171.2", "id=2")
  144. query = IssueQuery.new(:name => '_')
  145. query.add_filter('estimated_hours', '=', ['171.20'])
  146. issues = find_issues_with_query(query)
  147. assert_equal 1, issues.size
  148. assert_equal 2, issues.first.id
  149. end
  150. def test_operator_is_on_integer_custom_field
  151. f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true)
  152. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
  153. CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
  154. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
  155. query = IssueQuery.new(:name => '_')
  156. query.add_filter("cf_#{f.id}", '=', ['12'])
  157. issues = find_issues_with_query(query)
  158. assert_equal 1, issues.size
  159. assert_equal 2, issues.first.id
  160. end
  161. def test_operator_is_on_integer_custom_field_should_accept_negative_value
  162. f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true)
  163. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
  164. CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12')
  165. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
  166. query = IssueQuery.new(:name => '_')
  167. query.add_filter("cf_#{f.id}", '=', ['-12'])
  168. assert query.valid?
  169. issues = find_issues_with_query(query)
  170. assert_equal 1, issues.size
  171. assert_equal 2, issues.first.id
  172. end
  173. def test_operator_is_on_float_custom_field
  174. f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true)
  175. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
  176. CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12.7')
  177. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
  178. query = IssueQuery.new(:name => '_')
  179. query.add_filter("cf_#{f.id}", '=', ['12.7'])
  180. issues = find_issues_with_query(query)
  181. assert_equal 1, issues.size
  182. assert_equal 2, issues.first.id
  183. end
  184. def test_operator_is_on_float_custom_field_should_accept_negative_value
  185. f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true)
  186. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
  187. CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12.7')
  188. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
  189. query = IssueQuery.new(:name => '_')
  190. query.add_filter("cf_#{f.id}", '=', ['-12.7'])
  191. assert query.valid?
  192. issues = find_issues_with_query(query)
  193. assert_equal 1, issues.size
  194. assert_equal 2, issues.first.id
  195. end
  196. def test_operator_is_on_multi_list_custom_field
  197. f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
  198. :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
  199. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
  200. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
  201. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
  202. query = IssueQuery.new(:name => '_')
  203. query.add_filter("cf_#{f.id}", '=', ['value1'])
  204. issues = find_issues_with_query(query)
  205. assert_equal [1, 3], issues.map(&:id).sort
  206. query = IssueQuery.new(:name => '_')
  207. query.add_filter("cf_#{f.id}", '=', ['value2'])
  208. issues = find_issues_with_query(query)
  209. assert_equal [1], issues.map(&:id).sort
  210. end
  211. def test_operator_is_not_on_multi_list_custom_field
  212. f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
  213. :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
  214. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
  215. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
  216. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
  217. query = IssueQuery.new(:name => '_')
  218. query.add_filter("cf_#{f.id}", '!', ['value1'])
  219. issues = find_issues_with_query(query)
  220. assert !issues.map(&:id).include?(1)
  221. assert !issues.map(&:id).include?(3)
  222. query = IssueQuery.new(:name => '_')
  223. query.add_filter("cf_#{f.id}", '!', ['value2'])
  224. issues = find_issues_with_query(query)
  225. assert !issues.map(&:id).include?(1)
  226. assert issues.map(&:id).include?(3)
  227. end
  228. def test_operator_is_on_is_private_field
  229. # is_private filter only available for those who can set issues private
  230. User.current = User.find(2)
  231. query = IssueQuery.new(:name => '_')
  232. assert query.available_filters.key?('is_private')
  233. query.add_filter("is_private", '=', ['1'])
  234. issues = find_issues_with_query(query)
  235. assert issues.any?
  236. assert_nil issues.detect {|issue| !issue.is_private?}
  237. ensure
  238. User.current = nil
  239. end
  240. def test_operator_is_not_on_is_private_field
  241. # is_private filter only available for those who can set issues private
  242. User.current = User.find(2)
  243. query = IssueQuery.new(:name => '_')
  244. assert query.available_filters.key?('is_private')
  245. query.add_filter("is_private", '!', ['1'])
  246. issues = find_issues_with_query(query)
  247. assert issues.any?
  248. assert_nil issues.detect {|issue| issue.is_private?}
  249. ensure
  250. User.current = nil
  251. end
  252. def test_operator_greater_than
  253. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  254. query.add_filter('done_ratio', '>=', ['40'])
  255. assert query.statement.include?("#{Issue.table_name}.done_ratio >= 40.0")
  256. find_issues_with_query(query)
  257. end
  258. def test_operator_greater_than_a_float
  259. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  260. query.add_filter('estimated_hours', '>=', ['40.5'])
  261. assert query.statement.include?("#{Issue.table_name}.estimated_hours >= 40.5")
  262. find_issues_with_query(query)
  263. end
  264. def test_operator_greater_than_on_int_custom_field
  265. f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
  266. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
  267. CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
  268. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
  269. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  270. query.add_filter("cf_#{f.id}", '>=', ['8'])
  271. issues = find_issues_with_query(query)
  272. assert_equal 1, issues.size
  273. assert_equal 2, issues.first.id
  274. end
  275. def test_operator_lesser_than
  276. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  277. query.add_filter('done_ratio', '<=', ['30'])
  278. assert query.statement.include?("#{Issue.table_name}.done_ratio <= 30.0")
  279. find_issues_with_query(query)
  280. end
  281. def test_operator_lesser_than_on_custom_field
  282. f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
  283. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  284. query.add_filter("cf_#{f.id}", '<=', ['30'])
  285. assert_match /CAST.+ <= 30\.0/, query.statement
  286. find_issues_with_query(query)
  287. end
  288. def test_operator_lesser_than_on_date_custom_field
  289. f = IssueCustomField.create!(:name => 'filter', :field_format => 'date', :is_filter => true, :is_for_all => true)
  290. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '2013-04-11')
  291. CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '2013-05-14')
  292. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
  293. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  294. query.add_filter("cf_#{f.id}", '<=', ['2013-05-01'])
  295. issue_ids = find_issues_with_query(query).map(&:id)
  296. assert_include 1, issue_ids
  297. assert_not_include 2, issue_ids
  298. assert_not_include 3, issue_ids
  299. end
  300. def test_operator_between
  301. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  302. query.add_filter('done_ratio', '><', ['30', '40'])
  303. assert_include "#{Issue.table_name}.done_ratio BETWEEN 30.0 AND 40.0", query.statement
  304. find_issues_with_query(query)
  305. end
  306. def test_operator_between_on_custom_field
  307. f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
  308. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  309. query.add_filter("cf_#{f.id}", '><', ['30', '40'])
  310. assert_match /CAST.+ BETWEEN 30.0 AND 40.0/, query.statement
  311. find_issues_with_query(query)
  312. end
  313. def test_date_filter_should_not_accept_non_date_values
  314. query = IssueQuery.new(:name => '_')
  315. query.add_filter('created_on', '=', ['a'])
  316. assert query.has_filter?('created_on')
  317. assert !query.valid?
  318. end
  319. def test_date_filter_should_not_accept_invalid_date_values
  320. query = IssueQuery.new(:name => '_')
  321. query.add_filter('created_on', '=', ['2011-01-34'])
  322. assert query.has_filter?('created_on')
  323. assert !query.valid?
  324. end
  325. def test_relative_date_filter_should_not_accept_non_integer_values
  326. query = IssueQuery.new(:name => '_')
  327. query.add_filter('created_on', '>t-', ['a'])
  328. assert query.has_filter?('created_on')
  329. assert !query.valid?
  330. end
  331. def test_operator_date_equals
  332. query = IssueQuery.new(:name => '_')
  333. query.add_filter('due_date', '=', ['2011-07-10'])
  334. assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
  335. find_issues_with_query(query)
  336. end
  337. def test_operator_date_lesser_than
  338. query = IssueQuery.new(:name => '_')
  339. query.add_filter('due_date', '<=', ['2011-07-10'])
  340. assert_match /issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
  341. find_issues_with_query(query)
  342. end
  343. def test_operator_date_greater_than
  344. query = IssueQuery.new(:name => '_')
  345. query.add_filter('due_date', '>=', ['2011-07-10'])
  346. assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?'/, query.statement
  347. find_issues_with_query(query)
  348. end
  349. def test_operator_date_between
  350. query = IssueQuery.new(:name => '_')
  351. query.add_filter('due_date', '><', ['2011-06-23', '2011-07-10'])
  352. assert_match /issues\.due_date > '2011-06-22 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
  353. find_issues_with_query(query)
  354. end
  355. def test_operator_in_more_than
  356. Issue.find(7).update_attribute(:due_date, (Date.today + 15))
  357. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  358. query.add_filter('due_date', '>t+', ['15'])
  359. issues = find_issues_with_query(query)
  360. assert !issues.empty?
  361. issues.each {|issue| assert(issue.due_date >= (Date.today + 15))}
  362. end
  363. def test_operator_in_less_than
  364. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  365. query.add_filter('due_date', '<t+', ['15'])
  366. issues = find_issues_with_query(query)
  367. assert !issues.empty?
  368. issues.each {|issue| assert(issue.due_date <= (Date.today + 15))}
  369. end
  370. def test_operator_in_the_next_days
  371. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  372. query.add_filter('due_date', '><t+', ['15'])
  373. issues = find_issues_with_query(query)
  374. assert !issues.empty?
  375. issues.each {|issue| assert(issue.due_date >= Date.today && issue.due_date <= (Date.today + 15))}
  376. end
  377. def test_operator_less_than_ago
  378. Issue.find(7).update_attribute(:due_date, (Date.today - 3))
  379. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  380. query.add_filter('due_date', '>t-', ['3'])
  381. issues = find_issues_with_query(query)
  382. assert !issues.empty?
  383. issues.each {|issue| assert(issue.due_date >= (Date.today - 3))}
  384. end
  385. def test_operator_in_the_past_days
  386. Issue.find(7).update_attribute(:due_date, (Date.today - 3))
  387. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  388. query.add_filter('due_date', '><t-', ['3'])
  389. issues = find_issues_with_query(query)
  390. assert !issues.empty?
  391. issues.each {|issue| assert(issue.due_date >= (Date.today - 3) && issue.due_date <= Date.today)}
  392. end
  393. def test_operator_more_than_ago
  394. Issue.find(7).update_attribute(:due_date, (Date.today - 10))
  395. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  396. query.add_filter('due_date', '<t-', ['10'])
  397. assert query.statement.include?("#{Issue.table_name}.due_date <=")
  398. issues = find_issues_with_query(query)
  399. assert !issues.empty?
  400. issues.each {|issue| assert(issue.due_date <= (Date.today - 10))}
  401. end
  402. def test_operator_in
  403. Issue.find(7).update_attribute(:due_date, (Date.today + 2))
  404. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  405. query.add_filter('due_date', 't+', ['2'])
  406. issues = find_issues_with_query(query)
  407. assert !issues.empty?
  408. issues.each {|issue| assert_equal((Date.today + 2), issue.due_date)}
  409. end
  410. def test_operator_ago
  411. Issue.find(7).update_attribute(:due_date, (Date.today - 3))
  412. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  413. query.add_filter('due_date', 't-', ['3'])
  414. issues = find_issues_with_query(query)
  415. assert !issues.empty?
  416. issues.each {|issue| assert_equal((Date.today - 3), issue.due_date)}
  417. end
  418. def test_operator_today
  419. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  420. query.add_filter('due_date', 't', [''])
  421. issues = find_issues_with_query(query)
  422. assert !issues.empty?
  423. issues.each {|issue| assert_equal Date.today, issue.due_date}
  424. end
  425. def test_operator_this_week_on_date
  426. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  427. query.add_filter('due_date', 'w', [''])
  428. find_issues_with_query(query)
  429. end
  430. def test_operator_this_week_on_datetime
  431. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  432. query.add_filter('created_on', 'w', [''])
  433. find_issues_with_query(query)
  434. end
  435. def test_operator_contains
  436. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  437. query.add_filter('subject', '~', ['uNable'])
  438. assert query.statement.include?("LOWER(#{Issue.table_name}.subject) LIKE '%unable%'")
  439. result = find_issues_with_query(query)
  440. assert result.empty?
  441. result.each {|issue| assert issue.subject.downcase.include?('unable') }
  442. end
  443. def test_range_for_this_week_with_week_starting_on_monday
  444. I18n.locale = :fr
  445. assert_equal '1', I18n.t(:general_first_day_of_week)
  446. Date.stubs(:today).returns(Date.parse('2011-04-29'))
  447. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  448. query.add_filter('due_date', 'w', [''])
  449. assert query.statement.match(/issues\.due_date > '2011-04-24 23:59:59(\.9+)?' AND issues\.due_date <= '2011-05-01 23:59:59(\.9+)?/), "range not found in #{query.statement}"
  450. I18n.locale = :en
  451. end
  452. def test_range_for_this_week_with_week_starting_on_sunday
  453. I18n.locale = :en
  454. assert_equal '7', I18n.t(:general_first_day_of_week)
  455. Date.stubs(:today).returns(Date.parse('2011-04-29'))
  456. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  457. query.add_filter('due_date', 'w', [''])
  458. assert query.statement.match(/issues\.due_date > '2011-04-23 23:59:59(\.9+)?' AND issues\.due_date <= '2011-04-30 23:59:59(\.9+)?/), "range not found in #{query.statement}"
  459. end
  460. def test_operator_does_not_contains
  461. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  462. query.add_filter('subject', '!~', ['uNable'])
  463. assert query.statement.include?("LOWER(#{Issue.table_name}.subject) NOT LIKE '%unable%'")
  464. find_issues_with_query(query)
  465. end
  466. def test_filter_assigned_to_me
  467. user = User.find(2)
  468. group = Group.find(10)
  469. User.current = user
  470. i1 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => user)
  471. i2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => group)
  472. i3 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => Group.find(11))
  473. group.users << user
  474. query = IssueQuery.new(:name => '_', :filters => { 'assigned_to_id' => {:operator => '=', :values => ['me']}})
  475. result = query.issues
  476. assert_equal Issue.visible.all(:conditions => {:assigned_to_id => ([2] + user.reload.group_ids)}).sort_by(&:id), result.sort_by(&:id)
  477. assert result.include?(i1)
  478. assert result.include?(i2)
  479. assert !result.include?(i3)
  480. end
  481. def test_user_custom_field_filtered_on_me
  482. User.current = User.find(2)
  483. cf = IssueCustomField.create!(:field_format => 'user', :is_for_all => true, :is_filter => true, :name => 'User custom field', :tracker_ids => [1])
  484. issue1 = Issue.create!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '2'}, :subject => 'Test', :author_id => 1)
  485. issue2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '3'})
  486. query = IssueQuery.new(:name => '_', :project => Project.find(1))
  487. filter = query.available_filters["cf_#{cf.id}"]
  488. assert_not_nil filter
  489. assert_include 'me', filter[:values].map{|v| v[1]}
  490. query.filters = { "cf_#{cf.id}" => {:operator => '=', :values => ['me']}}
  491. result = query.issues
  492. assert_equal 1, result.size
  493. assert_equal issue1, result.first
  494. end
  495. def test_filter_my_projects
  496. User.current = User.find(2)
  497. query = IssueQuery.new(:name => '_')
  498. filter = query.available_filters['project_id']
  499. assert_not_nil filter
  500. assert_include 'mine', filter[:values].map{|v| v[1]}
  501. query.filters = { 'project_id' => {:operator => '=', :values => ['mine']}}
  502. result = query.issues
  503. assert_nil result.detect {|issue| !User.current.member_of?(issue.project)}
  504. end
  505. def test_filter_watched_issues
  506. User.current = User.find(1)
  507. query = IssueQuery.new(:name => '_', :filters => { 'watcher_id' => {:operator => '=', :values => ['me']}})
  508. result = find_issues_with_query(query)
  509. assert_not_nil result
  510. assert !result.empty?
  511. assert_equal Issue.visible.watched_by(User.current).sort_by(&:id), result.sort_by(&:id)
  512. User.current = nil
  513. end
  514. def test_filter_unwatched_issues
  515. User.current = User.find(1)
  516. query = IssueQuery.new(:name => '_', :filters => { 'watcher_id' => {:operator => '!', :values => ['me']}})
  517. result = find_issues_with_query(query)
  518. assert_not_nil result
  519. assert !result.empty?
  520. assert_equal((Issue.visible - Issue.watched_by(User.current)).sort_by(&:id).size, result.sort_by(&:id).size)
  521. User.current = nil
  522. end
  523. def test_filter_on_project_custom_field
  524. field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
  525. CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
  526. CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
  527. query = IssueQuery.new(:name => '_')
  528. filter_name = "project.cf_#{field.id}"
  529. assert_include filter_name, query.available_filters.keys
  530. query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
  531. assert_equal [3, 5], find_issues_with_query(query).map(&:project_id).uniq.sort
  532. end
  533. def test_filter_on_author_custom_field
  534. field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
  535. CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
  536. query = IssueQuery.new(:name => '_')
  537. filter_name = "author.cf_#{field.id}"
  538. assert_include filter_name, query.available_filters.keys
  539. query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
  540. assert_equal [3], find_issues_with_query(query).map(&:author_id).uniq.sort
  541. end
  542. def test_filter_on_assigned_to_custom_field
  543. field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
  544. CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
  545. query = IssueQuery.new(:name => '_')
  546. filter_name = "assigned_to.cf_#{field.id}"
  547. assert_include filter_name, query.available_filters.keys
  548. query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
  549. assert_equal [3], find_issues_with_query(query).map(&:assigned_to_id).uniq.sort
  550. end
  551. def test_filter_on_fixed_version_custom_field
  552. field = VersionCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
  553. CustomValue.create!(:custom_field => field, :customized => Version.find(2), :value => 'Foo')
  554. query = IssueQuery.new(:name => '_')
  555. filter_name = "fixed_version.cf_#{field.id}"
  556. assert_include filter_name, query.available_filters.keys
  557. query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
  558. assert_equal [2], find_issues_with_query(query).map(&:fixed_version_id).uniq.sort
  559. end
  560. def test_filter_on_relations_with_a_specific_issue
  561. IssueRelation.delete_all
  562. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
  563. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
  564. query = IssueQuery.new(:name => '_')
  565. query.filters = {"relates" => {:operator => '=', :values => ['1']}}
  566. assert_equal [2, 3], find_issues_with_query(query).map(&:id).sort
  567. query = IssueQuery.new(:name => '_')
  568. query.filters = {"relates" => {:operator => '=', :values => ['2']}}
  569. assert_equal [1], find_issues_with_query(query).map(&:id).sort
  570. end
  571. def test_filter_on_relations_with_any_issues_in_a_project
  572. IssueRelation.delete_all
  573. with_settings :cross_project_issue_relations => '1' do
  574. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
  575. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(2).issues.first)
  576. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
  577. end
  578. query = IssueQuery.new(:name => '_')
  579. query.filters = {"relates" => {:operator => '=p', :values => ['2']}}
  580. assert_equal [1, 2], find_issues_with_query(query).map(&:id).sort
  581. query = IssueQuery.new(:name => '_')
  582. query.filters = {"relates" => {:operator => '=p', :values => ['3']}}
  583. assert_equal [1], find_issues_with_query(query).map(&:id).sort
  584. query = IssueQuery.new(:name => '_')
  585. query.filters = {"relates" => {:operator => '=p', :values => ['4']}}
  586. assert_equal [], find_issues_with_query(query).map(&:id).sort
  587. end
  588. def test_filter_on_relations_with_any_issues_not_in_a_project
  589. IssueRelation.delete_all
  590. with_settings :cross_project_issue_relations => '1' do
  591. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
  592. #IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(1).issues.first)
  593. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
  594. end
  595. query = IssueQuery.new(:name => '_')
  596. query.filters = {"relates" => {:operator => '=!p', :values => ['1']}}
  597. assert_equal [1], find_issues_with_query(query).map(&:id).sort
  598. end
  599. def test_filter_on_relations_with_no_issues_in_a_project
  600. IssueRelation.delete_all
  601. with_settings :cross_project_issue_relations => '1' do
  602. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
  603. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(3).issues.first)
  604. IssueRelation.create!(:relation_type => "relates", :issue_to => Project.find(2).issues.first, :issue_from => Issue.find(3))
  605. end
  606. query = IssueQuery.new(:name => '_')
  607. query.filters = {"relates" => {:operator => '!p', :values => ['2']}}
  608. ids = find_issues_with_query(query).map(&:id).sort
  609. assert_include 2, ids
  610. assert_not_include 1, ids
  611. assert_not_include 3, ids
  612. end
  613. def test_filter_on_relations_with_no_issues
  614. IssueRelation.delete_all
  615. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
  616. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
  617. query = IssueQuery.new(:name => '_')
  618. query.filters = {"relates" => {:operator => '!*', :values => ['']}}
  619. ids = find_issues_with_query(query).map(&:id)
  620. assert_equal [], ids & [1, 2, 3]
  621. assert_include 4, ids
  622. end
  623. def test_filter_on_relations_with_any_issues
  624. IssueRelation.delete_all
  625. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
  626. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
  627. query = IssueQuery.new(:name => '_')
  628. query.filters = {"relates" => {:operator => '*', :values => ['']}}
  629. assert_equal [1, 2, 3], find_issues_with_query(query).map(&:id).sort
  630. end
  631. def test_filter_on_relations_should_not_ignore_other_filter
  632. issue = Issue.generate!
  633. issue1 = Issue.generate!(:status_id => 1)
  634. issue2 = Issue.generate!(:status_id => 2)
  635. IssueRelation.create!(:relation_type => "relates", :issue_from => issue, :issue_to => issue1)
  636. IssueRelation.create!(:relation_type => "relates", :issue_from => issue, :issue_to => issue2)
  637. query = IssueQuery.new(:name => '_')
  638. query.filters = {
  639. "status_id" => {:operator => '=', :values => ['1']},
  640. "relates" => {:operator => '=', :values => [issue.id.to_s]}
  641. }
  642. assert_equal [issue1], find_issues_with_query(query)
  643. end
  644. def test_statement_should_be_nil_with_no_filters
  645. q = IssueQuery.new(:name => '_')
  646. q.filters = {}
  647. assert q.valid?
  648. assert_nil q.statement
  649. end
  650. def test_default_columns
  651. q = IssueQuery.new
  652. assert q.columns.any?
  653. assert q.inline_columns.any?
  654. assert q.block_columns.empty?
  655. end
  656. def test_set_column_names
  657. q = IssueQuery.new
  658. q.column_names = ['tracker', :subject, '', 'unknonw_column']
  659. assert_equal [:id, :tracker, :subject], q.columns.collect {|c| c.name}
  660. end
  661. def test_has_column_should_accept_a_column_name
  662. q = IssueQuery.new
  663. q.column_names = ['tracker', :subject]
  664. assert q.has_column?(:tracker)
  665. assert !q.has_column?(:category)
  666. end
  667. def test_has_column_should_accept_a_column
  668. q = IssueQuery.new
  669. q.column_names = ['tracker', :subject]
  670. tracker_column = q.available_columns.detect {|c| c.name==:tracker}
  671. assert_kind_of QueryColumn, tracker_column
  672. category_column = q.available_columns.detect {|c| c.name==:category}
  673. assert_kind_of QueryColumn, category_column
  674. assert q.has_column?(tracker_column)
  675. assert !q.has_column?(category_column)
  676. end
  677. def test_inline_and_block_columns
  678. q = IssueQuery.new
  679. q.column_names = ['subject', 'description', 'tracker']
  680. assert_equal [:id, :subject, :tracker], q.inline_columns.map(&:name)
  681. assert_equal [:description], q.block_columns.map(&:name)
  682. end
  683. def test_custom_field_columns_should_be_inline
  684. q = IssueQuery.new
  685. columns = q.available_columns.select {|column| column.is_a? QueryCustomFieldColumn}
  686. assert columns.any?
  687. assert_nil columns.detect {|column| !column.inline?}
  688. end
  689. def test_query_should_preload_spent_hours
  690. q = IssueQuery.new(:name => '_', :column_names => [:subject, :spent_hours])
  691. assert q.has_column?(:spent_hours)
  692. issues = q.issues
  693. assert_not_nil issues.first.instance_variable_get("@spent_hours")
  694. end
  695. def test_groupable_columns_should_include_custom_fields
  696. q = IssueQuery.new
  697. column = q.groupable_columns.detect {|c| c.name == :cf_1}
  698. assert_not_nil column
  699. assert_kind_of QueryCustomFieldColumn, column
  700. end
  701. def test_groupable_columns_should_not_include_multi_custom_fields
  702. field = CustomField.find(1)
  703. field.update_attribute :multiple, true
  704. q = IssueQuery.new
  705. column = q.groupable_columns.detect {|c| c.name == :cf_1}
  706. assert_nil column
  707. end
  708. def test_groupable_columns_should_include_user_custom_fields
  709. cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'user')
  710. q = IssueQuery.new
  711. assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
  712. end
  713. def test_groupable_columns_should_include_version_custom_fields
  714. cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'version')
  715. q = IssueQuery.new
  716. assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
  717. end
  718. def test_grouped_with_valid_column
  719. q = IssueQuery.new(:group_by => 'status')
  720. assert q.grouped?
  721. assert_not_nil q.group_by_column
  722. assert_equal :status, q.group_by_column.name
  723. assert_not_nil q.group_by_statement
  724. assert_equal 'status', q.group_by_statement
  725. end
  726. def test_grouped_with_invalid_column
  727. q = IssueQuery.new(:group_by => 'foo')
  728. assert !q.grouped?
  729. assert_nil q.group_by_column
  730. assert_nil q.group_by_statement
  731. end
  732. def test_sortable_columns_should_sort_assignees_according_to_user_format_setting
  733. with_settings :user_format => 'lastname_coma_firstname' do
  734. q = IssueQuery.new
  735. assert q.sortable_columns.has_key?('assigned_to')
  736. assert_equal %w(users.lastname users.firstname users.id), q.sortable_columns['assigned_to']
  737. end
  738. end
  739. def test_sortable_columns_should_sort_authors_according_to_user_format_setting
  740. with_settings :user_format => 'lastname_coma_firstname' do
  741. q = IssueQuery.new
  742. assert q.sortable_columns.has_key?('author')
  743. assert_equal %w(authors.lastname authors.firstname authors.id), q.sortable_columns['author']
  744. end
  745. end
  746. def test_sortable_columns_should_include_custom_field
  747. q = IssueQuery.new
  748. assert q.sortable_columns['cf_1']
  749. end
  750. def test_sortable_columns_should_not_include_multi_custom_field
  751. field = CustomField.find(1)
  752. field.update_attribute :multiple, true
  753. q = IssueQuery.new
  754. assert !q.sortable_columns['cf_1']
  755. end
  756. def test_default_sort
  757. q = IssueQuery.new
  758. assert_equal [], q.sort_criteria
  759. end
  760. def test_set_sort_criteria_with_hash
  761. q = IssueQuery.new
  762. q.sort_criteria = {'0' => ['priority', 'desc'], '2' => ['tracker']}
  763. assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
  764. end
  765. def test_set_sort_criteria_with_array
  766. q = IssueQuery.new
  767. q.sort_criteria = [['priority', 'desc'], 'tracker']
  768. assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
  769. end
  770. def test_create_query_with_sort
  771. q = IssueQuery.new(:name => 'Sorted')
  772. q.sort_criteria = [['priority', 'desc'], 'tracker']
  773. assert q.save
  774. q.reload
  775. assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
  776. end
  777. def test_sort_by_string_custom_field_asc
  778. q = IssueQuery.new
  779. c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
  780. assert c
  781. assert c.sortable
  782. issues = q.issues(:order => "#{c.sortable} ASC")
  783. values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
  784. assert !values.empty?
  785. assert_equal values.sort, values
  786. end
  787. def test_sort_by_string_custom_field_desc
  788. q = IssueQuery.new
  789. c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
  790. assert c
  791. assert c.sortable
  792. issues = q.issues(:order => "#{c.sortable} DESC")
  793. values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
  794. assert !values.empty?
  795. assert_equal values.sort.reverse, values
  796. end
  797. def test_sort_by_float_custom_field_asc
  798. q = IssueQuery.new
  799. c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'float' }
  800. assert c
  801. assert c.sortable
  802. issues = q.issues(:order => "#{c.sortable} ASC")
  803. values = issues.collect {|i| begin; Kernel.Float(i.custom_value_for(c.custom_field).to_s); rescue; nil; end}.compact
  804. assert !values.empty?
  805. assert_equal values.sort, values
  806. end
  807. def test_invalid_query_should_raise_query_statement_invalid_error
  808. q = IssueQuery.new
  809. assert_raise Query::StatementInvalid do
  810. q.issues(:conditions => "foo = 1")
  811. end
  812. end
  813. def test_issue_count
  814. q = IssueQuery.new(:name => '_')
  815. issue_count = q.issue_count
  816. assert_equal q.issues.size, issue_count
  817. end
  818. def test_issue_count_with_archived_issues
  819. p = Project.generate! do |project|
  820. project.status = Project::STATUS_ARCHIVED
  821. end
  822. i = Issue.generate!( :project => p, :tracker => p.trackers.first )
  823. assert !i.visible?
  824. test_issue_count
  825. end
  826. def test_issue_count_by_association_group
  827. q = IssueQuery.new(:name => '_', :group_by => 'assigned_to')
  828. count_by_group = q.issue_count_by_group
  829. assert_kind_of Hash, count_by_group
  830. assert_equal %w(NilClass User), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
  831. assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
  832. assert count_by_group.has_key?(User.find(3))
  833. end
  834. def test_issue_count_by_list_custom_field_group
  835. q = IssueQuery.new(:name => '_', :group_by => 'cf_1')
  836. count_by_group = q.issue_count_by_group
  837. assert_kind_of Hash, count_by_group
  838. assert_equal %w(NilClass String), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
  839. assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
  840. assert count_by_group.has_key?('MySQL')
  841. end
  842. def test_issue_count_by_date_custom_field_group
  843. q = IssueQuery.new(:name => '_', :group_by => 'cf_8')
  844. count_by_group = q.issue_count_by_group
  845. assert_kind_of Hash, count_by_group
  846. assert_equal %w(Date NilClass), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
  847. assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
  848. end
  849. def test_issue_count_with_nil_group_only
  850. Issue.update_all("assigned_to_id = NULL")
  851. q = IssueQuery.new(:name => '_', :group_by => 'assigned_to')
  852. count_by_group = q.issue_count_by_group
  853. assert_kind_of Hash, count_by_group
  854. assert_equal 1, count_by_group.keys.size
  855. assert_nil count_by_group.keys.first
  856. end
  857. def test_issue_ids
  858. q = IssueQuery.new(:name => '_')
  859. order = "issues.subject, issues.id"
  860. issues = q.issues(:order => order)
  861. assert_equal issues.map(&:id), q.issue_ids(:order => order)
  862. end
  863. def test_label_for
  864. set_language_if_valid 'en'
  865. q = IssueQuery.new
  866. assert_equal 'Assignee', q.label_for('assigned_to_id')
  867. end
  868. def test_label_for_fr
  869. set_language_if_valid 'fr'
  870. q = IssueQuery.new
  871. s = "Assign\xc3\xa9 \xc3\xa0"
  872. s.force_encoding('UTF-8') if s.respond_to?(:force_encoding)
  873. assert_equal s, q.label_for('assigned_to_id')
  874. end
  875. def test_editable_by
  876. admin = User.find(1)
  877. manager = User.find(2)
  878. developer = User.find(3)
  879. # Public query on project 1
  880. q = IssueQuery.find(1)
  881. assert q.editable_by?(admin)
  882. assert q.editable_by?(manager)
  883. assert !q.editable_by?(developer)
  884. # Private query on project 1
  885. q = IssueQuery.find(2)
  886. assert q.editable_by?(admin)
  887. assert !q.editable_by?(manager)
  888. assert q.editable_by?(developer)
  889. # Private query for all projects
  890. q = IssueQuery.find(3)
  891. assert q.editable_by?(admin)
  892. assert !q.editable_by?(manager)
  893. assert q.editable_by?(developer)
  894. # Public query for all projects
  895. q = IssueQuery.find(4)
  896. assert q.editable_by?(admin)
  897. assert !q.editable_by?(manager)
  898. assert !q.editable_by?(developer)
  899. end
  900. def test_visible_scope
  901. query_ids = IssueQuery.visible(User.anonymous).map(&:id)
  902. assert query_ids.include?(1), 'public query on public project was not visible'
  903. assert query_ids.include?(4), 'public query for all projects was not visible'
  904. assert !query_ids.include?(2), 'private query on public project was visible'
  905. assert !query_ids.include?(3), 'private query for all projects was visible'
  906. assert !query_ids.include?(7), 'public query on private project was visible'
  907. end
  908. test "#available_filters should include users of visible projects in cross-project view" do
  909. users = IssueQuery.new.available_filters["assigned_to_id"]
  910. assert_not_nil users
  911. assert users[:values].map{|u|u[1]}.include?("3")
  912. end
  913. test "#available_filters should include users of subprojects" do
  914. user1 = User.generate!
  915. user2 = User.generate!
  916. project = Project.find(1)
  917. Member.create!(:principal => user1, :project => project.children.visible.first, :role_ids => [1])
  918. users = IssueQuery.new(:project => project).available_filters["assigned_to_id"]
  919. assert_not_nil users
  920. assert users[:values].map{|u|u[1]}.include?(user1.id.to_s)
  921. assert !users[:values].map{|u|u[1]}.include?(user2.id.to_s)
  922. end
  923. test "#available_filters should include visible projects in cross-project view" do
  924. projects = IssueQuery.new.available_filters["project_id"]
  925. assert_not_nil projects
  926. assert projects[:values].map{|u|u[1]}.include?("1")
  927. end
  928. test "#available_filters should include 'member_of_group' filter" do
  929. query = IssueQuery.new
  930. assert query.available_filters.keys.include?("member_of_group")
  931. assert_equal :list_optional, query.available_filters["member_of_group"][:type]
  932. assert query.available_filters["member_of_group"][:values].present?
  933. assert_equal Group.all.sort.map {|g| [g.name, g.id.to_s]},
  934. query.available_filters["member_of_group"][:values].sort
  935. end
  936. test "#available_filters should include 'assigned_to_role' filter" do
  937. query = IssueQuery.new
  938. assert query.available_filters.keys.include?("assigned_to_role")
  939. assert_equal :list_optional, query.available_filters["assigned_to_role"][:type]
  940. assert query.available_filters["assigned_to_role"][:values].include?(['Manager','1'])
  941. assert query.available_filters["assigned_to_role"][:values].include?(['Developer','2'])
  942. assert query.available_filters["assigned_to_role"][:values].include?(['Reporter','3'])
  943. assert ! query.available_filters["assigned_to_role"][:values].include?(['Non member','4'])
  944. assert ! query.available_filters["assigned_to_role"][:values].include?(['Anonymous','5'])
  945. end
  946. context "#statement" do
  947. context "with 'member_of_group' filter" do
  948. setup do
  949. Group.destroy_all # No fixtures
  950. @user_in_group = User.generate!
  951. @second_user_in_group = User.generate!
  952. @user_in_group2 = User.generate!
  953. @user_not_in_group = User.generate!
  954. @group = Group.generate!.reload
  955. @group.users << @user_in_group
  956. @group.users << @second_user_in_group
  957. @group2 = Group.generate!.reload
  958. @group2.users << @user_in_group2
  959. end
  960. should "search assigned to for users in the group" do
  961. @query = IssueQuery.new(:name => '_')
  962. @query.add_filter('member_of_group', '=', [@group.id.to_s])
  963. assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@group.id}')"
  964. assert_find_issues_with_query_is_successful @query
  965. end
  966. should "search not assigned to any group member (none)" do
  967. @query = IssueQuery.new(:name => '_')
  968. @query.add_filter('member_of_group', '!*', [''])
  969. # Users not in a group
  970. assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IS NULL OR #{Issue.table_name}.assigned_to_id NOT IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}','#{@group.id}','#{@group2.id}')"
  971. assert_find_issues_with_query_is_successful @query
  972. end
  973. should "search assigned to any group member (all)" do
  974. @query = IssueQuery.new(:name => '_')
  975. @query.add_filter('member_of_group', '*', [''])
  976. # Only users in a group
  977. assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}','#{@group.id}','#{@group2.id}')"
  978. assert_find_issues_with_query_is_successful @query
  979. end
  980. should "return an empty set with = empty group" do
  981. @empty_group = Group.generate!
  982. @query = IssueQuery.new(:name => '_')
  983. @query.add_filter('member_of_group', '=', [@empty_group.id.to_s])
  984. assert_equal [], find_issues_with_query(@query)
  985. end
  986. should "return issues with ! empty group" do
  987. @empty_group = Group.generate!
  988. @query = IssueQuery.new(:name => '_')
  989. @query.add_filter('member_of_group', '!', [@empty_group.id.to_s])
  990. assert_find_issues_with_query_is_successful @query
  991. end
  992. end
  993. context "with 'assigned_to_role' filter" do
  994. setup do
  995. @manager_role = Role.find_by_name('Manager')
  996. @developer_role = Role.find_by_name('Developer')
  997. @project = Project.generate!
  998. @manager = User.generate!
  999. @developer = User.generate!
  1000. @boss = User.generate!
  1001. @guest = User.generate!
  1002. User.add_to_project(@manager, @project, @manager_role)
  1003. User.add_to_project(@developer, @project, @developer_role)
  1004. User.add_to_project(@boss, @project, [@manager_role, @developer_role])
  1005. @issue1 = Issue.generate!(:project => @project, :assigned_to_id => @manager.id)
  1006. @issue2 = Issue.generate!(:project => @project, :assigned_to_id => @developer.id)
  1007. @issue3 = Issue.generate!(:project => @project, :assigned_to_id => @boss.id)
  1008. @issue4 = Issue.generate!(:project => @project, :assigned_to_id => @guest.id)
  1009. @issue5 = Issue.generate!(:project => @project)
  1010. end
  1011. should "search assigned to for users with the Role" do
  1012. @query = IssueQuery.new(:name => '_', :project => @project)
  1013. @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
  1014. assert_query_result [@issue1, @issue3], @query
  1015. end
  1016. should "search assigned to for users with the Role on the issue project" do
  1017. other_project = Project.generate!
  1018. User.add_to_project(@developer, other_project, @manager_role)
  1019. @query = IssueQuery.new(:name => '_', :project => @project)
  1020. @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
  1021. assert_query_result [@issue1, @issue3], @query
  1022. end
  1023. should "return an empty set with empty role" do
  1024. @empty_role = Role.generate!
  1025. @query = IssueQuery.new(:name => '_', :project => @project)
  1026. @query.add_filter('assigned_to_role', '=', [@empty_role.id.to_s])
  1027. assert_query_result [], @query
  1028. end
  1029. should "search assigned to for users without the Role" do
  1030. @query = IssueQuery.new(:name => '_', :project => @project)
  1031. @query.add_filter('assigned_to_role', '!', [@manager_role.id.to_s])
  1032. assert_query_result [@issue2, @issue4, @issue5], @query
  1033. end
  1034. should "search assigned to for users not assigned to any Role (none)" do
  1035. @query = IssueQuery.new(:name => '_', :project => @project)
  1036. @query.add_filter('assigned_to_role', '!*', [''])
  1037. assert_query_result [@issue4, @issue5], @query
  1038. end
  1039. should "search assigned to for users assigned to any Role (all)" do
  1040. @query = IssueQuery.new(:name => '_', :project => @project)
  1041. @query.add_filter('assigned_to_role', '*', [''])
  1042. assert_query_result [@issue1, @issue2, @issue3], @query
  1043. end
  1044. should "return issues with ! empty role" do
  1045. @empty_role = Role.generate!
  1046. @query = IssueQuery.new(:name => '_', :project => @project)
  1047. @query.add_filter('assigned_to_role', '!', [@empty_role.id.to_s])
  1048. assert_query_result [@issue1, @issue2, @issue3, @issue4, @issue5], @query
  1049. end
  1050. end
  1051. end
  1052. end