##// END OF EJS Templates
Filtering issues on "related to" may ignore other filters (#14401)....
Jean-Philippe Lang -
r11828:4a70a319c5e6
parent child
Show More
@@ -1,479 +1,478
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 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.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class IssueQuery < Query
18 class IssueQuery < Query
19
19
20 self.queried_class = Issue
20 self.queried_class = Issue
21
21
22 self.available_columns = [
22 self.available_columns = [
23 QueryColumn.new(:id, :sortable => "#{Issue.table_name}.id", :default_order => 'desc', :caption => '#', :frozen => true),
23 QueryColumn.new(:id, :sortable => "#{Issue.table_name}.id", :default_order => 'desc', :caption => '#', :frozen => true),
24 QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true),
24 QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true),
25 QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
25 QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
26 QueryColumn.new(:parent, :sortable => ["#{Issue.table_name}.root_id", "#{Issue.table_name}.lft ASC"], :default_order => 'desc', :caption => :field_parent_issue),
26 QueryColumn.new(:parent, :sortable => ["#{Issue.table_name}.root_id", "#{Issue.table_name}.lft ASC"], :default_order => 'desc', :caption => :field_parent_issue),
27 QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position", :groupable => true),
27 QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position", :groupable => true),
28 QueryColumn.new(:priority, :sortable => "#{IssuePriority.table_name}.position", :default_order => 'desc', :groupable => true),
28 QueryColumn.new(:priority, :sortable => "#{IssuePriority.table_name}.position", :default_order => 'desc', :groupable => true),
29 QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"),
29 QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"),
30 QueryColumn.new(:author, :sortable => lambda {User.fields_for_order_statement("authors")}, :groupable => true),
30 QueryColumn.new(:author, :sortable => lambda {User.fields_for_order_statement("authors")}, :groupable => true),
31 QueryColumn.new(:assigned_to, :sortable => lambda {User.fields_for_order_statement}, :groupable => true),
31 QueryColumn.new(:assigned_to, :sortable => lambda {User.fields_for_order_statement}, :groupable => true),
32 QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc'),
32 QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc'),
33 QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true),
33 QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true),
34 QueryColumn.new(:fixed_version, :sortable => lambda {Version.fields_for_order_statement}, :groupable => true),
34 QueryColumn.new(:fixed_version, :sortable => lambda {Version.fields_for_order_statement}, :groupable => true),
35 QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
35 QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
36 QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
36 QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
37 QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
37 QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
38 QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
38 QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
39 QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
39 QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
40 QueryColumn.new(:closed_on, :sortable => "#{Issue.table_name}.closed_on", :default_order => 'desc'),
40 QueryColumn.new(:closed_on, :sortable => "#{Issue.table_name}.closed_on", :default_order => 'desc'),
41 QueryColumn.new(:relations, :caption => :label_related_issues),
41 QueryColumn.new(:relations, :caption => :label_related_issues),
42 QueryColumn.new(:description, :inline => false)
42 QueryColumn.new(:description, :inline => false)
43 ]
43 ]
44
44
45 scope :visible, lambda {|*args|
45 scope :visible, lambda {|*args|
46 user = args.shift || User.current
46 user = args.shift || User.current
47 base = Project.allowed_to_condition(user, :view_issues, *args)
47 base = Project.allowed_to_condition(user, :view_issues, *args)
48 scope = includes(:project).where("#{table_name}.project_id IS NULL OR (#{base})")
48 scope = includes(:project).where("#{table_name}.project_id IS NULL OR (#{base})")
49
49
50 if user.admin?
50 if user.admin?
51 scope.where("#{table_name}.visibility <> ? OR #{table_name}.user_id = ?", VISIBILITY_PRIVATE, user.id)
51 scope.where("#{table_name}.visibility <> ? OR #{table_name}.user_id = ?", VISIBILITY_PRIVATE, user.id)
52 elsif user.memberships.any?
52 elsif user.memberships.any?
53 scope.where("#{table_name}.visibility = ?" +
53 scope.where("#{table_name}.visibility = ?" +
54 " OR (#{table_name}.visibility = ? AND #{table_name}.id IN (" +
54 " OR (#{table_name}.visibility = ? AND #{table_name}.id IN (" +
55 "SELECT DISTINCT q.id FROM #{table_name} q" +
55 "SELECT DISTINCT q.id FROM #{table_name} q" +
56 " INNER JOIN #{table_name_prefix}queries_roles#{table_name_suffix} qr on qr.query_id = q.id" +
56 " INNER JOIN #{table_name_prefix}queries_roles#{table_name_suffix} qr on qr.query_id = q.id" +
57 " INNER JOIN #{MemberRole.table_name} mr ON mr.role_id = qr.role_id" +
57 " INNER JOIN #{MemberRole.table_name} mr ON mr.role_id = qr.role_id" +
58 " INNER JOIN #{Member.table_name} m ON m.id = mr.member_id AND m.user_id = ?" +
58 " INNER JOIN #{Member.table_name} m ON m.id = mr.member_id AND m.user_id = ?" +
59 " WHERE q.project_id IS NULL OR q.project_id = m.project_id))" +
59 " WHERE q.project_id IS NULL OR q.project_id = m.project_id))" +
60 " OR #{table_name}.user_id = ?",
60 " OR #{table_name}.user_id = ?",
61 VISIBILITY_PUBLIC, VISIBILITY_ROLES, user.id, user.id)
61 VISIBILITY_PUBLIC, VISIBILITY_ROLES, user.id, user.id)
62 elsif user.logged?
62 elsif user.logged?
63 scope.where("#{table_name}.visibility = ? OR #{table_name}.user_id = ?", VISIBILITY_PUBLIC, user.id)
63 scope.where("#{table_name}.visibility = ? OR #{table_name}.user_id = ?", VISIBILITY_PUBLIC, user.id)
64 else
64 else
65 scope.where("#{table_name}.visibility = ?", VISIBILITY_PUBLIC)
65 scope.where("#{table_name}.visibility = ?", VISIBILITY_PUBLIC)
66 end
66 end
67 }
67 }
68
68
69 def initialize(attributes=nil, *args)
69 def initialize(attributes=nil, *args)
70 super attributes
70 super attributes
71 self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
71 self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
72 end
72 end
73
73
74 # Returns true if the query is visible to +user+ or the current user.
74 # Returns true if the query is visible to +user+ or the current user.
75 def visible?(user=User.current)
75 def visible?(user=User.current)
76 return true if user.admin?
76 return true if user.admin?
77 return false unless project.nil? || user.allowed_to?(:view_issues, project)
77 return false unless project.nil? || user.allowed_to?(:view_issues, project)
78 case visibility
78 case visibility
79 when VISIBILITY_PUBLIC
79 when VISIBILITY_PUBLIC
80 true
80 true
81 when VISIBILITY_ROLES
81 when VISIBILITY_ROLES
82 if project
82 if project
83 (user.roles_for_project(project) & roles).any?
83 (user.roles_for_project(project) & roles).any?
84 else
84 else
85 Member.where(:user_id => user.id).joins(:roles).where(:member_roles => {:role_id => roles.map(&:id)}).any?
85 Member.where(:user_id => user.id).joins(:roles).where(:member_roles => {:role_id => roles.map(&:id)}).any?
86 end
86 end
87 else
87 else
88 user == self.user
88 user == self.user
89 end
89 end
90 end
90 end
91
91
92 def is_private?
92 def is_private?
93 visibility == VISIBILITY_PRIVATE
93 visibility == VISIBILITY_PRIVATE
94 end
94 end
95
95
96 def is_public?
96 def is_public?
97 !is_private?
97 !is_private?
98 end
98 end
99
99
100 def draw_relations
100 def draw_relations
101 r = options[:draw_relations]
101 r = options[:draw_relations]
102 r.nil? || r == '1'
102 r.nil? || r == '1'
103 end
103 end
104
104
105 def draw_relations=(arg)
105 def draw_relations=(arg)
106 options[:draw_relations] = (arg == '0' ? '0' : nil)
106 options[:draw_relations] = (arg == '0' ? '0' : nil)
107 end
107 end
108
108
109 def draw_progress_line
109 def draw_progress_line
110 r = options[:draw_progress_line]
110 r = options[:draw_progress_line]
111 r == '1'
111 r == '1'
112 end
112 end
113
113
114 def draw_progress_line=(arg)
114 def draw_progress_line=(arg)
115 options[:draw_progress_line] = (arg == '1' ? '1' : nil)
115 options[:draw_progress_line] = (arg == '1' ? '1' : nil)
116 end
116 end
117
117
118 def build_from_params(params)
118 def build_from_params(params)
119 super
119 super
120 self.draw_relations = params[:draw_relations] || (params[:query] && params[:query][:draw_relations])
120 self.draw_relations = params[:draw_relations] || (params[:query] && params[:query][:draw_relations])
121 self.draw_progress_line = params[:draw_progress_line] || (params[:query] && params[:query][:draw_progress_line])
121 self.draw_progress_line = params[:draw_progress_line] || (params[:query] && params[:query][:draw_progress_line])
122 self
122 self
123 end
123 end
124
124
125 def initialize_available_filters
125 def initialize_available_filters
126 principals = []
126 principals = []
127 subprojects = []
127 subprojects = []
128 versions = []
128 versions = []
129 categories = []
129 categories = []
130 issue_custom_fields = []
130 issue_custom_fields = []
131
131
132 if project
132 if project
133 principals += project.principals.sort
133 principals += project.principals.sort
134 unless project.leaf?
134 unless project.leaf?
135 subprojects = project.descendants.visible.all
135 subprojects = project.descendants.visible.all
136 principals += Principal.member_of(subprojects)
136 principals += Principal.member_of(subprojects)
137 end
137 end
138 versions = project.shared_versions.all
138 versions = project.shared_versions.all
139 categories = project.issue_categories.all
139 categories = project.issue_categories.all
140 issue_custom_fields = project.all_issue_custom_fields
140 issue_custom_fields = project.all_issue_custom_fields
141 else
141 else
142 if all_projects.any?
142 if all_projects.any?
143 principals += Principal.member_of(all_projects)
143 principals += Principal.member_of(all_projects)
144 end
144 end
145 versions = Version.visible.find_all_by_sharing('system')
145 versions = Version.visible.find_all_by_sharing('system')
146 issue_custom_fields = IssueCustomField.where(:is_for_all => true)
146 issue_custom_fields = IssueCustomField.where(:is_for_all => true)
147 end
147 end
148 principals.uniq!
148 principals.uniq!
149 principals.sort!
149 principals.sort!
150 users = principals.select {|p| p.is_a?(User)}
150 users = principals.select {|p| p.is_a?(User)}
151
151
152 add_available_filter "status_id",
152 add_available_filter "status_id",
153 :type => :list_status, :values => IssueStatus.sorted.all.collect{|s| [s.name, s.id.to_s] }
153 :type => :list_status, :values => IssueStatus.sorted.all.collect{|s| [s.name, s.id.to_s] }
154
154
155 if project.nil?
155 if project.nil?
156 project_values = []
156 project_values = []
157 if User.current.logged? && User.current.memberships.any?
157 if User.current.logged? && User.current.memberships.any?
158 project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"]
158 project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"]
159 end
159 end
160 project_values += all_projects_values
160 project_values += all_projects_values
161 add_available_filter("project_id",
161 add_available_filter("project_id",
162 :type => :list, :values => project_values
162 :type => :list, :values => project_values
163 ) unless project_values.empty?
163 ) unless project_values.empty?
164 end
164 end
165
165
166 add_available_filter "tracker_id",
166 add_available_filter "tracker_id",
167 :type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s] }
167 :type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s] }
168 add_available_filter "priority_id",
168 add_available_filter "priority_id",
169 :type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] }
169 :type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] }
170
170
171 author_values = []
171 author_values = []
172 author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
172 author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
173 author_values += users.collect{|s| [s.name, s.id.to_s] }
173 author_values += users.collect{|s| [s.name, s.id.to_s] }
174 add_available_filter("author_id",
174 add_available_filter("author_id",
175 :type => :list, :values => author_values
175 :type => :list, :values => author_values
176 ) unless author_values.empty?
176 ) unless author_values.empty?
177
177
178 assigned_to_values = []
178 assigned_to_values = []
179 assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
179 assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
180 assigned_to_values += (Setting.issue_group_assignment? ?
180 assigned_to_values += (Setting.issue_group_assignment? ?
181 principals : users).collect{|s| [s.name, s.id.to_s] }
181 principals : users).collect{|s| [s.name, s.id.to_s] }
182 add_available_filter("assigned_to_id",
182 add_available_filter("assigned_to_id",
183 :type => :list_optional, :values => assigned_to_values
183 :type => :list_optional, :values => assigned_to_values
184 ) unless assigned_to_values.empty?
184 ) unless assigned_to_values.empty?
185
185
186 group_values = Group.all.collect {|g| [g.name, g.id.to_s] }
186 group_values = Group.all.collect {|g| [g.name, g.id.to_s] }
187 add_available_filter("member_of_group",
187 add_available_filter("member_of_group",
188 :type => :list_optional, :values => group_values
188 :type => :list_optional, :values => group_values
189 ) unless group_values.empty?
189 ) unless group_values.empty?
190
190
191 role_values = Role.givable.collect {|r| [r.name, r.id.to_s] }
191 role_values = Role.givable.collect {|r| [r.name, r.id.to_s] }
192 add_available_filter("assigned_to_role",
192 add_available_filter("assigned_to_role",
193 :type => :list_optional, :values => role_values
193 :type => :list_optional, :values => role_values
194 ) unless role_values.empty?
194 ) unless role_values.empty?
195
195
196 if versions.any?
196 if versions.any?
197 add_available_filter "fixed_version_id",
197 add_available_filter "fixed_version_id",
198 :type => :list_optional,
198 :type => :list_optional,
199 :values => versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] }
199 :values => versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] }
200 end
200 end
201
201
202 if categories.any?
202 if categories.any?
203 add_available_filter "category_id",
203 add_available_filter "category_id",
204 :type => :list_optional,
204 :type => :list_optional,
205 :values => categories.collect{|s| [s.name, s.id.to_s] }
205 :values => categories.collect{|s| [s.name, s.id.to_s] }
206 end
206 end
207
207
208 add_available_filter "subject", :type => :text
208 add_available_filter "subject", :type => :text
209 add_available_filter "created_on", :type => :date_past
209 add_available_filter "created_on", :type => :date_past
210 add_available_filter "updated_on", :type => :date_past
210 add_available_filter "updated_on", :type => :date_past
211 add_available_filter "closed_on", :type => :date_past
211 add_available_filter "closed_on", :type => :date_past
212 add_available_filter "start_date", :type => :date
212 add_available_filter "start_date", :type => :date
213 add_available_filter "due_date", :type => :date
213 add_available_filter "due_date", :type => :date
214 add_available_filter "estimated_hours", :type => :float
214 add_available_filter "estimated_hours", :type => :float
215 add_available_filter "done_ratio", :type => :integer
215 add_available_filter "done_ratio", :type => :integer
216
216
217 if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
217 if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
218 User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
218 User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
219 add_available_filter "is_private",
219 add_available_filter "is_private",
220 :type => :list,
220 :type => :list,
221 :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]]
221 :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]]
222 end
222 end
223
223
224 if User.current.logged?
224 if User.current.logged?
225 add_available_filter "watcher_id",
225 add_available_filter "watcher_id",
226 :type => :list, :values => [["<< #{l(:label_me)} >>", "me"]]
226 :type => :list, :values => [["<< #{l(:label_me)} >>", "me"]]
227 end
227 end
228
228
229 if subprojects.any?
229 if subprojects.any?
230 add_available_filter "subproject_id",
230 add_available_filter "subproject_id",
231 :type => :list_subprojects,
231 :type => :list_subprojects,
232 :values => subprojects.collect{|s| [s.name, s.id.to_s] }
232 :values => subprojects.collect{|s| [s.name, s.id.to_s] }
233 end
233 end
234
234
235 add_custom_fields_filters(issue_custom_fields)
235 add_custom_fields_filters(issue_custom_fields)
236
236
237 add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version
237 add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version
238
238
239 IssueRelation::TYPES.each do |relation_type, options|
239 IssueRelation::TYPES.each do |relation_type, options|
240 add_available_filter relation_type, :type => :relation, :label => options[:name]
240 add_available_filter relation_type, :type => :relation, :label => options[:name]
241 end
241 end
242
242
243 Tracker.disabled_core_fields(trackers).each {|field|
243 Tracker.disabled_core_fields(trackers).each {|field|
244 delete_available_filter field
244 delete_available_filter field
245 }
245 }
246 end
246 end
247
247
248 def available_columns
248 def available_columns
249 return @available_columns if @available_columns
249 return @available_columns if @available_columns
250 @available_columns = self.class.available_columns.dup
250 @available_columns = self.class.available_columns.dup
251 @available_columns += (project ?
251 @available_columns += (project ?
252 project.all_issue_custom_fields :
252 project.all_issue_custom_fields :
253 IssueCustomField
253 IssueCustomField
254 ).visible.collect {|cf| QueryCustomFieldColumn.new(cf) }
254 ).visible.collect {|cf| QueryCustomFieldColumn.new(cf) }
255
255
256 if User.current.allowed_to?(:view_time_entries, project, :global => true)
256 if User.current.allowed_to?(:view_time_entries, project, :global => true)
257 index = nil
257 index = nil
258 @available_columns.each_with_index {|column, i| index = i if column.name == :estimated_hours}
258 @available_columns.each_with_index {|column, i| index = i if column.name == :estimated_hours}
259 index = (index ? index + 1 : -1)
259 index = (index ? index + 1 : -1)
260 # insert the column after estimated_hours or at the end
260 # insert the column after estimated_hours or at the end
261 @available_columns.insert index, QueryColumn.new(:spent_hours,
261 @available_columns.insert index, QueryColumn.new(:spent_hours,
262 :sortable => "COALESCE((SELECT SUM(hours) FROM #{TimeEntry.table_name} WHERE #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id), 0)",
262 :sortable => "COALESCE((SELECT SUM(hours) FROM #{TimeEntry.table_name} WHERE #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id), 0)",
263 :default_order => 'desc',
263 :default_order => 'desc',
264 :caption => :label_spent_time
264 :caption => :label_spent_time
265 )
265 )
266 end
266 end
267
267
268 if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
268 if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
269 User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
269 User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
270 @available_columns << QueryColumn.new(:is_private, :sortable => "#{Issue.table_name}.is_private")
270 @available_columns << QueryColumn.new(:is_private, :sortable => "#{Issue.table_name}.is_private")
271 end
271 end
272
272
273 disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.sub(/_id$/, '')}
273 disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.sub(/_id$/, '')}
274 @available_columns.reject! {|column|
274 @available_columns.reject! {|column|
275 disabled_fields.include?(column.name.to_s)
275 disabled_fields.include?(column.name.to_s)
276 }
276 }
277
277
278 @available_columns
278 @available_columns
279 end
279 end
280
280
281 def default_columns_names
281 def default_columns_names
282 @default_columns_names ||= begin
282 @default_columns_names ||= begin
283 default_columns = Setting.issue_list_default_columns.map(&:to_sym)
283 default_columns = Setting.issue_list_default_columns.map(&:to_sym)
284
284
285 project.present? ? default_columns : [:project] | default_columns
285 project.present? ? default_columns : [:project] | default_columns
286 end
286 end
287 end
287 end
288
288
289 # Returns the issue count
289 # Returns the issue count
290 def issue_count
290 def issue_count
291 Issue.visible.joins(:status, :project).where(statement).count
291 Issue.visible.joins(:status, :project).where(statement).count
292 rescue ::ActiveRecord::StatementInvalid => e
292 rescue ::ActiveRecord::StatementInvalid => e
293 raise StatementInvalid.new(e.message)
293 raise StatementInvalid.new(e.message)
294 end
294 end
295
295
296 # Returns the issue count by group or nil if query is not grouped
296 # Returns the issue count by group or nil if query is not grouped
297 def issue_count_by_group
297 def issue_count_by_group
298 r = nil
298 r = nil
299 if grouped?
299 if grouped?
300 begin
300 begin
301 # Rails3 will raise an (unexpected) RecordNotFound if there's only a nil group value
301 # Rails3 will raise an (unexpected) RecordNotFound if there's only a nil group value
302 r = Issue.visible.
302 r = Issue.visible.
303 joins(:status, :project).
303 joins(:status, :project).
304 where(statement).
304 where(statement).
305 joins(joins_for_order_statement(group_by_statement)).
305 joins(joins_for_order_statement(group_by_statement)).
306 group(group_by_statement).
306 group(group_by_statement).
307 count
307 count
308 rescue ActiveRecord::RecordNotFound
308 rescue ActiveRecord::RecordNotFound
309 r = {nil => issue_count}
309 r = {nil => issue_count}
310 end
310 end
311 c = group_by_column
311 c = group_by_column
312 if c.is_a?(QueryCustomFieldColumn)
312 if c.is_a?(QueryCustomFieldColumn)
313 r = r.keys.inject({}) {|h, k| h[c.custom_field.cast_value(k)] = r[k]; h}
313 r = r.keys.inject({}) {|h, k| h[c.custom_field.cast_value(k)] = r[k]; h}
314 end
314 end
315 end
315 end
316 r
316 r
317 rescue ::ActiveRecord::StatementInvalid => e
317 rescue ::ActiveRecord::StatementInvalid => e
318 raise StatementInvalid.new(e.message)
318 raise StatementInvalid.new(e.message)
319 end
319 end
320
320
321 # Returns the issues
321 # Returns the issues
322 # Valid options are :order, :offset, :limit, :include, :conditions
322 # Valid options are :order, :offset, :limit, :include, :conditions
323 def issues(options={})
323 def issues(options={})
324 order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?)
324 order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?)
325
325
326 issues = Issue.visible.
326 issues = Issue.visible.
327 joins(:status, :project).
327 joins(:status, :project).
328 where(statement).
328 where(statement).
329 includes(([:status, :project] + (options[:include] || [])).uniq).
329 includes(([:status, :project] + (options[:include] || [])).uniq).
330 where(options[:conditions]).
330 where(options[:conditions]).
331 order(order_option).
331 order(order_option).
332 joins(joins_for_order_statement(order_option.join(','))).
332 joins(joins_for_order_statement(order_option.join(','))).
333 limit(options[:limit]).
333 limit(options[:limit]).
334 offset(options[:offset]).
334 offset(options[:offset]).
335 all
335 all
336
336
337 if has_column?(:spent_hours)
337 if has_column?(:spent_hours)
338 Issue.load_visible_spent_hours(issues)
338 Issue.load_visible_spent_hours(issues)
339 end
339 end
340 if has_column?(:relations)
340 if has_column?(:relations)
341 Issue.load_visible_relations(issues)
341 Issue.load_visible_relations(issues)
342 end
342 end
343 issues
343 issues
344 rescue ::ActiveRecord::StatementInvalid => e
344 rescue ::ActiveRecord::StatementInvalid => e
345 raise StatementInvalid.new(e.message)
345 raise StatementInvalid.new(e.message)
346 end
346 end
347
347
348 # Returns the issues ids
348 # Returns the issues ids
349 def issue_ids(options={})
349 def issue_ids(options={})
350 order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?)
350 order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?)
351
351
352 Issue.visible.
352 Issue.visible.
353 joins(:status, :project).
353 joins(:status, :project).
354 where(statement).
354 where(statement).
355 includes(([:status, :project] + (options[:include] || [])).uniq).
355 includes(([:status, :project] + (options[:include] || [])).uniq).
356 where(options[:conditions]).
356 where(options[:conditions]).
357 order(order_option).
357 order(order_option).
358 joins(joins_for_order_statement(order_option.join(','))).
358 joins(joins_for_order_statement(order_option.join(','))).
359 limit(options[:limit]).
359 limit(options[:limit]).
360 offset(options[:offset]).
360 offset(options[:offset]).
361 find_ids
361 find_ids
362 rescue ::ActiveRecord::StatementInvalid => e
362 rescue ::ActiveRecord::StatementInvalid => e
363 raise StatementInvalid.new(e.message)
363 raise StatementInvalid.new(e.message)
364 end
364 end
365
365
366 # Returns the journals
366 # Returns the journals
367 # Valid options are :order, :offset, :limit
367 # Valid options are :order, :offset, :limit
368 def journals(options={})
368 def journals(options={})
369 Journal.visible.
369 Journal.visible.
370 joins(:issue => [:project, :status]).
370 joins(:issue => [:project, :status]).
371 where(statement).
371 where(statement).
372 order(options[:order]).
372 order(options[:order]).
373 limit(options[:limit]).
373 limit(options[:limit]).
374 offset(options[:offset]).
374 offset(options[:offset]).
375 preload(:details, :user, {:issue => [:project, :author, :tracker, :status]}).
375 preload(:details, :user, {:issue => [:project, :author, :tracker, :status]}).
376 all
376 all
377 rescue ::ActiveRecord::StatementInvalid => e
377 rescue ::ActiveRecord::StatementInvalid => e
378 raise StatementInvalid.new(e.message)
378 raise StatementInvalid.new(e.message)
379 end
379 end
380
380
381 # Returns the versions
381 # Returns the versions
382 # Valid options are :conditions
382 # Valid options are :conditions
383 def versions(options={})
383 def versions(options={})
384 Version.visible.
384 Version.visible.
385 where(project_statement).
385 where(project_statement).
386 where(options[:conditions]).
386 where(options[:conditions]).
387 includes(:project).
387 includes(:project).
388 all
388 all
389 rescue ::ActiveRecord::StatementInvalid => e
389 rescue ::ActiveRecord::StatementInvalid => e
390 raise StatementInvalid.new(e.message)
390 raise StatementInvalid.new(e.message)
391 end
391 end
392
392
393 def sql_for_watcher_id_field(field, operator, value)
393 def sql_for_watcher_id_field(field, operator, value)
394 db_table = Watcher.table_name
394 db_table = Watcher.table_name
395 "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND " +
395 "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND " +
396 sql_for_field(field, '=', value, db_table, 'user_id') + ')'
396 sql_for_field(field, '=', value, db_table, 'user_id') + ')'
397 end
397 end
398
398
399 def sql_for_member_of_group_field(field, operator, value)
399 def sql_for_member_of_group_field(field, operator, value)
400 if operator == '*' # Any group
400 if operator == '*' # Any group
401 groups = Group.all
401 groups = Group.all
402 operator = '=' # Override the operator since we want to find by assigned_to
402 operator = '=' # Override the operator since we want to find by assigned_to
403 elsif operator == "!*"
403 elsif operator == "!*"
404 groups = Group.all
404 groups = Group.all
405 operator = '!' # Override the operator since we want to find by assigned_to
405 operator = '!' # Override the operator since we want to find by assigned_to
406 else
406 else
407 groups = Group.find_all_by_id(value)
407 groups = Group.find_all_by_id(value)
408 end
408 end
409 groups ||= []
409 groups ||= []
410
410
411 members_of_groups = groups.inject([]) {|user_ids, group|
411 members_of_groups = groups.inject([]) {|user_ids, group|
412 user_ids + group.user_ids + [group.id]
412 user_ids + group.user_ids + [group.id]
413 }.uniq.compact.sort.collect(&:to_s)
413 }.uniq.compact.sort.collect(&:to_s)
414
414
415 '(' + sql_for_field("assigned_to_id", operator, members_of_groups, Issue.table_name, "assigned_to_id", false) + ')'
415 '(' + sql_for_field("assigned_to_id", operator, members_of_groups, Issue.table_name, "assigned_to_id", false) + ')'
416 end
416 end
417
417
418 def sql_for_assigned_to_role_field(field, operator, value)
418 def sql_for_assigned_to_role_field(field, operator, value)
419 case operator
419 case operator
420 when "*", "!*" # Member / Not member
420 when "*", "!*" # Member / Not member
421 sw = operator == "!*" ? 'NOT' : ''
421 sw = operator == "!*" ? 'NOT' : ''
422 nl = operator == "!*" ? "#{Issue.table_name}.assigned_to_id IS NULL OR" : ''
422 nl = operator == "!*" ? "#{Issue.table_name}.assigned_to_id IS NULL OR" : ''
423 "(#{nl} #{Issue.table_name}.assigned_to_id #{sw} IN (SELECT DISTINCT #{Member.table_name}.user_id FROM #{Member.table_name}" +
423 "(#{nl} #{Issue.table_name}.assigned_to_id #{sw} IN (SELECT DISTINCT #{Member.table_name}.user_id FROM #{Member.table_name}" +
424 " WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id))"
424 " WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id))"
425 when "=", "!"
425 when "=", "!"
426 role_cond = value.any? ?
426 role_cond = value.any? ?
427 "#{MemberRole.table_name}.role_id IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")" :
427 "#{MemberRole.table_name}.role_id IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")" :
428 "1=0"
428 "1=0"
429
429
430 sw = operator == "!" ? 'NOT' : ''
430 sw = operator == "!" ? 'NOT' : ''
431 nl = operator == "!" ? "#{Issue.table_name}.assigned_to_id IS NULL OR" : ''
431 nl = operator == "!" ? "#{Issue.table_name}.assigned_to_id IS NULL OR" : ''
432 "(#{nl} #{Issue.table_name}.assigned_to_id #{sw} IN (SELECT DISTINCT #{Member.table_name}.user_id FROM #{Member.table_name}, #{MemberRole.table_name}" +
432 "(#{nl} #{Issue.table_name}.assigned_to_id #{sw} IN (SELECT DISTINCT #{Member.table_name}.user_id FROM #{Member.table_name}, #{MemberRole.table_name}" +
433 " WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id AND #{Member.table_name}.id = #{MemberRole.table_name}.member_id AND #{role_cond}))"
433 " WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id AND #{Member.table_name}.id = #{MemberRole.table_name}.member_id AND #{role_cond}))"
434 end
434 end
435 end
435 end
436
436
437 def sql_for_is_private_field(field, operator, value)
437 def sql_for_is_private_field(field, operator, value)
438 op = (operator == "=" ? 'IN' : 'NOT IN')
438 op = (operator == "=" ? 'IN' : 'NOT IN')
439 va = value.map {|v| v == '0' ? connection.quoted_false : connection.quoted_true}.uniq.join(',')
439 va = value.map {|v| v == '0' ? connection.quoted_false : connection.quoted_true}.uniq.join(',')
440
440
441 "#{Issue.table_name}.is_private #{op} (#{va})"
441 "#{Issue.table_name}.is_private #{op} (#{va})"
442 end
442 end
443
443
444 def sql_for_relations(field, operator, value, options={})
444 def sql_for_relations(field, operator, value, options={})
445 relation_options = IssueRelation::TYPES[field]
445 relation_options = IssueRelation::TYPES[field]
446 return relation_options unless relation_options
446 return relation_options unless relation_options
447
447
448 relation_type = field
448 relation_type = field
449 join_column, target_join_column = "issue_from_id", "issue_to_id"
449 join_column, target_join_column = "issue_from_id", "issue_to_id"
450 if relation_options[:reverse] || options[:reverse]
450 if relation_options[:reverse] || options[:reverse]
451 relation_type = relation_options[:reverse] || relation_type
451 relation_type = relation_options[:reverse] || relation_type
452 join_column, target_join_column = target_join_column, join_column
452 join_column, target_join_column = target_join_column, join_column
453 end
453 end
454
454
455 sql = case operator
455 sql = case operator
456 when "*", "!*"
456 when "*", "!*"
457 op = (operator == "*" ? 'IN' : 'NOT IN')
457 op = (operator == "*" ? 'IN' : 'NOT IN')
458 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}')"
458 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}')"
459 when "=", "!"
459 when "=", "!"
460 op = (operator == "=" ? 'IN' : 'NOT IN')
460 op = (operator == "=" ? 'IN' : 'NOT IN')
461 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})"
461 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})"
462 when "=p", "=!p", "!p"
462 when "=p", "=!p", "!p"
463 op = (operator == "!p" ? 'NOT IN' : 'IN')
463 op = (operator == "!p" ? 'NOT IN' : 'IN')
464 comp = (operator == "=!p" ? '<>' : '=')
464 comp = (operator == "=!p" ? '<>' : '=')
465 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{comp} #{value.first.to_i})"
465 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{comp} #{value.first.to_i})"
466 end
466 end
467
467
468 if relation_options[:sym] == field && !options[:reverse]
468 if relation_options[:sym] == field && !options[:reverse]
469 sqls = [sql, sql_for_relations(field, operator, value, :reverse => true)]
469 sqls = [sql, sql_for_relations(field, operator, value, :reverse => true)]
470 sqls.join(["!", "!*", "!p"].include?(operator) ? " AND " : " OR ")
470 sql = sqls.join(["!", "!*", "!p"].include?(operator) ? " AND " : " OR ")
471 else
472 sql
473 end
471 end
472 "(#{sql})"
474 end
473 end
475
474
476 IssueRelation::TYPES.keys.each do |relation_type|
475 IssueRelation::TYPES.keys.each do |relation_type|
477 alias_method "sql_for_#{relation_type}_field".to_sym, :sql_for_relations
476 alias_method "sql_for_#{relation_type}_field".to_sym, :sql_for_relations
478 end
477 end
479 end
478 end
@@ -1,1363 +1,1378
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 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.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
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
21 include Redmine::I18n
22
22
23 fixtures :projects, :enabled_modules, :users, :members,
23 fixtures :projects, :enabled_modules, :users, :members,
24 :member_roles, :roles, :trackers, :issue_statuses,
24 :member_roles, :roles, :trackers, :issue_statuses,
25 :issue_categories, :enumerations, :issues,
25 :issue_categories, :enumerations, :issues,
26 :watchers, :custom_fields, :custom_values, :versions,
26 :watchers, :custom_fields, :custom_values, :versions,
27 :queries,
27 :queries,
28 :projects_trackers,
28 :projects_trackers,
29 :custom_fields_trackers
29 :custom_fields_trackers
30
30
31 def test_query_with_roles_visibility_should_validate_roles
31 def test_query_with_roles_visibility_should_validate_roles
32 set_language_if_valid 'en'
32 set_language_if_valid 'en'
33 query = IssueQuery.new(:name => 'Query', :visibility => IssueQuery::VISIBILITY_ROLES)
33 query = IssueQuery.new(:name => 'Query', :visibility => IssueQuery::VISIBILITY_ROLES)
34 assert !query.save
34 assert !query.save
35 assert_include "Roles can't be blank", query.errors.full_messages
35 assert_include "Roles can't be blank", query.errors.full_messages
36 query.role_ids = [1, 2]
36 query.role_ids = [1, 2]
37 assert query.save
37 assert query.save
38 end
38 end
39
39
40 def test_changing_roles_visibility_should_clear_roles
40 def test_changing_roles_visibility_should_clear_roles
41 query = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_ROLES, :role_ids => [1, 2])
41 query = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_ROLES, :role_ids => [1, 2])
42 assert_equal 2, query.roles.count
42 assert_equal 2, query.roles.count
43
43
44 query.visibility = IssueQuery::VISIBILITY_PUBLIC
44 query.visibility = IssueQuery::VISIBILITY_PUBLIC
45 query.save!
45 query.save!
46 assert_equal 0, query.roles.count
46 assert_equal 0, query.roles.count
47 end
47 end
48
48
49 def test_available_filters_should_be_ordered
49 def test_available_filters_should_be_ordered
50 set_language_if_valid 'en'
50 set_language_if_valid 'en'
51 query = IssueQuery.new
51 query = IssueQuery.new
52 assert_equal 0, query.available_filters.keys.index('status_id')
52 assert_equal 0, query.available_filters.keys.index('status_id')
53 expected_order = [
53 expected_order = [
54 "Status",
54 "Status",
55 "Project",
55 "Project",
56 "Tracker",
56 "Tracker",
57 "Priority"
57 "Priority"
58 ]
58 ]
59 assert_equal expected_order,
59 assert_equal expected_order,
60 (query.available_filters.values.map{|v| v[:name]} & expected_order)
60 (query.available_filters.values.map{|v| v[:name]} & expected_order)
61 end
61 end
62
62
63 def test_available_filters_with_custom_fields_should_be_ordered
63 def test_available_filters_with_custom_fields_should_be_ordered
64 set_language_if_valid 'en'
64 set_language_if_valid 'en'
65 UserCustomField.create!(
65 UserCustomField.create!(
66 :name => 'order test', :field_format => 'string',
66 :name => 'order test', :field_format => 'string',
67 :is_for_all => true, :is_filter => true
67 :is_for_all => true, :is_filter => true
68 )
68 )
69 query = IssueQuery.new
69 query = IssueQuery.new
70 expected_order = [
70 expected_order = [
71 "Searchable field",
71 "Searchable field",
72 "Database",
72 "Database",
73 "Project's Development status",
73 "Project's Development status",
74 "Author's order test",
74 "Author's order test",
75 "Assignee's order test"
75 "Assignee's order test"
76 ]
76 ]
77 assert_equal expected_order,
77 assert_equal expected_order,
78 (query.available_filters.values.map{|v| v[:name]} & expected_order)
78 (query.available_filters.values.map{|v| v[:name]} & expected_order)
79 end
79 end
80
80
81 def test_custom_fields_for_all_projects_should_be_available_in_global_queries
81 def test_custom_fields_for_all_projects_should_be_available_in_global_queries
82 query = IssueQuery.new(:project => nil, :name => '_')
82 query = IssueQuery.new(:project => nil, :name => '_')
83 assert query.available_filters.has_key?('cf_1')
83 assert query.available_filters.has_key?('cf_1')
84 assert !query.available_filters.has_key?('cf_3')
84 assert !query.available_filters.has_key?('cf_3')
85 end
85 end
86
86
87 def test_system_shared_versions_should_be_available_in_global_queries
87 def test_system_shared_versions_should_be_available_in_global_queries
88 Version.find(2).update_attribute :sharing, 'system'
88 Version.find(2).update_attribute :sharing, 'system'
89 query = IssueQuery.new(:project => nil, :name => '_')
89 query = IssueQuery.new(:project => nil, :name => '_')
90 assert query.available_filters.has_key?('fixed_version_id')
90 assert query.available_filters.has_key?('fixed_version_id')
91 assert query.available_filters['fixed_version_id'][:values].detect {|v| v.last == '2'}
91 assert query.available_filters['fixed_version_id'][:values].detect {|v| v.last == '2'}
92 end
92 end
93
93
94 def test_project_filter_in_global_queries
94 def test_project_filter_in_global_queries
95 query = IssueQuery.new(:project => nil, :name => '_')
95 query = IssueQuery.new(:project => nil, :name => '_')
96 project_filter = query.available_filters["project_id"]
96 project_filter = query.available_filters["project_id"]
97 assert_not_nil project_filter
97 assert_not_nil project_filter
98 project_ids = project_filter[:values].map{|p| p[1]}
98 project_ids = project_filter[:values].map{|p| p[1]}
99 assert project_ids.include?("1") #public project
99 assert project_ids.include?("1") #public project
100 assert !project_ids.include?("2") #private project user cannot see
100 assert !project_ids.include?("2") #private project user cannot see
101 end
101 end
102
102
103 def find_issues_with_query(query)
103 def find_issues_with_query(query)
104 Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
104 Issue.includes([:assigned_to, :status, :tracker, :project, :priority]).where(
105 query.statement
105 query.statement
106 ).all
106 ).all
107 end
107 end
108
108
109 def assert_find_issues_with_query_is_successful(query)
109 def assert_find_issues_with_query_is_successful(query)
110 assert_nothing_raised do
110 assert_nothing_raised do
111 find_issues_with_query(query)
111 find_issues_with_query(query)
112 end
112 end
113 end
113 end
114
114
115 def assert_query_statement_includes(query, condition)
115 def assert_query_statement_includes(query, condition)
116 assert_include condition, query.statement
116 assert_include condition, query.statement
117 end
117 end
118
118
119 def assert_query_result(expected, query)
119 def assert_query_result(expected, query)
120 assert_nothing_raised do
120 assert_nothing_raised do
121 assert_equal expected.map(&:id).sort, query.issues.map(&:id).sort
121 assert_equal expected.map(&:id).sort, query.issues.map(&:id).sort
122 assert_equal expected.size, query.issue_count
122 assert_equal expected.size, query.issue_count
123 end
123 end
124 end
124 end
125
125
126 def test_query_should_allow_shared_versions_for_a_project_query
126 def test_query_should_allow_shared_versions_for_a_project_query
127 subproject_version = Version.find(4)
127 subproject_version = Version.find(4)
128 query = IssueQuery.new(:project => Project.find(1), :name => '_')
128 query = IssueQuery.new(:project => Project.find(1), :name => '_')
129 query.add_filter('fixed_version_id', '=', [subproject_version.id.to_s])
129 query.add_filter('fixed_version_id', '=', [subproject_version.id.to_s])
130
130
131 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IN ('4')")
131 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IN ('4')")
132 end
132 end
133
133
134 def test_query_with_multiple_custom_fields
134 def test_query_with_multiple_custom_fields
135 query = IssueQuery.find(1)
135 query = IssueQuery.find(1)
136 assert query.valid?
136 assert query.valid?
137 assert query.statement.include?("#{CustomValue.table_name}.value IN ('MySQL')")
137 assert query.statement.include?("#{CustomValue.table_name}.value IN ('MySQL')")
138 issues = find_issues_with_query(query)
138 issues = find_issues_with_query(query)
139 assert_equal 1, issues.length
139 assert_equal 1, issues.length
140 assert_equal Issue.find(3), issues.first
140 assert_equal Issue.find(3), issues.first
141 end
141 end
142
142
143 def test_operator_none
143 def test_operator_none
144 query = IssueQuery.new(:project => Project.find(1), :name => '_')
144 query = IssueQuery.new(:project => Project.find(1), :name => '_')
145 query.add_filter('fixed_version_id', '!*', [''])
145 query.add_filter('fixed_version_id', '!*', [''])
146 query.add_filter('cf_1', '!*', [''])
146 query.add_filter('cf_1', '!*', [''])
147 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NULL")
147 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NULL")
148 assert query.statement.include?("#{CustomValue.table_name}.value IS NULL OR #{CustomValue.table_name}.value = ''")
148 assert query.statement.include?("#{CustomValue.table_name}.value IS NULL OR #{CustomValue.table_name}.value = ''")
149 find_issues_with_query(query)
149 find_issues_with_query(query)
150 end
150 end
151
151
152 def test_operator_none_for_integer
152 def test_operator_none_for_integer
153 query = IssueQuery.new(:project => Project.find(1), :name => '_')
153 query = IssueQuery.new(:project => Project.find(1), :name => '_')
154 query.add_filter('estimated_hours', '!*', [''])
154 query.add_filter('estimated_hours', '!*', [''])
155 issues = find_issues_with_query(query)
155 issues = find_issues_with_query(query)
156 assert !issues.empty?
156 assert !issues.empty?
157 assert issues.all? {|i| !i.estimated_hours}
157 assert issues.all? {|i| !i.estimated_hours}
158 end
158 end
159
159
160 def test_operator_none_for_date
160 def test_operator_none_for_date
161 query = IssueQuery.new(:project => Project.find(1), :name => '_')
161 query = IssueQuery.new(:project => Project.find(1), :name => '_')
162 query.add_filter('start_date', '!*', [''])
162 query.add_filter('start_date', '!*', [''])
163 issues = find_issues_with_query(query)
163 issues = find_issues_with_query(query)
164 assert !issues.empty?
164 assert !issues.empty?
165 assert issues.all? {|i| i.start_date.nil?}
165 assert issues.all? {|i| i.start_date.nil?}
166 end
166 end
167
167
168 def test_operator_none_for_string_custom_field
168 def test_operator_none_for_string_custom_field
169 query = IssueQuery.new(:project => Project.find(1), :name => '_')
169 query = IssueQuery.new(:project => Project.find(1), :name => '_')
170 query.add_filter('cf_2', '!*', [''])
170 query.add_filter('cf_2', '!*', [''])
171 assert query.has_filter?('cf_2')
171 assert query.has_filter?('cf_2')
172 issues = find_issues_with_query(query)
172 issues = find_issues_with_query(query)
173 assert !issues.empty?
173 assert !issues.empty?
174 assert issues.all? {|i| i.custom_field_value(2).blank?}
174 assert issues.all? {|i| i.custom_field_value(2).blank?}
175 end
175 end
176
176
177 def test_operator_all
177 def test_operator_all
178 query = IssueQuery.new(:project => Project.find(1), :name => '_')
178 query = IssueQuery.new(:project => Project.find(1), :name => '_')
179 query.add_filter('fixed_version_id', '*', [''])
179 query.add_filter('fixed_version_id', '*', [''])
180 query.add_filter('cf_1', '*', [''])
180 query.add_filter('cf_1', '*', [''])
181 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NOT NULL")
181 assert query.statement.include?("#{Issue.table_name}.fixed_version_id IS NOT NULL")
182 assert query.statement.include?("#{CustomValue.table_name}.value IS NOT NULL AND #{CustomValue.table_name}.value <> ''")
182 assert query.statement.include?("#{CustomValue.table_name}.value IS NOT NULL AND #{CustomValue.table_name}.value <> ''")
183 find_issues_with_query(query)
183 find_issues_with_query(query)
184 end
184 end
185
185
186 def test_operator_all_for_date
186 def test_operator_all_for_date
187 query = IssueQuery.new(:project => Project.find(1), :name => '_')
187 query = IssueQuery.new(:project => Project.find(1), :name => '_')
188 query.add_filter('start_date', '*', [''])
188 query.add_filter('start_date', '*', [''])
189 issues = find_issues_with_query(query)
189 issues = find_issues_with_query(query)
190 assert !issues.empty?
190 assert !issues.empty?
191 assert issues.all? {|i| i.start_date.present?}
191 assert issues.all? {|i| i.start_date.present?}
192 end
192 end
193
193
194 def test_operator_all_for_string_custom_field
194 def test_operator_all_for_string_custom_field
195 query = IssueQuery.new(:project => Project.find(1), :name => '_')
195 query = IssueQuery.new(:project => Project.find(1), :name => '_')
196 query.add_filter('cf_2', '*', [''])
196 query.add_filter('cf_2', '*', [''])
197 assert query.has_filter?('cf_2')
197 assert query.has_filter?('cf_2')
198 issues = find_issues_with_query(query)
198 issues = find_issues_with_query(query)
199 assert !issues.empty?
199 assert !issues.empty?
200 assert issues.all? {|i| i.custom_field_value(2).present?}
200 assert issues.all? {|i| i.custom_field_value(2).present?}
201 end
201 end
202
202
203 def test_numeric_filter_should_not_accept_non_numeric_values
203 def test_numeric_filter_should_not_accept_non_numeric_values
204 query = IssueQuery.new(:name => '_')
204 query = IssueQuery.new(:name => '_')
205 query.add_filter('estimated_hours', '=', ['a'])
205 query.add_filter('estimated_hours', '=', ['a'])
206
206
207 assert query.has_filter?('estimated_hours')
207 assert query.has_filter?('estimated_hours')
208 assert !query.valid?
208 assert !query.valid?
209 end
209 end
210
210
211 def test_operator_is_on_float
211 def test_operator_is_on_float
212 Issue.update_all("estimated_hours = 171.2", "id=2")
212 Issue.update_all("estimated_hours = 171.2", "id=2")
213
213
214 query = IssueQuery.new(:name => '_')
214 query = IssueQuery.new(:name => '_')
215 query.add_filter('estimated_hours', '=', ['171.20'])
215 query.add_filter('estimated_hours', '=', ['171.20'])
216 issues = find_issues_with_query(query)
216 issues = find_issues_with_query(query)
217 assert_equal 1, issues.size
217 assert_equal 1, issues.size
218 assert_equal 2, issues.first.id
218 assert_equal 2, issues.first.id
219 end
219 end
220
220
221 def test_operator_is_on_integer_custom_field
221 def test_operator_is_on_integer_custom_field
222 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true)
222 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true)
223 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
223 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
224 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
224 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
225 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
225 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
226
226
227 query = IssueQuery.new(:name => '_')
227 query = IssueQuery.new(:name => '_')
228 query.add_filter("cf_#{f.id}", '=', ['12'])
228 query.add_filter("cf_#{f.id}", '=', ['12'])
229 issues = find_issues_with_query(query)
229 issues = find_issues_with_query(query)
230 assert_equal 1, issues.size
230 assert_equal 1, issues.size
231 assert_equal 2, issues.first.id
231 assert_equal 2, issues.first.id
232 end
232 end
233
233
234 def test_operator_is_on_integer_custom_field_should_accept_negative_value
234 def test_operator_is_on_integer_custom_field_should_accept_negative_value
235 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true)
235 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_for_all => true, :is_filter => true)
236 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
236 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
237 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12')
237 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12')
238 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
238 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
239
239
240 query = IssueQuery.new(:name => '_')
240 query = IssueQuery.new(:name => '_')
241 query.add_filter("cf_#{f.id}", '=', ['-12'])
241 query.add_filter("cf_#{f.id}", '=', ['-12'])
242 assert query.valid?
242 assert query.valid?
243 issues = find_issues_with_query(query)
243 issues = find_issues_with_query(query)
244 assert_equal 1, issues.size
244 assert_equal 1, issues.size
245 assert_equal 2, issues.first.id
245 assert_equal 2, issues.first.id
246 end
246 end
247
247
248 def test_operator_is_on_float_custom_field
248 def test_operator_is_on_float_custom_field
249 f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true)
249 f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true)
250 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
250 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
251 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12.7')
251 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12.7')
252 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
252 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
253
253
254 query = IssueQuery.new(:name => '_')
254 query = IssueQuery.new(:name => '_')
255 query.add_filter("cf_#{f.id}", '=', ['12.7'])
255 query.add_filter("cf_#{f.id}", '=', ['12.7'])
256 issues = find_issues_with_query(query)
256 issues = find_issues_with_query(query)
257 assert_equal 1, issues.size
257 assert_equal 1, issues.size
258 assert_equal 2, issues.first.id
258 assert_equal 2, issues.first.id
259 end
259 end
260
260
261 def test_operator_is_on_float_custom_field_should_accept_negative_value
261 def test_operator_is_on_float_custom_field_should_accept_negative_value
262 f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true)
262 f = IssueCustomField.create!(:name => 'filter', :field_format => 'float', :is_filter => true, :is_for_all => true)
263 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
263 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7.3')
264 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12.7')
264 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '-12.7')
265 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
265 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
266
266
267 query = IssueQuery.new(:name => '_')
267 query = IssueQuery.new(:name => '_')
268 query.add_filter("cf_#{f.id}", '=', ['-12.7'])
268 query.add_filter("cf_#{f.id}", '=', ['-12.7'])
269 assert query.valid?
269 assert query.valid?
270 issues = find_issues_with_query(query)
270 issues = find_issues_with_query(query)
271 assert_equal 1, issues.size
271 assert_equal 1, issues.size
272 assert_equal 2, issues.first.id
272 assert_equal 2, issues.first.id
273 end
273 end
274
274
275 def test_operator_is_on_multi_list_custom_field
275 def test_operator_is_on_multi_list_custom_field
276 f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
276 f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
277 :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
277 :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
278 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
278 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
279 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
279 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
280 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
280 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
281
281
282 query = IssueQuery.new(:name => '_')
282 query = IssueQuery.new(:name => '_')
283 query.add_filter("cf_#{f.id}", '=', ['value1'])
283 query.add_filter("cf_#{f.id}", '=', ['value1'])
284 issues = find_issues_with_query(query)
284 issues = find_issues_with_query(query)
285 assert_equal [1, 3], issues.map(&:id).sort
285 assert_equal [1, 3], issues.map(&:id).sort
286
286
287 query = IssueQuery.new(:name => '_')
287 query = IssueQuery.new(:name => '_')
288 query.add_filter("cf_#{f.id}", '=', ['value2'])
288 query.add_filter("cf_#{f.id}", '=', ['value2'])
289 issues = find_issues_with_query(query)
289 issues = find_issues_with_query(query)
290 assert_equal [1], issues.map(&:id).sort
290 assert_equal [1], issues.map(&:id).sort
291 end
291 end
292
292
293 def test_operator_is_not_on_multi_list_custom_field
293 def test_operator_is_not_on_multi_list_custom_field
294 f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
294 f = IssueCustomField.create!(:name => 'filter', :field_format => 'list', :is_filter => true, :is_for_all => true,
295 :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
295 :possible_values => ['value1', 'value2', 'value3'], :multiple => true)
296 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
296 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value1')
297 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
297 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => 'value2')
298 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
298 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => 'value1')
299
299
300 query = IssueQuery.new(:name => '_')
300 query = IssueQuery.new(:name => '_')
301 query.add_filter("cf_#{f.id}", '!', ['value1'])
301 query.add_filter("cf_#{f.id}", '!', ['value1'])
302 issues = find_issues_with_query(query)
302 issues = find_issues_with_query(query)
303 assert !issues.map(&:id).include?(1)
303 assert !issues.map(&:id).include?(1)
304 assert !issues.map(&:id).include?(3)
304 assert !issues.map(&:id).include?(3)
305
305
306 query = IssueQuery.new(:name => '_')
306 query = IssueQuery.new(:name => '_')
307 query.add_filter("cf_#{f.id}", '!', ['value2'])
307 query.add_filter("cf_#{f.id}", '!', ['value2'])
308 issues = find_issues_with_query(query)
308 issues = find_issues_with_query(query)
309 assert !issues.map(&:id).include?(1)
309 assert !issues.map(&:id).include?(1)
310 assert issues.map(&:id).include?(3)
310 assert issues.map(&:id).include?(3)
311 end
311 end
312
312
313 def test_operator_is_on_is_private_field
313 def test_operator_is_on_is_private_field
314 # is_private filter only available for those who can set issues private
314 # is_private filter only available for those who can set issues private
315 User.current = User.find(2)
315 User.current = User.find(2)
316
316
317 query = IssueQuery.new(:name => '_')
317 query = IssueQuery.new(:name => '_')
318 assert query.available_filters.key?('is_private')
318 assert query.available_filters.key?('is_private')
319
319
320 query.add_filter("is_private", '=', ['1'])
320 query.add_filter("is_private", '=', ['1'])
321 issues = find_issues_with_query(query)
321 issues = find_issues_with_query(query)
322 assert issues.any?
322 assert issues.any?
323 assert_nil issues.detect {|issue| !issue.is_private?}
323 assert_nil issues.detect {|issue| !issue.is_private?}
324 ensure
324 ensure
325 User.current = nil
325 User.current = nil
326 end
326 end
327
327
328 def test_operator_is_not_on_is_private_field
328 def test_operator_is_not_on_is_private_field
329 # is_private filter only available for those who can set issues private
329 # is_private filter only available for those who can set issues private
330 User.current = User.find(2)
330 User.current = User.find(2)
331
331
332 query = IssueQuery.new(:name => '_')
332 query = IssueQuery.new(:name => '_')
333 assert query.available_filters.key?('is_private')
333 assert query.available_filters.key?('is_private')
334
334
335 query.add_filter("is_private", '!', ['1'])
335 query.add_filter("is_private", '!', ['1'])
336 issues = find_issues_with_query(query)
336 issues = find_issues_with_query(query)
337 assert issues.any?
337 assert issues.any?
338 assert_nil issues.detect {|issue| issue.is_private?}
338 assert_nil issues.detect {|issue| issue.is_private?}
339 ensure
339 ensure
340 User.current = nil
340 User.current = nil
341 end
341 end
342
342
343 def test_operator_greater_than
343 def test_operator_greater_than
344 query = IssueQuery.new(:project => Project.find(1), :name => '_')
344 query = IssueQuery.new(:project => Project.find(1), :name => '_')
345 query.add_filter('done_ratio', '>=', ['40'])
345 query.add_filter('done_ratio', '>=', ['40'])
346 assert query.statement.include?("#{Issue.table_name}.done_ratio >= 40.0")
346 assert query.statement.include?("#{Issue.table_name}.done_ratio >= 40.0")
347 find_issues_with_query(query)
347 find_issues_with_query(query)
348 end
348 end
349
349
350 def test_operator_greater_than_a_float
350 def test_operator_greater_than_a_float
351 query = IssueQuery.new(:project => Project.find(1), :name => '_')
351 query = IssueQuery.new(:project => Project.find(1), :name => '_')
352 query.add_filter('estimated_hours', '>=', ['40.5'])
352 query.add_filter('estimated_hours', '>=', ['40.5'])
353 assert query.statement.include?("#{Issue.table_name}.estimated_hours >= 40.5")
353 assert query.statement.include?("#{Issue.table_name}.estimated_hours >= 40.5")
354 find_issues_with_query(query)
354 find_issues_with_query(query)
355 end
355 end
356
356
357 def test_operator_greater_than_on_int_custom_field
357 def test_operator_greater_than_on_int_custom_field
358 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
358 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
359 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
359 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '7')
360 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
360 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '12')
361 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
361 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
362
362
363 query = IssueQuery.new(:project => Project.find(1), :name => '_')
363 query = IssueQuery.new(:project => Project.find(1), :name => '_')
364 query.add_filter("cf_#{f.id}", '>=', ['8'])
364 query.add_filter("cf_#{f.id}", '>=', ['8'])
365 issues = find_issues_with_query(query)
365 issues = find_issues_with_query(query)
366 assert_equal 1, issues.size
366 assert_equal 1, issues.size
367 assert_equal 2, issues.first.id
367 assert_equal 2, issues.first.id
368 end
368 end
369
369
370 def test_operator_lesser_than
370 def test_operator_lesser_than
371 query = IssueQuery.new(:project => Project.find(1), :name => '_')
371 query = IssueQuery.new(:project => Project.find(1), :name => '_')
372 query.add_filter('done_ratio', '<=', ['30'])
372 query.add_filter('done_ratio', '<=', ['30'])
373 assert query.statement.include?("#{Issue.table_name}.done_ratio <= 30.0")
373 assert query.statement.include?("#{Issue.table_name}.done_ratio <= 30.0")
374 find_issues_with_query(query)
374 find_issues_with_query(query)
375 end
375 end
376
376
377 def test_operator_lesser_than_on_custom_field
377 def test_operator_lesser_than_on_custom_field
378 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
378 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
379 query = IssueQuery.new(:project => Project.find(1), :name => '_')
379 query = IssueQuery.new(:project => Project.find(1), :name => '_')
380 query.add_filter("cf_#{f.id}", '<=', ['30'])
380 query.add_filter("cf_#{f.id}", '<=', ['30'])
381 assert_match /CAST.+ <= 30\.0/, query.statement
381 assert_match /CAST.+ <= 30\.0/, query.statement
382 find_issues_with_query(query)
382 find_issues_with_query(query)
383 end
383 end
384
384
385 def test_operator_lesser_than_on_date_custom_field
385 def test_operator_lesser_than_on_date_custom_field
386 f = IssueCustomField.create!(:name => 'filter', :field_format => 'date', :is_filter => true, :is_for_all => true)
386 f = IssueCustomField.create!(:name => 'filter', :field_format => 'date', :is_filter => true, :is_for_all => true)
387 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '2013-04-11')
387 CustomValue.create!(:custom_field => f, :customized => Issue.find(1), :value => '2013-04-11')
388 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '2013-05-14')
388 CustomValue.create!(:custom_field => f, :customized => Issue.find(2), :value => '2013-05-14')
389 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
389 CustomValue.create!(:custom_field => f, :customized => Issue.find(3), :value => '')
390
390
391 query = IssueQuery.new(:project => Project.find(1), :name => '_')
391 query = IssueQuery.new(:project => Project.find(1), :name => '_')
392 query.add_filter("cf_#{f.id}", '<=', ['2013-05-01'])
392 query.add_filter("cf_#{f.id}", '<=', ['2013-05-01'])
393 issue_ids = find_issues_with_query(query).map(&:id)
393 issue_ids = find_issues_with_query(query).map(&:id)
394 assert_include 1, issue_ids
394 assert_include 1, issue_ids
395 assert_not_include 2, issue_ids
395 assert_not_include 2, issue_ids
396 assert_not_include 3, issue_ids
396 assert_not_include 3, issue_ids
397 end
397 end
398
398
399 def test_operator_between
399 def test_operator_between
400 query = IssueQuery.new(:project => Project.find(1), :name => '_')
400 query = IssueQuery.new(:project => Project.find(1), :name => '_')
401 query.add_filter('done_ratio', '><', ['30', '40'])
401 query.add_filter('done_ratio', '><', ['30', '40'])
402 assert_include "#{Issue.table_name}.done_ratio BETWEEN 30.0 AND 40.0", query.statement
402 assert_include "#{Issue.table_name}.done_ratio BETWEEN 30.0 AND 40.0", query.statement
403 find_issues_with_query(query)
403 find_issues_with_query(query)
404 end
404 end
405
405
406 def test_operator_between_on_custom_field
406 def test_operator_between_on_custom_field
407 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
407 f = IssueCustomField.create!(:name => 'filter', :field_format => 'int', :is_filter => true, :is_for_all => true)
408 query = IssueQuery.new(:project => Project.find(1), :name => '_')
408 query = IssueQuery.new(:project => Project.find(1), :name => '_')
409 query.add_filter("cf_#{f.id}", '><', ['30', '40'])
409 query.add_filter("cf_#{f.id}", '><', ['30', '40'])
410 assert_match /CAST.+ BETWEEN 30.0 AND 40.0/, query.statement
410 assert_match /CAST.+ BETWEEN 30.0 AND 40.0/, query.statement
411 find_issues_with_query(query)
411 find_issues_with_query(query)
412 end
412 end
413
413
414 def test_date_filter_should_not_accept_non_date_values
414 def test_date_filter_should_not_accept_non_date_values
415 query = IssueQuery.new(:name => '_')
415 query = IssueQuery.new(:name => '_')
416 query.add_filter('created_on', '=', ['a'])
416 query.add_filter('created_on', '=', ['a'])
417
417
418 assert query.has_filter?('created_on')
418 assert query.has_filter?('created_on')
419 assert !query.valid?
419 assert !query.valid?
420 end
420 end
421
421
422 def test_date_filter_should_not_accept_invalid_date_values
422 def test_date_filter_should_not_accept_invalid_date_values
423 query = IssueQuery.new(:name => '_')
423 query = IssueQuery.new(:name => '_')
424 query.add_filter('created_on', '=', ['2011-01-34'])
424 query.add_filter('created_on', '=', ['2011-01-34'])
425
425
426 assert query.has_filter?('created_on')
426 assert query.has_filter?('created_on')
427 assert !query.valid?
427 assert !query.valid?
428 end
428 end
429
429
430 def test_relative_date_filter_should_not_accept_non_integer_values
430 def test_relative_date_filter_should_not_accept_non_integer_values
431 query = IssueQuery.new(:name => '_')
431 query = IssueQuery.new(:name => '_')
432 query.add_filter('created_on', '>t-', ['a'])
432 query.add_filter('created_on', '>t-', ['a'])
433
433
434 assert query.has_filter?('created_on')
434 assert query.has_filter?('created_on')
435 assert !query.valid?
435 assert !query.valid?
436 end
436 end
437
437
438 def test_operator_date_equals
438 def test_operator_date_equals
439 query = IssueQuery.new(:name => '_')
439 query = IssueQuery.new(:name => '_')
440 query.add_filter('due_date', '=', ['2011-07-10'])
440 query.add_filter('due_date', '=', ['2011-07-10'])
441 assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
441 assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
442 find_issues_with_query(query)
442 find_issues_with_query(query)
443 end
443 end
444
444
445 def test_operator_date_lesser_than
445 def test_operator_date_lesser_than
446 query = IssueQuery.new(:name => '_')
446 query = IssueQuery.new(:name => '_')
447 query.add_filter('due_date', '<=', ['2011-07-10'])
447 query.add_filter('due_date', '<=', ['2011-07-10'])
448 assert_match /issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
448 assert_match /issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
449 find_issues_with_query(query)
449 find_issues_with_query(query)
450 end
450 end
451
451
452 def test_operator_date_greater_than
452 def test_operator_date_greater_than
453 query = IssueQuery.new(:name => '_')
453 query = IssueQuery.new(:name => '_')
454 query.add_filter('due_date', '>=', ['2011-07-10'])
454 query.add_filter('due_date', '>=', ['2011-07-10'])
455 assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?'/, query.statement
455 assert_match /issues\.due_date > '2011-07-09 23:59:59(\.9+)?'/, query.statement
456 find_issues_with_query(query)
456 find_issues_with_query(query)
457 end
457 end
458
458
459 def test_operator_date_between
459 def test_operator_date_between
460 query = IssueQuery.new(:name => '_')
460 query = IssueQuery.new(:name => '_')
461 query.add_filter('due_date', '><', ['2011-06-23', '2011-07-10'])
461 query.add_filter('due_date', '><', ['2011-06-23', '2011-07-10'])
462 assert_match /issues\.due_date > '2011-06-22 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
462 assert_match /issues\.due_date > '2011-06-22 23:59:59(\.9+)?' AND issues\.due_date <= '2011-07-10 23:59:59(\.9+)?/, query.statement
463 find_issues_with_query(query)
463 find_issues_with_query(query)
464 end
464 end
465
465
466 def test_operator_in_more_than
466 def test_operator_in_more_than
467 Issue.find(7).update_attribute(:due_date, (Date.today + 15))
467 Issue.find(7).update_attribute(:due_date, (Date.today + 15))
468 query = IssueQuery.new(:project => Project.find(1), :name => '_')
468 query = IssueQuery.new(:project => Project.find(1), :name => '_')
469 query.add_filter('due_date', '>t+', ['15'])
469 query.add_filter('due_date', '>t+', ['15'])
470 issues = find_issues_with_query(query)
470 issues = find_issues_with_query(query)
471 assert !issues.empty?
471 assert !issues.empty?
472 issues.each {|issue| assert(issue.due_date >= (Date.today + 15))}
472 issues.each {|issue| assert(issue.due_date >= (Date.today + 15))}
473 end
473 end
474
474
475 def test_operator_in_less_than
475 def test_operator_in_less_than
476 query = IssueQuery.new(:project => Project.find(1), :name => '_')
476 query = IssueQuery.new(:project => Project.find(1), :name => '_')
477 query.add_filter('due_date', '<t+', ['15'])
477 query.add_filter('due_date', '<t+', ['15'])
478 issues = find_issues_with_query(query)
478 issues = find_issues_with_query(query)
479 assert !issues.empty?
479 assert !issues.empty?
480 issues.each {|issue| assert(issue.due_date <= (Date.today + 15))}
480 issues.each {|issue| assert(issue.due_date <= (Date.today + 15))}
481 end
481 end
482
482
483 def test_operator_in_the_next_days
483 def test_operator_in_the_next_days
484 query = IssueQuery.new(:project => Project.find(1), :name => '_')
484 query = IssueQuery.new(:project => Project.find(1), :name => '_')
485 query.add_filter('due_date', '><t+', ['15'])
485 query.add_filter('due_date', '><t+', ['15'])
486 issues = find_issues_with_query(query)
486 issues = find_issues_with_query(query)
487 assert !issues.empty?
487 assert !issues.empty?
488 issues.each {|issue| assert(issue.due_date >= Date.today && issue.due_date <= (Date.today + 15))}
488 issues.each {|issue| assert(issue.due_date >= Date.today && issue.due_date <= (Date.today + 15))}
489 end
489 end
490
490
491 def test_operator_less_than_ago
491 def test_operator_less_than_ago
492 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
492 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
493 query = IssueQuery.new(:project => Project.find(1), :name => '_')
493 query = IssueQuery.new(:project => Project.find(1), :name => '_')
494 query.add_filter('due_date', '>t-', ['3'])
494 query.add_filter('due_date', '>t-', ['3'])
495 issues = find_issues_with_query(query)
495 issues = find_issues_with_query(query)
496 assert !issues.empty?
496 assert !issues.empty?
497 issues.each {|issue| assert(issue.due_date >= (Date.today - 3))}
497 issues.each {|issue| assert(issue.due_date >= (Date.today - 3))}
498 end
498 end
499
499
500 def test_operator_in_the_past_days
500 def test_operator_in_the_past_days
501 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
501 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
502 query = IssueQuery.new(:project => Project.find(1), :name => '_')
502 query = IssueQuery.new(:project => Project.find(1), :name => '_')
503 query.add_filter('due_date', '><t-', ['3'])
503 query.add_filter('due_date', '><t-', ['3'])
504 issues = find_issues_with_query(query)
504 issues = find_issues_with_query(query)
505 assert !issues.empty?
505 assert !issues.empty?
506 issues.each {|issue| assert(issue.due_date >= (Date.today - 3) && issue.due_date <= Date.today)}
506 issues.each {|issue| assert(issue.due_date >= (Date.today - 3) && issue.due_date <= Date.today)}
507 end
507 end
508
508
509 def test_operator_more_than_ago
509 def test_operator_more_than_ago
510 Issue.find(7).update_attribute(:due_date, (Date.today - 10))
510 Issue.find(7).update_attribute(:due_date, (Date.today - 10))
511 query = IssueQuery.new(:project => Project.find(1), :name => '_')
511 query = IssueQuery.new(:project => Project.find(1), :name => '_')
512 query.add_filter('due_date', '<t-', ['10'])
512 query.add_filter('due_date', '<t-', ['10'])
513 assert query.statement.include?("#{Issue.table_name}.due_date <=")
513 assert query.statement.include?("#{Issue.table_name}.due_date <=")
514 issues = find_issues_with_query(query)
514 issues = find_issues_with_query(query)
515 assert !issues.empty?
515 assert !issues.empty?
516 issues.each {|issue| assert(issue.due_date <= (Date.today - 10))}
516 issues.each {|issue| assert(issue.due_date <= (Date.today - 10))}
517 end
517 end
518
518
519 def test_operator_in
519 def test_operator_in
520 Issue.find(7).update_attribute(:due_date, (Date.today + 2))
520 Issue.find(7).update_attribute(:due_date, (Date.today + 2))
521 query = IssueQuery.new(:project => Project.find(1), :name => '_')
521 query = IssueQuery.new(:project => Project.find(1), :name => '_')
522 query.add_filter('due_date', 't+', ['2'])
522 query.add_filter('due_date', 't+', ['2'])
523 issues = find_issues_with_query(query)
523 issues = find_issues_with_query(query)
524 assert !issues.empty?
524 assert !issues.empty?
525 issues.each {|issue| assert_equal((Date.today + 2), issue.due_date)}
525 issues.each {|issue| assert_equal((Date.today + 2), issue.due_date)}
526 end
526 end
527
527
528 def test_operator_ago
528 def test_operator_ago
529 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
529 Issue.find(7).update_attribute(:due_date, (Date.today - 3))
530 query = IssueQuery.new(:project => Project.find(1), :name => '_')
530 query = IssueQuery.new(:project => Project.find(1), :name => '_')
531 query.add_filter('due_date', 't-', ['3'])
531 query.add_filter('due_date', 't-', ['3'])
532 issues = find_issues_with_query(query)
532 issues = find_issues_with_query(query)
533 assert !issues.empty?
533 assert !issues.empty?
534 issues.each {|issue| assert_equal((Date.today - 3), issue.due_date)}
534 issues.each {|issue| assert_equal((Date.today - 3), issue.due_date)}
535 end
535 end
536
536
537 def test_operator_today
537 def test_operator_today
538 query = IssueQuery.new(:project => Project.find(1), :name => '_')
538 query = IssueQuery.new(:project => Project.find(1), :name => '_')
539 query.add_filter('due_date', 't', [''])
539 query.add_filter('due_date', 't', [''])
540 issues = find_issues_with_query(query)
540 issues = find_issues_with_query(query)
541 assert !issues.empty?
541 assert !issues.empty?
542 issues.each {|issue| assert_equal Date.today, issue.due_date}
542 issues.each {|issue| assert_equal Date.today, issue.due_date}
543 end
543 end
544
544
545 def test_operator_this_week_on_date
545 def test_operator_this_week_on_date
546 query = IssueQuery.new(:project => Project.find(1), :name => '_')
546 query = IssueQuery.new(:project => Project.find(1), :name => '_')
547 query.add_filter('due_date', 'w', [''])
547 query.add_filter('due_date', 'w', [''])
548 find_issues_with_query(query)
548 find_issues_with_query(query)
549 end
549 end
550
550
551 def test_operator_this_week_on_datetime
551 def test_operator_this_week_on_datetime
552 query = IssueQuery.new(:project => Project.find(1), :name => '_')
552 query = IssueQuery.new(:project => Project.find(1), :name => '_')
553 query.add_filter('created_on', 'w', [''])
553 query.add_filter('created_on', 'w', [''])
554 find_issues_with_query(query)
554 find_issues_with_query(query)
555 end
555 end
556
556
557 def test_operator_contains
557 def test_operator_contains
558 query = IssueQuery.new(:project => Project.find(1), :name => '_')
558 query = IssueQuery.new(:project => Project.find(1), :name => '_')
559 query.add_filter('subject', '~', ['uNable'])
559 query.add_filter('subject', '~', ['uNable'])
560 assert query.statement.include?("LOWER(#{Issue.table_name}.subject) LIKE '%unable%'")
560 assert query.statement.include?("LOWER(#{Issue.table_name}.subject) LIKE '%unable%'")
561 result = find_issues_with_query(query)
561 result = find_issues_with_query(query)
562 assert result.empty?
562 assert result.empty?
563 result.each {|issue| assert issue.subject.downcase.include?('unable') }
563 result.each {|issue| assert issue.subject.downcase.include?('unable') }
564 end
564 end
565
565
566 def test_range_for_this_week_with_week_starting_on_monday
566 def test_range_for_this_week_with_week_starting_on_monday
567 I18n.locale = :fr
567 I18n.locale = :fr
568 assert_equal '1', I18n.t(:general_first_day_of_week)
568 assert_equal '1', I18n.t(:general_first_day_of_week)
569
569
570 Date.stubs(:today).returns(Date.parse('2011-04-29'))
570 Date.stubs(:today).returns(Date.parse('2011-04-29'))
571
571
572 query = IssueQuery.new(:project => Project.find(1), :name => '_')
572 query = IssueQuery.new(:project => Project.find(1), :name => '_')
573 query.add_filter('due_date', 'w', [''])
573 query.add_filter('due_date', 'w', [''])
574 assert query.statement.match(/issues\.due_date > '2011-04-24 23:59:59(\.9+)?' AND issues\.due_date <= '2011-05-01 23:59:59(\.9+)?/), "range not found in #{query.statement}"
574 assert query.statement.match(/issues\.due_date > '2011-04-24 23:59:59(\.9+)?' AND issues\.due_date <= '2011-05-01 23:59:59(\.9+)?/), "range not found in #{query.statement}"
575 I18n.locale = :en
575 I18n.locale = :en
576 end
576 end
577
577
578 def test_range_for_this_week_with_week_starting_on_sunday
578 def test_range_for_this_week_with_week_starting_on_sunday
579 I18n.locale = :en
579 I18n.locale = :en
580 assert_equal '7', I18n.t(:general_first_day_of_week)
580 assert_equal '7', I18n.t(:general_first_day_of_week)
581
581
582 Date.stubs(:today).returns(Date.parse('2011-04-29'))
582 Date.stubs(:today).returns(Date.parse('2011-04-29'))
583
583
584 query = IssueQuery.new(:project => Project.find(1), :name => '_')
584 query = IssueQuery.new(:project => Project.find(1), :name => '_')
585 query.add_filter('due_date', 'w', [''])
585 query.add_filter('due_date', 'w', [''])
586 assert query.statement.match(/issues\.due_date > '2011-04-23 23:59:59(\.9+)?' AND issues\.due_date <= '2011-04-30 23:59:59(\.9+)?/), "range not found in #{query.statement}"
586 assert query.statement.match(/issues\.due_date > '2011-04-23 23:59:59(\.9+)?' AND issues\.due_date <= '2011-04-30 23:59:59(\.9+)?/), "range not found in #{query.statement}"
587 end
587 end
588
588
589 def test_operator_does_not_contains
589 def test_operator_does_not_contains
590 query = IssueQuery.new(:project => Project.find(1), :name => '_')
590 query = IssueQuery.new(:project => Project.find(1), :name => '_')
591 query.add_filter('subject', '!~', ['uNable'])
591 query.add_filter('subject', '!~', ['uNable'])
592 assert query.statement.include?("LOWER(#{Issue.table_name}.subject) NOT LIKE '%unable%'")
592 assert query.statement.include?("LOWER(#{Issue.table_name}.subject) NOT LIKE '%unable%'")
593 find_issues_with_query(query)
593 find_issues_with_query(query)
594 end
594 end
595
595
596 def test_filter_assigned_to_me
596 def test_filter_assigned_to_me
597 user = User.find(2)
597 user = User.find(2)
598 group = Group.find(10)
598 group = Group.find(10)
599 User.current = user
599 User.current = user
600 i1 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => user)
600 i1 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => user)
601 i2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => group)
601 i2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => group)
602 i3 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => Group.find(11))
602 i3 = Issue.generate!(:project_id => 1, :tracker_id => 1, :assigned_to => Group.find(11))
603 group.users << user
603 group.users << user
604
604
605 query = IssueQuery.new(:name => '_', :filters => { 'assigned_to_id' => {:operator => '=', :values => ['me']}})
605 query = IssueQuery.new(:name => '_', :filters => { 'assigned_to_id' => {:operator => '=', :values => ['me']}})
606 result = query.issues
606 result = query.issues
607 assert_equal Issue.visible.where(:assigned_to_id => ([2] + user.reload.group_ids)).sort_by(&:id), result.sort_by(&:id)
607 assert_equal Issue.visible.where(:assigned_to_id => ([2] + user.reload.group_ids)).sort_by(&:id), result.sort_by(&:id)
608
608
609 assert result.include?(i1)
609 assert result.include?(i1)
610 assert result.include?(i2)
610 assert result.include?(i2)
611 assert !result.include?(i3)
611 assert !result.include?(i3)
612 end
612 end
613
613
614 def test_user_custom_field_filtered_on_me
614 def test_user_custom_field_filtered_on_me
615 User.current = User.find(2)
615 User.current = User.find(2)
616 cf = IssueCustomField.create!(:field_format => 'user', :is_for_all => true, :is_filter => true, :name => 'User custom field', :tracker_ids => [1])
616 cf = IssueCustomField.create!(:field_format => 'user', :is_for_all => true, :is_filter => true, :name => 'User custom field', :tracker_ids => [1])
617 issue1 = Issue.create!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '2'}, :subject => 'Test', :author_id => 1)
617 issue1 = Issue.create!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '2'}, :subject => 'Test', :author_id => 1)
618 issue2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '3'})
618 issue2 = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {cf.id.to_s => '3'})
619
619
620 query = IssueQuery.new(:name => '_', :project => Project.find(1))
620 query = IssueQuery.new(:name => '_', :project => Project.find(1))
621 filter = query.available_filters["cf_#{cf.id}"]
621 filter = query.available_filters["cf_#{cf.id}"]
622 assert_not_nil filter
622 assert_not_nil filter
623 assert_include 'me', filter[:values].map{|v| v[1]}
623 assert_include 'me', filter[:values].map{|v| v[1]}
624
624
625 query.filters = { "cf_#{cf.id}" => {:operator => '=', :values => ['me']}}
625 query.filters = { "cf_#{cf.id}" => {:operator => '=', :values => ['me']}}
626 result = query.issues
626 result = query.issues
627 assert_equal 1, result.size
627 assert_equal 1, result.size
628 assert_equal issue1, result.first
628 assert_equal issue1, result.first
629 end
629 end
630
630
631 def test_filter_my_projects
631 def test_filter_my_projects
632 User.current = User.find(2)
632 User.current = User.find(2)
633 query = IssueQuery.new(:name => '_')
633 query = IssueQuery.new(:name => '_')
634 filter = query.available_filters['project_id']
634 filter = query.available_filters['project_id']
635 assert_not_nil filter
635 assert_not_nil filter
636 assert_include 'mine', filter[:values].map{|v| v[1]}
636 assert_include 'mine', filter[:values].map{|v| v[1]}
637
637
638 query.filters = { 'project_id' => {:operator => '=', :values => ['mine']}}
638 query.filters = { 'project_id' => {:operator => '=', :values => ['mine']}}
639 result = query.issues
639 result = query.issues
640 assert_nil result.detect {|issue| !User.current.member_of?(issue.project)}
640 assert_nil result.detect {|issue| !User.current.member_of?(issue.project)}
641 end
641 end
642
642
643 def test_filter_watched_issues
643 def test_filter_watched_issues
644 User.current = User.find(1)
644 User.current = User.find(1)
645 query = IssueQuery.new(:name => '_', :filters => { 'watcher_id' => {:operator => '=', :values => ['me']}})
645 query = IssueQuery.new(:name => '_', :filters => { 'watcher_id' => {:operator => '=', :values => ['me']}})
646 result = find_issues_with_query(query)
646 result = find_issues_with_query(query)
647 assert_not_nil result
647 assert_not_nil result
648 assert !result.empty?
648 assert !result.empty?
649 assert_equal Issue.visible.watched_by(User.current).sort_by(&:id), result.sort_by(&:id)
649 assert_equal Issue.visible.watched_by(User.current).sort_by(&:id), result.sort_by(&:id)
650 User.current = nil
650 User.current = nil
651 end
651 end
652
652
653 def test_filter_unwatched_issues
653 def test_filter_unwatched_issues
654 User.current = User.find(1)
654 User.current = User.find(1)
655 query = IssueQuery.new(:name => '_', :filters => { 'watcher_id' => {:operator => '!', :values => ['me']}})
655 query = IssueQuery.new(:name => '_', :filters => { 'watcher_id' => {:operator => '!', :values => ['me']}})
656 result = find_issues_with_query(query)
656 result = find_issues_with_query(query)
657 assert_not_nil result
657 assert_not_nil result
658 assert !result.empty?
658 assert !result.empty?
659 assert_equal((Issue.visible - Issue.watched_by(User.current)).sort_by(&:id).size, result.sort_by(&:id).size)
659 assert_equal((Issue.visible - Issue.watched_by(User.current)).sort_by(&:id).size, result.sort_by(&:id).size)
660 User.current = nil
660 User.current = nil
661 end
661 end
662
662
663 def test_filter_on_project_custom_field
663 def test_filter_on_project_custom_field
664 field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
664 field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
665 CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
665 CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
666 CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
666 CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
667
667
668 query = IssueQuery.new(:name => '_')
668 query = IssueQuery.new(:name => '_')
669 filter_name = "project.cf_#{field.id}"
669 filter_name = "project.cf_#{field.id}"
670 assert_include filter_name, query.available_filters.keys
670 assert_include filter_name, query.available_filters.keys
671 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
671 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
672 assert_equal [3, 5], find_issues_with_query(query).map(&:project_id).uniq.sort
672 assert_equal [3, 5], find_issues_with_query(query).map(&:project_id).uniq.sort
673 end
673 end
674
674
675 def test_filter_on_author_custom_field
675 def test_filter_on_author_custom_field
676 field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
676 field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
677 CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
677 CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
678
678
679 query = IssueQuery.new(:name => '_')
679 query = IssueQuery.new(:name => '_')
680 filter_name = "author.cf_#{field.id}"
680 filter_name = "author.cf_#{field.id}"
681 assert_include filter_name, query.available_filters.keys
681 assert_include filter_name, query.available_filters.keys
682 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
682 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
683 assert_equal [3], find_issues_with_query(query).map(&:author_id).uniq.sort
683 assert_equal [3], find_issues_with_query(query).map(&:author_id).uniq.sort
684 end
684 end
685
685
686 def test_filter_on_assigned_to_custom_field
686 def test_filter_on_assigned_to_custom_field
687 field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
687 field = UserCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
688 CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
688 CustomValue.create!(:custom_field => field, :customized => User.find(3), :value => 'Foo')
689
689
690 query = IssueQuery.new(:name => '_')
690 query = IssueQuery.new(:name => '_')
691 filter_name = "assigned_to.cf_#{field.id}"
691 filter_name = "assigned_to.cf_#{field.id}"
692 assert_include filter_name, query.available_filters.keys
692 assert_include filter_name, query.available_filters.keys
693 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
693 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
694 assert_equal [3], find_issues_with_query(query).map(&:assigned_to_id).uniq.sort
694 assert_equal [3], find_issues_with_query(query).map(&:assigned_to_id).uniq.sort
695 end
695 end
696
696
697 def test_filter_on_fixed_version_custom_field
697 def test_filter_on_fixed_version_custom_field
698 field = VersionCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
698 field = VersionCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
699 CustomValue.create!(:custom_field => field, :customized => Version.find(2), :value => 'Foo')
699 CustomValue.create!(:custom_field => field, :customized => Version.find(2), :value => 'Foo')
700
700
701 query = IssueQuery.new(:name => '_')
701 query = IssueQuery.new(:name => '_')
702 filter_name = "fixed_version.cf_#{field.id}"
702 filter_name = "fixed_version.cf_#{field.id}"
703 assert_include filter_name, query.available_filters.keys
703 assert_include filter_name, query.available_filters.keys
704 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
704 query.filters = {filter_name => {:operator => '=', :values => ['Foo']}}
705 assert_equal [2], find_issues_with_query(query).map(&:fixed_version_id).uniq.sort
705 assert_equal [2], find_issues_with_query(query).map(&:fixed_version_id).uniq.sort
706 end
706 end
707
707
708 def test_filter_on_relations_with_a_specific_issue
708 def test_filter_on_relations_with_a_specific_issue
709 IssueRelation.delete_all
709 IssueRelation.delete_all
710 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
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))
711 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
712
712
713 query = IssueQuery.new(:name => '_')
713 query = IssueQuery.new(:name => '_')
714 query.filters = {"relates" => {:operator => '=', :values => ['1']}}
714 query.filters = {"relates" => {:operator => '=', :values => ['1']}}
715 assert_equal [2, 3], find_issues_with_query(query).map(&:id).sort
715 assert_equal [2, 3], find_issues_with_query(query).map(&:id).sort
716
716
717 query = IssueQuery.new(:name => '_')
717 query = IssueQuery.new(:name => '_')
718 query.filters = {"relates" => {:operator => '=', :values => ['2']}}
718 query.filters = {"relates" => {:operator => '=', :values => ['2']}}
719 assert_equal [1], find_issues_with_query(query).map(&:id).sort
719 assert_equal [1], find_issues_with_query(query).map(&:id).sort
720 end
720 end
721
721
722 def test_filter_on_relations_with_any_issues_in_a_project
722 def test_filter_on_relations_with_any_issues_in_a_project
723 IssueRelation.delete_all
723 IssueRelation.delete_all
724 with_settings :cross_project_issue_relations => '1' do
724 with_settings :cross_project_issue_relations => '1' do
725 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
725 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
726 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(2).issues.first)
726 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(2).issues.first)
727 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
727 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
728 end
728 end
729
729
730 query = IssueQuery.new(:name => '_')
730 query = IssueQuery.new(:name => '_')
731 query.filters = {"relates" => {:operator => '=p', :values => ['2']}}
731 query.filters = {"relates" => {:operator => '=p', :values => ['2']}}
732 assert_equal [1, 2], find_issues_with_query(query).map(&:id).sort
732 assert_equal [1, 2], find_issues_with_query(query).map(&:id).sort
733
733
734 query = IssueQuery.new(:name => '_')
734 query = IssueQuery.new(:name => '_')
735 query.filters = {"relates" => {:operator => '=p', :values => ['3']}}
735 query.filters = {"relates" => {:operator => '=p', :values => ['3']}}
736 assert_equal [1], find_issues_with_query(query).map(&:id).sort
736 assert_equal [1], find_issues_with_query(query).map(&:id).sort
737
737
738 query = IssueQuery.new(:name => '_')
738 query = IssueQuery.new(:name => '_')
739 query.filters = {"relates" => {:operator => '=p', :values => ['4']}}
739 query.filters = {"relates" => {:operator => '=p', :values => ['4']}}
740 assert_equal [], find_issues_with_query(query).map(&:id).sort
740 assert_equal [], find_issues_with_query(query).map(&:id).sort
741 end
741 end
742
742
743 def test_filter_on_relations_with_any_issues_not_in_a_project
743 def test_filter_on_relations_with_any_issues_not_in_a_project
744 IssueRelation.delete_all
744 IssueRelation.delete_all
745 with_settings :cross_project_issue_relations => '1' do
745 with_settings :cross_project_issue_relations => '1' do
746 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
746 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
747 #IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(1).issues.first)
747 #IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(1).issues.first)
748 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
748 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(3).issues.first)
749 end
749 end
750
750
751 query = IssueQuery.new(:name => '_')
751 query = IssueQuery.new(:name => '_')
752 query.filters = {"relates" => {:operator => '=!p', :values => ['1']}}
752 query.filters = {"relates" => {:operator => '=!p', :values => ['1']}}
753 assert_equal [1], find_issues_with_query(query).map(&:id).sort
753 assert_equal [1], find_issues_with_query(query).map(&:id).sort
754 end
754 end
755
755
756 def test_filter_on_relations_with_no_issues_in_a_project
756 def test_filter_on_relations_with_no_issues_in_a_project
757 IssueRelation.delete_all
757 IssueRelation.delete_all
758 with_settings :cross_project_issue_relations => '1' do
758 with_settings :cross_project_issue_relations => '1' do
759 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
759 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Project.find(2).issues.first)
760 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(3).issues.first)
760 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(2), :issue_to => Project.find(3).issues.first)
761 IssueRelation.create!(:relation_type => "relates", :issue_to => Project.find(2).issues.first, :issue_from => Issue.find(3))
761 IssueRelation.create!(:relation_type => "relates", :issue_to => Project.find(2).issues.first, :issue_from => Issue.find(3))
762 end
762 end
763
763
764 query = IssueQuery.new(:name => '_')
764 query = IssueQuery.new(:name => '_')
765 query.filters = {"relates" => {:operator => '!p', :values => ['2']}}
765 query.filters = {"relates" => {:operator => '!p', :values => ['2']}}
766 ids = find_issues_with_query(query).map(&:id).sort
766 ids = find_issues_with_query(query).map(&:id).sort
767 assert_include 2, ids
767 assert_include 2, ids
768 assert_not_include 1, ids
768 assert_not_include 1, ids
769 assert_not_include 3, ids
769 assert_not_include 3, ids
770 end
770 end
771
771
772 def test_filter_on_relations_with_no_issues
772 def test_filter_on_relations_with_no_issues
773 IssueRelation.delete_all
773 IssueRelation.delete_all
774 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
774 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
775 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
775 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
776
776
777 query = IssueQuery.new(:name => '_')
777 query = IssueQuery.new(:name => '_')
778 query.filters = {"relates" => {:operator => '!*', :values => ['']}}
778 query.filters = {"relates" => {:operator => '!*', :values => ['']}}
779 ids = find_issues_with_query(query).map(&:id)
779 ids = find_issues_with_query(query).map(&:id)
780 assert_equal [], ids & [1, 2, 3]
780 assert_equal [], ids & [1, 2, 3]
781 assert_include 4, ids
781 assert_include 4, ids
782 end
782 end
783
783
784 def test_filter_on_relations_with_any_issues
784 def test_filter_on_relations_with_any_issues
785 IssueRelation.delete_all
785 IssueRelation.delete_all
786 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
786 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(2))
787 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
787 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(3), :issue_to => Issue.find(1))
788
788
789 query = IssueQuery.new(:name => '_')
789 query = IssueQuery.new(:name => '_')
790 query.filters = {"relates" => {:operator => '*', :values => ['']}}
790 query.filters = {"relates" => {:operator => '*', :values => ['']}}
791 assert_equal [1, 2, 3], find_issues_with_query(query).map(&:id).sort
791 assert_equal [1, 2, 3], find_issues_with_query(query).map(&:id).sort
792 end
792 end
793
793
794 def test_filter_on_relations_should_not_ignore_other_filter
795 issue = Issue.generate!
796 issue1 = Issue.generate!(:status_id => 1)
797 issue2 = Issue.generate!(:status_id => 2)
798 IssueRelation.create!(:relation_type => "relates", :issue_from => issue, :issue_to => issue1)
799 IssueRelation.create!(:relation_type => "relates", :issue_from => issue, :issue_to => issue2)
800
801 query = IssueQuery.new(:name => '_')
802 query.filters = {
803 "status_id" => {:operator => '=', :values => ['1']},
804 "relates" => {:operator => '=', :values => [issue.id.to_s]}
805 }
806 assert_equal [issue1], find_issues_with_query(query)
807 end
808
794 def test_statement_should_be_nil_with_no_filters
809 def test_statement_should_be_nil_with_no_filters
795 q = IssueQuery.new(:name => '_')
810 q = IssueQuery.new(:name => '_')
796 q.filters = {}
811 q.filters = {}
797
812
798 assert q.valid?
813 assert q.valid?
799 assert_nil q.statement
814 assert_nil q.statement
800 end
815 end
801
816
802 def test_default_columns
817 def test_default_columns
803 q = IssueQuery.new
818 q = IssueQuery.new
804 assert q.columns.any?
819 assert q.columns.any?
805 assert q.inline_columns.any?
820 assert q.inline_columns.any?
806 assert q.block_columns.empty?
821 assert q.block_columns.empty?
807 end
822 end
808
823
809 def test_set_column_names
824 def test_set_column_names
810 q = IssueQuery.new
825 q = IssueQuery.new
811 q.column_names = ['tracker', :subject, '', 'unknonw_column']
826 q.column_names = ['tracker', :subject, '', 'unknonw_column']
812 assert_equal [:id, :tracker, :subject], q.columns.collect {|c| c.name}
827 assert_equal [:id, :tracker, :subject], q.columns.collect {|c| c.name}
813 end
828 end
814
829
815 def test_has_column_should_accept_a_column_name
830 def test_has_column_should_accept_a_column_name
816 q = IssueQuery.new
831 q = IssueQuery.new
817 q.column_names = ['tracker', :subject]
832 q.column_names = ['tracker', :subject]
818 assert q.has_column?(:tracker)
833 assert q.has_column?(:tracker)
819 assert !q.has_column?(:category)
834 assert !q.has_column?(:category)
820 end
835 end
821
836
822 def test_has_column_should_accept_a_column
837 def test_has_column_should_accept_a_column
823 q = IssueQuery.new
838 q = IssueQuery.new
824 q.column_names = ['tracker', :subject]
839 q.column_names = ['tracker', :subject]
825
840
826 tracker_column = q.available_columns.detect {|c| c.name==:tracker}
841 tracker_column = q.available_columns.detect {|c| c.name==:tracker}
827 assert_kind_of QueryColumn, tracker_column
842 assert_kind_of QueryColumn, tracker_column
828 category_column = q.available_columns.detect {|c| c.name==:category}
843 category_column = q.available_columns.detect {|c| c.name==:category}
829 assert_kind_of QueryColumn, category_column
844 assert_kind_of QueryColumn, category_column
830
845
831 assert q.has_column?(tracker_column)
846 assert q.has_column?(tracker_column)
832 assert !q.has_column?(category_column)
847 assert !q.has_column?(category_column)
833 end
848 end
834
849
835 def test_inline_and_block_columns
850 def test_inline_and_block_columns
836 q = IssueQuery.new
851 q = IssueQuery.new
837 q.column_names = ['subject', 'description', 'tracker']
852 q.column_names = ['subject', 'description', 'tracker']
838
853
839 assert_equal [:id, :subject, :tracker], q.inline_columns.map(&:name)
854 assert_equal [:id, :subject, :tracker], q.inline_columns.map(&:name)
840 assert_equal [:description], q.block_columns.map(&:name)
855 assert_equal [:description], q.block_columns.map(&:name)
841 end
856 end
842
857
843 def test_custom_field_columns_should_be_inline
858 def test_custom_field_columns_should_be_inline
844 q = IssueQuery.new
859 q = IssueQuery.new
845 columns = q.available_columns.select {|column| column.is_a? QueryCustomFieldColumn}
860 columns = q.available_columns.select {|column| column.is_a? QueryCustomFieldColumn}
846 assert columns.any?
861 assert columns.any?
847 assert_nil columns.detect {|column| !column.inline?}
862 assert_nil columns.detect {|column| !column.inline?}
848 end
863 end
849
864
850 def test_query_should_preload_spent_hours
865 def test_query_should_preload_spent_hours
851 q = IssueQuery.new(:name => '_', :column_names => [:subject, :spent_hours])
866 q = IssueQuery.new(:name => '_', :column_names => [:subject, :spent_hours])
852 assert q.has_column?(:spent_hours)
867 assert q.has_column?(:spent_hours)
853 issues = q.issues
868 issues = q.issues
854 assert_not_nil issues.first.instance_variable_get("@spent_hours")
869 assert_not_nil issues.first.instance_variable_get("@spent_hours")
855 end
870 end
856
871
857 def test_groupable_columns_should_include_custom_fields
872 def test_groupable_columns_should_include_custom_fields
858 q = IssueQuery.new
873 q = IssueQuery.new
859 column = q.groupable_columns.detect {|c| c.name == :cf_1}
874 column = q.groupable_columns.detect {|c| c.name == :cf_1}
860 assert_not_nil column
875 assert_not_nil column
861 assert_kind_of QueryCustomFieldColumn, column
876 assert_kind_of QueryCustomFieldColumn, column
862 end
877 end
863
878
864 def test_groupable_columns_should_not_include_multi_custom_fields
879 def test_groupable_columns_should_not_include_multi_custom_fields
865 field = CustomField.find(1)
880 field = CustomField.find(1)
866 field.update_attribute :multiple, true
881 field.update_attribute :multiple, true
867
882
868 q = IssueQuery.new
883 q = IssueQuery.new
869 column = q.groupable_columns.detect {|c| c.name == :cf_1}
884 column = q.groupable_columns.detect {|c| c.name == :cf_1}
870 assert_nil column
885 assert_nil column
871 end
886 end
872
887
873 def test_groupable_columns_should_include_user_custom_fields
888 def test_groupable_columns_should_include_user_custom_fields
874 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'user')
889 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'user')
875
890
876 q = IssueQuery.new
891 q = IssueQuery.new
877 assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
892 assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
878 end
893 end
879
894
880 def test_groupable_columns_should_include_version_custom_fields
895 def test_groupable_columns_should_include_version_custom_fields
881 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'version')
896 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1], :field_format => 'version')
882
897
883 q = IssueQuery.new
898 q = IssueQuery.new
884 assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
899 assert q.groupable_columns.detect {|c| c.name == "cf_#{cf.id}".to_sym}
885 end
900 end
886
901
887 def test_grouped_with_valid_column
902 def test_grouped_with_valid_column
888 q = IssueQuery.new(:group_by => 'status')
903 q = IssueQuery.new(:group_by => 'status')
889 assert q.grouped?
904 assert q.grouped?
890 assert_not_nil q.group_by_column
905 assert_not_nil q.group_by_column
891 assert_equal :status, q.group_by_column.name
906 assert_equal :status, q.group_by_column.name
892 assert_not_nil q.group_by_statement
907 assert_not_nil q.group_by_statement
893 assert_equal 'status', q.group_by_statement
908 assert_equal 'status', q.group_by_statement
894 end
909 end
895
910
896 def test_grouped_with_invalid_column
911 def test_grouped_with_invalid_column
897 q = IssueQuery.new(:group_by => 'foo')
912 q = IssueQuery.new(:group_by => 'foo')
898 assert !q.grouped?
913 assert !q.grouped?
899 assert_nil q.group_by_column
914 assert_nil q.group_by_column
900 assert_nil q.group_by_statement
915 assert_nil q.group_by_statement
901 end
916 end
902
917
903 def test_sortable_columns_should_sort_assignees_according_to_user_format_setting
918 def test_sortable_columns_should_sort_assignees_according_to_user_format_setting
904 with_settings :user_format => 'lastname_coma_firstname' do
919 with_settings :user_format => 'lastname_coma_firstname' do
905 q = IssueQuery.new
920 q = IssueQuery.new
906 assert q.sortable_columns.has_key?('assigned_to')
921 assert q.sortable_columns.has_key?('assigned_to')
907 assert_equal %w(users.lastname users.firstname users.id), q.sortable_columns['assigned_to']
922 assert_equal %w(users.lastname users.firstname users.id), q.sortable_columns['assigned_to']
908 end
923 end
909 end
924 end
910
925
911 def test_sortable_columns_should_sort_authors_according_to_user_format_setting
926 def test_sortable_columns_should_sort_authors_according_to_user_format_setting
912 with_settings :user_format => 'lastname_coma_firstname' do
927 with_settings :user_format => 'lastname_coma_firstname' do
913 q = IssueQuery.new
928 q = IssueQuery.new
914 assert q.sortable_columns.has_key?('author')
929 assert q.sortable_columns.has_key?('author')
915 assert_equal %w(authors.lastname authors.firstname authors.id), q.sortable_columns['author']
930 assert_equal %w(authors.lastname authors.firstname authors.id), q.sortable_columns['author']
916 end
931 end
917 end
932 end
918
933
919 def test_sortable_columns_should_include_custom_field
934 def test_sortable_columns_should_include_custom_field
920 q = IssueQuery.new
935 q = IssueQuery.new
921 assert q.sortable_columns['cf_1']
936 assert q.sortable_columns['cf_1']
922 end
937 end
923
938
924 def test_sortable_columns_should_not_include_multi_custom_field
939 def test_sortable_columns_should_not_include_multi_custom_field
925 field = CustomField.find(1)
940 field = CustomField.find(1)
926 field.update_attribute :multiple, true
941 field.update_attribute :multiple, true
927
942
928 q = IssueQuery.new
943 q = IssueQuery.new
929 assert !q.sortable_columns['cf_1']
944 assert !q.sortable_columns['cf_1']
930 end
945 end
931
946
932 def test_default_sort
947 def test_default_sort
933 q = IssueQuery.new
948 q = IssueQuery.new
934 assert_equal [], q.sort_criteria
949 assert_equal [], q.sort_criteria
935 end
950 end
936
951
937 def test_set_sort_criteria_with_hash
952 def test_set_sort_criteria_with_hash
938 q = IssueQuery.new
953 q = IssueQuery.new
939 q.sort_criteria = {'0' => ['priority', 'desc'], '2' => ['tracker']}
954 q.sort_criteria = {'0' => ['priority', 'desc'], '2' => ['tracker']}
940 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
955 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
941 end
956 end
942
957
943 def test_set_sort_criteria_with_array
958 def test_set_sort_criteria_with_array
944 q = IssueQuery.new
959 q = IssueQuery.new
945 q.sort_criteria = [['priority', 'desc'], 'tracker']
960 q.sort_criteria = [['priority', 'desc'], 'tracker']
946 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
961 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
947 end
962 end
948
963
949 def test_create_query_with_sort
964 def test_create_query_with_sort
950 q = IssueQuery.new(:name => 'Sorted')
965 q = IssueQuery.new(:name => 'Sorted')
951 q.sort_criteria = [['priority', 'desc'], 'tracker']
966 q.sort_criteria = [['priority', 'desc'], 'tracker']
952 assert q.save
967 assert q.save
953 q.reload
968 q.reload
954 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
969 assert_equal [['priority', 'desc'], ['tracker', 'asc']], q.sort_criteria
955 end
970 end
956
971
957 def test_sort_by_string_custom_field_asc
972 def test_sort_by_string_custom_field_asc
958 q = IssueQuery.new
973 q = IssueQuery.new
959 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
974 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
960 assert c
975 assert c
961 assert c.sortable
976 assert c.sortable
962 issues = q.issues(:order => "#{c.sortable} ASC")
977 issues = q.issues(:order => "#{c.sortable} ASC")
963 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
978 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
964 assert !values.empty?
979 assert !values.empty?
965 assert_equal values.sort, values
980 assert_equal values.sort, values
966 end
981 end
967
982
968 def test_sort_by_string_custom_field_desc
983 def test_sort_by_string_custom_field_desc
969 q = IssueQuery.new
984 q = IssueQuery.new
970 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
985 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'string' }
971 assert c
986 assert c
972 assert c.sortable
987 assert c.sortable
973 issues = q.issues(:order => "#{c.sortable} DESC")
988 issues = q.issues(:order => "#{c.sortable} DESC")
974 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
989 values = issues.collect {|i| i.custom_value_for(c.custom_field).to_s}
975 assert !values.empty?
990 assert !values.empty?
976 assert_equal values.sort.reverse, values
991 assert_equal values.sort.reverse, values
977 end
992 end
978
993
979 def test_sort_by_float_custom_field_asc
994 def test_sort_by_float_custom_field_asc
980 q = IssueQuery.new
995 q = IssueQuery.new
981 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'float' }
996 c = q.available_columns.find {|col| col.is_a?(QueryCustomFieldColumn) && col.custom_field.field_format == 'float' }
982 assert c
997 assert c
983 assert c.sortable
998 assert c.sortable
984 issues = q.issues(:order => "#{c.sortable} ASC")
999 issues = q.issues(:order => "#{c.sortable} ASC")
985 values = issues.collect {|i| begin; Kernel.Float(i.custom_value_for(c.custom_field).to_s); rescue; nil; end}.compact
1000 values = issues.collect {|i| begin; Kernel.Float(i.custom_value_for(c.custom_field).to_s); rescue; nil; end}.compact
986 assert !values.empty?
1001 assert !values.empty?
987 assert_equal values.sort, values
1002 assert_equal values.sort, values
988 end
1003 end
989
1004
990 def test_invalid_query_should_raise_query_statement_invalid_error
1005 def test_invalid_query_should_raise_query_statement_invalid_error
991 q = IssueQuery.new
1006 q = IssueQuery.new
992 assert_raise Query::StatementInvalid do
1007 assert_raise Query::StatementInvalid do
993 q.issues(:conditions => "foo = 1")
1008 q.issues(:conditions => "foo = 1")
994 end
1009 end
995 end
1010 end
996
1011
997 def test_issue_count
1012 def test_issue_count
998 q = IssueQuery.new(:name => '_')
1013 q = IssueQuery.new(:name => '_')
999 issue_count = q.issue_count
1014 issue_count = q.issue_count
1000 assert_equal q.issues.size, issue_count
1015 assert_equal q.issues.size, issue_count
1001 end
1016 end
1002
1017
1003 def test_issue_count_with_archived_issues
1018 def test_issue_count_with_archived_issues
1004 p = Project.generate! do |project|
1019 p = Project.generate! do |project|
1005 project.status = Project::STATUS_ARCHIVED
1020 project.status = Project::STATUS_ARCHIVED
1006 end
1021 end
1007 i = Issue.generate!( :project => p, :tracker => p.trackers.first )
1022 i = Issue.generate!( :project => p, :tracker => p.trackers.first )
1008 assert !i.visible?
1023 assert !i.visible?
1009
1024
1010 test_issue_count
1025 test_issue_count
1011 end
1026 end
1012
1027
1013 def test_issue_count_by_association_group
1028 def test_issue_count_by_association_group
1014 q = IssueQuery.new(:name => '_', :group_by => 'assigned_to')
1029 q = IssueQuery.new(:name => '_', :group_by => 'assigned_to')
1015 count_by_group = q.issue_count_by_group
1030 count_by_group = q.issue_count_by_group
1016 assert_kind_of Hash, count_by_group
1031 assert_kind_of Hash, count_by_group
1017 assert_equal %w(NilClass User), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
1032 assert_equal %w(NilClass User), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
1018 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
1033 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
1019 assert count_by_group.has_key?(User.find(3))
1034 assert count_by_group.has_key?(User.find(3))
1020 end
1035 end
1021
1036
1022 def test_issue_count_by_list_custom_field_group
1037 def test_issue_count_by_list_custom_field_group
1023 q = IssueQuery.new(:name => '_', :group_by => 'cf_1')
1038 q = IssueQuery.new(:name => '_', :group_by => 'cf_1')
1024 count_by_group = q.issue_count_by_group
1039 count_by_group = q.issue_count_by_group
1025 assert_kind_of Hash, count_by_group
1040 assert_kind_of Hash, count_by_group
1026 assert_equal %w(NilClass String), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
1041 assert_equal %w(NilClass String), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
1027 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
1042 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
1028 assert count_by_group.has_key?('MySQL')
1043 assert count_by_group.has_key?('MySQL')
1029 end
1044 end
1030
1045
1031 def test_issue_count_by_date_custom_field_group
1046 def test_issue_count_by_date_custom_field_group
1032 q = IssueQuery.new(:name => '_', :group_by => 'cf_8')
1047 q = IssueQuery.new(:name => '_', :group_by => 'cf_8')
1033 count_by_group = q.issue_count_by_group
1048 count_by_group = q.issue_count_by_group
1034 assert_kind_of Hash, count_by_group
1049 assert_kind_of Hash, count_by_group
1035 assert_equal %w(Date NilClass), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
1050 assert_equal %w(Date NilClass), count_by_group.keys.collect {|k| k.class.name}.uniq.sort
1036 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
1051 assert_equal %w(Fixnum), count_by_group.values.collect {|k| k.class.name}.uniq
1037 end
1052 end
1038
1053
1039 def test_issue_count_with_nil_group_only
1054 def test_issue_count_with_nil_group_only
1040 Issue.update_all("assigned_to_id = NULL")
1055 Issue.update_all("assigned_to_id = NULL")
1041
1056
1042 q = IssueQuery.new(:name => '_', :group_by => 'assigned_to')
1057 q = IssueQuery.new(:name => '_', :group_by => 'assigned_to')
1043 count_by_group = q.issue_count_by_group
1058 count_by_group = q.issue_count_by_group
1044 assert_kind_of Hash, count_by_group
1059 assert_kind_of Hash, count_by_group
1045 assert_equal 1, count_by_group.keys.size
1060 assert_equal 1, count_by_group.keys.size
1046 assert_nil count_by_group.keys.first
1061 assert_nil count_by_group.keys.first
1047 end
1062 end
1048
1063
1049 def test_issue_ids
1064 def test_issue_ids
1050 q = IssueQuery.new(:name => '_')
1065 q = IssueQuery.new(:name => '_')
1051 order = "issues.subject, issues.id"
1066 order = "issues.subject, issues.id"
1052 issues = q.issues(:order => order)
1067 issues = q.issues(:order => order)
1053 assert_equal issues.map(&:id), q.issue_ids(:order => order)
1068 assert_equal issues.map(&:id), q.issue_ids(:order => order)
1054 end
1069 end
1055
1070
1056 def test_label_for
1071 def test_label_for
1057 set_language_if_valid 'en'
1072 set_language_if_valid 'en'
1058 q = IssueQuery.new
1073 q = IssueQuery.new
1059 assert_equal 'Assignee', q.label_for('assigned_to_id')
1074 assert_equal 'Assignee', q.label_for('assigned_to_id')
1060 end
1075 end
1061
1076
1062 def test_label_for_fr
1077 def test_label_for_fr
1063 set_language_if_valid 'fr'
1078 set_language_if_valid 'fr'
1064 q = IssueQuery.new
1079 q = IssueQuery.new
1065 s = "Assign\xc3\xa9 \xc3\xa0"
1080 s = "Assign\xc3\xa9 \xc3\xa0"
1066 s.force_encoding('UTF-8') if s.respond_to?(:force_encoding)
1081 s.force_encoding('UTF-8') if s.respond_to?(:force_encoding)
1067 assert_equal s, q.label_for('assigned_to_id')
1082 assert_equal s, q.label_for('assigned_to_id')
1068 end
1083 end
1069
1084
1070 def test_editable_by
1085 def test_editable_by
1071 admin = User.find(1)
1086 admin = User.find(1)
1072 manager = User.find(2)
1087 manager = User.find(2)
1073 developer = User.find(3)
1088 developer = User.find(3)
1074
1089
1075 # Public query on project 1
1090 # Public query on project 1
1076 q = IssueQuery.find(1)
1091 q = IssueQuery.find(1)
1077 assert q.editable_by?(admin)
1092 assert q.editable_by?(admin)
1078 assert q.editable_by?(manager)
1093 assert q.editable_by?(manager)
1079 assert !q.editable_by?(developer)
1094 assert !q.editable_by?(developer)
1080
1095
1081 # Private query on project 1
1096 # Private query on project 1
1082 q = IssueQuery.find(2)
1097 q = IssueQuery.find(2)
1083 assert q.editable_by?(admin)
1098 assert q.editable_by?(admin)
1084 assert !q.editable_by?(manager)
1099 assert !q.editable_by?(manager)
1085 assert q.editable_by?(developer)
1100 assert q.editable_by?(developer)
1086
1101
1087 # Private query for all projects
1102 # Private query for all projects
1088 q = IssueQuery.find(3)
1103 q = IssueQuery.find(3)
1089 assert q.editable_by?(admin)
1104 assert q.editable_by?(admin)
1090 assert !q.editable_by?(manager)
1105 assert !q.editable_by?(manager)
1091 assert q.editable_by?(developer)
1106 assert q.editable_by?(developer)
1092
1107
1093 # Public query for all projects
1108 # Public query for all projects
1094 q = IssueQuery.find(4)
1109 q = IssueQuery.find(4)
1095 assert q.editable_by?(admin)
1110 assert q.editable_by?(admin)
1096 assert !q.editable_by?(manager)
1111 assert !q.editable_by?(manager)
1097 assert !q.editable_by?(developer)
1112 assert !q.editable_by?(developer)
1098 end
1113 end
1099
1114
1100 def test_visible_scope
1115 def test_visible_scope
1101 query_ids = IssueQuery.visible(User.anonymous).map(&:id)
1116 query_ids = IssueQuery.visible(User.anonymous).map(&:id)
1102
1117
1103 assert query_ids.include?(1), 'public query on public project was not visible'
1118 assert query_ids.include?(1), 'public query on public project was not visible'
1104 assert query_ids.include?(4), 'public query for all projects was not visible'
1119 assert query_ids.include?(4), 'public query for all projects was not visible'
1105 assert !query_ids.include?(2), 'private query on public project was visible'
1120 assert !query_ids.include?(2), 'private query on public project was visible'
1106 assert !query_ids.include?(3), 'private query for all projects was visible'
1121 assert !query_ids.include?(3), 'private query for all projects was visible'
1107 assert !query_ids.include?(7), 'public query on private project was visible'
1122 assert !query_ids.include?(7), 'public query on private project was visible'
1108 end
1123 end
1109
1124
1110 def test_query_with_public_visibility_should_be_visible_to_anyone
1125 def test_query_with_public_visibility_should_be_visible_to_anyone
1111 q = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_PUBLIC)
1126 q = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_PUBLIC)
1112
1127
1113 assert q.visible?(User.anonymous)
1128 assert q.visible?(User.anonymous)
1114 assert IssueQuery.visible(User.anonymous).find_by_id(q.id)
1129 assert IssueQuery.visible(User.anonymous).find_by_id(q.id)
1115
1130
1116 assert q.visible?(User.find(7))
1131 assert q.visible?(User.find(7))
1117 assert IssueQuery.visible(User.find(7)).find_by_id(q.id)
1132 assert IssueQuery.visible(User.find(7)).find_by_id(q.id)
1118
1133
1119 assert q.visible?(User.find(2))
1134 assert q.visible?(User.find(2))
1120 assert IssueQuery.visible(User.find(2)).find_by_id(q.id)
1135 assert IssueQuery.visible(User.find(2)).find_by_id(q.id)
1121
1136
1122 assert q.visible?(User.find(1))
1137 assert q.visible?(User.find(1))
1123 assert IssueQuery.visible(User.find(1)).find_by_id(q.id)
1138 assert IssueQuery.visible(User.find(1)).find_by_id(q.id)
1124 end
1139 end
1125
1140
1126 def test_query_with_roles_visibility_should_be_visible_to_user_with_role
1141 def test_query_with_roles_visibility_should_be_visible_to_user_with_role
1127 q = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_ROLES, :role_ids => [1,2])
1142 q = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_ROLES, :role_ids => [1,2])
1128
1143
1129 assert !q.visible?(User.anonymous)
1144 assert !q.visible?(User.anonymous)
1130 assert_nil IssueQuery.visible(User.anonymous).find_by_id(q.id)
1145 assert_nil IssueQuery.visible(User.anonymous).find_by_id(q.id)
1131
1146
1132 assert !q.visible?(User.find(7))
1147 assert !q.visible?(User.find(7))
1133 assert_nil IssueQuery.visible(User.find(7)).find_by_id(q.id)
1148 assert_nil IssueQuery.visible(User.find(7)).find_by_id(q.id)
1134
1149
1135 assert q.visible?(User.find(2))
1150 assert q.visible?(User.find(2))
1136 assert IssueQuery.visible(User.find(2)).find_by_id(q.id)
1151 assert IssueQuery.visible(User.find(2)).find_by_id(q.id)
1137
1152
1138 assert q.visible?(User.find(1))
1153 assert q.visible?(User.find(1))
1139 assert IssueQuery.visible(User.find(1)).find_by_id(q.id)
1154 assert IssueQuery.visible(User.find(1)).find_by_id(q.id)
1140 end
1155 end
1141
1156
1142 def test_query_with_private_visibility_should_be_visible_to_owner
1157 def test_query_with_private_visibility_should_be_visible_to_owner
1143 q = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_PRIVATE, :user => User.find(7))
1158 q = IssueQuery.create!(:name => 'Query', :visibility => IssueQuery::VISIBILITY_PRIVATE, :user => User.find(7))
1144
1159
1145 assert !q.visible?(User.anonymous)
1160 assert !q.visible?(User.anonymous)
1146 assert_nil IssueQuery.visible(User.anonymous).find_by_id(q.id)
1161 assert_nil IssueQuery.visible(User.anonymous).find_by_id(q.id)
1147
1162
1148 assert q.visible?(User.find(7))
1163 assert q.visible?(User.find(7))
1149 assert IssueQuery.visible(User.find(7)).find_by_id(q.id)
1164 assert IssueQuery.visible(User.find(7)).find_by_id(q.id)
1150
1165
1151 assert !q.visible?(User.find(2))
1166 assert !q.visible?(User.find(2))
1152 assert_nil IssueQuery.visible(User.find(2)).find_by_id(q.id)
1167 assert_nil IssueQuery.visible(User.find(2)).find_by_id(q.id)
1153
1168
1154 assert q.visible?(User.find(1))
1169 assert q.visible?(User.find(1))
1155 assert_nil IssueQuery.visible(User.find(1)).find_by_id(q.id)
1170 assert_nil IssueQuery.visible(User.find(1)).find_by_id(q.id)
1156 end
1171 end
1157
1172
1158 test "#available_filters should include users of visible projects in cross-project view" do
1173 test "#available_filters should include users of visible projects in cross-project view" do
1159 users = IssueQuery.new.available_filters["assigned_to_id"]
1174 users = IssueQuery.new.available_filters["assigned_to_id"]
1160 assert_not_nil users
1175 assert_not_nil users
1161 assert users[:values].map{|u|u[1]}.include?("3")
1176 assert users[:values].map{|u|u[1]}.include?("3")
1162 end
1177 end
1163
1178
1164 test "#available_filters should include users of subprojects" do
1179 test "#available_filters should include users of subprojects" do
1165 user1 = User.generate!
1180 user1 = User.generate!
1166 user2 = User.generate!
1181 user2 = User.generate!
1167 project = Project.find(1)
1182 project = Project.find(1)
1168 Member.create!(:principal => user1, :project => project.children.visible.first, :role_ids => [1])
1183 Member.create!(:principal => user1, :project => project.children.visible.first, :role_ids => [1])
1169
1184
1170 users = IssueQuery.new(:project => project).available_filters["assigned_to_id"]
1185 users = IssueQuery.new(:project => project).available_filters["assigned_to_id"]
1171 assert_not_nil users
1186 assert_not_nil users
1172 assert users[:values].map{|u|u[1]}.include?(user1.id.to_s)
1187 assert users[:values].map{|u|u[1]}.include?(user1.id.to_s)
1173 assert !users[:values].map{|u|u[1]}.include?(user2.id.to_s)
1188 assert !users[:values].map{|u|u[1]}.include?(user2.id.to_s)
1174 end
1189 end
1175
1190
1176 test "#available_filters should include visible projects in cross-project view" do
1191 test "#available_filters should include visible projects in cross-project view" do
1177 projects = IssueQuery.new.available_filters["project_id"]
1192 projects = IssueQuery.new.available_filters["project_id"]
1178 assert_not_nil projects
1193 assert_not_nil projects
1179 assert projects[:values].map{|u|u[1]}.include?("1")
1194 assert projects[:values].map{|u|u[1]}.include?("1")
1180 end
1195 end
1181
1196
1182 test "#available_filters should include 'member_of_group' filter" do
1197 test "#available_filters should include 'member_of_group' filter" do
1183 query = IssueQuery.new
1198 query = IssueQuery.new
1184 assert query.available_filters.keys.include?("member_of_group")
1199 assert query.available_filters.keys.include?("member_of_group")
1185 assert_equal :list_optional, query.available_filters["member_of_group"][:type]
1200 assert_equal :list_optional, query.available_filters["member_of_group"][:type]
1186 assert query.available_filters["member_of_group"][:values].present?
1201 assert query.available_filters["member_of_group"][:values].present?
1187 assert_equal Group.all.sort.map {|g| [g.name, g.id.to_s]},
1202 assert_equal Group.all.sort.map {|g| [g.name, g.id.to_s]},
1188 query.available_filters["member_of_group"][:values].sort
1203 query.available_filters["member_of_group"][:values].sort
1189 end
1204 end
1190
1205
1191 test "#available_filters should include 'assigned_to_role' filter" do
1206 test "#available_filters should include 'assigned_to_role' filter" do
1192 query = IssueQuery.new
1207 query = IssueQuery.new
1193 assert query.available_filters.keys.include?("assigned_to_role")
1208 assert query.available_filters.keys.include?("assigned_to_role")
1194 assert_equal :list_optional, query.available_filters["assigned_to_role"][:type]
1209 assert_equal :list_optional, query.available_filters["assigned_to_role"][:type]
1195
1210
1196 assert query.available_filters["assigned_to_role"][:values].include?(['Manager','1'])
1211 assert query.available_filters["assigned_to_role"][:values].include?(['Manager','1'])
1197 assert query.available_filters["assigned_to_role"][:values].include?(['Developer','2'])
1212 assert query.available_filters["assigned_to_role"][:values].include?(['Developer','2'])
1198 assert query.available_filters["assigned_to_role"][:values].include?(['Reporter','3'])
1213 assert query.available_filters["assigned_to_role"][:values].include?(['Reporter','3'])
1199
1214
1200 assert ! query.available_filters["assigned_to_role"][:values].include?(['Non member','4'])
1215 assert ! query.available_filters["assigned_to_role"][:values].include?(['Non member','4'])
1201 assert ! query.available_filters["assigned_to_role"][:values].include?(['Anonymous','5'])
1216 assert ! query.available_filters["assigned_to_role"][:values].include?(['Anonymous','5'])
1202 end
1217 end
1203
1218
1204 def test_available_filters_should_include_custom_field_according_to_user_visibility
1219 def test_available_filters_should_include_custom_field_according_to_user_visibility
1205 visible_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => true)
1220 visible_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => true)
1206 hidden_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => false, :role_ids => [1])
1221 hidden_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => false, :role_ids => [1])
1207
1222
1208 with_current_user User.find(3) do
1223 with_current_user User.find(3) do
1209 query = IssueQuery.new
1224 query = IssueQuery.new
1210 assert_include "cf_#{visible_field.id}", query.available_filters.keys
1225 assert_include "cf_#{visible_field.id}", query.available_filters.keys
1211 assert_not_include "cf_#{hidden_field.id}", query.available_filters.keys
1226 assert_not_include "cf_#{hidden_field.id}", query.available_filters.keys
1212 end
1227 end
1213 end
1228 end
1214
1229
1215 def test_available_columns_should_include_custom_field_according_to_user_visibility
1230 def test_available_columns_should_include_custom_field_according_to_user_visibility
1216 visible_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => true)
1231 visible_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => true)
1217 hidden_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => false, :role_ids => [1])
1232 hidden_field = IssueCustomField.generate!(:is_for_all => true, :is_filter => true, :visible => false, :role_ids => [1])
1218
1233
1219 with_current_user User.find(3) do
1234 with_current_user User.find(3) do
1220 query = IssueQuery.new
1235 query = IssueQuery.new
1221 assert_include :"cf_#{visible_field.id}", query.available_columns.map(&:name)
1236 assert_include :"cf_#{visible_field.id}", query.available_columns.map(&:name)
1222 assert_not_include :"cf_#{hidden_field.id}", query.available_columns.map(&:name)
1237 assert_not_include :"cf_#{hidden_field.id}", query.available_columns.map(&:name)
1223 end
1238 end
1224 end
1239 end
1225
1240
1226 context "#statement" do
1241 context "#statement" do
1227 context "with 'member_of_group' filter" do
1242 context "with 'member_of_group' filter" do
1228 setup do
1243 setup do
1229 Group.destroy_all # No fixtures
1244 Group.destroy_all # No fixtures
1230 @user_in_group = User.generate!
1245 @user_in_group = User.generate!
1231 @second_user_in_group = User.generate!
1246 @second_user_in_group = User.generate!
1232 @user_in_group2 = User.generate!
1247 @user_in_group2 = User.generate!
1233 @user_not_in_group = User.generate!
1248 @user_not_in_group = User.generate!
1234
1249
1235 @group = Group.generate!.reload
1250 @group = Group.generate!.reload
1236 @group.users << @user_in_group
1251 @group.users << @user_in_group
1237 @group.users << @second_user_in_group
1252 @group.users << @second_user_in_group
1238
1253
1239 @group2 = Group.generate!.reload
1254 @group2 = Group.generate!.reload
1240 @group2.users << @user_in_group2
1255 @group2.users << @user_in_group2
1241
1256
1242 end
1257 end
1243
1258
1244 should "search assigned to for users in the group" do
1259 should "search assigned to for users in the group" do
1245 @query = IssueQuery.new(:name => '_')
1260 @query = IssueQuery.new(:name => '_')
1246 @query.add_filter('member_of_group', '=', [@group.id.to_s])
1261 @query.add_filter('member_of_group', '=', [@group.id.to_s])
1247
1262
1248 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@group.id}')"
1263 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@group.id}')"
1249 assert_find_issues_with_query_is_successful @query
1264 assert_find_issues_with_query_is_successful @query
1250 end
1265 end
1251
1266
1252 should "search not assigned to any group member (none)" do
1267 should "search not assigned to any group member (none)" do
1253 @query = IssueQuery.new(:name => '_')
1268 @query = IssueQuery.new(:name => '_')
1254 @query.add_filter('member_of_group', '!*', [''])
1269 @query.add_filter('member_of_group', '!*', [''])
1255
1270
1256 # Users not in a group
1271 # Users not in a group
1257 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IS NULL OR #{Issue.table_name}.assigned_to_id NOT IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}','#{@group.id}','#{@group2.id}')"
1272 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IS NULL OR #{Issue.table_name}.assigned_to_id NOT IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}','#{@group.id}','#{@group2.id}')"
1258 assert_find_issues_with_query_is_successful @query
1273 assert_find_issues_with_query_is_successful @query
1259 end
1274 end
1260
1275
1261 should "search assigned to any group member (all)" do
1276 should "search assigned to any group member (all)" do
1262 @query = IssueQuery.new(:name => '_')
1277 @query = IssueQuery.new(:name => '_')
1263 @query.add_filter('member_of_group', '*', [''])
1278 @query.add_filter('member_of_group', '*', [''])
1264
1279
1265 # Only users in a group
1280 # Only users in a group
1266 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}','#{@group.id}','#{@group2.id}')"
1281 assert_query_statement_includes @query, "#{Issue.table_name}.assigned_to_id IN ('#{@user_in_group.id}','#{@second_user_in_group.id}','#{@user_in_group2.id}','#{@group.id}','#{@group2.id}')"
1267 assert_find_issues_with_query_is_successful @query
1282 assert_find_issues_with_query_is_successful @query
1268 end
1283 end
1269
1284
1270 should "return an empty set with = empty group" do
1285 should "return an empty set with = empty group" do
1271 @empty_group = Group.generate!
1286 @empty_group = Group.generate!
1272 @query = IssueQuery.new(:name => '_')
1287 @query = IssueQuery.new(:name => '_')
1273 @query.add_filter('member_of_group', '=', [@empty_group.id.to_s])
1288 @query.add_filter('member_of_group', '=', [@empty_group.id.to_s])
1274
1289
1275 assert_equal [], find_issues_with_query(@query)
1290 assert_equal [], find_issues_with_query(@query)
1276 end
1291 end
1277
1292
1278 should "return issues with ! empty group" do
1293 should "return issues with ! empty group" do
1279 @empty_group = Group.generate!
1294 @empty_group = Group.generate!
1280 @query = IssueQuery.new(:name => '_')
1295 @query = IssueQuery.new(:name => '_')
1281 @query.add_filter('member_of_group', '!', [@empty_group.id.to_s])
1296 @query.add_filter('member_of_group', '!', [@empty_group.id.to_s])
1282
1297
1283 assert_find_issues_with_query_is_successful @query
1298 assert_find_issues_with_query_is_successful @query
1284 end
1299 end
1285 end
1300 end
1286
1301
1287 context "with 'assigned_to_role' filter" do
1302 context "with 'assigned_to_role' filter" do
1288 setup do
1303 setup do
1289 @manager_role = Role.find_by_name('Manager')
1304 @manager_role = Role.find_by_name('Manager')
1290 @developer_role = Role.find_by_name('Developer')
1305 @developer_role = Role.find_by_name('Developer')
1291
1306
1292 @project = Project.generate!
1307 @project = Project.generate!
1293 @manager = User.generate!
1308 @manager = User.generate!
1294 @developer = User.generate!
1309 @developer = User.generate!
1295 @boss = User.generate!
1310 @boss = User.generate!
1296 @guest = User.generate!
1311 @guest = User.generate!
1297 User.add_to_project(@manager, @project, @manager_role)
1312 User.add_to_project(@manager, @project, @manager_role)
1298 User.add_to_project(@developer, @project, @developer_role)
1313 User.add_to_project(@developer, @project, @developer_role)
1299 User.add_to_project(@boss, @project, [@manager_role, @developer_role])
1314 User.add_to_project(@boss, @project, [@manager_role, @developer_role])
1300
1315
1301 @issue1 = Issue.generate!(:project => @project, :assigned_to_id => @manager.id)
1316 @issue1 = Issue.generate!(:project => @project, :assigned_to_id => @manager.id)
1302 @issue2 = Issue.generate!(:project => @project, :assigned_to_id => @developer.id)
1317 @issue2 = Issue.generate!(:project => @project, :assigned_to_id => @developer.id)
1303 @issue3 = Issue.generate!(:project => @project, :assigned_to_id => @boss.id)
1318 @issue3 = Issue.generate!(:project => @project, :assigned_to_id => @boss.id)
1304 @issue4 = Issue.generate!(:project => @project, :assigned_to_id => @guest.id)
1319 @issue4 = Issue.generate!(:project => @project, :assigned_to_id => @guest.id)
1305 @issue5 = Issue.generate!(:project => @project)
1320 @issue5 = Issue.generate!(:project => @project)
1306 end
1321 end
1307
1322
1308 should "search assigned to for users with the Role" do
1323 should "search assigned to for users with the Role" do
1309 @query = IssueQuery.new(:name => '_', :project => @project)
1324 @query = IssueQuery.new(:name => '_', :project => @project)
1310 @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
1325 @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
1311
1326
1312 assert_query_result [@issue1, @issue3], @query
1327 assert_query_result [@issue1, @issue3], @query
1313 end
1328 end
1314
1329
1315 should "search assigned to for users with the Role on the issue project" do
1330 should "search assigned to for users with the Role on the issue project" do
1316 other_project = Project.generate!
1331 other_project = Project.generate!
1317 User.add_to_project(@developer, other_project, @manager_role)
1332 User.add_to_project(@developer, other_project, @manager_role)
1318
1333
1319 @query = IssueQuery.new(:name => '_', :project => @project)
1334 @query = IssueQuery.new(:name => '_', :project => @project)
1320 @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
1335 @query.add_filter('assigned_to_role', '=', [@manager_role.id.to_s])
1321
1336
1322 assert_query_result [@issue1, @issue3], @query
1337 assert_query_result [@issue1, @issue3], @query
1323 end
1338 end
1324
1339
1325 should "return an empty set with empty role" do
1340 should "return an empty set with empty role" do
1326 @empty_role = Role.generate!
1341 @empty_role = Role.generate!
1327 @query = IssueQuery.new(:name => '_', :project => @project)
1342 @query = IssueQuery.new(:name => '_', :project => @project)
1328 @query.add_filter('assigned_to_role', '=', [@empty_role.id.to_s])
1343 @query.add_filter('assigned_to_role', '=', [@empty_role.id.to_s])
1329
1344
1330 assert_query_result [], @query
1345 assert_query_result [], @query
1331 end
1346 end
1332
1347
1333 should "search assigned to for users without the Role" do
1348 should "search assigned to for users without the Role" do
1334 @query = IssueQuery.new(:name => '_', :project => @project)
1349 @query = IssueQuery.new(:name => '_', :project => @project)
1335 @query.add_filter('assigned_to_role', '!', [@manager_role.id.to_s])
1350 @query.add_filter('assigned_to_role', '!', [@manager_role.id.to_s])
1336
1351
1337 assert_query_result [@issue2, @issue4, @issue5], @query
1352 assert_query_result [@issue2, @issue4, @issue5], @query
1338 end
1353 end
1339
1354
1340 should "search assigned to for users not assigned to any Role (none)" do
1355 should "search assigned to for users not assigned to any Role (none)" do
1341 @query = IssueQuery.new(:name => '_', :project => @project)
1356 @query = IssueQuery.new(:name => '_', :project => @project)
1342 @query.add_filter('assigned_to_role', '!*', [''])
1357 @query.add_filter('assigned_to_role', '!*', [''])
1343
1358
1344 assert_query_result [@issue4, @issue5], @query
1359 assert_query_result [@issue4, @issue5], @query
1345 end
1360 end
1346
1361
1347 should "search assigned to for users assigned to any Role (all)" do
1362 should "search assigned to for users assigned to any Role (all)" do
1348 @query = IssueQuery.new(:name => '_', :project => @project)
1363 @query = IssueQuery.new(:name => '_', :project => @project)
1349 @query.add_filter('assigned_to_role', '*', [''])
1364 @query.add_filter('assigned_to_role', '*', [''])
1350
1365
1351 assert_query_result [@issue1, @issue2, @issue3], @query
1366 assert_query_result [@issue1, @issue2, @issue3], @query
1352 end
1367 end
1353
1368
1354 should "return issues with ! empty role" do
1369 should "return issues with ! empty role" do
1355 @empty_role = Role.generate!
1370 @empty_role = Role.generate!
1356 @query = IssueQuery.new(:name => '_', :project => @project)
1371 @query = IssueQuery.new(:name => '_', :project => @project)
1357 @query.add_filter('assigned_to_role', '!', [@empty_role.id.to_s])
1372 @query.add_filter('assigned_to_role', '!', [@empty_role.id.to_s])
1358
1373
1359 assert_query_result [@issue1, @issue2, @issue3, @issue4, @issue5], @query
1374 assert_query_result [@issue1, @issue2, @issue3, @issue4, @issue5], @query
1360 end
1375 end
1361 end
1376 end
1362 end
1377 end
1363 end
1378 end
General Comments 0
You need to be logged in to leave comments. Login now