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

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