Chris@245
|
1 # Redmine - project management software
|
Chris@245
|
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
|
Chris@0
|
3 #
|
Chris@0
|
4 # This program is free software; you can redistribute it and/or
|
Chris@0
|
5 # modify it under the terms of the GNU General Public License
|
Chris@0
|
6 # as published by the Free Software Foundation; either version 2
|
Chris@0
|
7 # of the License, or (at your option) any later version.
|
Chris@441
|
8 #
|
Chris@0
|
9 # This program is distributed in the hope that it will be useful,
|
Chris@0
|
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
Chris@0
|
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
Chris@0
|
12 # GNU General Public License for more details.
|
Chris@441
|
13 #
|
Chris@0
|
14 # You should have received a copy of the GNU General Public License
|
Chris@0
|
15 # along with this program; if not, write to the Free Software
|
Chris@0
|
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
Chris@0
|
17
|
Chris@119
|
18 require File.expand_path('../../test_helper', __FILE__)
|
Chris@0
|
19
|
Chris@0
|
20 class IssueTest < ActiveSupport::TestCase
|
Chris@0
|
21 fixtures :projects, :users, :members, :member_roles, :roles,
|
Chris@0
|
22 :trackers, :projects_trackers,
|
Chris@0
|
23 :enabled_modules,
|
Chris@0
|
24 :versions,
|
Chris@441
|
25 :issue_statuses, :issue_categories, :issue_relations, :workflows,
|
Chris@0
|
26 :enumerations,
|
Chris@0
|
27 :issues,
|
Chris@0
|
28 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
|
Chris@0
|
29 :time_entries
|
Chris@0
|
30
|
Chris@0
|
31 def test_create
|
Chris@909
|
32 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
|
Chris@909
|
33 :status_id => 1, :priority => IssuePriority.all.first,
|
Chris@909
|
34 :subject => 'test_create',
|
Chris@909
|
35 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
|
Chris@0
|
36 assert issue.save
|
Chris@0
|
37 issue.reload
|
Chris@0
|
38 assert_equal 1.5, issue.estimated_hours
|
Chris@0
|
39 end
|
Chris@441
|
40
|
Chris@0
|
41 def test_create_minimal
|
Chris@909
|
42 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
|
Chris@909
|
43 :status_id => 1, :priority => IssuePriority.all.first,
|
Chris@909
|
44 :subject => 'test_create')
|
Chris@0
|
45 assert issue.save
|
Chris@0
|
46 assert issue.description.nil?
|
Chris@0
|
47 end
|
Chris@441
|
48
|
Chris@0
|
49 def test_create_with_required_custom_field
|
Chris@0
|
50 field = IssueCustomField.find_by_name('Database')
|
Chris@0
|
51 field.update_attribute(:is_required, true)
|
Chris@441
|
52
|
Chris@909
|
53 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
|
Chris@909
|
54 :status_id => 1, :subject => 'test_create',
|
Chris@909
|
55 :description => 'IssueTest#test_create_with_required_custom_field')
|
Chris@0
|
56 assert issue.available_custom_fields.include?(field)
|
Chris@0
|
57 # No value for the custom field
|
Chris@0
|
58 assert !issue.save
|
Chris@0
|
59 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
|
Chris@0
|
60 # Blank value
|
Chris@0
|
61 issue.custom_field_values = { field.id => '' }
|
Chris@0
|
62 assert !issue.save
|
Chris@0
|
63 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
|
Chris@0
|
64 # Invalid value
|
Chris@0
|
65 issue.custom_field_values = { field.id => 'SQLServer' }
|
Chris@0
|
66 assert !issue.save
|
Chris@0
|
67 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
|
Chris@0
|
68 # Valid value
|
Chris@0
|
69 issue.custom_field_values = { field.id => 'PostgreSQL' }
|
Chris@0
|
70 assert issue.save
|
Chris@0
|
71 issue.reload
|
Chris@0
|
72 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
|
Chris@0
|
73 end
|
Chris@441
|
74
|
Chris@909
|
75 def test_create_with_group_assignment
|
Chris@909
|
76 with_settings :issue_group_assignment => '1' do
|
Chris@909
|
77 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
|
Chris@909
|
78 :subject => 'Group assignment',
|
Chris@909
|
79 :assigned_to_id => 11).save
|
Chris@909
|
80 issue = Issue.first(:order => 'id DESC')
|
Chris@909
|
81 assert_kind_of Group, issue.assigned_to
|
Chris@909
|
82 assert_equal Group.find(11), issue.assigned_to
|
Chris@909
|
83 end
|
Chris@909
|
84 end
|
Chris@909
|
85
|
Chris@441
|
86 def assert_visibility_match(user, issues)
|
Chris@441
|
87 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
|
Chris@441
|
88 end
|
Chris@441
|
89
|
Chris@0
|
90 def test_visible_scope_for_anonymous
|
Chris@0
|
91 # Anonymous user should see issues of public projects only
|
Chris@0
|
92 issues = Issue.visible(User.anonymous).all
|
Chris@0
|
93 assert issues.any?
|
Chris@0
|
94 assert_nil issues.detect {|issue| !issue.project.is_public?}
|
Chris@441
|
95 assert_nil issues.detect {|issue| issue.is_private?}
|
Chris@441
|
96 assert_visibility_match User.anonymous, issues
|
Chris@441
|
97 end
|
Chris@441
|
98
|
Chris@441
|
99 def test_visible_scope_for_anonymous_with_own_issues_visibility
|
Chris@441
|
100 Role.anonymous.update_attribute :issues_visibility, 'own'
|
Chris@909
|
101 Issue.create!(:project_id => 1, :tracker_id => 1,
|
Chris@909
|
102 :author_id => User.anonymous.id,
|
Chris@909
|
103 :subject => 'Issue by anonymous')
|
Chris@441
|
104
|
Chris@441
|
105 issues = Issue.visible(User.anonymous).all
|
Chris@441
|
106 assert issues.any?
|
Chris@441
|
107 assert_nil issues.detect {|issue| issue.author != User.anonymous}
|
Chris@441
|
108 assert_visibility_match User.anonymous, issues
|
Chris@441
|
109 end
|
Chris@441
|
110
|
Chris@441
|
111 def test_visible_scope_for_anonymous_without_view_issues_permissions
|
Chris@0
|
112 # Anonymous user should not see issues without permission
|
Chris@0
|
113 Role.anonymous.remove_permission!(:view_issues)
|
Chris@0
|
114 issues = Issue.visible(User.anonymous).all
|
Chris@0
|
115 assert issues.empty?
|
Chris@441
|
116 assert_visibility_match User.anonymous, issues
|
Chris@0
|
117 end
|
Chris@441
|
118
|
Chris@441
|
119 def test_visible_scope_for_non_member
|
Chris@0
|
120 user = User.find(9)
|
Chris@0
|
121 assert user.projects.empty?
|
Chris@0
|
122 # Non member user should see issues of public projects only
|
Chris@0
|
123 issues = Issue.visible(user).all
|
Chris@0
|
124 assert issues.any?
|
Chris@0
|
125 assert_nil issues.detect {|issue| !issue.project.is_public?}
|
Chris@441
|
126 assert_nil issues.detect {|issue| issue.is_private?}
|
Chris@441
|
127 assert_visibility_match user, issues
|
Chris@441
|
128 end
|
Chris@441
|
129
|
Chris@441
|
130 def test_visible_scope_for_non_member_with_own_issues_visibility
|
Chris@441
|
131 Role.non_member.update_attribute :issues_visibility, 'own'
|
Chris@441
|
132 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
|
Chris@441
|
133 user = User.find(9)
|
Chris@441
|
134
|
Chris@441
|
135 issues = Issue.visible(user).all
|
Chris@441
|
136 assert issues.any?
|
Chris@441
|
137 assert_nil issues.detect {|issue| issue.author != user}
|
Chris@441
|
138 assert_visibility_match user, issues
|
Chris@441
|
139 end
|
Chris@441
|
140
|
Chris@441
|
141 def test_visible_scope_for_non_member_without_view_issues_permissions
|
Chris@0
|
142 # Non member user should not see issues without permission
|
Chris@0
|
143 Role.non_member.remove_permission!(:view_issues)
|
Chris@441
|
144 user = User.find(9)
|
Chris@441
|
145 assert user.projects.empty?
|
Chris@0
|
146 issues = Issue.visible(user).all
|
Chris@0
|
147 assert issues.empty?
|
Chris@441
|
148 assert_visibility_match user, issues
|
Chris@441
|
149 end
|
Chris@441
|
150
|
Chris@441
|
151 def test_visible_scope_for_member
|
Chris@441
|
152 user = User.find(9)
|
Chris@0
|
153 # User should see issues of projects for which he has view_issues permissions only
|
Chris@441
|
154 Role.non_member.remove_permission!(:view_issues)
|
Chris@441
|
155 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
|
Chris@0
|
156 issues = Issue.visible(user).all
|
Chris@0
|
157 assert issues.any?
|
Chris@441
|
158 assert_nil issues.detect {|issue| issue.project_id != 3}
|
Chris@441
|
159 assert_nil issues.detect {|issue| issue.is_private?}
|
Chris@441
|
160 assert_visibility_match user, issues
|
Chris@0
|
161 end
|
Chris@441
|
162
|
Chris@909
|
163 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
|
Chris@909
|
164 user = User.find(8)
|
Chris@909
|
165 assert user.groups.any?
|
Chris@909
|
166 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
|
Chris@909
|
167 Role.non_member.remove_permission!(:view_issues)
|
Chris@909
|
168
|
Chris@909
|
169 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
|
Chris@909
|
170 :status_id => 1, :priority => IssuePriority.all.first,
|
Chris@909
|
171 :subject => 'Assignment test',
|
Chris@909
|
172 :assigned_to => user.groups.first,
|
Chris@909
|
173 :is_private => true)
|
Chris@909
|
174
|
Chris@909
|
175 Role.find(2).update_attribute :issues_visibility, 'default'
|
Chris@909
|
176 issues = Issue.visible(User.find(8)).all
|
Chris@909
|
177 assert issues.any?
|
Chris@909
|
178 assert issues.include?(issue)
|
Chris@909
|
179
|
Chris@909
|
180 Role.find(2).update_attribute :issues_visibility, 'own'
|
Chris@909
|
181 issues = Issue.visible(User.find(8)).all
|
Chris@909
|
182 assert issues.any?
|
Chris@909
|
183 assert issues.include?(issue)
|
Chris@909
|
184 end
|
Chris@909
|
185
|
Chris@0
|
186 def test_visible_scope_for_admin
|
Chris@0
|
187 user = User.find(1)
|
Chris@0
|
188 user.members.each(&:destroy)
|
Chris@0
|
189 assert user.projects.empty?
|
Chris@0
|
190 issues = Issue.visible(user).all
|
Chris@0
|
191 assert issues.any?
|
Chris@0
|
192 # Admin should see issues on private projects that he does not belong to
|
Chris@0
|
193 assert issues.detect {|issue| !issue.project.is_public?}
|
Chris@441
|
194 # Admin should see private issues of other users
|
Chris@441
|
195 assert issues.detect {|issue| issue.is_private? && issue.author != user}
|
Chris@441
|
196 assert_visibility_match user, issues
|
Chris@0
|
197 end
|
Chris@441
|
198
|
Chris@441
|
199 def test_visible_scope_with_project
|
Chris@441
|
200 project = Project.find(1)
|
Chris@441
|
201 issues = Issue.visible(User.find(2), :project => project).all
|
Chris@441
|
202 projects = issues.collect(&:project).uniq
|
Chris@441
|
203 assert_equal 1, projects.size
|
Chris@441
|
204 assert_equal project, projects.first
|
Chris@441
|
205 end
|
Chris@441
|
206
|
Chris@441
|
207 def test_visible_scope_with_project_and_subprojects
|
Chris@441
|
208 project = Project.find(1)
|
Chris@441
|
209 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).all
|
Chris@441
|
210 projects = issues.collect(&:project).uniq
|
Chris@441
|
211 assert projects.size > 1
|
Chris@441
|
212 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
|
Chris@441
|
213 end
|
Chris@441
|
214
|
Chris@441
|
215 def test_visible_and_nested_set_scopes
|
Chris@441
|
216 assert_equal 0, Issue.find(1).descendants.visible.all.size
|
Chris@441
|
217 end
|
Chris@441
|
218
|
Chris@0
|
219 def test_errors_full_messages_should_include_custom_fields_errors
|
Chris@0
|
220 field = IssueCustomField.find_by_name('Database')
|
Chris@441
|
221
|
Chris@909
|
222 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
|
Chris@909
|
223 :status_id => 1, :subject => 'test_create',
|
Chris@909
|
224 :description => 'IssueTest#test_create_with_required_custom_field')
|
Chris@0
|
225 assert issue.available_custom_fields.include?(field)
|
Chris@0
|
226 # Invalid value
|
Chris@0
|
227 issue.custom_field_values = { field.id => 'SQLServer' }
|
Chris@441
|
228
|
Chris@0
|
229 assert !issue.valid?
|
Chris@0
|
230 assert_equal 1, issue.errors.full_messages.size
|
Chris@909
|
231 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
|
Chris@909
|
232 issue.errors.full_messages.first
|
Chris@0
|
233 end
|
Chris@441
|
234
|
Chris@0
|
235 def test_update_issue_with_required_custom_field
|
Chris@0
|
236 field = IssueCustomField.find_by_name('Database')
|
Chris@0
|
237 field.update_attribute(:is_required, true)
|
Chris@441
|
238
|
Chris@0
|
239 issue = Issue.find(1)
|
Chris@0
|
240 assert_nil issue.custom_value_for(field)
|
Chris@0
|
241 assert issue.available_custom_fields.include?(field)
|
Chris@0
|
242 # No change to custom values, issue can be saved
|
Chris@0
|
243 assert issue.save
|
Chris@0
|
244 # Blank value
|
Chris@0
|
245 issue.custom_field_values = { field.id => '' }
|
Chris@0
|
246 assert !issue.save
|
Chris@0
|
247 # Valid value
|
Chris@0
|
248 issue.custom_field_values = { field.id => 'PostgreSQL' }
|
Chris@0
|
249 assert issue.save
|
Chris@0
|
250 issue.reload
|
Chris@0
|
251 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
|
Chris@0
|
252 end
|
Chris@441
|
253
|
Chris@0
|
254 def test_should_not_update_attributes_if_custom_fields_validation_fails
|
Chris@0
|
255 issue = Issue.find(1)
|
Chris@0
|
256 field = IssueCustomField.find_by_name('Database')
|
Chris@0
|
257 assert issue.available_custom_fields.include?(field)
|
Chris@441
|
258
|
Chris@0
|
259 issue.custom_field_values = { field.id => 'Invalid' }
|
Chris@0
|
260 issue.subject = 'Should be not be saved'
|
Chris@0
|
261 assert !issue.save
|
Chris@441
|
262
|
Chris@0
|
263 issue.reload
|
Chris@0
|
264 assert_equal "Can't print recipes", issue.subject
|
Chris@0
|
265 end
|
Chris@441
|
266
|
Chris@0
|
267 def test_should_not_recreate_custom_values_objects_on_update
|
Chris@0
|
268 field = IssueCustomField.find_by_name('Database')
|
Chris@441
|
269
|
Chris@0
|
270 issue = Issue.find(1)
|
Chris@0
|
271 issue.custom_field_values = { field.id => 'PostgreSQL' }
|
Chris@0
|
272 assert issue.save
|
Chris@0
|
273 custom_value = issue.custom_value_for(field)
|
Chris@0
|
274 issue.reload
|
Chris@0
|
275 issue.custom_field_values = { field.id => 'MySQL' }
|
Chris@0
|
276 assert issue.save
|
Chris@0
|
277 issue.reload
|
Chris@0
|
278 assert_equal custom_value.id, issue.custom_value_for(field).id
|
Chris@0
|
279 end
|
Chris@441
|
280
|
Chris@909
|
281 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
|
Chris@909
|
282 issue = Issue.new(:project_id => 1)
|
Chris@909
|
283 issue.attributes = {:tracker_id => 1, :author_id => 1, :status_id => 1, :subject => 'Test', :custom_field_values => {'2' => 'Test'}}
|
Chris@909
|
284 issue.save!
|
Chris@909
|
285
|
Chris@909
|
286 assert !Tracker.find(2).custom_field_ids.include?(2)
|
Chris@909
|
287
|
Chris@909
|
288 issue = Issue.find(issue.id)
|
Chris@909
|
289 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
|
Chris@909
|
290
|
Chris@909
|
291 issue = Issue.find(issue.id)
|
Chris@909
|
292 custom_value = issue.custom_value_for(2)
|
Chris@909
|
293 assert_not_nil custom_value
|
Chris@909
|
294 assert_equal 'Test', custom_value.value
|
Chris@909
|
295 end
|
Chris@909
|
296
|
Chris@0
|
297 def test_assigning_tracker_id_should_reload_custom_fields_values
|
Chris@0
|
298 issue = Issue.new(:project => Project.find(1))
|
Chris@0
|
299 assert issue.custom_field_values.empty?
|
Chris@0
|
300 issue.tracker_id = 1
|
Chris@0
|
301 assert issue.custom_field_values.any?
|
Chris@0
|
302 end
|
Chris@441
|
303
|
Chris@0
|
304 def test_assigning_attributes_should_assign_tracker_id_first
|
Chris@0
|
305 attributes = ActiveSupport::OrderedHash.new
|
Chris@0
|
306 attributes['custom_field_values'] = { '1' => 'MySQL' }
|
Chris@0
|
307 attributes['tracker_id'] = '1'
|
Chris@0
|
308 issue = Issue.new(:project => Project.find(1))
|
Chris@0
|
309 issue.attributes = attributes
|
Chris@0
|
310 assert_not_nil issue.custom_value_for(1)
|
Chris@0
|
311 assert_equal 'MySQL', issue.custom_value_for(1).value
|
Chris@0
|
312 end
|
Chris@441
|
313
|
Chris@0
|
314 def test_should_update_issue_with_disabled_tracker
|
Chris@0
|
315 p = Project.find(1)
|
Chris@0
|
316 issue = Issue.find(1)
|
Chris@441
|
317
|
Chris@0
|
318 p.trackers.delete(issue.tracker)
|
Chris@0
|
319 assert !p.trackers.include?(issue.tracker)
|
Chris@441
|
320
|
Chris@0
|
321 issue.reload
|
Chris@0
|
322 issue.subject = 'New subject'
|
Chris@0
|
323 assert issue.save
|
Chris@0
|
324 end
|
Chris@441
|
325
|
Chris@0
|
326 def test_should_not_set_a_disabled_tracker
|
Chris@0
|
327 p = Project.find(1)
|
Chris@0
|
328 p.trackers.delete(Tracker.find(2))
|
Chris@441
|
329
|
Chris@0
|
330 issue = Issue.find(1)
|
Chris@0
|
331 issue.tracker_id = 2
|
Chris@0
|
332 issue.subject = 'New subject'
|
Chris@0
|
333 assert !issue.save
|
Chris@909
|
334 assert_not_nil issue.errors[:tracker_id]
|
Chris@0
|
335 end
|
Chris@441
|
336
|
Chris@0
|
337 def test_category_based_assignment
|
Chris@909
|
338 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
|
Chris@909
|
339 :status_id => 1, :priority => IssuePriority.all.first,
|
Chris@909
|
340 :subject => 'Assignment test',
|
Chris@909
|
341 :description => 'Assignment test', :category_id => 1)
|
Chris@0
|
342 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
|
Chris@0
|
343 end
|
Chris@441
|
344
|
Chris@245
|
345 def test_new_statuses_allowed_to
|
Chris@245
|
346 Workflow.delete_all
|
Chris@441
|
347
|
Chris@245
|
348 Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2, :author => false, :assignee => false)
|
Chris@245
|
349 Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3, :author => true, :assignee => false)
|
Chris@245
|
350 Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4, :author => false, :assignee => true)
|
Chris@245
|
351 Workflow.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 5, :author => true, :assignee => true)
|
Chris@245
|
352 status = IssueStatus.find(1)
|
Chris@245
|
353 role = Role.find(1)
|
Chris@245
|
354 tracker = Tracker.find(1)
|
Chris@245
|
355 user = User.find(2)
|
Chris@441
|
356
|
Chris@245
|
357 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1)
|
Chris@245
|
358 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
|
Chris@441
|
359
|
Chris@245
|
360 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author => user)
|
Chris@909
|
361 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
|
Chris@441
|
362
|
Chris@245
|
363 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :assigned_to => user)
|
Chris@909
|
364 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
|
Chris@441
|
365
|
Chris@245
|
366 issue = Issue.generate!(:tracker => tracker, :status => status, :project_id => 1, :author => user, :assigned_to => user)
|
Chris@245
|
367 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
|
Chris@245
|
368 end
|
Chris@441
|
369
|
Chris@0
|
370 def test_copy
|
Chris@0
|
371 issue = Issue.new.copy_from(1)
|
Chris@0
|
372 assert issue.save
|
Chris@0
|
373 issue.reload
|
Chris@0
|
374 orig = Issue.find(1)
|
Chris@0
|
375 assert_equal orig.subject, issue.subject
|
Chris@0
|
376 assert_equal orig.tracker, issue.tracker
|
Chris@0
|
377 assert_equal "125", issue.custom_value_for(2).value
|
Chris@0
|
378 end
|
Chris@0
|
379
|
Chris@0
|
380 def test_copy_should_copy_status
|
Chris@0
|
381 orig = Issue.find(8)
|
Chris@0
|
382 assert orig.status != IssueStatus.default
|
Chris@441
|
383
|
Chris@0
|
384 issue = Issue.new.copy_from(orig)
|
Chris@0
|
385 assert issue.save
|
Chris@0
|
386 issue.reload
|
Chris@0
|
387 assert_equal orig.status, issue.status
|
Chris@0
|
388 end
|
Chris@441
|
389
|
Chris@0
|
390 def test_should_close_duplicates
|
Chris@0
|
391 # Create 3 issues
|
Chris@909
|
392 issue1 = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
|
Chris@909
|
393 :status_id => 1, :priority => IssuePriority.all.first,
|
Chris@909
|
394 :subject => 'Duplicates test', :description => 'Duplicates test')
|
Chris@0
|
395 assert issue1.save
|
Chris@0
|
396 issue2 = issue1.clone
|
Chris@0
|
397 assert issue2.save
|
Chris@0
|
398 issue3 = issue1.clone
|
Chris@0
|
399 assert issue3.save
|
Chris@441
|
400
|
Chris@0
|
401 # 2 is a dupe of 1
|
Chris@0
|
402 IssueRelation.create(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
|
Chris@0
|
403 # And 3 is a dupe of 2
|
Chris@0
|
404 IssueRelation.create(:issue_from => issue3, :issue_to => issue2, :relation_type => IssueRelation::TYPE_DUPLICATES)
|
Chris@0
|
405 # And 3 is a dupe of 1 (circular duplicates)
|
Chris@0
|
406 IssueRelation.create(:issue_from => issue3, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
|
Chris@441
|
407
|
Chris@0
|
408 assert issue1.reload.duplicates.include?(issue2)
|
Chris@441
|
409
|
Chris@0
|
410 # Closing issue 1
|
Chris@0
|
411 issue1.init_journal(User.find(:first), "Closing issue1")
|
Chris@0
|
412 issue1.status = IssueStatus.find :first, :conditions => {:is_closed => true}
|
Chris@0
|
413 assert issue1.save
|
Chris@0
|
414 # 2 and 3 should be also closed
|
Chris@0
|
415 assert issue2.reload.closed?
|
Chris@441
|
416 assert issue3.reload.closed?
|
Chris@0
|
417 end
|
Chris@441
|
418
|
Chris@0
|
419 def test_should_not_close_duplicated_issue
|
Chris@0
|
420 # Create 3 issues
|
Chris@909
|
421 issue1 = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
|
Chris@909
|
422 :status_id => 1, :priority => IssuePriority.all.first,
|
Chris@909
|
423 :subject => 'Duplicates test', :description => 'Duplicates test')
|
Chris@0
|
424 assert issue1.save
|
Chris@0
|
425 issue2 = issue1.clone
|
Chris@0
|
426 assert issue2.save
|
Chris@441
|
427
|
Chris@0
|
428 # 2 is a dupe of 1
|
Chris@0
|
429 IssueRelation.create(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_DUPLICATES)
|
Chris@0
|
430 # 2 is a dup of 1 but 1 is not a duplicate of 2
|
Chris@0
|
431 assert !issue2.reload.duplicates.include?(issue1)
|
Chris@441
|
432
|
Chris@0
|
433 # Closing issue 2
|
Chris@0
|
434 issue2.init_journal(User.find(:first), "Closing issue2")
|
Chris@0
|
435 issue2.status = IssueStatus.find :first, :conditions => {:is_closed => true}
|
Chris@0
|
436 assert issue2.save
|
Chris@0
|
437 # 1 should not be also closed
|
Chris@0
|
438 assert !issue1.reload.closed?
|
Chris@0
|
439 end
|
Chris@441
|
440
|
Chris@0
|
441 def test_assignable_versions
|
Chris@0
|
442 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
|
Chris@0
|
443 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
|
Chris@0
|
444 end
|
Chris@441
|
445
|
Chris@0
|
446 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
|
Chris@0
|
447 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 1, :subject => 'New issue')
|
Chris@0
|
448 assert !issue.save
|
Chris@909
|
449 assert_not_nil issue.errors[:fixed_version_id]
|
Chris@0
|
450 end
|
Chris@441
|
451
|
Chris@0
|
452 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
|
Chris@0
|
453 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 2, :subject => 'New issue')
|
Chris@0
|
454 assert !issue.save
|
Chris@909
|
455 assert_not_nil issue.errors[:fixed_version_id]
|
Chris@0
|
456 end
|
Chris@441
|
457
|
Chris@0
|
458 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
|
Chris@0
|
459 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :fixed_version_id => 3, :subject => 'New issue')
|
Chris@0
|
460 assert issue.save
|
Chris@0
|
461 end
|
Chris@441
|
462
|
Chris@0
|
463 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
|
Chris@0
|
464 issue = Issue.find(11)
|
Chris@0
|
465 assert_equal 'closed', issue.fixed_version.status
|
Chris@0
|
466 issue.subject = 'Subject changed'
|
Chris@0
|
467 assert issue.save
|
Chris@0
|
468 end
|
Chris@441
|
469
|
Chris@0
|
470 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
|
Chris@0
|
471 issue = Issue.find(11)
|
Chris@0
|
472 issue.status_id = 1
|
Chris@0
|
473 assert !issue.save
|
Chris@909
|
474 assert_not_nil issue.errors[:base]
|
Chris@0
|
475 end
|
Chris@441
|
476
|
Chris@0
|
477 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
|
Chris@0
|
478 issue = Issue.find(11)
|
Chris@0
|
479 issue.status_id = 1
|
Chris@0
|
480 issue.fixed_version_id = 3
|
Chris@0
|
481 assert issue.save
|
Chris@0
|
482 end
|
Chris@441
|
483
|
Chris@0
|
484 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
|
Chris@0
|
485 issue = Issue.find(12)
|
Chris@0
|
486 assert_equal 'locked', issue.fixed_version.status
|
Chris@0
|
487 issue.status_id = 1
|
Chris@0
|
488 assert issue.save
|
Chris@0
|
489 end
|
Chris@441
|
490
|
Chris@0
|
491 def test_move_to_another_project_with_same_category
|
Chris@0
|
492 issue = Issue.find(1)
|
Chris@0
|
493 assert issue.move_to_project(Project.find(2))
|
Chris@0
|
494 issue.reload
|
Chris@0
|
495 assert_equal 2, issue.project_id
|
Chris@0
|
496 # Category changes
|
Chris@0
|
497 assert_equal 4, issue.category_id
|
Chris@0
|
498 # Make sure time entries were move to the target project
|
Chris@0
|
499 assert_equal 2, issue.time_entries.first.project_id
|
Chris@0
|
500 end
|
Chris@441
|
501
|
Chris@0
|
502 def test_move_to_another_project_without_same_category
|
Chris@0
|
503 issue = Issue.find(2)
|
Chris@0
|
504 assert issue.move_to_project(Project.find(2))
|
Chris@0
|
505 issue.reload
|
Chris@0
|
506 assert_equal 2, issue.project_id
|
Chris@0
|
507 # Category cleared
|
Chris@0
|
508 assert_nil issue.category_id
|
Chris@0
|
509 end
|
Chris@441
|
510
|
Chris@0
|
511 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
|
Chris@0
|
512 issue = Issue.find(1)
|
Chris@0
|
513 issue.update_attribute(:fixed_version_id, 1)
|
Chris@0
|
514 assert issue.move_to_project(Project.find(2))
|
Chris@0
|
515 issue.reload
|
Chris@0
|
516 assert_equal 2, issue.project_id
|
Chris@0
|
517 # Cleared fixed_version
|
Chris@0
|
518 assert_equal nil, issue.fixed_version
|
Chris@0
|
519 end
|
Chris@441
|
520
|
Chris@0
|
521 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
|
Chris@0
|
522 issue = Issue.find(1)
|
Chris@0
|
523 issue.update_attribute(:fixed_version_id, 4)
|
Chris@0
|
524 assert issue.move_to_project(Project.find(5))
|
Chris@0
|
525 issue.reload
|
Chris@0
|
526 assert_equal 5, issue.project_id
|
Chris@0
|
527 # Keep fixed_version
|
Chris@0
|
528 assert_equal 4, issue.fixed_version_id
|
Chris@0
|
529 end
|
Chris@441
|
530
|
Chris@0
|
531 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
|
Chris@0
|
532 issue = Issue.find(1)
|
Chris@0
|
533 issue.update_attribute(:fixed_version_id, 1)
|
Chris@0
|
534 assert issue.move_to_project(Project.find(5))
|
Chris@0
|
535 issue.reload
|
Chris@0
|
536 assert_equal 5, issue.project_id
|
Chris@0
|
537 # Cleared fixed_version
|
Chris@0
|
538 assert_equal nil, issue.fixed_version
|
Chris@0
|
539 end
|
Chris@441
|
540
|
Chris@0
|
541 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
|
Chris@0
|
542 issue = Issue.find(1)
|
Chris@0
|
543 issue.update_attribute(:fixed_version_id, 7)
|
Chris@0
|
544 assert issue.move_to_project(Project.find(2))
|
Chris@0
|
545 issue.reload
|
Chris@0
|
546 assert_equal 2, issue.project_id
|
Chris@0
|
547 # Keep fixed_version
|
Chris@0
|
548 assert_equal 7, issue.fixed_version_id
|
Chris@0
|
549 end
|
Chris@441
|
550
|
Chris@0
|
551 def test_move_to_another_project_with_disabled_tracker
|
Chris@0
|
552 issue = Issue.find(1)
|
Chris@0
|
553 target = Project.find(2)
|
Chris@0
|
554 target.tracker_ids = [3]
|
Chris@0
|
555 target.save
|
Chris@0
|
556 assert_equal false, issue.move_to_project(target)
|
Chris@0
|
557 issue.reload
|
Chris@0
|
558 assert_equal 1, issue.project_id
|
Chris@0
|
559 end
|
Chris@441
|
560
|
Chris@0
|
561 def test_copy_to_the_same_project
|
Chris@0
|
562 issue = Issue.find(1)
|
Chris@0
|
563 copy = nil
|
Chris@0
|
564 assert_difference 'Issue.count' do
|
Chris@0
|
565 copy = issue.move_to_project(issue.project, nil, :copy => true)
|
Chris@0
|
566 end
|
Chris@0
|
567 assert_kind_of Issue, copy
|
Chris@0
|
568 assert_equal issue.project, copy.project
|
Chris@0
|
569 assert_equal "125", copy.custom_value_for(2).value
|
Chris@0
|
570 end
|
Chris@441
|
571
|
Chris@0
|
572 def test_copy_to_another_project_and_tracker
|
Chris@0
|
573 issue = Issue.find(1)
|
Chris@0
|
574 copy = nil
|
Chris@0
|
575 assert_difference 'Issue.count' do
|
Chris@0
|
576 copy = issue.move_to_project(Project.find(3), Tracker.find(2), :copy => true)
|
Chris@0
|
577 end
|
Chris@0
|
578 copy.reload
|
Chris@0
|
579 assert_kind_of Issue, copy
|
Chris@0
|
580 assert_equal Project.find(3), copy.project
|
Chris@0
|
581 assert_equal Tracker.find(2), copy.tracker
|
Chris@0
|
582 # Custom field #2 is not associated with target tracker
|
Chris@0
|
583 assert_nil copy.custom_value_for(2)
|
Chris@0
|
584 end
|
Chris@0
|
585
|
Chris@0
|
586 context "#move_to_project" do
|
Chris@0
|
587 context "as a copy" do
|
Chris@0
|
588 setup do
|
Chris@0
|
589 @issue = Issue.find(1)
|
Chris@0
|
590 @copy = nil
|
Chris@0
|
591 end
|
Chris@0
|
592
|
Chris@441
|
593 should "not create a journal" do
|
Chris@441
|
594 @copy = @issue.move_to_project(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:assigned_to_id => 3}})
|
Chris@441
|
595 assert_equal 0, @copy.reload.journals.size
|
Chris@441
|
596 end
|
Chris@441
|
597
|
Chris@0
|
598 should "allow assigned_to changes" do
|
Chris@0
|
599 @copy = @issue.move_to_project(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:assigned_to_id => 3}})
|
Chris@0
|
600 assert_equal 3, @copy.assigned_to_id
|
Chris@0
|
601 end
|
Chris@0
|
602
|
Chris@0
|
603 should "allow status changes" do
|
Chris@0
|
604 @copy = @issue.move_to_project(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:status_id => 2}})
|
Chris@0
|
605 assert_equal 2, @copy.status_id
|
Chris@0
|
606 end
|
Chris@0
|
607
|
Chris@0
|
608 should "allow start date changes" do
|
Chris@0
|
609 date = Date.today
|
Chris@0
|
610 @copy = @issue.move_to_project(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:start_date => date}})
|
Chris@0
|
611 assert_equal date, @copy.start_date
|
Chris@0
|
612 end
|
Chris@0
|
613
|
Chris@0
|
614 should "allow due date changes" do
|
Chris@0
|
615 date = Date.today
|
Chris@0
|
616 @copy = @issue.move_to_project(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:due_date => date}})
|
Chris@0
|
617
|
Chris@0
|
618 assert_equal date, @copy.due_date
|
Chris@0
|
619 end
|
Chris@441
|
620
|
Chris@441
|
621 should "set current user as author" do
|
Chris@441
|
622 User.current = User.find(9)
|
Chris@441
|
623 @copy = @issue.move_to_project(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {}})
|
Chris@441
|
624
|
Chris@441
|
625 assert_equal User.current, @copy.author
|
Chris@441
|
626 end
|
Chris@441
|
627
|
Chris@441
|
628 should "keep journal notes" do
|
Chris@441
|
629 date = Date.today
|
Chris@441
|
630 notes = "Notes added when copying"
|
Chris@441
|
631 User.current = User.find(9)
|
Chris@441
|
632 @issue.init_journal(User.current, notes)
|
Chris@441
|
633 @copy = @issue.move_to_project(Project.find(3), Tracker.find(2), {:copy => true, :attributes => {:start_date => date}})
|
Chris@441
|
634
|
Chris@441
|
635 assert_equal 1, @copy.journals.size
|
Chris@441
|
636 journal = @copy.journals.first
|
Chris@441
|
637 assert_equal 0, journal.details.size
|
Chris@441
|
638 assert_equal notes, journal.notes
|
Chris@441
|
639 end
|
Chris@0
|
640 end
|
Chris@0
|
641 end
|
Chris@441
|
642
|
Chris@0
|
643 def test_recipients_should_not_include_users_that_cannot_view_the_issue
|
Chris@0
|
644 issue = Issue.find(12)
|
Chris@0
|
645 assert issue.recipients.include?(issue.author.mail)
|
Chris@0
|
646 # move the issue to a private project
|
Chris@0
|
647 copy = issue.move_to_project(Project.find(5), Tracker.find(2), :copy => true)
|
Chris@0
|
648 # author is not a member of project anymore
|
Chris@0
|
649 assert !copy.recipients.include?(copy.author.mail)
|
Chris@0
|
650 end
|
Chris@0
|
651
|
Chris@909
|
652 def test_recipients_should_include_the_assigned_group_members
|
Chris@909
|
653 group_member = User.generate_with_protected!
|
Chris@909
|
654 group = Group.generate!
|
Chris@909
|
655 group.users << group_member
|
Chris@909
|
656
|
Chris@909
|
657 issue = Issue.find(12)
|
Chris@909
|
658 issue.assigned_to = group
|
Chris@909
|
659 assert issue.recipients.include?(group_member.mail)
|
Chris@909
|
660 end
|
Chris@909
|
661
|
Chris@0
|
662 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
|
Chris@0
|
663 user = User.find(3)
|
Chris@0
|
664 issue = Issue.find(9)
|
Chris@0
|
665 Watcher.create!(:user => user, :watchable => issue)
|
Chris@0
|
666 assert issue.watched_by?(user)
|
Chris@0
|
667 assert !issue.watcher_recipients.include?(user.mail)
|
Chris@0
|
668 end
|
Chris@441
|
669
|
Chris@0
|
670 def test_issue_destroy
|
Chris@0
|
671 Issue.find(1).destroy
|
Chris@0
|
672 assert_nil Issue.find_by_id(1)
|
Chris@0
|
673 assert_nil TimeEntry.find_by_issue_id(1)
|
Chris@0
|
674 end
|
Chris@441
|
675
|
Chris@0
|
676 def test_blocked
|
Chris@0
|
677 blocked_issue = Issue.find(9)
|
Chris@0
|
678 blocking_issue = Issue.find(10)
|
Chris@441
|
679
|
Chris@0
|
680 assert blocked_issue.blocked?
|
Chris@0
|
681 assert !blocking_issue.blocked?
|
Chris@0
|
682 end
|
Chris@441
|
683
|
Chris@0
|
684 def test_blocked_issues_dont_allow_closed_statuses
|
Chris@0
|
685 blocked_issue = Issue.find(9)
|
Chris@441
|
686
|
Chris@0
|
687 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
|
Chris@0
|
688 assert !allowed_statuses.empty?
|
Chris@0
|
689 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
|
Chris@0
|
690 assert closed_statuses.empty?
|
Chris@0
|
691 end
|
Chris@441
|
692
|
Chris@0
|
693 def test_unblocked_issues_allow_closed_statuses
|
Chris@0
|
694 blocking_issue = Issue.find(10)
|
Chris@441
|
695
|
Chris@0
|
696 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
|
Chris@0
|
697 assert !allowed_statuses.empty?
|
Chris@0
|
698 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
|
Chris@0
|
699 assert !closed_statuses.empty?
|
Chris@0
|
700 end
|
Chris@441
|
701
|
chris@37
|
702 def test_rescheduling_an_issue_should_reschedule_following_issue
|
chris@37
|
703 issue1 = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => '-', :start_date => Date.today, :due_date => Date.today + 2)
|
chris@37
|
704 issue2 = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1, :status_id => 1, :subject => '-', :start_date => Date.today, :due_date => Date.today + 2)
|
chris@37
|
705 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
|
chris@37
|
706 assert_equal issue1.due_date + 1, issue2.reload.start_date
|
Chris@441
|
707
|
chris@37
|
708 issue1.due_date = Date.today + 5
|
chris@37
|
709 issue1.save!
|
chris@37
|
710 assert_equal issue1.due_date + 1, issue2.reload.start_date
|
chris@37
|
711 end
|
Chris@441
|
712
|
Chris@0
|
713 def test_overdue
|
Chris@0
|
714 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
|
Chris@0
|
715 assert !Issue.new(:due_date => Date.today).overdue?
|
Chris@0
|
716 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
|
Chris@0
|
717 assert !Issue.new(:due_date => nil).overdue?
|
Chris@0
|
718 assert !Issue.new(:due_date => 1.day.ago.to_date, :status => IssueStatus.find(:first, :conditions => {:is_closed => true})).overdue?
|
Chris@0
|
719 end
|
chris@22
|
720
|
chris@22
|
721 context "#behind_schedule?" do
|
chris@22
|
722 should "be false if the issue has no start_date" do
|
chris@22
|
723 assert !Issue.new(:start_date => nil, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule?
|
chris@22
|
724 end
|
chris@22
|
725
|
chris@22
|
726 should "be false if the issue has no end_date" do
|
chris@22
|
727 assert !Issue.new(:start_date => 1.day.from_now.to_date, :due_date => nil, :done_ratio => 0).behind_schedule?
|
chris@22
|
728 end
|
chris@22
|
729
|
chris@22
|
730 should "be false if the issue has more done than it's calendar time" do
|
chris@22
|
731 assert !Issue.new(:start_date => 50.days.ago.to_date, :due_date => 50.days.from_now.to_date, :done_ratio => 90).behind_schedule?
|
chris@22
|
732 end
|
chris@22
|
733
|
chris@22
|
734 should "be true if the issue hasn't been started at all" do
|
chris@22
|
735 assert Issue.new(:start_date => 1.day.ago.to_date, :due_date => 1.day.from_now.to_date, :done_ratio => 0).behind_schedule?
|
chris@22
|
736 end
|
chris@22
|
737
|
chris@22
|
738 should "be true if the issue has used more calendar time than it's done ratio" do
|
chris@22
|
739 assert Issue.new(:start_date => 100.days.ago.to_date, :due_date => Date.today, :done_ratio => 90).behind_schedule?
|
chris@22
|
740 end
|
chris@22
|
741 end
|
chris@37
|
742
|
chris@37
|
743 context "#assignable_users" do
|
chris@37
|
744 should "be Users" do
|
chris@37
|
745 assert_kind_of User, Issue.find(1).assignable_users.first
|
chris@37
|
746 end
|
chris@37
|
747
|
chris@37
|
748 should "include the issue author" do
|
chris@37
|
749 project = Project.find(1)
|
chris@37
|
750 non_project_member = User.generate!
|
chris@37
|
751 issue = Issue.generate_for_project!(project, :author => non_project_member)
|
chris@37
|
752
|
chris@37
|
753 assert issue.assignable_users.include?(non_project_member)
|
chris@37
|
754 end
|
chris@37
|
755
|
Chris@909
|
756 should "include the current assignee" do
|
Chris@909
|
757 project = Project.find(1)
|
Chris@909
|
758 user = User.generate!
|
Chris@909
|
759 issue = Issue.generate_for_project!(project, :assigned_to => user)
|
Chris@909
|
760 user.lock!
|
Chris@909
|
761
|
Chris@909
|
762 assert Issue.find(issue.id).assignable_users.include?(user)
|
Chris@909
|
763 end
|
Chris@909
|
764
|
chris@37
|
765 should "not show the issue author twice" do
|
chris@37
|
766 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
|
chris@37
|
767 assert_equal 2, assignable_user_ids.length
|
Chris@441
|
768
|
chris@37
|
769 assignable_user_ids.each do |user_id|
|
chris@37
|
770 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length, "User #{user_id} appears more or less than once"
|
chris@37
|
771 end
|
chris@37
|
772 end
|
Chris@909
|
773
|
Chris@909
|
774 context "with issue_group_assignment" do
|
Chris@909
|
775 should "include groups" do
|
Chris@909
|
776 issue = Issue.new(:project => Project.find(2))
|
Chris@909
|
777
|
Chris@909
|
778 with_settings :issue_group_assignment => '1' do
|
Chris@909
|
779 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
|
Chris@909
|
780 assert issue.assignable_users.include?(Group.find(11))
|
Chris@909
|
781 end
|
Chris@909
|
782 end
|
Chris@909
|
783 end
|
Chris@909
|
784
|
Chris@909
|
785 context "without issue_group_assignment" do
|
Chris@909
|
786 should "not include groups" do
|
Chris@909
|
787 issue = Issue.new(:project => Project.find(2))
|
Chris@909
|
788
|
Chris@909
|
789 with_settings :issue_group_assignment => '0' do
|
Chris@909
|
790 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
|
Chris@909
|
791 assert !issue.assignable_users.include?(Group.find(11))
|
Chris@909
|
792 end
|
Chris@909
|
793 end
|
Chris@909
|
794 end
|
Chris@0
|
795 end
|
Chris@441
|
796
|
Chris@0
|
797 def test_create_should_send_email_notification
|
Chris@0
|
798 ActionMailer::Base.deliveries.clear
|
Chris@909
|
799 issue = Issue.new(:project_id => 1, :tracker_id => 1,
|
Chris@909
|
800 :author_id => 3, :status_id => 1,
|
Chris@909
|
801 :priority => IssuePriority.all.first,
|
Chris@909
|
802 :subject => 'test_create', :estimated_hours => '1:30')
|
Chris@0
|
803
|
Chris@0
|
804 assert issue.save
|
Chris@0
|
805 assert_equal 1, ActionMailer::Base.deliveries.size
|
Chris@0
|
806 end
|
chris@37
|
807
|
Chris@0
|
808 def test_stale_issue_should_not_send_email_notification
|
Chris@0
|
809 ActionMailer::Base.deliveries.clear
|
Chris@0
|
810 issue = Issue.find(1)
|
Chris@0
|
811 stale = Issue.find(1)
|
Chris@441
|
812
|
Chris@0
|
813 issue.init_journal(User.find(1))
|
Chris@0
|
814 issue.subject = 'Subjet update'
|
Chris@0
|
815 assert issue.save
|
Chris@0
|
816 assert_equal 1, ActionMailer::Base.deliveries.size
|
Chris@0
|
817 ActionMailer::Base.deliveries.clear
|
Chris@441
|
818
|
Chris@0
|
819 stale.init_journal(User.find(1))
|
Chris@0
|
820 stale.subject = 'Another subjet update'
|
Chris@0
|
821 assert_raise ActiveRecord::StaleObjectError do
|
Chris@0
|
822 stale.save
|
Chris@0
|
823 end
|
Chris@0
|
824 assert ActionMailer::Base.deliveries.empty?
|
Chris@0
|
825 end
|
Chris@441
|
826
|
Chris@245
|
827 def test_journalized_description
|
Chris@245
|
828 IssueCustomField.delete_all
|
Chris@441
|
829
|
Chris@245
|
830 i = Issue.first
|
Chris@245
|
831 old_description = i.description
|
Chris@245
|
832 new_description = "This is the new description"
|
Chris@441
|
833
|
Chris@245
|
834 i.init_journal(User.find(2))
|
Chris@245
|
835 i.description = new_description
|
Chris@245
|
836 assert_difference 'Journal.count', 1 do
|
Chris@245
|
837 assert_difference 'JournalDetail.count', 1 do
|
Chris@245
|
838 i.save!
|
Chris@245
|
839 end
|
Chris@245
|
840 end
|
Chris@441
|
841
|
Chris@245
|
842 detail = JournalDetail.first(:order => 'id DESC')
|
Chris@245
|
843 assert_equal i, detail.journal.journalized
|
Chris@245
|
844 assert_equal 'attr', detail.property
|
Chris@245
|
845 assert_equal 'description', detail.prop_key
|
Chris@245
|
846 assert_equal old_description, detail.old_value
|
Chris@245
|
847 assert_equal new_description, detail.value
|
Chris@245
|
848 end
|
Chris@909
|
849
|
Chris@507
|
850 def test_blank_descriptions_should_not_be_journalized
|
Chris@507
|
851 IssueCustomField.delete_all
|
Chris@507
|
852 Issue.update_all("description = NULL", "id=1")
|
Chris@909
|
853
|
Chris@507
|
854 i = Issue.find(1)
|
Chris@507
|
855 i.init_journal(User.find(2))
|
Chris@507
|
856 i.subject = "blank description"
|
Chris@507
|
857 i.description = "\r\n"
|
Chris@909
|
858
|
Chris@507
|
859 assert_difference 'Journal.count', 1 do
|
Chris@507
|
860 assert_difference 'JournalDetail.count', 1 do
|
Chris@507
|
861 i.save!
|
Chris@507
|
862 end
|
Chris@507
|
863 end
|
Chris@507
|
864 end
|
Chris@909
|
865
|
Chris@507
|
866 def test_description_eol_should_be_normalized
|
Chris@507
|
867 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
|
Chris@507
|
868 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
|
Chris@507
|
869 end
|
Chris@441
|
870
|
Chris@0
|
871 def test_saving_twice_should_not_duplicate_journal_details
|
Chris@0
|
872 i = Issue.find(:first)
|
Chris@0
|
873 i.init_journal(User.find(2), 'Some notes')
|
Chris@0
|
874 # initial changes
|
Chris@0
|
875 i.subject = 'New subject'
|
Chris@0
|
876 i.done_ratio = i.done_ratio + 10
|
Chris@0
|
877 assert_difference 'Journal.count' do
|
Chris@0
|
878 assert i.save
|
Chris@0
|
879 end
|
Chris@0
|
880 # 1 more change
|
Chris@0
|
881 i.priority = IssuePriority.find(:first, :conditions => ["id <> ?", i.priority_id])
|
Chris@0
|
882 assert_no_difference 'Journal.count' do
|
Chris@0
|
883 assert_difference 'JournalDetail.count', 1 do
|
Chris@0
|
884 i.save
|
Chris@0
|
885 end
|
Chris@0
|
886 end
|
Chris@0
|
887 # no more change
|
Chris@0
|
888 assert_no_difference 'Journal.count' do
|
Chris@0
|
889 assert_no_difference 'JournalDetail.count' do
|
Chris@0
|
890 i.save
|
Chris@0
|
891 end
|
Chris@0
|
892 end
|
Chris@0
|
893 end
|
Chris@0
|
894
|
Chris@128
|
895 def test_all_dependent_issues
|
Chris@128
|
896 IssueRelation.delete_all
|
Chris@909
|
897 assert IssueRelation.create!(:issue_from => Issue.find(1),
|
Chris@909
|
898 :issue_to => Issue.find(2),
|
Chris@909
|
899 :relation_type => IssueRelation::TYPE_PRECEDES)
|
Chris@909
|
900 assert IssueRelation.create!(:issue_from => Issue.find(2),
|
Chris@909
|
901 :issue_to => Issue.find(3),
|
Chris@909
|
902 :relation_type => IssueRelation::TYPE_PRECEDES)
|
Chris@909
|
903 assert IssueRelation.create!(:issue_from => Issue.find(3),
|
Chris@909
|
904 :issue_to => Issue.find(8),
|
Chris@909
|
905 :relation_type => IssueRelation::TYPE_PRECEDES)
|
Chris@441
|
906
|
Chris@128
|
907 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
|
Chris@128
|
908 end
|
Chris@128
|
909
|
Chris@128
|
910 def test_all_dependent_issues_with_persistent_circular_dependency
|
Chris@128
|
911 IssueRelation.delete_all
|
Chris@909
|
912 assert IssueRelation.create!(:issue_from => Issue.find(1),
|
Chris@909
|
913 :issue_to => Issue.find(2),
|
Chris@909
|
914 :relation_type => IssueRelation::TYPE_PRECEDES)
|
Chris@909
|
915 assert IssueRelation.create!(:issue_from => Issue.find(2),
|
Chris@909
|
916 :issue_to => Issue.find(3),
|
Chris@909
|
917 :relation_type => IssueRelation::TYPE_PRECEDES)
|
Chris@128
|
918 # Validation skipping
|
Chris@909
|
919 assert IssueRelation.new(:issue_from => Issue.find(3),
|
Chris@909
|
920 :issue_to => Issue.find(1),
|
Chris@909
|
921 :relation_type => IssueRelation::TYPE_PRECEDES).save(false)
|
Chris@441
|
922
|
Chris@128
|
923 assert_equal [2, 3], Issue.find(1).all_dependent_issues.collect(&:id).sort
|
Chris@128
|
924 end
|
Chris@441
|
925
|
Chris@441
|
926 def test_all_dependent_issues_with_persistent_multiple_circular_dependencies
|
Chris@441
|
927 IssueRelation.delete_all
|
Chris@909
|
928 assert IssueRelation.create!(:issue_from => Issue.find(1),
|
Chris@909
|
929 :issue_to => Issue.find(2),
|
Chris@909
|
930 :relation_type => IssueRelation::TYPE_RELATES)
|
Chris@909
|
931 assert IssueRelation.create!(:issue_from => Issue.find(2),
|
Chris@909
|
932 :issue_to => Issue.find(3),
|
Chris@909
|
933 :relation_type => IssueRelation::TYPE_RELATES)
|
Chris@909
|
934 assert IssueRelation.create!(:issue_from => Issue.find(3),
|
Chris@909
|
935 :issue_to => Issue.find(8),
|
Chris@909
|
936 :relation_type => IssueRelation::TYPE_RELATES)
|
Chris@441
|
937 # Validation skipping
|
Chris@909
|
938 assert IssueRelation.new(:issue_from => Issue.find(8),
|
Chris@909
|
939 :issue_to => Issue.find(2),
|
Chris@909
|
940 :relation_type => IssueRelation::TYPE_RELATES).save(false)
|
Chris@909
|
941 assert IssueRelation.new(:issue_from => Issue.find(3),
|
Chris@909
|
942 :issue_to => Issue.find(1),
|
Chris@909
|
943 :relation_type => IssueRelation::TYPE_RELATES).save(false)
|
Chris@441
|
944
|
Chris@441
|
945 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
|
Chris@441
|
946 end
|
Chris@441
|
947
|
Chris@0
|
948 context "#done_ratio" do
|
Chris@0
|
949 setup do
|
Chris@0
|
950 @issue = Issue.find(1)
|
Chris@0
|
951 @issue_status = IssueStatus.find(1)
|
Chris@0
|
952 @issue_status.update_attribute(:default_done_ratio, 50)
|
chris@37
|
953 @issue2 = Issue.find(2)
|
chris@37
|
954 @issue_status2 = IssueStatus.find(2)
|
chris@37
|
955 @issue_status2.update_attribute(:default_done_ratio, 0)
|
Chris@0
|
956 end
|
Chris@441
|
957
|
Chris@0
|
958 context "with Setting.issue_done_ratio using the issue_field" do
|
Chris@0
|
959 setup do
|
Chris@0
|
960 Setting.issue_done_ratio = 'issue_field'
|
Chris@0
|
961 end
|
Chris@441
|
962
|
Chris@0
|
963 should "read the issue's field" do
|
Chris@0
|
964 assert_equal 0, @issue.done_ratio
|
chris@37
|
965 assert_equal 30, @issue2.done_ratio
|
Chris@0
|
966 end
|
Chris@0
|
967 end
|
Chris@0
|
968
|
Chris@0
|
969 context "with Setting.issue_done_ratio using the issue_status" do
|
Chris@0
|
970 setup do
|
Chris@0
|
971 Setting.issue_done_ratio = 'issue_status'
|
Chris@0
|
972 end
|
Chris@441
|
973
|
Chris@0
|
974 should "read the Issue Status's default done ratio" do
|
Chris@0
|
975 assert_equal 50, @issue.done_ratio
|
chris@37
|
976 assert_equal 0, @issue2.done_ratio
|
Chris@0
|
977 end
|
Chris@0
|
978 end
|
Chris@0
|
979 end
|
Chris@0
|
980
|
Chris@0
|
981 context "#update_done_ratio_from_issue_status" do
|
Chris@0
|
982 setup do
|
Chris@0
|
983 @issue = Issue.find(1)
|
Chris@0
|
984 @issue_status = IssueStatus.find(1)
|
Chris@0
|
985 @issue_status.update_attribute(:default_done_ratio, 50)
|
chris@37
|
986 @issue2 = Issue.find(2)
|
chris@37
|
987 @issue_status2 = IssueStatus.find(2)
|
chris@37
|
988 @issue_status2.update_attribute(:default_done_ratio, 0)
|
Chris@0
|
989 end
|
Chris@441
|
990
|
Chris@0
|
991 context "with Setting.issue_done_ratio using the issue_field" do
|
Chris@0
|
992 setup do
|
Chris@0
|
993 Setting.issue_done_ratio = 'issue_field'
|
Chris@0
|
994 end
|
Chris@441
|
995
|
Chris@0
|
996 should "not change the issue" do
|
Chris@0
|
997 @issue.update_done_ratio_from_issue_status
|
chris@37
|
998 @issue2.update_done_ratio_from_issue_status
|
Chris@0
|
999
|
chris@37
|
1000 assert_equal 0, @issue.read_attribute(:done_ratio)
|
chris@37
|
1001 assert_equal 30, @issue2.read_attribute(:done_ratio)
|
Chris@0
|
1002 end
|
Chris@0
|
1003 end
|
Chris@0
|
1004
|
Chris@0
|
1005 context "with Setting.issue_done_ratio using the issue_status" do
|
Chris@0
|
1006 setup do
|
Chris@0
|
1007 Setting.issue_done_ratio = 'issue_status'
|
Chris@0
|
1008 end
|
Chris@441
|
1009
|
chris@37
|
1010 should "change the issue's done ratio" do
|
Chris@0
|
1011 @issue.update_done_ratio_from_issue_status
|
chris@37
|
1012 @issue2.update_done_ratio_from_issue_status
|
Chris@0
|
1013
|
chris@37
|
1014 assert_equal 50, @issue.read_attribute(:done_ratio)
|
chris@37
|
1015 assert_equal 0, @issue2.read_attribute(:done_ratio)
|
Chris@0
|
1016 end
|
Chris@0
|
1017 end
|
Chris@0
|
1018 end
|
Chris@0
|
1019
|
Chris@0
|
1020 test "#by_tracker" do
|
Chris@441
|
1021 User.current = User.anonymous
|
Chris@0
|
1022 groups = Issue.by_tracker(Project.find(1))
|
Chris@0
|
1023 assert_equal 3, groups.size
|
Chris@0
|
1024 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
|
Chris@0
|
1025 end
|
Chris@0
|
1026
|
Chris@0
|
1027 test "#by_version" do
|
Chris@441
|
1028 User.current = User.anonymous
|
Chris@0
|
1029 groups = Issue.by_version(Project.find(1))
|
Chris@0
|
1030 assert_equal 3, groups.size
|
Chris@0
|
1031 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
|
Chris@0
|
1032 end
|
Chris@0
|
1033
|
Chris@0
|
1034 test "#by_priority" do
|
Chris@441
|
1035 User.current = User.anonymous
|
Chris@0
|
1036 groups = Issue.by_priority(Project.find(1))
|
Chris@0
|
1037 assert_equal 4, groups.size
|
Chris@0
|
1038 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
|
Chris@0
|
1039 end
|
Chris@0
|
1040
|
Chris@0
|
1041 test "#by_category" do
|
Chris@441
|
1042 User.current = User.anonymous
|
Chris@0
|
1043 groups = Issue.by_category(Project.find(1))
|
Chris@0
|
1044 assert_equal 2, groups.size
|
Chris@0
|
1045 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
|
Chris@0
|
1046 end
|
Chris@0
|
1047
|
Chris@0
|
1048 test "#by_assigned_to" do
|
Chris@441
|
1049 User.current = User.anonymous
|
Chris@0
|
1050 groups = Issue.by_assigned_to(Project.find(1))
|
Chris@0
|
1051 assert_equal 2, groups.size
|
Chris@0
|
1052 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
|
Chris@0
|
1053 end
|
Chris@0
|
1054
|
Chris@0
|
1055 test "#by_author" do
|
Chris@441
|
1056 User.current = User.anonymous
|
Chris@0
|
1057 groups = Issue.by_author(Project.find(1))
|
Chris@0
|
1058 assert_equal 4, groups.size
|
Chris@0
|
1059 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
|
Chris@0
|
1060 end
|
Chris@0
|
1061
|
Chris@0
|
1062 test "#by_subproject" do
|
Chris@441
|
1063 User.current = User.anonymous
|
Chris@0
|
1064 groups = Issue.by_subproject(Project.find(1))
|
Chris@441
|
1065 # Private descendant not visible
|
Chris@441
|
1066 assert_equal 1, groups.size
|
Chris@441
|
1067 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
|
Chris@0
|
1068 end
|
Chris@441
|
1069
|
Chris@0
|
1070 context ".allowed_target_projects_on_move" do
|
Chris@0
|
1071 should "return all active projects for admin users" do
|
Chris@0
|
1072 User.current = User.find(1)
|
Chris@0
|
1073 assert_equal Project.active.count, Issue.allowed_target_projects_on_move.size
|
Chris@0
|
1074 end
|
Chris@441
|
1075
|
Chris@0
|
1076 should "return allowed projects for non admin users" do
|
Chris@0
|
1077 User.current = User.find(2)
|
Chris@0
|
1078 Role.non_member.remove_permission! :move_issues
|
Chris@0
|
1079 assert_equal 3, Issue.allowed_target_projects_on_move.size
|
Chris@441
|
1080
|
Chris@0
|
1081 Role.non_member.add_permission! :move_issues
|
Chris@0
|
1082 assert_equal Project.active.count, Issue.allowed_target_projects_on_move.size
|
Chris@0
|
1083 end
|
Chris@0
|
1084 end
|
Chris@0
|
1085
|
Chris@0
|
1086 def test_recently_updated_with_limit_scopes
|
Chris@0
|
1087 #should return the last updated issue
|
Chris@0
|
1088 assert_equal 1, Issue.recently_updated.with_limit(1).length
|
Chris@0
|
1089 assert_equal Issue.find(:first, :order => "updated_on DESC"), Issue.recently_updated.with_limit(1).first
|
Chris@0
|
1090 end
|
Chris@0
|
1091
|
Chris@0
|
1092 def test_on_active_projects_scope
|
Chris@0
|
1093 assert Project.find(2).archive
|
Chris@441
|
1094
|
Chris@0
|
1095 before = Issue.on_active_project.length
|
Chris@0
|
1096 # test inclusion to results
|
Chris@0
|
1097 issue = Issue.generate_for_project!(Project.find(1), :tracker => Project.find(2).trackers.first)
|
Chris@0
|
1098 assert_equal before + 1, Issue.on_active_project.length
|
Chris@0
|
1099
|
Chris@0
|
1100 # Move to an archived project
|
Chris@0
|
1101 issue.project = Project.find(2)
|
Chris@0
|
1102 assert issue.save
|
Chris@0
|
1103 assert_equal before, Issue.on_active_project.length
|
Chris@0
|
1104 end
|
chris@37
|
1105
|
chris@37
|
1106 context "Issue#recipients" do
|
chris@37
|
1107 setup do
|
chris@37
|
1108 @project = Project.find(1)
|
chris@37
|
1109 @author = User.generate_with_protected!
|
chris@37
|
1110 @assignee = User.generate_with_protected!
|
chris@37
|
1111 @issue = Issue.generate_for_project!(@project, :assigned_to => @assignee, :author => @author)
|
chris@37
|
1112 end
|
Chris@441
|
1113
|
chris@37
|
1114 should "include project recipients" do
|
chris@37
|
1115 assert @project.recipients.present?
|
chris@37
|
1116 @project.recipients.each do |project_recipient|
|
chris@37
|
1117 assert @issue.recipients.include?(project_recipient)
|
chris@37
|
1118 end
|
chris@37
|
1119 end
|
chris@37
|
1120
|
chris@37
|
1121 should "include the author if the author is active" do
|
chris@37
|
1122 assert @issue.author, "No author set for Issue"
|
chris@37
|
1123 assert @issue.recipients.include?(@issue.author.mail)
|
chris@37
|
1124 end
|
Chris@441
|
1125
|
chris@37
|
1126 should "include the assigned to user if the assigned to user is active" do
|
chris@37
|
1127 assert @issue.assigned_to, "No assigned_to set for Issue"
|
chris@37
|
1128 assert @issue.recipients.include?(@issue.assigned_to.mail)
|
chris@37
|
1129 end
|
chris@37
|
1130
|
chris@37
|
1131 should "not include users who opt out of all email" do
|
chris@37
|
1132 @author.update_attribute(:mail_notification, :none)
|
chris@37
|
1133
|
chris@37
|
1134 assert !@issue.recipients.include?(@issue.author.mail)
|
chris@37
|
1135 end
|
chris@37
|
1136
|
chris@37
|
1137 should "not include the issue author if they are only notified of assigned issues" do
|
chris@37
|
1138 @author.update_attribute(:mail_notification, :only_assigned)
|
chris@37
|
1139
|
chris@37
|
1140 assert !@issue.recipients.include?(@issue.author.mail)
|
chris@37
|
1141 end
|
chris@37
|
1142
|
chris@37
|
1143 should "not include the assigned user if they are only notified of owned issues" do
|
chris@37
|
1144 @assignee.update_attribute(:mail_notification, :only_owner)
|
chris@37
|
1145
|
chris@37
|
1146 assert !@issue.recipients.include?(@issue.assigned_to.mail)
|
chris@37
|
1147 end
|
chris@37
|
1148
|
chris@37
|
1149 end
|
Chris@0
|
1150 end
|