comparison test/unit/query_test.rb @ 1115:433d4f72a19b redmine-2.2

Update to Redmine SVN revision 11137 on 2.2-stable branch
author Chris Cannam
date Mon, 07 Jan 2013 12:01:42 +0000
parents cbb26bc654de
children 622f24f53b42 261b3d9a4903
comparison
equal deleted inserted replaced
929:5f33065ddc4b 1115:433d4f72a19b
1 # Redmine - project management software 1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 # 3 #
4 # This program is free software; you can redistribute it and/or 4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License 5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2 6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version. 7 # of the License, or (at your option) any later version.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 require File.expand_path('../../test_helper', __FILE__) 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 class QueryTest < ActiveSupport::TestCase 20 class QueryTest < ActiveSupport::TestCase
21 include Redmine::I18n
22
21 fixtures :projects, :enabled_modules, :users, :members, 23 fixtures :projects, :enabled_modules, :users, :members,
22 :member_roles, :roles, :trackers, :issue_statuses, 24 :member_roles, :roles, :trackers, :issue_statuses,
23 :issue_categories, :enumerations, :issues, 25 :issue_categories, :enumerations, :issues,
24 :watchers, :custom_fields, :custom_values, :versions, 26 :watchers, :custom_fields, :custom_values, :versions,
25 :queries, 27 :queries,
26 :projects_trackers 28 :projects_trackers,
29 :custom_fields_trackers
27 30
28 def test_custom_fields_for_all_projects_should_be_available_in_global_queries 31 def test_custom_fields_for_all_projects_should_be_available_in_global_queries
29 query = Query.new(:project => nil, :name => '_') 32 query = Query.new(:project => nil, :name => '_')
30 assert query.available_filters.has_key?('cf_1') 33 assert query.available_filters.has_key?('cf_1')
31 assert !query.available_filters.has_key?('cf_3') 34 assert !query.available_filters.has_key?('cf_3')
46 assert project_ids.include?("1") #public project 49 assert project_ids.include?("1") #public project
47 assert !project_ids.include?("2") #private project user cannot see 50 assert !project_ids.include?("2") #private project user cannot see
48 end 51 end
49 52
50 def find_issues_with_query(query) 53 def find_issues_with_query(query)
51 Issue.find :all, 54 Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
52 :include => [ :assigned_to, :status, :tracker, :project, :priority ], 55 query.statement
53 :conditions => query.statement 56 ).all
54 end 57 end
55 58
56 def assert_find_issues_with_query_is_successful(query) 59 def assert_find_issues_with_query_is_successful(query)
57 assert_nothing_raised do 60 assert_nothing_raised do
58 find_issues_with_query(query) 61 find_issues_with_query(query)
110 issues = find_issues_with_query(query) 113 issues = find_issues_with_query(query)
111 assert !issues.empty? 114 assert !issues.empty?
112 assert issues.all? {|i| i.start_date.nil?} 115 assert issues.all? {|i| i.start_date.nil?}
113 end 116 end
114 117
118 def test_operator_none_for_string_custom_field
119 query = Query.new(:project => Project.find(1), :name => '_')
120 query.add_filter('cf_2', '!*', [''])
121 assert query.has_filter?('cf_2')
122 issues = find_issues_with_query(query)
123 assert !issues.empty?
124 assert issues.all? {|i| i.custom_field_value(2).blank?}
125 end
126
115 def test_operator_all 127 def test_operator_all
116 query = Query.new(:project => Project.find(1), :name => '_') 128 query = Query.new(:project => Project.find(1), :name => '_')
117 query.add_filter('fixed_version_id', '*', ['']) 129 query.add_filter('fixed_version_id', '*', [''])
118 query.add_filter('cf_1', '*', ['']) 130 query.add_filter('cf_1', '*', [''])
119 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NOT NULL") 131 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NOT NULL")
127 issues = find_issues_with_query(query) 139 issues = find_issues_with_query(query)
128 assert !issues.empty? 140 assert !issues.empty?
129 assert issues.all? {|i| i.start_date.present?} 141 assert issues.all? {|i| i.start_date.present?}
130 end 142 end
131 143
144 def test_operator_all_for_string_custom_field
145 query = Query.new(:project => Project.find(1), :name => '_')
146 query.add_filter('cf_2', '*', [''])
147 assert query.has_filter?('cf_2')
148 issues = find_issues_with_query(query)
149 assert !issues.empty?
150 assert issues.all? {|i| i.custom_field_value(2).present?}
151 end
152
132 def test_numeric_filter_should_not_accept_non_numeric_values 153 def test_numeric_filter_should_not_accept_non_numeric_values
133 query = Query.new(:name => '_') 154 query = Query.new(:name => '_')
134 query.add_filter('estimated_hours', '=', ['a']) 155 query.add_filter('estimated_hours', '=', ['a'])
135 156
136 assert query.has_filter?('estimated_hours') 157 assert query.has_filter?('estimated_hours')
145 issues = find_issues_with_query(query) 166 issues = find_issues_with_query(query)
146 assert_equal 1, issues.size 167 assert_equal 1, issues.size
147 assert_equal 2, issues.first.id 168 assert_equal 2, issues.first.id
148 end 169 end
149 170
171 def test_operator_is_on_integer_custom_field
172 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true)
173 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
174 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
175 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
176
177 query = Query.new(:name => '_')
178 query.add_filter("cf_#{f.id}", '=', ['12'])
179 issues = find_issues_with_query(query)
180 assert_equal 1, issues.size
181 assert_equal 2, issues.first.id
182 end
183
184 def test_operator_is_on_integer_custom_field_should_accept_negative_value
185 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true)
186 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
187 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12')
188 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
189
190 query = Query.new(:name => '_')
191 query.add_filter("cf_#{f.id}", '=', ['-12'])
192 assert query.valid?
193 issues = find_issues_with_query(query)
194 assert_equal 1, issues.size
195 assert_equal 2, issues.first.id
196 end
197
198 def test_operator_is_on_float_custom_field
199 f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true)
200 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
201 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12.7')
202 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
203
204 query = Query.new(:name => '_')
205 query.add_filter("cf_#{f.id}", '=', ['12.7'])
206 issues = find_issues_with_query(query)
207 assert_equal 1, issues.size
208 assert_equal 2, issues.first.id
209 end
210
211 def test_operator_is_on_float_custom_field_should_accept_negative_value
212 f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true)
213 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
214 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12.7')
215 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
216
217 query = Query.new(:name => '_')
218 query.add_filter("cf_#{f.id}", '=', ['-12.7'])
219 assert query.valid?
220 issues = find_issues_with_query(query)
221 assert_equal 1, issues.size
222 assert_equal 2, issues.first.id
223 end
224
225 def test_operator_is_on_multi_list_custom_field
226 f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
227 :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
228 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
229 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
230 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
231
232 query = Query.new(:name => '_')
233 query.add_filter("cf_#{f.id}", '=', ['value1'])
234 issues = find_issues_with_query(query)
235 assert_equal [1, 3], issues.map(&:id).sort
236
237 query = Query.new(:name => '_')
238 query.add_filter("cf_#{f.id}", '=', ['value2'])
239 issues = find_issues_with_query(query)
240 assert_equal [1], issues.map(&:id).sort
241 end
242
243 def test_operator_is_not_on_multi_list_custom_field
244 f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
245 :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
246 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
247 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
248 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
249
250 query = Query.new(:name => '_')
251 query.add_filter("cf_#{f.id}", '!', ['value1'])
252 issues = find_issues_with_query(query)
253 assert !issues.map(&:id).include?(1)
254 assert !issues.map(&:id).include?(3)
255
256 query = Query.new(:name => '_')
257 query.add_filter("cf_#{f.id}", '!', ['value2'])
258 issues = find_issues_with_query(query)
259 assert !issues.map(&:id).include?(1)
260 assert issues.map(&:id).include?(3)
261 end
262
263 def test_operator_is_on_is_private_field
264 # is_private filter only available for those who can set issues private
265 User.current = User.find(2)
266
267 query = Query.new(:name => '_')
268 assert query.available_filters.key?('is_private')
269
270 query.add_filter("is_private", '=', ['1'])
271 issues = find_issues_with_query(query)
272 assert issues.any?
273 assert_nil issues.detect {|issue| !issue.is_private?}
274 ensure
275 User.current = nil
276 end
277
278 def test_operator_is_not_on_is_private_field
279 # is_private filter only available for those who can set issues private
280 User.current = User.find(2)
281
282 query = Query.new(:name => '_')
283 assert query.available_filters.key?('is_private')
284
285 query.add_filter("is_private", '!', ['1'])
286 issues = find_issues_with_query(query)
287 assert issues.any?
288 assert_nil issues.detect {|issue| issue.is_private?}
289 ensure
290 User.current = nil
291 end
292
150 def test_operator_greater_than 293 def test_operator_greater_than
151 query = Query.new(:project => Project.find(1), :name => '_') 294 query = Query.new(:project => Project.find(1), :name => '_')
152 query.add_filter('done_ratio', '>=', ['40']) 295 query.add_filter('done_ratio', '>=', ['40'])
153 assert query.statement.include?("#{Issue.table_name}.done_ratio >= 40.0") 296 assert query.statement.include?("#{Issue.table_name}.done_ratio >= 40.0")
154 find_issues_with_query(query) 297 find_issues_with_query(query)
159 query.add_filter('estimated_hours', '>=', ['40.5']) 302 query.add_filter('estimated_hours', '>=', ['40.5'])
160 assert query.statement.include?("#{Issue.table_name}.estimated_hours >= 40.5") 303 assert query.statement.include?("#{Issue.table_name}.estimated_hours >= 40.5")
161 find_issues_with_query(query) 304 find_issues_with_query(query)
162 end 305 end
163 306
164 def test_operator_greater_than_on_custom_field 307 def test_operator_greater_than_on_int_custom_field
165 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true) 308 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
166 query = Query.new(:project => Project.find(1), :name => '_') 309 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
167 query.add_filter("cf_#{f.id}", '>=', ['40']) 310 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
168 assert query.statement.include?("CAST(custom_values.value AS decimal(60,3)) >= 40.0") 311 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
169 find_issues_with_query(query) 312
313 query = Query.new(:project => Project.find(1), :name => '_')
314 query.add_filter("cf_#{f.id}", '>=', ['8'])
315 issues = find_issues_with_query(query)
316 assert_equal 1, issues.size
317 assert_equal 2, issues.first.id
170 end 318 end
171 319
172 def test_operator_lesser_than 320 def test_operator_lesser_than
173 query = Query.new(:project => Project.find(1), :name => '_') 321 query = Query.new(:project => Project.find(1), :name => '_')
174 query.add_filter('done_ratio', '<=', ['30']) 322 query.add_filter('done_ratio', '<=', ['30'])
263 def test_operator_in_less_than 411 def test_operator_in_less_than
264 query = Query.new(:project => Project.find(1), :name => '_') 412 query = Query.new(:project => Project.find(1), :name => '_')
265 query.add_filter('due_date', '<t+', ['15']) 413 query.add_filter('due_date', '<t+', ['15'])
266 issues = find_issues_with_query(query) 414 issues = find_issues_with_query(query)
267 assert !issues.empty? 415 assert !issues.empty?
416 issues.each {|issue| assert(issue.due_date <= (Date.today + 15))}
417 end
418
419 def test_operator_in_the_next_days
420 query = Query.new(:project => Project.find(1), :name => '_')
421 query.add_filter('due_date', '><t+', ['15'])
422 issues = find_issues_with_query(query)
423 assert !issues.empty?
268 issues.each {|issue| assert(issue.due_date >= Date.today && issue.due_date <= (Date.today + 15))} 424 issues.each {|issue| assert(issue.due_date >= Date.today && issue.due_date <= (Date.today + 15))}
269 end 425 end
270 426
271 def test_operator_less_than_ago 427 def test_operator_less_than_ago
272 Issue.find(7).update_attribute(:due_date, (Date.today - 3)) 428 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
273 query = Query.new(:project => Project.find(1), :name => '_') 429 query = Query.new(:project => Project.find(1), :name => '_')
274 query.add_filter('due_date', '>t-', ['3']) 430 query.add_filter('due_date', '>t-', ['3'])
431 issues = find_issues_with_query(query)
432 assert !issues.empty?
433 issues.each {|issue| assert(issue.due_date >= (Date.today - 3))}
434 end
435
436 def test_operator_in_the_past_days
437 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
438 query = Query.new(:project => Project.find(1), :name => '_')
439 query.add_filter('due_date', '><t-', ['3'])
275 issues = find_issues_with_query(query) 440 issues = find_issues_with_query(query)
276 assert !issues.empty? 441 assert !issues.empty?
277 issues.each {|issue| assert(issue.due_date >= (Date.today - 3) && issue.due_date <= Date.today)} 442 issues.each {|issue| assert(issue.due_date >= (Date.today - 3) && issue.due_date <= Date.today)}
278 end 443 end
279 444
380 assert result.include?(i1) 545 assert result.include?(i1)
381 assert result.include?(i2) 546 assert result.include?(i2)
382 assert !result.include?(i3) 547 assert !result.include?(i3)
383 end 548 end
384 549
550 def test_user_custom_field_filtered_on_me
551 User.current = User.find(2)
552 cf = IssueCustomField.create!(:field_format => 'user', :is_for_all => true, :is_filter => true, :name => 'User custom field', :tracker_ids => [1])
553 issue1 = Issue.create!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '2'}, :subject => 'Test', :author_id => 1)
554 issue2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '3'})
555
556 query = Query.new(:name => '_', :project => Project.find(1))
557 filter = query.available_filters["cf_#{cf.id}"]
558 assert_not_nil filter
559 assert_include 'me', filter[:values].map{|v| v[1]}
560
561 query.filters = { "cf_#{cf.id}" => {:operator => '=', :values => ['me']}}
562 result = query.issues
563 assert_equal 1, result.size
564 assert_equal issue1, result.first
565 end
566
567 def test_filter_my_projects
568 User.current = User.find(2)
569 query = Query.new(:name => '_')
570 filter = query.available_filters['project_id']
571 assert_not_nil filter
572 assert_include 'mine', filter[:values].map{|v| v[1]}
573
574 query.filters = { 'project_id' => {:operator => '=', :values => ['mine']}}
575 result = query.issues
576 assert_nil result.detect {|issue| !User.current.member_of?(issue.project)}
577 end
578
385 def test_filter_watched_issues 579 def test_filter_watched_issues
386 User.current = User.find(1) 580 User.current = User.find(1)
387 query = Query.new(:name => '_', :filters => { 'watcher_id' => {:operator => '=', :values => ['me']}}) 581 query = Query.new(:name => '_', :filters => { 'watcher_id' => {:operator => '=', :values => ['me']}})
388 result = find_issues_with_query(query) 582 result = find_issues_with_query(query)
389 assert_not_nil result 583 assert_not_nil result
400 assert !result.empty? 594 assert !result.empty?
401 assert_equal((Issue.visible - Issue.watched_by(User.current)).sort_by(&:id).size, result.sort_by(&:id).size) 595 assert_equal((Issue.visible - Issue.watched_by(User.current)).sort_by(&:id).size, result.sort_by(&:id).size)
402 User.current = nil 596 User.current = nil
403 end 597 end
404 598
599 def test_filter_on_project_custom_field
600 field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
601 CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
602 CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
603
604 query = Query.new(:name => '_')
605 filter_name = "project.cf_#{field.id}"
606 assert_include filter_name, query.available_filters.keys
607 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
608 assert_equal [3, 5], find_issues_with_query(query).map(&:project_id).uniq.sort
609 end
610
611 def test_filter_on_author_custom_field
612 field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
613 CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
614
615 query = Query.new(:name => '_')
616 filter_name = "author.cf_#{field.id}"
617 assert_include filter_name, query.available_filters.keys
618 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
619 assert_equal [3], find_issues_with_query(query).map(&:author_id).uniq.sort
620 end
621
622 def test_filter_on_assigned_to_custom_field
623 field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
624 CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
625
626 query = Query.new(:name => '_')
627 filter_name = "assigned_to.cf_#{field.id}"
628 assert_include filter_name, query.available_filters.keys
629 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
630 assert_equal [3], find_issues_with_query(query).map(&:assigned_to_id).uniq.sort
631 end
632
633 def test_filter_on_fixed_version_custom_field
634 field = VersionCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
635 CustomValue.create!(:custom_field => field, :customized => Version.find(2), :value => 'Foo')
636
637 query = Query.new(:name => '_')
638 filter_name = "fixed_version.cf_#{field.id}"
639 assert_include filter_name, query.available_filters.keys
640 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
641 assert_equal [2], find_issues_with_query(query).map(&:fixed_version_id).uniq.sort
642 end
643
644 def test_filter_on_relations_with_a_specific_issue
645 IssueRelation.delete_all
646 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
647 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
648
649 query = Query.new(:name => '_')
650 query.filters = {"relates" => {:operator => '=', :values => ['1']}}
651 assert_equal [2, 3], find_issues_with_query(query).map(&:id).sort
652
653 query = Query.new(:name => '_')
654 query.filters = {"relates" => {:operator => '=', :values => ['2']}}
655 assert_equal [1], find_issues_with_query(query).map(&:id).sort
656 end
657
658 def test_filter_on_relations_with_any_issues_in_a_project
659 IssueRelation.delete_all
660 with_settings :cross_project_issue_relations => '1' do
661 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
662 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(2).issues.first)
663 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
664 end
665
666 query = Query.new(:name => '_')
667 query.filters = {"relates" => {:operator => '=p', :values => ['2']}}
668 assert_equal [1, 2], find_issues_with_query(query).map(&:id).sort
669
670 query = Query.new(:name => '_')
671 query.filters = {"relates" => {:operator => '=p', :values => ['3']}}
672 assert_equal [1], find_issues_with_query(query).map(&:id).sort
673
674 query = Query.new(:name => '_')
675 query.filters = {"relates" => {:operator => '=p', :values => ['4']}}
676 assert_equal [], find_issues_with_query(query).map(&:id).sort
677 end
678
679 def test_filter_on_relations_with_any_issues_not_in_a_project
680 IssueRelation.delete_all
681 with_settings :cross_project_issue_relations => '1' do
682 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
683 #IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(1).issues.first)
684 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
685 end
686
687 query = Query.new(:name => '_')
688 query.filters = {"relates" => {:operator => '=!p', :values => ['1']}}
689 assert_equal [1], find_issues_with_query(query).map(&:id).sort
690 end
691
692 def test_filter_on_relations_with_no_issues_in_a_project
693 IssueRelation.delete_all
694 with_settings :cross_project_issue_relations => '1' do
695 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
696 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(3).issues.first)
697 IssueRelation.create!(:relation_type => "relates", :issue_to => Project.find(2).issues.first, :issue_from => Issue.find(3))
698 end
699
700 query = Query.new(:name => '_')
701 query.filters = {"relates" => {:operator => '!p', :values => ['2']}}
702 ids = find_issues_with_query(query).map(&:id).sort
703 assert_include 2, ids
704 assert_not_include 1, ids
705 assert_not_include 3, ids
706 end
707
708 def test_filter_on_relations_with_no_issues
709 IssueRelation.delete_all
710 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
711 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
712
713 query = Query.new(:name => '_')
714 query.filters = {"relates" => {:operator => '!*', :values => ['']}}
715 ids = find_issues_with_query(query).map(&:id)
716 assert_equal [], ids & [1, 2, 3]
717 assert_include 4, ids
718 end
719
720 def test_filter_on_relations_with_any_issues
721 IssueRelation.delete_all
722 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
723 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
724
725 query = Query.new(:name => '_')
726 query.filters = {"relates" => {:operator => '*', :values => ['']}}
727 assert_equal [1, 2, 3], find_issues_with_query(query).map(&:id).sort
728 end
729
405 def test_statement_should_be_nil_with_no_filters 730 def test_statement_should_be_nil_with_no_filters
406 q = Query.new(:name => '_') 731 q = Query.new(:name => '_')
407 q.filters = {} 732 q.filters = {}
408 733
409 assert q.valid? 734 assert q.valid?
410 assert_nil q.statement 735 assert_nil q.statement
411 end 736 end
412 737
413 def test_default_columns 738 def test_default_columns
414 q = Query.new 739 q = Query.new
415 assert !q.columns.empty? 740 assert q.columns.any?
741 assert q.inline_columns.any?
742 assert q.block_columns.empty?
416 end 743 end
417 744
418 def test_set_column_names 745 def test_set_column_names
419 q = Query.new 746 q = Query.new
420 q.column_names = ['tracker', :subject, '', 'unknonw_column'] 747 q.column_names = ['tracker', :subject, '', 'unknonw_column']
421 assert_equal [:tracker, :subject], q.columns.collect {|c| c.name} 748 assert_equal [:tracker, :subject], q.columns.collect {|c| c.name}
422 c = q.columns.first 749 c = q.columns.first
423 assert q.has_column?(c) 750 assert q.has_column?(c)
424 end 751 end
425 752
753 def test_inline_and_block_columns
754 q = Query.new
755 q.column_names = ['subject', 'description', 'tracker']
756
757 assert_equal [:subject, :tracker], q.inline_columns.map(&:name)
758 assert_equal [:description], q.block_columns.map(&:name)
759 end
760
761 def test_custom_field_columns_should_be_inline
762 q = Query.new
763 columns = q.available_columns.select {|column| column.is_a? QueryCustomFieldColumn}
764 assert columns.any?
765 assert_nil columns.detect {|column| !column.inline?}
766 end
767
768 def test_query_should_preload_spent_hours
769 q = Query.new(:name => '_', :column_names => [:subject, :spent_hours])
770 assert q.has_column?(:spent_hours)
771 issues = q.issues
772 assert_not_nil issues.first.instance_variable_get("@spent_hours")
773 end
774
426 def test_groupable_columns_should_include_custom_fields 775 def test_groupable_columns_should_include_custom_fields
427 q = Query.new 776 q = Query.new
428 assert q.groupable_columns.detect {|c| c.is_a? QueryCustomFieldColumn} 777 column = q.groupable_columns.detect {|c| c.name == :cf_1}
778 assert_not_nil column
779 assert_kind_of QueryCustomFieldColumn, column
780 end
781
782 def test_groupable_columns_should_not_include_multi_custom_fields
783 field = CustomField.find(1)
784 field.update_attribute :multiple, true
785
786 q = Query.new
787 column = q.groupable_columns.detect {|c| c.name == :cf_1}
788 assert_nil column
789 end
790
791 def test_groupable_columns_should_include_user_custom_fields
792 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'user')
793
794 q = Query.new
795 assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
796 end
797
798 def test_groupable_columns_should_include_version_custom_fields
799 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'version')
800
801 q = Query.new
802 assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
429 end 803 end
430 804
431 def test_grouped_with_valid_column 805 def test_grouped_with_valid_column
432 q = Query.new(:group_by => 'status') 806 q = Query.new(:group_by => 'status')
433 assert q.grouped? 807 assert q.grouped?
458 assert q.sortable_columns.has_key?('author') 832 assert q.sortable_columns.has_key?('author')
459 assert_equal %w(authors.lastname authors.firstname authors.id), q.sortable_columns['author'] 833 assert_equal %w(authors.lastname authors.firstname authors.id), q.sortable_columns['author']
460 end 834 end
461 end 835 end
462 836
837 def test_sortable_columns_should_include_custom_field
838 q = Query.new
839 assert q.sortable_columns['cf_1']
840 end
841
842 def test_sortable_columns_should_not_include_multi_custom_field
843 field = CustomField.find(1)
844 field.update_attribute :multiple, true
845
846 q = Query.new
847 assert !q.sortable_columns['cf_1']
848 end
849
463 def test_default_sort 850 def test_default_sort
464 q = Query.new 851 q = Query.new
465 assert_equal [], q.sort_criteria 852 assert_equal [], q.sort_criteria
466 end 853 end
467 854
488 def test_sort_by_string_custom_field_asc 875 def test_sort_by_string_custom_field_asc
489 q = Query.new 876 q = Query.new
490 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' } 877 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
491 assert c 878 assert c
492 assert c.sortable 879 assert c.sortable
493 issues = Issue.find :all, 880 issues = Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
494 :include => [ :assigned_to, :status, :tracker, :project, :priority ], 881 q.statement
495 :conditions => q.statement, 882 ).order("#{c.sortable} ASC").all
496 :order => "#{c.sortable} ASC"
497 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s} 883 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
498 assert !values.empty? 884 assert !values.empty?
499 assert_equal values.sort, values 885 assert_equal values.sort, values
500 end 886 end
501 887
502 def test_sort_by_string_custom_field_desc 888 def test_sort_by_string_custom_field_desc
503 q = Query.new 889 q = Query.new
504 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' } 890 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
505 assert c 891 assert c
506 assert c.sortable 892 assert c.sortable
507 issues = Issue.find :all, 893 issues = Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
508 :include => [ :assigned_to, :status, :tracker, :project, :priority ], 894 q.statement
509 :conditions => q.statement, 895 ).order("#{c.sortable} DESC").all
510 :order => "#{c.sortable} DESC"
511 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s} 896 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
512 assert !values.empty? 897 assert !values.empty?
513 assert_equal values.sort.reverse, values 898 assert_equal values.sort.reverse, values
514 end 899 end
515 900
516 def test_sort_by_float_custom_field_asc 901 def test_sort_by_float_custom_field_asc
517 q = Query.new 902 q = Query.new
518 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'float' } 903 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'float' }
519 assert c 904 assert c
520 assert c.sortable 905 assert c.sortable
521 issues = Issue.find :all, 906 issues = Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
522 :include => [ :assigned_to, :status, :tracker, :project, :priority ], 907 q.statement
523 :conditions => q.statement, 908 ).order("#{c.sortable} ASC").all
524 :order => "#{c.sortable} ASC"
525 values = issues.collect {|i| begin; Kernel.Float(i.custom_value_for(c.custom_field).to_s); rescue; nil; end}.compact 909 values = issues.collect {|i| begin; Kernel.Float(i.custom_value_for(c.custom_field).to_s); rescue; nil; end}.compact
526 assert !values.empty? 910 assert !values.empty?
527 assert_equal values.sort, values 911 assert_equal values.sort, values
528 end 912 end
529 913
539 issue_count = q.issue_count 923 issue_count = q.issue_count
540 assert_equal q.issues.size, issue_count 924 assert_equal q.issues.size, issue_count
541 end 925 end
542 926
543 def test_issue_count_with_archived_issues 927 def test_issue_count_with_archived_issues
544 p = Project.generate!( :status => Project::STATUS_ARCHIVED ) 928 p = Project.generate! do |project|
929 project.status = Project::STATUS_ARCHIVED
930 end
545 i = Issue.generate!( :project => p, :tracker => p.trackers.first ) 931 i = Issue.generate!( :project => p, :tracker => p.trackers.first )
546 assert !i.visible? 932 assert !i.visible?
547 933
548 test_issue_count 934 test_issue_count
549 end 935 end
572 assert_kind_of Hash, count_by_group 958 assert_kind_of Hash, count_by_group
573 assert_equal %w(Date NilClass), count_by_group.keys.collect {|k| k.class.name}.uniq.sort 959 assert_equal %w(Date NilClass), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
574 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq 960 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
575 end 961 end
576 962
963 def test_issue_count_with_nil_group_only
964 Issue.update_all("assigned_to_id = NULL")
965
966 q = Query.new(:name => '_', :group_by => 'assigned_to')
967 count_by_group = q.issue_count_by_group
968 assert_kind_of Hash, count_by_group
969 assert_equal 1, count_by_group.keys.size
970 assert_nil count_by_group.keys.first
971 end
972
973 def test_issue_ids
974 q = Query.new(:name => '_')
975 order = "issues.subject, issues.id"
976 issues = q.issues(:order => order)
977 assert_equal issues.map(&:id), q.issue_ids(:order => order)
978 end
979
577 def test_label_for 980 def test_label_for
578 q = Query.new 981 set_language_if_valid 'en'
579 assert_equal 'assigned_to', q.label_for('assigned_to_id') 982 q = Query.new
983 assert_equal 'Assignee', q.label_for('assigned_to_id')
984 end
985
986 def test_label_for_fr
987 set_language_if_valid 'fr'
988 q = Query.new
989 s = "Assign\xc3\xa9 \xc3\xa0"
990 s.force_encoding('UTF-8') if s.respond_to?(:force_encoding)
991 assert_equal s, q.label_for('assigned_to_id')
580 end 992 end
581 993
582 def test_editable_by 994 def test_editable_by
583 admin = User.find(1) 995 admin = User.find(1)
584 manager = User.find(2) 996 manager = User.find(2)
626 1038
627 should "include users of visible projects in cross-project view" do 1039 should "include users of visible projects in cross-project view" do
628 users = @query.available_filters["assigned_to_id"] 1040 users = @query.available_filters["assigned_to_id"]
629 assert_not_nil users 1041 assert_not_nil users
630 assert users[:values].map{|u|u[1]}.include?("3") 1042 assert users[:values].map{|u|u[1]}.include?("3")
1043 end
1044
1045 should "include users of subprojects" do
1046 user1 = User.generate!
1047 user2 = User.generate!
1048 project = Project.find(1)
1049 Member.create!(:principal => user1, :project => project.children.visible.first, :role_ids => [1])
1050 @query.project = project
1051
1052 users = @query.available_filters["assigned_to_id"]
1053 assert_not_nil users
1054 assert users[:values].map{|u|u[1]}.include?(user1.id.to_s)
1055 assert !users[:values].map{|u|u[1]}.include?(user2.id.to_s)
631 end 1056 end
632 1057
633 should "include visible projects in cross-project view" do 1058 should "include visible projects in cross-project view" do
634 projects = @query.available_filters["project_id"] 1059 projects = @query.available_filters["project_id"]
635 assert_not_nil projects 1060 assert_not_nil projects
756 @guest = User.generate! 1181 @guest = User.generate!
757 User.add_to_project(@manager, @project, @manager_role) 1182 User.add_to_project(@manager, @project, @manager_role)
758 User.add_to_project(@developer, @project, @developer_role) 1183 User.add_to_project(@developer, @project, @developer_role)
759 User.add_to_project(@boss, @project, [@manager_role, @developer_role]) 1184 User.add_to_project(@boss, @project, [@manager_role, @developer_role])
760 1185
761 @issue1 = Issue.generate_for_project!(@project, :assigned_to_id => @manager.id) 1186 @issue1 = Issue.generate!(:project => @project, :assigned_to_id => @manager.id)
762 @issue2 = Issue.generate_for_project!(@project, :assigned_to_id => @developer.id) 1187 @issue2 = Issue.generate!(:project => @project, :assigned_to_id => @developer.id)
763 @issue3 = Issue.generate_for_project!(@project, :assigned_to_id => @boss.id) 1188 @issue3 = Issue.generate!(:project => @project, :assigned_to_id => @boss.id)
764 @issue4 = Issue.generate_for_project!(@project, :assigned_to_id => @guest.id) 1189 @issue4 = Issue.generate!(:project => @project, :assigned_to_id => @guest.id)
765 @issue5 = Issue.generate_for_project!(@project) 1190 @issue5 = Issue.generate!(:project => @project)
766 end 1191 end
767 1192
768 should "search assigned to for users with the Role" do 1193 should "search assigned to for users with the Role" do
769 @query = Query.new(:name => '_', :project => @project) 1194 @query = Query.new(:name => '_', :project => @project)
770 @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s]) 1195 @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])