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 96KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443
  1. # frozen_string_literal: true
  2. # Redmine - project management software
  3. # Copyright (C) 2006-2019 Jean-Philippe Lang
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License
  7. # as published by the Free Software Foundation; either version 2
  8. # of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. require File.expand_path('../../test_helper', __FILE__)
  19. class QueryTest < ActiveSupport::TestCase
  20. include Redmine::I18n
  21. fixtures :projects, :enabled_modules, :users, :members,
  22. :member_roles, :roles, :trackers, :issue_statuses,
  23. :issue_categories, :enumerations, :issues,
  24. :watchers, :custom_fields, :custom_values, :versions,
  25. :queries,
  26. :projects_trackers,
  27. :custom_fields_trackers,
  28. :workflows, :journals,
  29. :attachments, :time_entries
  30. INTEGER_KLASS = RUBY_VERSION >= "2.4" ? Integer : Fixnum
  31. def setup
  32. User.current = nil
  33. end
  34. def test_query_with_roles_visibility_should_validate_roles
  35. set_language_if_valid 'en'
  36. query = IssueQuery.new(:name => 'Query', :visibility => IssueQuery::VISIBILITY_ROLES)
  37. assert !query.save
  38. assert_include "Roles cannot be blank", query.errors.full_messages
  39. query.role_ids = [1, 2]
  40. assert query.save
  41. end
  42. def test_changing_roles_visibility_should_clear_roles
  43. query = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_ROLES, :role_ids => [1, 2])
  44. assert_equal 2, query.roles.count
  45. query.visibility = IssueQuery::VISIBILITY_PUBLIC
  46. query.save!
  47. assert_equal 0, query.roles.count
  48. end
  49. def test_available_filters_should_be_ordered
  50. set_language_if_valid 'en'
  51. query = IssueQuery.new
  52. assert_equal 0, query.available_filters.keys.index('status_id')
  53. expected_order = [
  54. "Status",
  55. "Project",
  56. "Tracker",
  57. "Priority"
  58. ]
  59. assert_equal expected_order,
  60. (query.available_filters.values.map{|v| v[:name]} & expected_order)
  61. end
  62. def test_available_filters_with_custom_fields_should_be_ordered
  63. set_language_if_valid 'en'
  64. UserCustomField.create!(
  65. :name => 'order test', :field_format => 'string',
  66. :is_for_all => true, :is_filter => true
  67. )
  68. query = IssueQuery.new
  69. expected_order = [
  70. "Searchable field",
  71. "Database",
  72. "Project's Development status",
  73. "Author's order test",
  74. "Assignee's order test"
  75. ]
  76. assert_equal expected_order,
  77. (query.available_filters.values.map{|v| v[:name]} & expected_order)
  78. end
  79. def test_custom_fields_for_all_projects_should_be_available_in_global_queries
  80. query = IssueQuery.new(:project => nil, :name => '_')
  81. assert query.available_filters.has_key?('cf_1')
  82. assert !query.available_filters.has_key?('cf_3')
  83. end
  84. def test_system_shared_versions_should_be_available_in_global_queries
  85. Version.find(2).update_attribute :sharing, 'system'
  86. query = IssueQuery.new(:project => nil, :name => '_')
  87. assert query.available_filters.has_key?('fixed_version_id')
  88. assert query.available_filters['fixed_version_id'][:values].detect {|v| v[1] == '2'}
  89. end
  90. def test_project_filter_in_global_queries
  91. query = IssueQuery.new(:project => nil, :name => '_')
  92. project_filter = query.available_filters["project_id"]
  93. assert_not_nil project_filter
  94. project_ids = project_filter[:values].map{|p| p[1]}
  95. assert project_ids.include?("1") # public project
  96. assert !project_ids.include?("2") # private project user cannot see
  97. end
  98. def test_available_filters_should_not_include_fields_disabled_on_all_trackers
  99. Tracker.all.each do |tracker|
  100. tracker.core_fields = Tracker::CORE_FIELDS - ['start_date']
  101. tracker.save!
  102. end
  103. query = IssueQuery.new(:name => '_')
  104. assert_include 'due_date', query.available_filters
  105. assert_not_include 'start_date', query.available_filters
  106. end
  107. def test_filter_values_without_project_should_be_arrays
  108. q = IssueQuery.new
  109. assert_nil q.project
  110. q.available_filters.each do |name, filter|
  111. values = filter.values
  112. assert (values.nil? || values.is_a?(Array)),
  113. "#values for #{name} filter returned a #{values.class.name}"
  114. end
  115. end
  116. def test_filter_values_with_project_should_be_arrays
  117. q = IssueQuery.new(:project => Project.find(1))
  118. assert_not_nil q.project
  119. q.available_filters.each do |name, filter|
  120. values = filter.values
  121. assert (values.nil? || values.is_a?(Array)),
  122. "#values for #{name} filter returned a #{values.class.name}"
  123. end
  124. end
  125. def find_issues_with_query(query)
  126. Issue.joins(:status, :tracker, :project, :priority).where(
  127. query.statement
  128. ).to_a
  129. end
  130. def assert_find_issues_with_query_is_successful(query)
  131. assert_nothing_raised do
  132. find_issues_with_query(query)
  133. end
  134. end
  135. def assert_query_statement_includes(query, condition)
  136. assert_include condition, query.statement
  137. end
  138. def assert_query_result(expected, query)
  139. assert_nothing_raised do
  140. assert_equal expected.map(&:id).sort, query.issues.map(&:id).sort
  141. assert_equal expected.size, query.issue_count
  142. end
  143. end
  144. def test_query_should_allow_shared_versions_for_a_project_query
  145. subproject_version = Version.find(4)
  146. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  147. filter = query.available_filters["fixed_version_id"]
  148. assert_not_nil filter
  149. assert_include subproject_version.id.to_s, filter[:values].map(&:second)
  150. end
  151. def test_query_with_multiple_custom_fields
  152. query = IssueQuery.find(1)
  153. assert query.valid?
  154. issues = find_issues_with_query(query)
  155. assert_equal 1, issues.length
  156. assert_equal Issue.find(3), issues.first
  157. end
  158. def test_operator_none
  159. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  160. query.add_filter('fixed_version_id', '!*', [''])
  161. query.add_filter('cf_1', '!*', [''])
  162. assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NULL")
  163. assert query.statement.include?("#{CustomValue.table_name}.value IS NULL OR #{CustomValue.table_name}.value = ''")
  164. find_issues_with_query(query)
  165. end
  166. def test_operator_none_for_integer
  167. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  168. query.add_filter('estimated_hours', '!*', [''])
  169. issues = find_issues_with_query(query)
  170. assert !issues.empty?
  171. assert issues.all? {|i| !i.estimated_hours}
  172. end
  173. def test_operator_none_for_date
  174. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  175. query.add_filter('start_date', '!*', [''])
  176. issues = find_issues_with_query(query)
  177. assert !issues.empty?
  178. assert issues.all? {|i| i.start_date.nil?}
  179. end
  180. def test_operator_none_for_string_custom_field
  181. CustomField.find(2).update_attribute :default_value, ""
  182. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  183. query.add_filter('cf_2', '!*', [''])
  184. assert query.has_filter?('cf_2')
  185. issues = find_issues_with_query(query)
  186. assert !issues.empty?
  187. assert issues.all? {|i| i.custom_field_value(2).blank?}
  188. end
  189. def test_operator_none_for_text
  190. query = IssueQuery.new(:name => '_')
  191. query.add_filter('status_id', '*', [''])
  192. query.add_filter('description', '!*', [''])
  193. assert query.has_filter?('description')
  194. issues = find_issues_with_query(query)
  195. assert issues.any?
  196. assert issues.all? {|i| i.description.blank?}
  197. assert_equal [11, 12], issues.map(&:id).sort
  198. end
  199. def test_operator_all
  200. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  201. query.add_filter('fixed_version_id', '*', [''])
  202. query.add_filter('cf_1', '*', [''])
  203. assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NOT NULL")
  204. assert query.statement.include?("#{CustomValue.table_name}.value IS NOT NULL AND #{CustomValue.table_name}.value <> ''")
  205. find_issues_with_query(query)
  206. end
  207. def test_operator_all_for_date
  208. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  209. query.add_filter('start_date', '*', [''])
  210. issues = find_issues_with_query(query)
  211. assert !issues.empty?
  212. assert issues.all? {|i| i.start_date.present?}
  213. end
  214. def test_operator_all_for_string_custom_field
  215. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  216. query.add_filter('cf_2', '*', [''])
  217. assert query.has_filter?('cf_2')
  218. issues = find_issues_with_query(query)
  219. assert !issues.empty?
  220. assert issues.all? {|i| i.custom_field_value(2).present?}
  221. end
  222. def test_numeric_filter_should_not_accept_non_numeric_values
  223. query = IssueQuery.new(:name => '_')
  224. query.add_filter('estimated_hours', '=', ['a'])
  225. assert query.has_filter?('estimated_hours')
  226. assert !query.valid?
  227. end
  228. def test_operator_is_on_float
  229. Issue.where(:id => 2).update_all("estimated_hours = 171.2")
  230. query = IssueQuery.new(:name => '_')
  231. query.add_filter('estimated_hours', '=', ['171.20'])
  232. issues = find_issues_with_query(query)
  233. assert_equal 1, issues.size
  234. assert_equal 2, issues.first.id
  235. end
  236. def test_operator_is_on_issue_id_should_accept_comma_separated_values
  237. query = IssueQuery.new(:name => '_')
  238. query.add_filter("issue_id", '=', ['1,3'])
  239. issues = find_issues_with_query(query)
  240. assert_equal 2, issues.size
  241. assert_equal [1,3], issues.map(&:id).sort
  242. end
  243. def test_operator_is_on_parent_id_should_accept_comma_separated_values
  244. Issue.where(:id => [2,4]).update_all(:parent_id => 1)
  245. Issue.where(:id => 5).update_all(:parent_id => 3)
  246. query = IssueQuery.new(:name => '_')
  247. query.add_filter("parent_id", '=', ['1,3'])
  248. issues = find_issues_with_query(query)
  249. assert_equal 3, issues.size
  250. assert_equal [2,4,5], issues.map(&:id).sort
  251. end
  252. def test_operator_is_on_child_id_should_accept_comma_separated_values
  253. Issue.where(:id => [2,4]).update_all(:parent_id => 1)
  254. Issue.where(:id => 5).update_all(:parent_id => 3)
  255. query = IssueQuery.new(:name => '_')
  256. query.add_filter("child_id", '=', ['2,4,5'])
  257. issues = find_issues_with_query(query)
  258. assert_equal 2, issues.size
  259. assert_equal [1,3], issues.map(&:id).sort
  260. end
  261. def test_operator_between_on_issue_id_should_return_range
  262. query = IssueQuery.new(:name => '_')
  263. query.add_filter("issue_id", '><', ['2','3'])
  264. issues = find_issues_with_query(query)
  265. assert_equal 2, issues.size
  266. assert_equal [2,3], issues.map(&:id).sort
  267. end
  268. def test_operator_is_on_integer_custom_field
  269. f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true, :trackers => Tracker.all)
  270. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
  271. CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
  272. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
  273. query = IssueQuery.new(:name => '_')
  274. query.add_filter("cf_#{f.id}", '=', ['12'])
  275. issues = find_issues_with_query(query)
  276. assert_equal 1, issues.size
  277. assert_equal 2, issues.first.id
  278. end
  279. def test_operator_is_on_integer_custom_field_should_accept_negative_value
  280. f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true, :trackers => Tracker.all)
  281. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
  282. CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12')
  283. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
  284. query = IssueQuery.new(:name => '_')
  285. query.add_filter("cf_#{f.id}", '=', ['-12'])
  286. assert query.valid?
  287. issues = find_issues_with_query(query)
  288. assert_equal 1, issues.size
  289. assert_equal 2, issues.first.id
  290. end
  291. def test_operator_is_on_float_custom_field
  292. f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true, :trackers => Tracker.all)
  293. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
  294. CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12.7')
  295. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
  296. query = IssueQuery.new(:name => '_')
  297. query.add_filter("cf_#{f.id}", '=', ['12.7'])
  298. issues = find_issues_with_query(query)
  299. assert_equal 1, issues.size
  300. assert_equal 2, issues.first.id
  301. end
  302. def test_operator_is_on_float_custom_field_should_accept_negative_value
  303. f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true, :trackers => Tracker.all)
  304. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
  305. CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12.7')
  306. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
  307. query = IssueQuery.new(:name => '_')
  308. query.add_filter("cf_#{f.id}", '=', ['-12.7'])
  309. assert query.valid?
  310. issues = find_issues_with_query(query)
  311. assert_equal 1, issues.size
  312. assert_equal 2, issues.first.id
  313. end
  314. def test_operator_is_on_multi_list_custom_field
  315. f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
  316. :possible_values => ['value1', 'value2', 'value3'], :multiple => true, :trackers => Tracker.all)
  317. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
  318. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
  319. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
  320. query = IssueQuery.new(:name => '_')
  321. query.add_filter("cf_#{f.id}", '=', ['value1'])
  322. issues = find_issues_with_query(query)
  323. assert_equal [1, 3], issues.map(&:id).sort
  324. query = IssueQuery.new(:name => '_')
  325. query.add_filter("cf_#{f.id}", '=', ['value2'])
  326. issues = find_issues_with_query(query)
  327. assert_equal [1], issues.map(&:id).sort
  328. end
  329. def test_operator_is_not_on_multi_list_custom_field
  330. f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
  331. :possible_values => ['value1', 'value2', 'value3'], :multiple => true, :trackers => Tracker.all)
  332. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
  333. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
  334. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
  335. query = IssueQuery.new(:name => '_')
  336. query.add_filter("cf_#{f.id}", '!', ['value1'])
  337. issues = find_issues_with_query(query)
  338. assert !issues.map(&:id).include?(1)
  339. assert !issues.map(&:id).include?(3)
  340. query = IssueQuery.new(:name => '_')
  341. query.add_filter("cf_#{f.id}", '!', ['value2'])
  342. issues = find_issues_with_query(query)
  343. assert !issues.map(&:id).include?(1)
  344. assert issues.map(&:id).include?(3)
  345. end
  346. def test_operator_is_on_string_custom_field_with_utf8_value
  347. f = IssueCustomField.create!(:name => 'filter', :field_format => 'string', :is_filter => true, :is_for_all => true, :trackers => Tracker.all)
  348. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'Kiểm')
  349. query = IssueQuery.new(:name => '_')
  350. query.add_filter("cf_#{f.id}", '=', ['Kiểm'])
  351. issues = find_issues_with_query(query)
  352. assert_equal [1], issues.map(&:id).sort
  353. end
  354. def test_operator_is_on_is_private_field
  355. # is_private filter only available for those who can set issues private
  356. User.current = User.find(2)
  357. query = IssueQuery.new(:name => '_')
  358. assert query.available_filters.key?('is_private')
  359. query.add_filter("is_private", '=', ['1'])
  360. issues = find_issues_with_query(query)
  361. assert issues.any?
  362. assert_nil issues.detect {|issue| !issue.is_private?}
  363. ensure
  364. User.current = nil
  365. end
  366. def test_operator_is_not_on_is_private_field
  367. # is_private filter only available for those who can set issues private
  368. User.current = User.find(2)
  369. query = IssueQuery.new(:name => '_')
  370. assert query.available_filters.key?('is_private')
  371. query.add_filter("is_private", '!', ['1'])
  372. issues = find_issues_with_query(query)
  373. assert issues.any?
  374. assert_nil issues.detect {|issue| issue.is_private?}
  375. ensure
  376. User.current = nil
  377. end
  378. def test_operator_greater_than
  379. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  380. query.add_filter('done_ratio', '>=', ['40'])
  381. assert query.statement.include?("#{Issue.table_name}.done_ratio >= 40.0")
  382. find_issues_with_query(query)
  383. end
  384. def test_operator_greater_than_a_float
  385. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  386. query.add_filter('estimated_hours', '>=', ['40.5'])
  387. assert query.statement.include?("#{Issue.table_name}.estimated_hours >= 40.5")
  388. find_issues_with_query(query)
  389. end
  390. def test_operator_greater_than_on_int_custom_field
  391. f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true, :trackers => Tracker.all)
  392. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
  393. CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
  394. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
  395. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  396. query.add_filter("cf_#{f.id}", '>=', ['8'])
  397. issues = find_issues_with_query(query)
  398. assert_equal 1, issues.size
  399. assert_equal 2, issues.first.id
  400. end
  401. def test_operator_lesser_than
  402. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  403. query.add_filter('done_ratio', '<=', ['30'])
  404. assert query.statement.include?("#{Issue.table_name}.done_ratio <= 30.0")
  405. find_issues_with_query(query)
  406. end
  407. def test_operator_lesser_than_on_custom_field
  408. f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
  409. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  410. query.add_filter("cf_#{f.id}", '<=', ['30'])
  411. assert_match /CAST.+ <= 30\.0/, query.statement
  412. find_issues_with_query(query)
  413. end
  414. def test_operator_lesser_than_on_date_custom_field
  415. f = IssueCustomField.create!(:name => 'filter', :field_format => 'date', :is_filter => true, :is_for_all => true, :trackers => Tracker.all)
  416. CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '2013-04-11')
  417. CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '2013-05-14')
  418. CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
  419. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  420. query.add_filter("cf_#{f.id}", '<=', ['2013-05-01'])
  421. issue_ids = find_issues_with_query(query).map(&:id)
  422. assert_include 1, issue_ids
  423. assert_not_include 2, issue_ids
  424. assert_not_include 3, issue_ids
  425. end
  426. def test_operator_between
  427. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  428. query.add_filter('done_ratio', '><', ['30', '40'])
  429. assert_include "#{Issue.table_name}.done_ratio BETWEEN 30.0 AND 40.0", query.statement
  430. find_issues_with_query(query)
  431. end
  432. def test_operator_between_on_custom_field
  433. f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
  434. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  435. query.add_filter("cf_#{f.id}", '><', ['30', '40'])
  436. assert_match /CAST.+ BETWEEN 30.0 AND 40.0/, query.statement
  437. find_issues_with_query(query)
  438. end
  439. def test_date_filter_should_not_accept_non_date_values
  440. query = IssueQuery.new(:name => '_')
  441. query.add_filter('created_on', '=', ['a'])
  442. assert query.has_filter?('created_on')
  443. assert !query.valid?
  444. end
  445. def test_date_filter_should_not_accept_invalid_date_values
  446. query = IssueQuery.new(:name => '_')
  447. query.add_filter('created_on', '=', ['2011-01-34'])
  448. assert query.has_filter?('created_on')
  449. assert !query.valid?
  450. end
  451. def test_relative_date_filter_should_not_accept_non_integer_values
  452. query = IssueQuery.new(:name => '_')
  453. query.add_filter('created_on', '>t-', ['a'])
  454. assert query.has_filter?('created_on')
  455. assert !query.valid?
  456. end
  457. def test_operator_date_equals
  458. query = IssueQuery.new(:name => '_')
  459. query.add_filter('due_date', '=', ['2011-07-10'])
  460. assert_match /issues\.due_date > '#{quoted_date "2011-07-09"} 23:59:59(\.\d+)?' AND issues\.due_date <= '#{quoted_date "2011-07-10"} 23:59:59(\.\d+)?/,
  461. query.statement
  462. find_issues_with_query(query)
  463. end
  464. def test_operator_date_lesser_than
  465. query = IssueQuery.new(:name => '_')
  466. query.add_filter('due_date', '<=', ['2011-07-10'])
  467. assert_match /issues\.due_date <= '#{quoted_date "2011-07-10"} 23:59:59(\.\d+)?/, query.statement
  468. find_issues_with_query(query)
  469. end
  470. def test_operator_date_lesser_than_with_timestamp
  471. query = IssueQuery.new(:name => '_')
  472. query.add_filter('updated_on', '<=', ['2011-07-10T19:13:52'])
  473. assert_match /issues\.updated_on <= '#{quoted_date "2011-07-10"} 19:13:52/, query.statement
  474. find_issues_with_query(query)
  475. end
  476. def test_operator_date_greater_than
  477. query = IssueQuery.new(:name => '_')
  478. query.add_filter('due_date', '>=', ['2011-07-10'])
  479. assert_match /issues\.due_date > '#{quoted_date "2011-07-09"} 23:59:59(\.\d+)?'/, query.statement
  480. find_issues_with_query(query)
  481. end
  482. def test_operator_date_greater_than_with_timestamp
  483. query = IssueQuery.new(:name => '_')
  484. query.add_filter('updated_on', '>=', ['2011-07-10T19:13:52'])
  485. assert_match /issues\.updated_on > '#{quoted_date "2011-07-10"} 19:13:51(\.0+)?'/, query.statement
  486. find_issues_with_query(query)
  487. end
  488. def test_operator_date_between
  489. query = IssueQuery.new(:name => '_')
  490. query.add_filter('due_date', '><', ['2011-06-23', '2011-07-10'])
  491. assert_match /issues\.due_date > '#{quoted_date "2011-06-22"} 23:59:59(\.\d+)?' AND issues\.due_date <= '#{quoted_date "2011-07-10"} 23:59:59(\.\d+)?'/,
  492. query.statement
  493. find_issues_with_query(query)
  494. end
  495. def test_operator_in_more_than
  496. Issue.find(7).update_attribute(:due_date, (Date.today + 15))
  497. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  498. query.add_filter('due_date', '>t+', ['15'])
  499. issues = find_issues_with_query(query)
  500. assert !issues.empty?
  501. issues.each {|issue| assert(issue.due_date >= (Date.today + 15))}
  502. end
  503. def test_operator_in_less_than
  504. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  505. query.add_filter('due_date', '<t+', ['15'])
  506. issues = find_issues_with_query(query)
  507. assert !issues.empty?
  508. issues.each {|issue| assert(issue.due_date <= (Date.today + 15))}
  509. end
  510. def test_operator_in_the_next_days
  511. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  512. query.add_filter('due_date', '><t+', ['15'])
  513. issues = find_issues_with_query(query)
  514. assert !issues.empty?
  515. issues.each {|issue| assert(issue.due_date >= Date.today && issue.due_date <= (Date.today + 15))}
  516. end
  517. def test_operator_less_than_ago
  518. Issue.find(7).update_attribute(:due_date, (Date.today - 3))
  519. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  520. query.add_filter('due_date', '>t-', ['3'])
  521. issues = find_issues_with_query(query)
  522. assert !issues.empty?
  523. issues.each {|issue| assert(issue.due_date >= (Date.today - 3))}
  524. end
  525. def test_operator_in_the_past_days
  526. Issue.find(7).update_attribute(:due_date, (Date.today - 3))
  527. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  528. query.add_filter('due_date', '><t-', ['3'])
  529. issues = find_issues_with_query(query)
  530. assert !issues.empty?
  531. issues.each {|issue| assert(issue.due_date >= (Date.today - 3) && issue.due_date <= Date.today)}
  532. end
  533. def test_operator_more_than_ago
  534. Issue.find(7).update_attribute(:due_date, (Date.today - 10))
  535. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  536. query.add_filter('due_date', '<t-', ['10'])
  537. assert query.statement.include?("#{Issue.table_name}.due_date <=")
  538. issues = find_issues_with_query(query)
  539. assert !issues.empty?
  540. issues.each {|issue| assert(issue.due_date <= (Date.today - 10))}
  541. end
  542. def test_operator_in
  543. Issue.find(7).update_attribute(:due_date, (Date.today + 2))
  544. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  545. query.add_filter('due_date', 't+', ['2'])
  546. issues = find_issues_with_query(query)
  547. assert !issues.empty?
  548. issues.each {|issue| assert_equal((Date.today + 2), issue.due_date)}
  549. end
  550. def test_operator_ago
  551. Issue.find(7).update_attribute(:due_date, (Date.today - 3))
  552. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  553. query.add_filter('due_date', 't-', ['3'])
  554. issues = find_issues_with_query(query)
  555. assert !issues.empty?
  556. issues.each {|issue| assert_equal((Date.today - 3), issue.due_date)}
  557. end
  558. def test_operator_today
  559. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  560. query.add_filter('due_date', 't', [''])
  561. issues = find_issues_with_query(query)
  562. assert !issues.empty?
  563. issues.each {|issue| assert_equal Date.today, issue.due_date}
  564. end
  565. def test_operator_tomorrow
  566. issue = Issue.generate!(:due_date => User.current.today.tomorrow)
  567. other_issues = []
  568. other_issues << Issue.generate!(:due_date => User.current.today.yesterday)
  569. other_issues << Issue.generate!(:due_date => User.current.today + 2)
  570. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  571. query.add_filter('due_date', 'nd', [''])
  572. issues = find_issues_with_query(query)
  573. assert_include issue, issues
  574. other_issues.each {|i| assert_not_include i, issues }
  575. end
  576. def test_operator_date_periods
  577. %w(t ld w lw l2w m lm y nd nw nm).each do |operator|
  578. query = IssueQuery.new(:name => '_')
  579. query.add_filter('due_date', operator, [''])
  580. assert query.valid?
  581. assert query.issues
  582. end
  583. end
  584. def test_operator_datetime_periods
  585. %w(t ld w lw l2w m lm y).each do |operator|
  586. query = IssueQuery.new(:name => '_')
  587. query.add_filter('created_on', operator, [''])
  588. assert query.valid?
  589. assert query.issues
  590. end
  591. end
  592. def test_operator_contains
  593. issue = Issue.generate!(:subject => 'AbCdEfG')
  594. query = IssueQuery.new(:name => '_')
  595. query.add_filter('subject', '~', ['cdeF'])
  596. result = find_issues_with_query(query)
  597. assert_include issue, result
  598. result.each {|issue| assert issue.subject.downcase.include?('cdef') }
  599. end
  600. def test_operator_contains_with_utf8_string
  601. issue = Issue.generate!(:subject => 'Subject contains Kiểm')
  602. query = IssueQuery.new(:name => '_')
  603. query.add_filter('subject', '~', ['Kiểm'])
  604. result = find_issues_with_query(query)
  605. assert_include issue, result
  606. assert_equal 1, result.size
  607. end
  608. def test_operator_does_not_contain
  609. issue = Issue.generate!(:subject => 'AbCdEfG')
  610. query = IssueQuery.new(:name => '_')
  611. query.add_filter('subject', '!~', ['cdeF'])
  612. result = find_issues_with_query(query)
  613. assert_not_include issue, result
  614. end
  615. def test_range_for_this_week_with_week_starting_on_monday
  616. I18n.locale = :fr
  617. assert_equal '1', I18n.t(:general_first_day_of_week)
  618. Date.stubs(:today).returns(Date.parse('2011-04-29'))
  619. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  620. query.add_filter('due_date', 'w', [''])
  621. assert_match /issues\.due_date > '#{quoted_date "2011-04-24"} 23:59:59(\.\d+)?' AND issues\.due_date <= '#{quoted_date "2011-05-01"} 23:59:59(\.\d+)?/,
  622. query.statement
  623. I18n.locale = :en
  624. end
  625. def test_range_for_this_week_with_week_starting_on_sunday
  626. I18n.locale = :en
  627. assert_equal '7', I18n.t(:general_first_day_of_week)
  628. Date.stubs(:today).returns(Date.parse('2011-04-29'))
  629. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  630. query.add_filter('due_date', 'w', [''])
  631. assert_match /issues\.due_date > '#{quoted_date "2011-04-23"} 23:59:59(\.\d+)?' AND issues\.due_date <= '#{quoted_date "2011-04-30"} 23:59:59(\.\d+)?/,
  632. query.statement
  633. end
  634. def test_range_for_next_week_with_week_starting_on_monday
  635. I18n.locale = :fr
  636. assert_equal '1', I18n.t(:general_first_day_of_week)
  637. Date.stubs(:today).returns(Date.parse('2011-04-29')) # Friday
  638. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  639. query.add_filter('due_date', 'nw', [''])
  640. assert_match /issues\.due_date > '#{quoted_date "2011-05-01"} 23:59:59(\.\d+)?' AND issues\.due_date <= '#{quoted_date "2011-05-08"} 23:59:59(\.\d+)?/,
  641. query.statement
  642. I18n.locale = :en
  643. end
  644. def test_range_for_next_week_with_week_starting_on_sunday
  645. I18n.locale = :en
  646. assert_equal '7', I18n.t(:general_first_day_of_week)
  647. Date.stubs(:today).returns(Date.parse('2011-04-29')) # Friday
  648. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  649. query.add_filter('due_date', 'nw', [''])
  650. assert_match /issues\.due_date > '#{quoted_date "2011-04-30"} 23:59:59(\.\d+)?' AND issues\.due_date <= '#{quoted_date "2011-05-07"} 23:59:59(\.\d+)?/,
  651. query.statement
  652. end
  653. def test_range_for_next_month
  654. Date.stubs(:today).returns(Date.parse('2011-04-29')) # Friday
  655. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  656. query.add_filter('due_date', 'nm', [''])
  657. assert_match /issues\.due_date > '#{quoted_date "2011-04-30"} 23:59:59(\.\d+)?' AND issues\.due_date <= '#{quoted_date "2011-05-31"} 23:59:59(\.\d+)?/,
  658. query.statement
  659. end
  660. def test_filter_assigned_to_me
  661. user = User.find(2)
  662. group = Group.find(10)
  663. group.users << user
  664. other_group = Group.find(11)
  665. Member.create!(:project_id => 1, :principal => group, :role_ids => [1])
  666. Member.create!(:project_id => 1, :principal => other_group, :role_ids => [1])
  667. User.current = user
  668. with_settings :issue_group_assignment => '1' do
  669. i1 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => user)
  670. i2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => group)
  671. i3 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => other_group)
  672. query = IssueQuery.new(:name => '_', :filters => { 'assigned_to_id' => {:operator => '=', :values => ['me']}})
  673. result = query.issues
  674. assert_equal Issue.visible.where(:assigned_to_id => ([2] + user.reload.group_ids)).sort_by(&:id), result.sort_by(&:id)
  675. assert result.include?(i1)
  676. assert result.include?(i2)
  677. assert !result.include?(i3)
  678. end
  679. end
  680. def test_filter_updated_by
  681. user = User.generate!
  682. Journal.create!(:user_id => user.id, :journalized => Issue.find(2), :notes => 'Notes')
  683. Journal.create!(:user_id => user.id, :journalized => Issue.find(3), :notes => 'Notes')
  684. Journal.create!(:user_id => 2, :journalized => Issue.find(3), :notes => 'Notes')
  685. query = IssueQuery.new(:name => '_')
  686. filter_name = "updated_by"
  687. assert_include filter_name, query.available_filters.keys
  688. query.filters = {filter_name => {:operator => '=', :values => [user.id]}}
  689. assert_equal [2, 3], find_issues_with_query(query).map(&:id).sort
  690. query.filters = {filter_name => {:operator => '!', :values => [user.id]}}
  691. assert_equal (Issue.ids.sort - [2, 3]), find_issues_with_query(query).map(&:id).sort
  692. end
  693. def test_filter_updated_by_should_ignore_private_notes_that_are_not_visible
  694. user = User.generate!
  695. Journal.create!(:user_id => user.id, :journalized => Issue.find(2), :notes => 'Notes', :private_notes => true)
  696. Journal.create!(:user_id => user.id, :journalized => Issue.find(3), :notes => 'Notes')
  697. query = IssueQuery.new(:name => '_')
  698. filter_name = "updated_by"
  699. assert_include filter_name, query.available_filters.keys
  700. with_current_user User.anonymous do
  701. query.filters = {filter_name => {:operator => '=', :values => [user.id]}}
  702. assert_equal [3], find_issues_with_query(query).map(&:id).sort
  703. end
  704. end
  705. def test_filter_updated_by_me
  706. user = User.generate!
  707. Journal.create!(:user_id => user.id, :journalized => Issue.find(2), :notes => 'Notes')
  708. with_current_user user do
  709. query = IssueQuery.new(:name => '_')
  710. filter_name = "updated_by"
  711. assert_include filter_name, query.available_filters.keys
  712. query.filters = {filter_name => {:operator => '=', :values => ['me']}}
  713. assert_equal [2], find_issues_with_query(query).map(&:id).sort
  714. end
  715. end
  716. def test_filter_last_updated_by
  717. user = User.generate!
  718. Journal.create!(:user_id => user.id, :journalized => Issue.find(2), :notes => 'Notes')
  719. Journal.create!(:user_id => user.id, :journalized => Issue.find(3), :notes => 'Notes')
  720. Journal.create!(:user_id => 2, :journalized => Issue.find(3), :notes => 'Notes')
  721. query = IssueQuery.new(:name => '_')
  722. filter_name = "last_updated_by"
  723. assert_include filter_name, query.available_filters.keys
  724. query.filters = {filter_name => {:operator => '=', :values => [user.id]}}
  725. assert_equal [2], find_issues_with_query(query).map(&:id).sort
  726. end
  727. def test_filter_last_updated_by_should_ignore_private_notes_that_are_not_visible
  728. user1 = User.generate!
  729. user2 = User.generate!
  730. Journal.create!(:user_id => user1.id, :journalized => Issue.find(2), :notes => 'Notes')
  731. Journal.create!(:user_id => user2.id, :journalized => Issue.find(2), :notes => 'Notes', :private_notes => true)
  732. query = IssueQuery.new(:name => '_')
  733. filter_name = "last_updated_by"
  734. assert_include filter_name, query.available_filters.keys
  735. with_current_user User.anonymous do
  736. query.filters = {filter_name => {:operator => '=', :values => [user1.id]}}
  737. assert_equal [2], find_issues_with_query(query).map(&:id).sort
  738. query.filters = {filter_name => {:operator => '=', :values => [user2.id]}}
  739. assert_equal [], find_issues_with_query(query).map(&:id).sort
  740. end
  741. with_current_user User.find(2) do
  742. query.filters = {filter_name => {:operator => '=', :values => [user1.id]}}
  743. assert_equal [], find_issues_with_query(query).map(&:id).sort
  744. query.filters = {filter_name => {:operator => '=', :values => [user2.id]}}
  745. assert_equal [2], find_issues_with_query(query).map(&:id).sort
  746. end
  747. end
  748. def test_user_custom_field_filtered_on_me
  749. User.current = User.find(2)
  750. cf = IssueCustomField.create!(:field_format => 'user', :is_for_all => true, :is_filter => true, :name => 'User custom field', :tracker_ids => [1])
  751. issue1 = Issue.create!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '2'}, :subject => 'Test', :author_id => 1)
  752. issue2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '3'})
  753. query = IssueQuery.new(:name => '_', :project => Project.find(1))
  754. filter = query.available_filters["cf_#{cf.id}"]
  755. assert_not_nil filter
  756. assert_include 'me', filter[:values].map{|v| v[1]}
  757. query.filters = { "cf_#{cf.id}" => {:operator => '=', :values => ['me']}}
  758. result = query.issues
  759. assert_equal 1, result.size
  760. assert_equal issue1, result.first
  761. end
  762. def test_filter_on_me_by_anonymous_user
  763. User.current = nil
  764. query = IssueQuery.new(:name => '_', :filters => { 'assigned_to_id' => {:operator => '=', :values => ['me']}})
  765. assert_equal [], query.issues
  766. end
  767. def test_filter_my_projects
  768. User.current = User.find(2)
  769. query = IssueQuery.new(:name => '_')
  770. filter = query.available_filters['project_id']
  771. assert_not_nil filter
  772. assert_include 'mine', filter[:values].map{|v| v[1]}
  773. query.filters = { 'project_id' => {:operator => '=', :values => ['mine']}}
  774. result = query.issues
  775. assert_nil result.detect {|issue| !User.current.member_of?(issue.project)}
  776. end
  777. def test_filter_my_bookmarks
  778. User.current = User.find(1)
  779. query = ProjectQuery.new(:name => '_')
  780. filter = query.available_filters['id']
  781. assert_not_nil filter
  782. assert_include 'bookmarks', filter[:values].map{|v| v[1]}
  783. query.filters = { 'id' => {:operator => '=', :values => ['bookmarks']}}
  784. result = query.results_scope
  785. assert_equal [1,5], result.map(&:id).sort
  786. end
  787. def test_filter_my_bookmarks_for_user_without_bookmarked_projects
  788. User.current = User.find(2)
  789. query = ProjectQuery.new(:name => '_')
  790. filter = query.available_filters['id']
  791. assert_not_include 'bookmarks', filter[:values].map{|v| v[1]}
  792. end
  793. def test_filter_watched_issues
  794. User.current = User.find(1)
  795. query = IssueQuery.new(:name => '_', :filters => { 'watcher_id' => {:operator => '=', :values => ['me']}})
  796. result = find_issues_with_query(query)
  797. assert_not_nil result
  798. assert !result.empty?
  799. assert_equal Issue.visible.watched_by(User.current).sort_by(&:id), result.sort_by(&:id)
  800. User.current = nil
  801. end
  802. def test_filter_unwatched_issues
  803. User.current = User.find(1)
  804. query = IssueQuery.new(:name => '_', :filters => { 'watcher_id' => {:operator => '!', :values => ['me']}})
  805. result = find_issues_with_query(query)
  806. assert_not_nil result
  807. assert !result.empty?
  808. assert_equal((Issue.visible - Issue.watched_by(User.current)).sort_by(&:id).size, result.sort_by(&:id).size)
  809. User.current = nil
  810. end
  811. def test_filter_on_watched_issues_with_view_issue_watchers_permission
  812. User.current = User.find(1)
  813. User.current.admin = true
  814. assert User.current.allowed_to?(:view_issue_watchers, Project.find(1))
  815. Issue.find(1).add_watcher User.current
  816. Issue.find(3).add_watcher User.find(3)
  817. query = IssueQuery.new(:name => '_', :filters => { 'watcher_id' => {:operator => '=', :values => ['me', '3']}})
  818. result = find_issues_with_query(query)
  819. assert_includes result, Issue.find(1)
  820. assert_includes result, Issue.find(3)
  821. ensure
  822. User.current.reload
  823. User.current = nil
  824. end
  825. def test_filter_on_watched_issues_without_view_issue_watchers_permission
  826. User.current = User.find(1)
  827. User.current.admin = false
  828. assert !User.current.allowed_to?(:view_issue_watchers, Project.find(1))
  829. Issue.find(1).add_watcher User.current
  830. Issue.find(3).add_watcher User.find(3)
  831. query = IssueQuery.new(:name => '_', :filters => { 'watcher_id' => {:operator => '=', :values => ['me', '3']}})
  832. result = find_issues_with_query(query)
  833. assert_includes result, Issue.find(1)
  834. assert_not_includes result, Issue.find(3)
  835. ensure
  836. User.current.reload
  837. User.current = nil
  838. end
  839. def test_filter_on_custom_field_should_ignore_projects_with_field_disabled
  840. field = IssueCustomField.generate!(:trackers => Tracker.all, :project_ids => [1, 3, 4], :is_for_all => false, :is_filter => true)
  841. Issue.generate!(:project_id => 3, :tracker_id => 2, :custom_field_values => {field.id.to_s => 'Foo'})
  842. Issue.generate!(:project_id => 4, :tracker_id => 2, :custom_field_values => {field.id.to_s => 'Foo'})
  843. query = IssueQuery.new(:name => '_', :project => Project.find(1))
  844. query.filters = {"cf_#{field.id}" => {:operator => '=', :values => ['Foo']}}
  845. assert_equal 2, find_issues_with_query(query).size
  846. field.project_ids = [1, 3] # Disable the field for project 4
  847. field.save!
  848. assert_equal 1, find_issues_with_query(query).size
  849. end
  850. def test_filter_on_custom_field_should_ignore_trackers_with_field_disabled
  851. field = IssueCustomField.generate!(:tracker_ids => [1, 2], :is_for_all => true, :is_filter => true)
  852. Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => 'Foo'})
  853. Issue.generate!(:project_id => 1, :tracker_id => 2, :custom_field_values => {field.id.to_s => 'Foo'})
  854. query = IssueQuery.new(:name => '_', :project => Project.find(1))
  855. query.filters = {"cf_#{field.id}" => {:operator => '=', :values => ['Foo']}}
  856. assert_equal 2, find_issues_with_query(query).size
  857. field.tracker_ids = [1] # Disable the field for tracker 2
  858. field.save!
  859. assert_equal 1, find_issues_with_query(query).size
  860. end
  861. def test_filter_on_project_custom_field
  862. field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
  863. CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
  864. CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
  865. query = IssueQuery.new(:name => '_')
  866. filter_name = "project.cf_#{field.id}"
  867. assert_include filter_name, query.available_filters.keys
  868. query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
  869. assert_equal [3, 5], find_issues_with_query(query).map(&:project_id).uniq.sort
  870. end
  871. def test_filter_on_author_custom_field
  872. field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
  873. CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
  874. query = IssueQuery.new(:name => '_')
  875. filter_name = "author.cf_#{field.id}"
  876. assert_include filter_name, query.available_filters.keys
  877. query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
  878. assert_equal [3], find_issues_with_query(query).map(&:author_id).uniq.sort
  879. end
  880. def test_filter_on_assigned_to_custom_field
  881. field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
  882. CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
  883. query = IssueQuery.new(:name => '_')
  884. filter_name = "assigned_to.cf_#{field.id}"
  885. assert_include filter_name, query.available_filters.keys
  886. query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
  887. assert_equal [3], find_issues_with_query(query).map(&:assigned_to_id).uniq.sort
  888. end
  889. def test_filter_on_fixed_version_custom_field
  890. field = VersionCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
  891. CustomValue.create!(:custom_field => field, :customized => Version.find(2), :value => 'Foo')
  892. query = IssueQuery.new(:name => '_')
  893. filter_name = "fixed_version.cf_#{field.id}"
  894. assert_include filter_name, query.available_filters.keys
  895. query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
  896. assert_equal [2], find_issues_with_query(query).map(&:fixed_version_id).uniq.sort
  897. end
  898. def test_filter_on_fixed_version_due_date
  899. query = IssueQuery.new(:name => '_')
  900. filter_name = "fixed_version.due_date"
  901. assert_include filter_name, query.available_filters.keys
  902. query.filters = {filter_name => {:operator => '=', :values => [20.day.from_now.to_date.to_s(:db)]}}
  903. issues = find_issues_with_query(query)
  904. assert_equal [2], issues.map(&:fixed_version_id).uniq.sort
  905. assert_equal [2, 12], issues.map(&:id).sort
  906. query = IssueQuery.new(:name => '_')
  907. query.filters = {filter_name => {:operator => '>=', :values => [21.day.from_now.to_date.to_s(:db)]}}
  908. assert_equal 0, find_issues_with_query(query).size
  909. end
  910. def test_filter_on_fixed_version_status
  911. query = IssueQuery.new(:name => '_')
  912. filter_name = "fixed_version.status"
  913. assert_include filter_name, query.available_filters.keys
  914. query.filters = {filter_name => {:operator => '=', :values => ['closed']}}
  915. issues = find_issues_with_query(query)
  916. assert_equal [1], issues.map(&:fixed_version_id).sort
  917. assert_equal [11], issues.map(&:id).sort
  918. # "is not" operator should include issues without target version
  919. query = IssueQuery.new(:name => '_')
  920. query.filters = {filter_name => {:operator => '!', :values => ['open', 'closed', 'locked']}, "project_id" => {:operator => '=', :values => [1]}}
  921. assert_equal [1, 3, 7, 8], find_issues_with_query(query).map(&:id).uniq.sort
  922. end
  923. def test_filter_on_version_custom_field
  924. field = IssueCustomField.generate!(:field_format => 'version', :is_filter => true)
  925. issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => '2'})
  926. query = IssueQuery.new(:name => '_')
  927. filter_name = "cf_#{field.id}"
  928. assert_include filter_name, query.available_filters.keys
  929. query.filters = {filter_name => {:operator => '=', :values => ['2']}}
  930. issues = find_issues_with_query(query)
  931. assert_equal [issue.id], issues.map(&:id).sort
  932. end
  933. def test_filter_on_attribute_of_version_custom_field
  934. field = IssueCustomField.generate!(:field_format => 'version', :is_filter => true)
  935. version = Version.generate!(:effective_date => '2017-01-14')
  936. issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => version.id.to_s})
  937. query = IssueQuery.new(:name => '_')
  938. filter_name = "cf_#{field.id}.due_date"
  939. assert_include filter_name, query.available_filters.keys
  940. query.filters = {filter_name => {:operator => '=', :values => ['2017-01-14']}}
  941. issues = find_issues_with_query(query)
  942. assert_equal [issue.id], issues.map(&:id).sort
  943. end
  944. def test_filter_on_custom_field_of_version_custom_field
  945. field = IssueCustomField.generate!(:field_format => 'version', :is_filter => true)
  946. attr = VersionCustomField.generate!(:field_format => 'string', :is_filter => true)
  947. version = Version.generate!(:custom_field_values => {attr.id.to_s => 'ABC'})
  948. issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => version.id.to_s})
  949. query = IssueQuery.new(:name => '_')
  950. filter_name = "cf_#{field.id}.cf_#{attr.id}"
  951. assert_include filter_name, query.available_filters.keys
  952. query.filters = {filter_name => {:operator => '=', :values => ['ABC']}}
  953. issues = find_issues_with_query(query)
  954. assert_equal [issue.id], issues.map(&:id).sort
  955. end
  956. def test_filter_on_relations_with_a_specific_issue
  957. IssueRelation.delete_all
  958. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
  959. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
  960. query = IssueQuery.new(:name => '_')
  961. query.filters = {"relates" => {:operator => '=', :values => ['1']}}
  962. assert_equal [2, 3], find_issues_with_query(query).map(&:id).sort
  963. query = IssueQuery.new(:name => '_')
  964. query.filters = {"relates" => {:operator => '=', :values => ['2']}}
  965. assert_equal [1], find_issues_with_query(query).map(&:id).sort
  966. end
  967. def test_filter_on_relations_with_any_issues_in_a_project
  968. IssueRelation.delete_all
  969. with_settings :cross_project_issue_relations => '1' do
  970. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
  971. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(2).issues.first)
  972. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
  973. end
  974. query = IssueQuery.new(:name => '_')
  975. query.filters = {"relates" => {:operator => '=p', :values => ['2']}}
  976. assert_equal [1, 2], find_issues_with_query(query).map(&:id).sort
  977. query = IssueQuery.new(:name => '_')
  978. query.filters = {"relates" => {:operator => '=p', :values => ['3']}}
  979. assert_equal [1], find_issues_with_query(query).map(&:id).sort
  980. query = IssueQuery.new(:name => '_')
  981. query.filters = {"relates" => {:operator => '=p', :values => ['4']}}
  982. assert_equal [], find_issues_with_query(query).map(&:id).sort
  983. end
  984. def test_filter_on_relations_with_any_issues_not_in_a_project
  985. IssueRelation.delete_all
  986. with_settings :cross_project_issue_relations => '1' do
  987. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
  988. # IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(1).issues.first)
  989. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
  990. end
  991. query = IssueQuery.new(:name => '_')
  992. query.filters = {"relates" => {:operator => '=!p', :values => ['1']}}
  993. assert_equal [1], find_issues_with_query(query).map(&:id).sort
  994. end
  995. def test_filter_on_relations_with_no_issues_in_a_project
  996. IssueRelation.delete_all
  997. with_settings :cross_project_issue_relations => '1' do
  998. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
  999. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(3).issues.first)
  1000. IssueRelation.create!(:relation_type => "relates", :issue_to => Project.find(2).issues.first, :issue_from => Issue.find(3))
  1001. end
  1002. query = IssueQuery.new(:name => '_')
  1003. query.filters = {"relates" => {:operator => '!p', :values => ['2']}}
  1004. ids = find_issues_with_query(query).map(&:id).sort
  1005. assert_include 2, ids
  1006. assert_not_include 1, ids
  1007. assert_not_include 3, ids
  1008. end
  1009. def test_filter_on_relations_with_any_open_issues
  1010. IssueRelation.delete_all
  1011. # Issue 1 is blocked by 8, which is closed
  1012. IssueRelation.create!(:relation_type => "blocked", :issue_from => Issue.find(1), :issue_to => Issue.find(8))
  1013. # Issue 2 is blocked by 3, which is open
  1014. IssueRelation.create!(:relation_type => "blocked", :issue_from => Issue.find(2), :issue_to => Issue.find(3))
  1015. query = IssueQuery.new(:name => '_')
  1016. query.filters = {"blocked" => {:operator => "*o", :values => ['']}}
  1017. ids = find_issues_with_query(query).map(&:id)
  1018. assert_equal [], ids & [1]
  1019. assert_include 2, ids
  1020. end
  1021. def test_filter_on_blocked_by_no_open_issues
  1022. IssueRelation.delete_all
  1023. # Issue 1 is blocked by 8, which is closed
  1024. IssueRelation.create!(:relation_type => "blocked", :issue_from => Issue.find(1), :issue_to => Issue.find(8))
  1025. # Issue 2 is blocked by 3, which is open
  1026. IssueRelation.create!(:relation_type => "blocked", :issue_from => Issue.find(2), :issue_to => Issue.find(3))
  1027. query = IssueQuery.new(:name => '_')
  1028. query.filters = {"blocked" => {:operator => "!o", :values => ['']}}
  1029. ids = find_issues_with_query(query).map(&:id)
  1030. assert_equal [], ids & [2]
  1031. assert_include 1, ids
  1032. end
  1033. def test_filter_on_related_with_no_open_issues
  1034. IssueRelation.delete_all
  1035. # Issue 1 is blocked by 8, which is closed
  1036. IssueRelation.create!(relation_type: 'relates', issue_from: Issue.find(1), issue_to: Issue.find(8))
  1037. # Issue 2 is blocked by 3, which is open
  1038. IssueRelation.create!(relation_type: 'relates', issue_from: Issue.find(2), issue_to: Issue.find(3))
  1039. query = IssueQuery.new(:name => '_')
  1040. query.filters = { 'relates' => { operator: '!o', values: [''] } }
  1041. ids = find_issues_with_query(query).map(&:id)
  1042. assert_equal [], ids & [2]
  1043. assert_include 1, ids
  1044. end
  1045. def test_filter_on_relations_with_no_issues
  1046. IssueRelation.delete_all
  1047. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
  1048. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
  1049. query = IssueQuery.new(:name => '_')
  1050. query.filters = {"relates" => {:operator => '!*', :values => ['']}}
  1051. ids = find_issues_with_query(query).map(&:id)
  1052. assert_equal [], ids & [1, 2, 3]
  1053. assert_include 4, ids
  1054. end
  1055. def test_filter_on_relations_with_any_issues
  1056. IssueRelation.delete_all
  1057. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
  1058. IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
  1059. query = IssueQuery.new(:name => '_')
  1060. query.filters = {"relates" => {:operator => '*', :values => ['']}}
  1061. assert_equal [1, 2, 3], find_issues_with_query(query).map(&:id).sort
  1062. end
  1063. def test_filter_on_relations_should_not_ignore_other_filter
  1064. issue = Issue.generate!
  1065. issue1 = Issue.generate!(:status_id => 1)
  1066. issue2 = Issue.generate!(:status_id => 2)
  1067. IssueRelation.create!(:relation_type => "relates", :issue_from => issue, :issue_to => issue1)
  1068. IssueRelation.create!(:relation_type => "relates", :issue_from => issue, :issue_to => issue2)
  1069. query = IssueQuery.new(:name => '_')
  1070. query.filters = {
  1071. "status_id" => {:operator => '=', :values => ['1']},
  1072. "relates" => {:operator => '=', :values => [issue.id.to_s]}
  1073. }
  1074. assert_equal [issue1], find_issues_with_query(query)
  1075. end
  1076. def test_filter_on_parent
  1077. Issue.delete_all
  1078. parent = Issue.generate_with_descendants!
  1079. query = IssueQuery.new(:name => '_')
  1080. query.filters = {"parent_id" => {:operator => '=', :values => [parent.id.to_s]}}
  1081. assert_equal parent.children.map(&:id).sort, find_issues_with_query(query).map(&:id).sort
  1082. query.filters = {"parent_id" => {:operator => '~', :values => [parent.id.to_s]}}
  1083. assert_equal parent.descendants.map(&:id).sort, find_issues_with_query(query).map(&:id).sort
  1084. query.filters = {"parent_id" => {:operator => '*', :values => ['']}}
  1085. assert_equal parent.descendants.map(&:id).sort, find_issues_with_query(query).map(&:id).sort
  1086. query.filters = {"parent_id" => {:operator => '!*', :values => ['']}}
  1087. assert_equal [parent.id], find_issues_with_query(query).map(&:id).sort
  1088. end
  1089. def test_filter_on_invalid_parent_should_return_no_results
  1090. query = IssueQuery.new(:name => '_')
  1091. query.filters = {"parent_id" => {:operator => '=', :values => '99999999999'}}
  1092. assert_equal [], find_issues_with_query(query).map(&:id).sort
  1093. query.filters = {"parent_id" => {:operator => '~', :values => '99999999999'}}
  1094. assert_equal [], find_issues_with_query(query)
  1095. end
  1096. def test_filter_on_child
  1097. Issue.delete_all
  1098. parent = Issue.generate_with_descendants!
  1099. child, leaf = parent.children.sort_by(&:id)
  1100. grandchild = child.children.first
  1101. query = IssueQuery.new(:name => '_')
  1102. query.filters = {"child_id" => {:operator => '=', :values => [grandchild.id.to_s]}}
  1103. assert_equal [child.id], find_issues_with_query(query).map(&:id).sort
  1104. query.filters = {"child_id" => {:operator => '~', :values => [grandchild.id.to_s]}}
  1105. assert_equal [parent, child].map(&:id).sort, find_issues_with_query(query).map(&:id).sort
  1106. query.filters = {"child_id" => {:operator => '*', :values => ['']}}
  1107. assert_equal [parent, child].map(&:id).sort, find_issues_with_query(query).map(&:id).sort
  1108. query.filters = {"child_id" => {:operator => '!*', :values => ['']}}
  1109. assert_equal [grandchild, leaf].map(&:id).sort, find_issues_with_query(query).map(&:id).sort
  1110. end
  1111. def test_filter_on_invalid_child_should_return_no_results
  1112. query = IssueQuery.new(:name => '_')
  1113. query.filters = {"child_id" => {:operator => '=', :values => '99999999999'}}
  1114. assert_equal [], find_issues_with_query(query)
  1115. query.filters = {"child_id" => {:operator => '~', :values => '99999999999'}}
  1116. assert_equal [].map(&:id).sort, find_issues_with_query(query)
  1117. end
  1118. def test_filter_on_attachment_any
  1119. query = IssueQuery.new(:name => '_')
  1120. query.filters = {"attachment" => {:operator => '*', :values => ['']}}
  1121. issues = find_issues_with_query(query)
  1122. assert issues.any?
  1123. assert_nil issues.detect {|issue| issue.attachments.empty?}
  1124. end
  1125. def test_filter_on_attachment_none
  1126. query = IssueQuery.new(:name => '_')
  1127. query.filters = {"attachment" => {:operator => '!*', :values => ['']}}
  1128. issues = find_issues_with_query(query)
  1129. assert issues.any?
  1130. assert_nil issues.detect {|issue| issue.attachments.any?}
  1131. end
  1132. def test_filter_on_attachment_contains
  1133. query = IssueQuery.new(:name => '_')
  1134. query.filters = {"attachment" => {:operator => '~', :values => ['error281']}}
  1135. issues = find_issues_with_query(query)
  1136. assert issues.any?
  1137. assert_nil issues.detect {|issue| ! issue.attachments.any? {|attachment| attachment.filename.include?('error281')}}
  1138. end
  1139. def test_filter_on_attachment_not_contains
  1140. query = IssueQuery.new(:name => '_')
  1141. query.filters = {"attachment" => {:operator => '!~', :values => ['error281']}}
  1142. issues = find_issues_with_query(query)
  1143. assert issues.any?
  1144. assert_nil issues.detect {|issue| issue.attachments.any? {|attachment| attachment.filename.include?('error281')}}
  1145. end
  1146. def test_filter_on_attachment_when_starts_with
  1147. query = IssueQuery.new(:name => '_')
  1148. query.filters = {"attachment" => {:operator => '^', :values => ['testfile']}}
  1149. issues = find_issues_with_query(query)
  1150. assert_equal [14], issues.collect(&:id).sort
  1151. end
  1152. def test_filter_on_attachment_when_ends_with
  1153. query = IssueQuery.new(:name => '_')
  1154. query.filters = {"attachment" => {:operator => '$', :values => ['zip']}}
  1155. issues = find_issues_with_query(query)
  1156. assert_equal [3, 4], issues.collect(&:id).sort
  1157. end
  1158. def test_filter_on_subject_when_starts_with
  1159. query = IssueQuery.new(:name => '_')
  1160. query.filters = {'subject' => {:operator => '^', :values => ['issue']}}
  1161. issues = find_issues_with_query(query)
  1162. assert_equal [4, 6, 7, 10], issues.collect(&:id).sort
  1163. end
  1164. def test_filter_on_subject_when_ends_with
  1165. query = IssueQuery.new(:name => '_')
  1166. query.filters = {'subject' => {:operator => '$', :values => ['issue']}}
  1167. issues = find_issues_with_query(query)
  1168. assert_equal [5, 8, 9], issues.collect(&:id).sort
  1169. end
  1170. def test_statement_should_be_nil_with_no_filters
  1171. q = IssueQuery.new(:name => '_')
  1172. q.filters = {}
  1173. assert q.valid?
  1174. assert_nil q.statement
  1175. end
  1176. def test_available_filters_as_json_should_include_missing_assigned_to_id_values
  1177. user = User.generate!
  1178. with_current_user User.find(1) do
  1179. q = IssueQuery.new
  1180. q.filters = {"assigned_to_id" => {:operator => '=', :values => user.id.to_s}}
  1181. filters = q.available_filters_as_json
  1182. assert_include [user.name, user.id.to_s], filters['assigned_to_id']['values']
  1183. end
  1184. end
  1185. def test_available_filters_as_json_should_include_missing_author_id_values
  1186. user = User.generate!
  1187. with_current_user User.find(1) do
  1188. q = IssueQuery.new
  1189. q.filters = {"author_id" => {:operator => '=', :values => user.id.to_s}}
  1190. filters = q.available_filters_as_json
  1191. assert_include [user.name, user.id.to_s], filters['author_id']['values']
  1192. end
  1193. end
  1194. def test_default_columns
  1195. q = IssueQuery.new
  1196. assert q.columns.any?
  1197. assert q.inline_columns.any?
  1198. assert q.block_columns.empty?
  1199. end
  1200. def test_set_column_names
  1201. q = IssueQuery.new
  1202. q.column_names = ['tracker', :subject, '', 'unknonw_column']
  1203. assert_equal [:id, :tracker, :subject], q.columns.collect {|c| c.name}
  1204. end
  1205. def test_has_column_should_accept_a_column_name
  1206. q = IssueQuery.new
  1207. q.column_names = ['tracker', :subject]
  1208. assert q.has_column?(:tracker)
  1209. assert !q.has_column?(:category)
  1210. end
  1211. def test_has_column_should_accept_a_column
  1212. q = IssueQuery.new
  1213. q.column_names = ['tracker', :subject]
  1214. tracker_column = q.available_columns.detect {|c| c.name==:tracker}
  1215. assert_kind_of QueryColumn, tracker_column
  1216. category_column = q.available_columns.detect {|c| c.name==:category}
  1217. assert_kind_of QueryColumn, category_column
  1218. assert q.has_column?(tracker_column)
  1219. assert !q.has_column?(category_column)
  1220. end
  1221. def test_has_column_should_return_true_for_default_column
  1222. with_settings :issue_list_default_columns => %w(tracker subject) do
  1223. q = IssueQuery.new
  1224. assert q.has_column?(:tracker)
  1225. assert !q.has_column?(:category)
  1226. end
  1227. end
  1228. def test_inline_and_block_columns
  1229. q = IssueQuery.new
  1230. q.column_names = ['subject', 'description', 'tracker', 'last_notes']
  1231. assert_equal [:id, :subject, :tracker], q.inline_columns.map(&:name)
  1232. assert_equal [:description, :last_notes], q.block_columns.map(&:name)
  1233. end
  1234. def test_custom_field_columns_should_be_inline
  1235. q = IssueQuery.new
  1236. columns = q.available_columns.select {|column| column.is_a? QueryCustomFieldColumn}
  1237. assert columns.any?
  1238. assert_nil columns.detect {|column| !column.inline?}
  1239. end
  1240. def test_query_should_preload_spent_hours
  1241. q = IssueQuery.new(:name => '_', :column_names => [:subject, :spent_hours])
  1242. assert q.has_column?(:spent_hours)
  1243. issues = q.issues
  1244. assert_not_nil issues.first.instance_variable_get("@spent_hours")
  1245. end
  1246. def test_query_should_preload_last_updated_by
  1247. with_current_user User.find(2) do
  1248. q = IssueQuery.new(:name => '_', :column_names => [:subject, :last_updated_by])
  1249. q.filters = {"issue_id" => {:operator => '=', :values => ['1,2,3']}}
  1250. assert q.has_column?(:last_updated_by)
  1251. issues = q.issues.sort_by(&:id)
  1252. assert issues.all? {|issue| !issue.instance_variable_get("@last_updated_by").nil?}
  1253. assert_equal ["User", "User", "NilClass"], issues.map { |i| i.last_updated_by.class.name}
  1254. assert_equal ["John Smith", "John Smith", ""], issues.map { |i| i.last_updated_by.to_s }
  1255. end
  1256. end
  1257. def test_query_should_preload_last_notes
  1258. q = IssueQuery.new(:name => '_', :column_names => [:subject, :last_notes])
  1259. assert q.has_column?(:last_notes)
  1260. issues = q.issues
  1261. assert_not_nil issues.first.instance_variable_get("@last_notes")
  1262. end
  1263. def test_groupable_columns_should_include_custom_fields
  1264. q = IssueQuery.new
  1265. column = q.groupable_columns.detect {|c| c.name == :cf_1}
  1266. assert_not_nil column
  1267. assert_kind_of QueryCustomFieldColumn, column
  1268. end
  1269. def test_groupable_columns_should_not_include_multi_custom_fields
  1270. field = CustomField.find(1)
  1271. field.update_attribute :multiple, true
  1272. q = IssueQuery.new
  1273. column = q.groupable_columns.detect {|c| c.name == :cf_1}
  1274. assert_nil column
  1275. end
  1276. def test_groupable_columns_should_include_user_custom_fields
  1277. cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'user')
  1278. q = IssueQuery.new
  1279. assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
  1280. end
  1281. def test_groupable_columns_should_include_version_custom_fields
  1282. cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'version')
  1283. q = IssueQuery.new
  1284. assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
  1285. end
  1286. def test_grouped_with_valid_column
  1287. q = IssueQuery.new(:group_by => 'status')
  1288. assert q.grouped?
  1289. assert_not_nil q.group_by_column
  1290. assert_equal :status, q.group_by_column.name
  1291. assert_not_nil q.group_by_statement
  1292. assert_equal 'status', q.group_by_statement
  1293. end
  1294. def test_grouped_with_invalid_column
  1295. q = IssueQuery.new(:group_by => 'foo')
  1296. assert !q.grouped?
  1297. assert_nil q.group_by_column
  1298. assert_nil q.group_by_statement
  1299. end
  1300. def test_sortable_columns_should_sort_assignees_according_to_user_format_setting
  1301. with_settings :user_format => 'lastname_comma_firstname' do
  1302. q = IssueQuery.new
  1303. assert q.sortable_columns.has_key?('assigned_to')
  1304. assert_equal %w(users.lastname users.firstname users.id), q.sortable_columns['assigned_to']
  1305. end
  1306. end
  1307. def test_sortable_columns_should_sort_authors_according_to_user_format_setting
  1308. with_settings :user_format => 'lastname_comma_firstname' do
  1309. q = IssueQuery.new
  1310. assert q.sortable_columns.has_key?('author')
  1311. assert_equal %w(authors.lastname authors.firstname authors.id), q.sortable_columns['author']
  1312. end
  1313. end
  1314. def test_sortable_columns_should_sort_last_updated_by_according_to_user_format_setting
  1315. with_settings :user_format => 'lastname_comma_firstname' do
  1316. q = IssueQuery.new
  1317. q.sort_criteria = [['last_updated_by', 'desc']]
  1318. assert q.sortable_columns.has_key?('last_updated_by')
  1319. assert_equal %w(last_journal_user.lastname last_journal_user.firstname last_journal_user.id), q.sortable_columns['last_updated_by']
  1320. end
  1321. end
  1322. def test_sortable_columns_should_include_custom_field
  1323. q = IssueQuery.new
  1324. assert q.sortable_columns['cf_1']
  1325. end
  1326. def test_sortable_columns_should_not_include_multi_custom_field
  1327. field = CustomField.find(1)
  1328. field.update_attribute :multiple, true
  1329. q = IssueQuery.new
  1330. assert !q.sortable_columns['cf_1']
  1331. end
  1332. def test_default_sort
  1333. q = IssueQuery.new
  1334. assert_equal [['id', 'desc']], q.sort_criteria
  1335. end
  1336. def test_sort_criteria_should_have_only_first_three_elements
  1337. q = IssueQuery.new
  1338. q.sort_criteria = [['priority', 'desc'], ['tracker', 'asc'], ['priority', 'asc'], ['id', 'asc'], ['project', 'asc'], ['subject', 'asc']]
  1339. assert_equal [['priority', 'desc'], ['tracker', 'asc'], ['priority', 'asc']], q.sort_criteria
  1340. end
  1341. def test_sort_criteria_should_remove_blank_keys
  1342. q = IssueQuery.new
  1343. q.sort_criteria = [['priority', 'desc'], [nil, 'desc'], ['', 'asc'], ['project', 'asc']]
  1344. assert_equal [['priority', 'desc'], ['project', 'asc']], q.sort_criteria
  1345. end
  1346. def test_set_sort_criteria_with_hash
  1347. q = IssueQuery.new
  1348. q.sort_criteria = {'0' => ['priority', 'desc'], '2' => ['tracker']}
  1349. assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
  1350. end
  1351. def test_set_sort_criteria_with_array
  1352. q = IssueQuery.new
  1353. q.sort_criteria = [['priority', 'desc'], 'tracker']
  1354. assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
  1355. end
  1356. def test_create_query_with_sort
  1357. q = IssueQuery.new(:name => 'Sorted')
  1358. q.sort_criteria = [['priority', 'desc'], 'tracker']
  1359. assert q.save
  1360. q.reload
  1361. assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
  1362. end
  1363. def test_sort_by_string_custom_field_asc
  1364. q = IssueQuery.new
  1365. c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
  1366. assert c
  1367. assert c.sortable
  1368. q.sort_criteria = [[c.name.to_s, 'asc']]
  1369. issues = q.issues
  1370. values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
  1371. assert !values.empty?
  1372. assert_equal values.sort, values
  1373. end
  1374. def test_sort_by_string_custom_field_desc
  1375. q = IssueQuery.new
  1376. c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
  1377. assert c
  1378. assert c.sortable
  1379. q.sort_criteria = [[c.name.to_s, 'desc']]
  1380. issues = q.issues
  1381. values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
  1382. assert !values.empty?
  1383. assert_equal values.sort.reverse, values
  1384. end
  1385. def test_sort_by_float_custom_field_asc
  1386. q = IssueQuery.new
  1387. c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'float' }
  1388. assert c
  1389. assert c.sortable
  1390. q.sort_criteria = [[c.name.to_s, 'asc']]
  1391. issues = q.issues
  1392. values = issues.collect {|i| begin; Kernel.Float(i.custom_value_for(c.custom_field).to_s); rescue; nil; end}.compact
  1393. assert !values.empty?
  1394. assert_equal values.sort, values
  1395. end
  1396. def test_sort_by_total_for_estimated_hours
  1397. # Prepare issues
  1398. parent = issues(:issues_001)
  1399. child = issues(:issues_002)
  1400. private_child = issues(:issues_003)
  1401. other = issues(:issues_007)
  1402. User.current = users(:users_001)
  1403. parent.safe_attributes = {:estimated_hours => 1}
  1404. child.safe_attributes = {:estimated_hours => 2, :parent_issue_id => 1}
  1405. private_child.safe_attributes = {:estimated_hours => 4, :parent_issue_id => 1, :is_private => true}
  1406. other.safe_attributes = {:estimated_hours => 5}
  1407. [parent, child, private_child, other].each(&:save!)
  1408. q = IssueQuery.new(
  1409. :name => '_',
  1410. :filters => { 'issue_id' => {:operator => '=', :values => ['1,7']} },
  1411. :sort_criteria => [['total_estimated_hours', 'asc']]
  1412. )
  1413. # With private_child, `parent' is "bigger" than `other'
  1414. ids = q.issue_ids
  1415. assert_equal [7, 1], ids, "Private issue was not used to calculate sort order"
  1416. # Without the invisible private_child, `other' is "bigger" than `parent'
  1417. User.current = User.anonymous
  1418. ids = q.issue_ids
  1419. assert_equal [1, 7], ids, "Private issue was used to calculate sort order"
  1420. end
  1421. def test_set_totalable_names
  1422. q = IssueQuery.new
  1423. q.totalable_names = ['estimated_hours', :spent_hours, '']
  1424. assert_equal [:estimated_hours, :spent_hours], q.totalable_columns.map(&:name)
  1425. end
  1426. def test_totalable_columns_should_default_to_settings
  1427. with_settings :issue_list_default_totals => ['estimated_hours'] do
  1428. q = IssueQuery.new
  1429. assert_equal [:estimated_hours], q.totalable_columns.map(&:name)
  1430. end
  1431. end
  1432. def test_available_totalable_columns_should_include_estimated_hours
  1433. q = IssueQuery.new
  1434. assert_include :estimated_hours, q.available_totalable_columns.map(&:name)
  1435. end
  1436. def test_available_totalable_columns_should_include_spent_hours
  1437. User.current = User.find(1)
  1438. q = IssueQuery.new
  1439. assert_include :spent_hours, q.available_totalable_columns.map(&:name)
  1440. end
  1441. def test_available_totalable_columns_should_include_int_custom_field
  1442. field = IssueCustomField.generate!(:field_format => 'int', :is_for_all => true)
  1443. q = IssueQuery.new
  1444. assert_include "cf_#{field.id}".to_sym, q.available_totalable_columns.map(&:name)
  1445. end
  1446. def test_available_totalable_columns_should_include_float_custom_field
  1447. field = IssueCustomField.generate!(:field_format => 'float', :is_for_all => true)
  1448. q = IssueQuery.new
  1449. assert_include "cf_#{field.id}".to_sym, q.available_totalable_columns.map(&:name)
  1450. end
  1451. def test_total_for_estimated_hours
  1452. Issue.delete_all
  1453. Issue.generate!(:estimated_hours => 5.5)
  1454. Issue.generate!(:estimated_hours => 1.1)
  1455. Issue.generate!
  1456. q = IssueQuery.new
  1457. assert_equal 6.6, q.total_for(:estimated_hours)
  1458. end
  1459. def test_total_by_group_for_estimated_hours
  1460. Issue.delete_all
  1461. Issue.generate!(:estimated_hours => 5.5, :assigned_to_id => 2)
  1462. Issue.generate!(:estimated_hours => 1.1, :assigned_to_id => 3)
  1463. Issue.generate!(:estimated_hours => 3.5)
  1464. q = IssueQuery.new(:group_by => 'assigned_to')
  1465. assert_equal(
  1466. {nil => 3.5, User.find(2) => 5.5, User.find(3) => 1.1},
  1467. q.total_by_group_for(:estimated_hours)
  1468. )
  1469. end
  1470. def test_total_for_spent_hours
  1471. TimeEntry.delete_all
  1472. TimeEntry.generate!(:hours => 5.5)
  1473. TimeEntry.generate!(:hours => 1.1)
  1474. q = IssueQuery.new
  1475. assert_equal 6.6, q.total_for(:spent_hours)
  1476. end
  1477. def test_total_by_group_for_spent_hours
  1478. TimeEntry.delete_all
  1479. TimeEntry.generate!(:hours => 5.5, :issue_id => 1)
  1480. TimeEntry.generate!(:hours => 1.1, :issue_id => 2)
  1481. Issue.where(:id => 1).update_all(:assigned_to_id => 2)
  1482. Issue.where(:id => 2).update_all(:assigned_to_id => 3)
  1483. q = IssueQuery.new(:group_by => 'assigned_to')
  1484. assert_equal(
  1485. {User.find(2) => 5.5, User.find(3) => 1.1},
  1486. q.total_by_group_for(:spent_hours)
  1487. )
  1488. end
  1489. def test_total_by_project_group_for_spent_hours
  1490. TimeEntry.delete_all
  1491. TimeEntry.generate!(:hours => 5.5, :issue_id => 1)
  1492. TimeEntry.generate!(:hours => 1.1, :issue_id => 2)
  1493. Issue.where(:id => 1).update_all(:assigned_to_id => 2)
  1494. Issue.where(:id => 2).update_all(:assigned_to_id => 3)
  1495. q = IssueQuery.new(:group_by => 'project')
  1496. assert_equal(
  1497. {Project.find(1) => 6.6},
  1498. q.total_by_group_for(:spent_hours)
  1499. )
  1500. end
  1501. def test_total_for_int_custom_field
  1502. field = IssueCustomField.generate!(:field_format => 'int', :is_for_all => true)
  1503. CustomValue.create!(:customized => Issue.find(1), :custom_field => field, :value => '2')
  1504. CustomValue.create!(:customized => Issue.find(2), :custom_field => field, :value => '7')
  1505. CustomValue.create!(:customized => Issue.find(3), :custom_field => field, :value => '')
  1506. q = IssueQuery.new
  1507. assert_equal 9, q.total_for("cf_#{field.id}")
  1508. end
  1509. def test_total_by_group_for_int_custom_field
  1510. field = IssueCustomField.generate!(:field_format => 'int', :is_for_all => true)
  1511. CustomValue.create!(:customized => Issue.find(1), :custom_field => field, :value => '2')
  1512. CustomValue.create!(:customized => Issue.find(2), :custom_field => field, :value => '7')
  1513. Issue.where(:id => 1).update_all(:assigned_to_id => 2)
  1514. Issue.where(:id => 2).update_all(:assigned_to_id => 3)
  1515. q = IssueQuery.new(:group_by => 'assigned_to')
  1516. assert_equal(
  1517. {User.find(2) => 2, User.find(3) => 7},
  1518. q.total_by_group_for("cf_#{field.id}")
  1519. )
  1520. end
  1521. def test_total_for_float_custom_field
  1522. field = IssueCustomField.generate!(:field_format => 'float', :is_for_all => true)
  1523. CustomValue.create!(:customized => Issue.find(1), :custom_field => field, :value => '2.3')
  1524. CustomValue.create!(:customized => Issue.find(2), :custom_field => field, :value => '7')
  1525. CustomValue.create!(:customized => Issue.find(3), :custom_field => field, :value => '')
  1526. q = IssueQuery.new
  1527. assert_equal 9.3, q.total_for("cf_#{field.id}")
  1528. end
  1529. def test_invalid_query_should_raise_query_statement_invalid_error
  1530. q = IssueQuery.new
  1531. assert_raise Query::StatementInvalid do
  1532. q.issues(:conditions => "foo = 1")
  1533. end
  1534. end
  1535. def test_issue_count
  1536. q = IssueQuery.new(:name => '_')
  1537. issue_count = q.issue_count
  1538. assert_equal q.issues.size, issue_count
  1539. end
  1540. def test_issue_count_with_archived_issues
  1541. p = Project.generate! do |project|
  1542. project.status = Project::STATUS_ARCHIVED
  1543. end
  1544. i = Issue.generate!( :project => p, :tracker => p.trackers.first )
  1545. assert !i.visible?
  1546. test_issue_count
  1547. end
  1548. def test_issue_count_by_association_group
  1549. q = IssueQuery.new(:name => '_', :group_by => 'assigned_to')
  1550. count_by_group = q.result_count_by_group
  1551. assert_kind_of Hash, count_by_group
  1552. assert_equal %w(NilClass User), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
  1553. assert_equal %W(#{INTEGER_KLASS}), count_by_group.values.collect {|k| k.class.name}.uniq
  1554. assert count_by_group.has_key?(User.find(3))
  1555. end
  1556. def test_issue_count_by_list_custom_field_group
  1557. q = IssueQuery.new(:name => '_', :group_by => 'cf_1')
  1558. count_by_group = q.result_count_by_group
  1559. assert_kind_of Hash, count_by_group
  1560. assert_equal %w(NilClass String), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
  1561. assert_equal %W(#{INTEGER_KLASS}), count_by_group.values.collect {|k| k.class.name}.uniq
  1562. assert count_by_group.has_key?('MySQL')
  1563. end
  1564. def test_issue_count_by_date_custom_field_group
  1565. q = IssueQuery.new(:name => '_', :group_by => 'cf_8')
  1566. count_by_group = q.result_count_by_group
  1567. assert_kind_of Hash, count_by_group
  1568. assert_equal %w(Date NilClass), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
  1569. assert_equal %W(#{INTEGER_KLASS}), count_by_group.values.collect {|k| k.class.name}.uniq
  1570. end
  1571. def test_issue_count_with_nil_group_only
  1572. Issue.update_all("assigned_to_id = NULL")
  1573. q = IssueQuery.new(:name => '_', :group_by => 'assigned_to')
  1574. count_by_group = q.result_count_by_group
  1575. assert_kind_of Hash, count_by_group
  1576. assert_equal 1, count_by_group.keys.size
  1577. assert_nil count_by_group.keys.first
  1578. end
  1579. def test_issue_ids
  1580. q = IssueQuery.new(:name => '_')
  1581. q.sort_criteria = ['subject', 'id']
  1582. issues = q.issues
  1583. assert_equal issues.map(&:id), q.issue_ids
  1584. end
  1585. def test_label_for
  1586. set_language_if_valid 'en'
  1587. q = IssueQuery.new
  1588. assert_equal 'Assignee', q.label_for('assigned_to_id')
  1589. end
  1590. def test_label_for_fr
  1591. set_language_if_valid 'fr'
  1592. q = IssueQuery.new
  1593. assert_equal 'Assigné à', q.label_for('assigned_to_id')
  1594. end
  1595. def test_editable_by
  1596. admin = User.find(1)
  1597. manager = User.find(2)
  1598. developer = User.find(3)
  1599. # Public query on project 1
  1600. q = IssueQuery.find(1)
  1601. assert q.editable_by?(admin)
  1602. assert q.editable_by?(manager)
  1603. assert !q.editable_by?(developer)
  1604. # Private query on project 1
  1605. q = IssueQuery.find(2)
  1606. assert q.editable_by?(admin)
  1607. assert !q.editable_by?(manager)
  1608. assert q.editable_by?(developer)
  1609. # Private query for all projects
  1610. q = IssueQuery.find(3)
  1611. assert q.editable_by?(admin)
  1612. assert !q.editable_by?(manager)
  1613. assert q.editable_by?(developer)
  1614. end
  1615. def test_editable_by_for_global_query
  1616. admin = User.find(1)
  1617. manager = User.find(2)
  1618. developer = User.find(3)
  1619. q = IssueQuery.find(4)
  1620. assert q.editable_by?(admin)
  1621. assert !q.editable_by?(manager)
  1622. assert !q.editable_by?(developer)
  1623. end
  1624. def test_editable_by_for_global_query_with_project_set
  1625. admin = User.find(1)
  1626. manager = User.find(2)
  1627. developer = User.find(3)
  1628. q = IssueQuery.find(4)
  1629. q.project = Project.find(1)
  1630. assert q.editable_by?(admin)
  1631. assert !q.editable_by?(manager)
  1632. assert !q.editable_by?(developer)
  1633. end
  1634. def test_visible_scope
  1635. query_ids = IssueQuery.visible(User.anonymous).map(&:id)
  1636. assert query_ids.include?(1), 'public query on public project was not visible'
  1637. assert query_ids.include?(4), 'public query for all projects was not visible'
  1638. assert !query_ids.include?(2), 'private query on public project was visible'
  1639. assert !query_ids.include?(3), 'private query for all projects was visible'
  1640. assert !query_ids.include?(7), 'public query on private project was visible'
  1641. end
  1642. def test_query_with_public_visibility_should_be_visible_to_anyone
  1643. q = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_PUBLIC)
  1644. assert q.visible?(User.anonymous)
  1645. assert IssueQuery.visible(User.anonymous).find_by_id(q.id)
  1646. assert q.visible?(User.find(7))
  1647. assert IssueQuery.visible(User.find(7)).find_by_id(q.id)
  1648. assert q.visible?(User.find(2))
  1649. assert IssueQuery.visible(User.find(2)).find_by_id(q.id)
  1650. assert q.visible?(User.find(1))
  1651. assert IssueQuery.visible(User.find(1)).find_by_id(q.id)
  1652. end
  1653. def test_query_with_roles_visibility_should_be_visible_to_user_with_role
  1654. q = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_ROLES, :role_ids => [1,2])
  1655. assert !q.visible?(User.anonymous)
  1656. assert_nil IssueQuery.visible(User.anonymous).find_by_id(q.id)
  1657. assert !q.visible?(User.find(7))
  1658. assert_nil IssueQuery.visible(User.find(7)).find_by_id(q.id)
  1659. assert q.visible?(User.find(2))
  1660. assert IssueQuery.visible(User.find(2)).find_by_id(q.id)
  1661. assert q.visible?(User.find(1))
  1662. assert IssueQuery.visible(User.find(1)).find_by_id(q.id)
  1663. # Should ignore archived project memberships
  1664. Project.find(1).archive
  1665. assert !q.visible?(User.find(3))
  1666. assert_nil IssueQuery.visible(User.find(3)).find_by_id(q.id)
  1667. end
  1668. def test_query_with_private_visibility_should_be_visible_to_owner
  1669. q = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_PRIVATE, :user => User.find(7))
  1670. assert !q.visible?(User.anonymous)
  1671. assert_nil IssueQuery.visible(User.anonymous).find_by_id(q.id)
  1672. assert q.visible?(User.find(7))
  1673. assert IssueQuery.visible(User.find(7)).find_by_id(q.id)
  1674. assert !q.visible?(User.find(2))
  1675. assert_nil IssueQuery.visible(User.find(2)).find_by_id(q.id)
  1676. assert q.visible?(User.find(1))
  1677. assert_nil IssueQuery.visible(User.find(1)).find_by_id(q.id)
  1678. end
  1679. def test_build_from_params_should_not_update_query_with_nil_param_values
  1680. q = IssueQuery.create!(:name => 'Query',
  1681. :type => "IssueQuery",
  1682. :user => User.find(7),
  1683. :filters => {"status_id" => {:values => ["1"], :operator => "o"}},
  1684. :column_names => [:tracker, :status],
  1685. :sort_criteria => ['id', 'asc'],
  1686. :group_by => "project",
  1687. :options => { :totalable_names=>[:estimated_hours], :draw_relations => '1', :draw_progress_line => '1' }
  1688. )
  1689. old_attributes = q.attributes
  1690. q.build_from_params({})
  1691. assert_equal old_attributes, q.attributes
  1692. end
  1693. test "#available_filters should include users of visible projects in cross-project view" do
  1694. users = IssueQuery.new.available_filters["assigned_to_id"]
  1695. assert_not_nil users
  1696. assert users[:values].map{|u| u[1]}.include?("3")
  1697. end
  1698. test "#available_filters should include users of subprojects" do
  1699. user1 = User.generate!
  1700. user2 = User.generate!
  1701. project = Project.find(1)
  1702. Member.create!(:principal => user1, :project => project.children.visible.first, :role_ids => [1])
  1703. users = IssueQuery.new(:project => project).available_filters["assigned_to_id"]
  1704. assert_not_nil users
  1705. assert users[:values].map{|u| u[1]}.include?(user1.id.to_s)
  1706. assert !users[:values].map{|u| u[1]}.include?(user2.id.to_s)
  1707. end
  1708. test "#available_filters should include visible projects in cross-project view" do
  1709. projects = IssueQuery.new.available_filters["project_id"]
  1710. assert_not_nil projects
  1711. assert projects[:values].map{|u| u[1]}.include?("1")
  1712. end
  1713. test "#available_filters should include 'member_of_group' filter" do
  1714. query = IssueQuery.new
  1715. assert query.available_filters.key?("member_of_group")
  1716. assert_equal :list_optional, query.available_filters["member_of_group"][:type]
  1717. assert query.available_filters["member_of_group"][:values].present?
  1718. assert_equal Group.givable.sort.map {|g| [g.name, g.id.to_s]},
  1719. query.available_filters["member_of_group"][:values].sort
  1720. end
  1721. test "#available_filters should include 'assigned_to_role' filter" do
  1722. query = IssueQuery.new
  1723. assert query.available_filters.key?("assigned_to_role")
  1724. assert_equal :list_optional, query.available_filters["assigned_to_role"][:type]
  1725. assert query.available_filters["assigned_to_role"][:values].include?(['Manager','1'])
  1726. assert query.available_filters["assigned_to_role"][:values].include?(['Developer','2'])
  1727. assert query.available_filters["assigned_to_role"][:values].include?(['Reporter','3'])
  1728. assert ! query.available_filters["assigned_to_role"][:values].include?(['Non member','4'])
  1729. assert ! query.available_filters["assigned_to_role"][:values].include?(['Anonymous','5'])
  1730. end
  1731. def test_available_filters_should_include_custom_field_according_to_user_visibility
  1732. visible_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => true)
  1733. hidden_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => false, :role_ids => [1])
  1734. with_current_user User.find(3) do
  1735. query = IssueQuery.new
  1736. assert_include "cf_#{visible_field.id}", query.available_filters.keys
  1737. assert_not_include "cf_#{hidden_field.id}", query.available_filters.keys
  1738. end
  1739. end
  1740. def test_available_columns_should_include_custom_field_according_to_user_visibility
  1741. visible_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => true)
  1742. hidden_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => false, :role_ids => [1])
  1743. with_current_user User.find(3) do
  1744. query = IssueQuery.new
  1745. assert_include :"cf_#{visible_field.id}", query.available_columns.map(&:name)
  1746. assert_not_include :"cf_#{hidden_field.id}", query.available_columns.map(&:name)
  1747. end
  1748. end
  1749. def test_available_columns_should_not_include_total_estimated_hours_when_trackers_disabled_estimated_hours
  1750. Tracker.visible.each do |tracker|
  1751. tracker.core_fields = tracker.core_fields.reject{|field| field == 'estimated_hours'}
  1752. tracker.save!
  1753. end
  1754. query = IssueQuery.new
  1755. available_columns = query.available_columns.map(&:name)
  1756. assert_not_include :estimated_hours, available_columns
  1757. assert_not_include :total_estimated_hours, available_columns
  1758. tracker = Tracker.visible.first
  1759. tracker.core_fields = ['estimated_hours']
  1760. tracker.save!
  1761. query = IssueQuery.new
  1762. available_columns = query.available_columns.map(&:name)
  1763. assert_include :estimated_hours, available_columns
  1764. assert_include :total_estimated_hours, available_columns
  1765. end
  1766. def setup_member_of_group
  1767. Group.destroy_all # No fixtures
  1768. @user_in_group = User.generate!
  1769. @second_user_in_group = User.generate!
  1770. @user_in_group2 = User.generate!
  1771. @user_not_in_group = User.generate!
  1772. @group = Group.generate!.reload
  1773. @group.users << @user_in_group
  1774. @group.users << @second_user_in_group
  1775. @group2 = Group.generate!.reload
  1776. @group2.users << @user_in_group2
  1777. @query = IssueQuery.new(:name => '_')
  1778. end
  1779. test "member_of_group filter should search assigned to for users in the group" do
  1780. setup_member_of_group
  1781. @query.add_filter('member_of_group', '=', [@group.id.to_s])
  1782. assert_find_issues_with_query_is_successful @query
  1783. end
  1784. test "member_of_group filter should search not assigned to any group member (none)" do
  1785. setup_member_of_group
  1786. @query.add_filter('member_of_group', '!*', [''])
  1787. assert_find_issues_with_query_is_successful @query
  1788. end
  1789. test "member_of_group filter should search assigned to any group member (all)" do
  1790. setup_member_of_group
  1791. @query.add_filter('member_of_group', '*', [''])
  1792. assert_find_issues_with_query_is_successful @query
  1793. end
  1794. test "member_of_group filter should return an empty set with = empty group" do
  1795. setup_member_of_group
  1796. @empty_group = Group.generate!
  1797. @query.add_filter('member_of_group', '=', [@empty_group.id.to_s])
  1798. assert_equal [], find_issues_with_query(@query)
  1799. end
  1800. test "member_of_group filter should return issues with ! empty group" do
  1801. setup_member_of_group
  1802. @empty_group = Group.generate!
  1803. @query.add_filter('member_of_group', '!', [@empty_group.id.to_s])
  1804. assert_find_issues_with_query_is_successful @query
  1805. end
  1806. def setup_assigned_to_role
  1807. @manager_role = Role.find_by_name('Manager')
  1808. @developer_role = Role.find_by_name('Developer')
  1809. @project = Project.generate!
  1810. @manager = User.generate!
  1811. @developer = User.generate!
  1812. @boss = User.generate!
  1813. @guest = User.generate!
  1814. User.add_to_project(@manager, @project, @manager_role)
  1815. User.add_to_project(@developer, @project, @developer_role)
  1816. User.add_to_project(@boss, @project, [@manager_role, @developer_role])
  1817. @issue1 = Issue.generate!(:project => @project, :assigned_to_id => @manager.id)
  1818. @issue2 = Issue.generate!(:project => @project, :assigned_to_id => @developer.id)
  1819. @issue3 = Issue.generate!(:project => @project, :assigned_to_id => @boss.id)
  1820. @issue4 = Issue.generate!(:project => @project, :author_id => @guest.id, :assigned_to_id => @guest.id)
  1821. @issue5 = Issue.generate!(:project => @project)
  1822. @query = IssueQuery.new(:name => '_', :project => @project)
  1823. end
  1824. test "assigned_to_role filter should search assigned to for users with the Role" do
  1825. setup_assigned_to_role
  1826. @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
  1827. assert_query_result [@issue1, @issue3], @query
  1828. end
  1829. test "assigned_to_role filter should search assigned to for users with the Role on the issue project" do
  1830. setup_assigned_to_role
  1831. other_project = Project.generate!
  1832. User.add_to_project(@developer, other_project, @manager_role)
  1833. @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
  1834. assert_query_result [@issue1, @issue3], @query
  1835. end
  1836. test "assigned_to_role filter should return an empty set with empty role" do
  1837. setup_assigned_to_role
  1838. @empty_role = Role.generate!
  1839. @query.add_filter('assigned_to_role', '=', [@empty_role.id.to_s])
  1840. assert_query_result [], @query
  1841. end
  1842. test "assigned_to_role filter should search assigned to for users without the Role" do
  1843. setup_assigned_to_role
  1844. @query.add_filter('assigned_to_role', '!', [@manager_role.id.to_s])
  1845. assert_query_result [@issue2, @issue4, @issue5], @query
  1846. end
  1847. test "assigned_to_role filter should search assigned to for users not assigned to any Role (none)" do
  1848. setup_assigned_to_role
  1849. @query.add_filter('assigned_to_role', '!*', [''])
  1850. assert_query_result [@issue4, @issue5], @query
  1851. end
  1852. test "assigned_to_role filter should search assigned to for users assigned to any Role (all)" do
  1853. setup_assigned_to_role
  1854. @query.add_filter('assigned_to_role', '*', [''])
  1855. assert_query_result [@issue1, @issue2, @issue3], @query
  1856. end
  1857. test "assigned_to_role filter should return issues with ! empty role" do
  1858. setup_assigned_to_role
  1859. @empty_role = Role.generate!
  1860. @query.add_filter('assigned_to_role', '!', [@empty_role.id.to_s])
  1861. assert_query_result [@issue1, @issue2, @issue3, @issue4, @issue5], @query
  1862. end
  1863. def test_query_column_should_accept_a_symbol_as_caption
  1864. set_language_if_valid 'en'
  1865. c = QueryColumn.new('foo', :caption => :general_text_Yes)
  1866. assert_equal 'Yes', c.caption
  1867. end
  1868. def test_query_column_should_accept_a_proc_as_caption
  1869. c = QueryColumn.new('foo', :caption => lambda {'Foo'})
  1870. assert_equal 'Foo', c.caption
  1871. end
  1872. def test_date_clause_should_respect_user_time_zone_with_local_default
  1873. @query = IssueQuery.new(:name => '_')
  1874. # user is in Hawaii (-10)
  1875. User.current = users(:users_001)
  1876. User.current.pref.update_attribute :time_zone, 'Hawaii'
  1877. # assume timestamps are stored in server local time
  1878. local_zone = Time.zone
  1879. from = Date.parse '2016-03-20'
  1880. to = Date.parse '2016-03-22'
  1881. assert c = @query.send(:date_clause, 'table', 'field', from, to, false)
  1882. # the dates should have been interpreted in the user's time zone and
  1883. # converted to local time
  1884. # what we get exactly in the sql depends on the local time zone, therefore
  1885. # it's computed here.
  1886. f = User.current.time_zone.local(from.year, from.month, from.day).yesterday.end_of_day.in_time_zone(local_zone)
  1887. t = User.current.time_zone.local(to.year, to.month, to.day).end_of_day.in_time_zone(local_zone)
  1888. assert_equal "table.field > '#{Query.connection.quoted_date f}' AND table.field <= '#{Query.connection.quoted_date t}'", c
  1889. end
  1890. def test_date_clause_should_respect_user_time_zone_with_utc_default
  1891. @query = IssueQuery.new(:name => '_')
  1892. # user is in Hawaii (-10)
  1893. User.current = users(:users_001)
  1894. User.current.pref.update_attribute :time_zone, 'Hawaii'
  1895. # assume timestamps are stored as utc
  1896. ActiveRecord::Base.default_timezone = :utc
  1897. from = Date.parse '2016-03-20'
  1898. to = Date.parse '2016-03-22'
  1899. assert c = @query.send(:date_clause, 'table', 'field', from, to, false)
  1900. # the dates should have been interpreted in the user's time zone and
  1901. # converted to utc. March 20 in Hawaii begins at 10am UTC.
  1902. f = Time.new(2016, 3, 20, 9, 59, 59, 0).end_of_hour
  1903. t = Time.new(2016, 3, 23, 9, 59, 59, 0).end_of_hour
  1904. assert_equal "table.field > '#{Query.connection.quoted_date f}' AND table.field <= '#{Query.connection.quoted_date t}'", c
  1905. ensure
  1906. ActiveRecord::Base.default_timezone = :local # restore Redmine default
  1907. end
  1908. def test_filter_on_subprojects
  1909. query = IssueQuery.new(:name => '_', :project => Project.find(1))
  1910. filter_name = "subproject_id"
  1911. assert_include filter_name, query.available_filters.keys
  1912. # "is" operator should include issues of parent project + issues of the selected subproject
  1913. query.filters = {filter_name => {:operator => '=', :values => ['3']}}
  1914. issues = find_issues_with_query(query)
  1915. assert_equal [1, 2, 3, 5, 7, 8, 11, 12, 13, 14], issues.map(&:id).sort
  1916. # "is not" operator should include issues of parent project + issues of all active subprojects - issues of the selected subprojects
  1917. query = IssueQuery.new(:name => '_', :project => Project.find(1))
  1918. query.filters = {filter_name => {:operator => '!', :values => ['3']}}
  1919. issues = find_issues_with_query(query)
  1920. assert_equal [1, 2, 3, 6, 7, 8, 9, 10, 11, 12], issues.map(&:id).sort
  1921. end
  1922. def test_filter_updated_on_none_should_return_issues_with_updated_on_equal_with_created_on
  1923. query = IssueQuery.new(:name => '_', :project => Project.find(1))
  1924. query.filters = {'updated_on' => {:operator => '!*', :values => ['']}}
  1925. issues = find_issues_with_query(query)
  1926. assert_equal [3, 6, 7, 8, 9, 10, 14], issues.map(&:id).sort
  1927. end
  1928. def test_filter_updated_on_any_should_return_issues_with_updated_on_greater_than_created_on
  1929. query = IssueQuery.new(:name => '_', :project => Project.find(1))
  1930. query.filters = {'updated_on' => {:operator => '*', :values => ['']}}
  1931. issues = find_issues_with_query(query)
  1932. assert_equal [1, 2, 5, 11, 12, 13], issues.map(&:id).sort
  1933. end
  1934. def test_issue_statuses_should_return_only_statuses_used_by_that_project
  1935. query = IssueQuery.new(:name => '_', :project => Project.find(1))
  1936. query.filters = {'status_id' => {:operator => '=', :values => []}}
  1937. WorkflowTransition.delete_all
  1938. WorkflowTransition.create(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
  1939. WorkflowTransition.create(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
  1940. WorkflowTransition.create(:role_id => 1, :tracker_id => 1, :old_status_id => 2, :new_status_id => 3)
  1941. WorkflowTransition.create(:role_id => 1, :tracker_id => 2, :old_status_id => 1, :new_status_id => 3)
  1942. assert_equal ['1','2','3','4'], query.available_filters['status_id'][:values].map(&:second)
  1943. end
  1944. def test_issue_statuses_without_project_should_return_all_statuses
  1945. query = IssueQuery.new(:name => '_')
  1946. query.filters = {'status_id' => {:operator => '=', :values => []}}
  1947. WorkflowTransition.delete_all
  1948. WorkflowTransition.create(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
  1949. WorkflowTransition.create(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
  1950. WorkflowTransition.create(:role_id => 1, :tracker_id => 1, :old_status_id => 2, :new_status_id => 3)
  1951. WorkflowTransition.create(:role_id => 1, :tracker_id => 2, :old_status_id => 1, :new_status_id => 3)
  1952. assert_equal ['1','2','3','4','5','6'], query.available_filters['status_id'][:values].map(&:second)
  1953. end
  1954. def test_project_status_filter_should_be_available_in_global_queries
  1955. query = IssueQuery.new(:project => nil, :name => '_')
  1956. assert query.available_filters.has_key?('project.status')
  1957. end
  1958. def test_project_status_filter_should_be_available_when_project_has_subprojects
  1959. query = IssueQuery.new(:project => Project.find(1), :name => '_')
  1960. assert query.available_filters.has_key?('project.status')
  1961. end
  1962. def test_project_status_filter_should_not_be_available_when_project_is_leaf
  1963. query = IssueQuery.new(:project => Project.find(2), :name => '_')
  1964. assert !query.available_filters.has_key?('project.status')
  1965. end
  1966. def test_project_statuses_values_should_return_only_active_and_closed_statuses
  1967. set_language_if_valid 'en'
  1968. query = IssueQuery.new(:project => nil, :name => '_')
  1969. project_status_filter = query.available_filters['project.status']
  1970. assert_not_nil project_status_filter
  1971. assert_equal [["active", "1"], ["closed", "5"]], project_status_filter[:values]
  1972. end
  1973. def test_as_params_should_serialize_query
  1974. query = IssueQuery.new(name: "_")
  1975. query.add_filter('subject', '!~', ['asdf'])
  1976. query.group_by = 'tracker'
  1977. query.totalable_names = %w(estimated_hours)
  1978. query.column_names = %w(id subject estimated_hours)
  1979. assert hsh = query.as_params
  1980. new_query = IssueQuery.build_from_params(hsh)
  1981. assert_equal query.filters, new_query.filters
  1982. assert_equal query.group_by, new_query.group_by
  1983. assert_equal query.column_names, new_query.column_names
  1984. assert_equal query.totalable_names, new_query.totalable_names
  1985. end
  1986. def test_issue_query_filter_by_spent_time
  1987. query = IssueQuery.new(:name => '_')
  1988. query.filters = {'spent_time' => {:operator => '*', :values => ['']}}
  1989. assert_equal [3, 1], query.issues.pluck(:id)
  1990. query.filters = {'spent_time' => {:operator => '!*', :values => ['']}}
  1991. assert_equal [13, 12, 11, 8, 7, 5, 2], query.issues.pluck(:id)
  1992. query.filters = {'spent_time' => {:operator => '>=', :values => ['10']}}
  1993. assert_equal [1], query.issues.pluck(:id)
  1994. query.filters = {'spent_time' => {:operator => '<=', :values => ['10']}}
  1995. assert_equal [13, 12, 11, 8, 7, 5, 3, 2], query.issues.pluck(:id)
  1996. query.filters = {'spent_time' => {:operator => '><', :values => ['1', '2']}}
  1997. assert_equal [3], query.issues.pluck(:id)
  1998. end
  1999. def test_issues_should_be_in_the_same_order_when_paginating
  2000. q = IssueQuery.new
  2001. q.sort_criteria = {'0' => ['priority', 'desc']}
  2002. issue_ids = q.issues.pluck(:id)
  2003. paginated_issue_ids = []
  2004. # Test with a maximum of 2 records per page.
  2005. ((q.issue_count / 2) + 1).times do |i|
  2006. paginated_issue_ids += q.issues(:offset => (i * 2), :limit => 2).pluck(:id)
  2007. end
  2008. # Non-paginated issue ids and paginated issue ids should be in the same order.
  2009. assert_equal issue_ids, paginated_issue_ids
  2010. end
  2011. end