Mercurial > hg > soundsoftware-site
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]) |