##// END OF EJS Templates
Make sure that attachments are created in the same order they were selected (#12310)....
Jean-Philippe Lang -
r10571:a17f4c8375d1
parent child
Show More
@@ -1,102 +1,114
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 module Redmine
19 19 module Acts
20 20 module Attachable
21 21 def self.included(base)
22 22 base.extend ClassMethods
23 23 end
24 24
25 25 module ClassMethods
26 26 def acts_as_attachable(options = {})
27 27 cattr_accessor :attachable_options
28 28 self.attachable_options = {}
29 29 attachable_options[:view_permission] = options.delete(:view_permission) || "view_#{self.name.pluralize.underscore}".to_sym
30 30 attachable_options[:delete_permission] = options.delete(:delete_permission) || "edit_#{self.name.pluralize.underscore}".to_sym
31 31
32 32 has_many :attachments, options.merge(:as => :container,
33 33 :order => "#{Attachment.table_name}.created_on ASC, #{Attachment.table_name}.id ASC",
34 34 :dependent => :destroy)
35 35 send :include, Redmine::Acts::Attachable::InstanceMethods
36 36 before_save :attach_saved_attachments
37 37 end
38 38 end
39 39
40 40 module InstanceMethods
41 41 def self.included(base)
42 42 base.extend ClassMethods
43 43 end
44 44
45 45 def attachments_visible?(user=User.current)
46 46 (respond_to?(:visible?) ? visible?(user) : true) &&
47 47 user.allowed_to?(self.class.attachable_options[:view_permission], self.project)
48 48 end
49 49
50 50 def attachments_deletable?(user=User.current)
51 51 (respond_to?(:visible?) ? visible?(user) : true) &&
52 52 user.allowed_to?(self.class.attachable_options[:delete_permission], self.project)
53 53 end
54 54
55 55 def saved_attachments
56 56 @saved_attachments ||= []
57 57 end
58 58
59 59 def unsaved_attachments
60 60 @unsaved_attachments ||= []
61 61 end
62 62
63 63 def save_attachments(attachments, author=User.current)
64 64 if attachments.is_a?(Hash)
65 attachments = attachments.values
65 attachments = attachments.stringify_keys
66 attachments = attachments.to_a.sort {|a, b|
67 if a.first.to_i > 0 && b.first.to_i > 0
68 a.first.to_i <=> b.first.to_i
69 elsif a.first.to_i > 0
70 1
71 elsif b.first.to_i > 0
72 -1
73 else
74 a.first <=> b.first
75 end
76 }
77 attachments = attachments.map(&:last)
66 78 end
67 79 if attachments.is_a?(Array)
68 80 attachments.each do |attachment|
69 81 a = nil
70 82 if file = attachment['file']
71 83 next unless file.size > 0
72 84 a = Attachment.create(:file => file, :author => author)
73 85 elsif token = attachment['token']
74 86 a = Attachment.find_by_token(token)
75 87 next unless a
76 88 a.filename = attachment['filename'] unless attachment['filename'].blank?
77 89 a.content_type = attachment['content_type']
78 90 end
79 91 next unless a
80 92 a.description = attachment['description'].to_s.strip
81 93 if a.new_record?
82 94 unsaved_attachments << a
83 95 else
84 96 saved_attachments << a
85 97 end
86 98 end
87 99 end
88 100 {:files => saved_attachments, :unsaved => unsaved_attachments}
89 101 end
90 102
91 103 def attach_saved_attachments
92 104 saved_attachments.each do |attachment|
93 105 self.attachments << attachment
94 106 end
95 107 end
96 108
97 109 module ClassMethods
98 110 end
99 111 end
100 112 end
101 113 end
102 114 end
@@ -1,1891 +1,1905
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class IssueTest < ActiveSupport::TestCase
21 21 fixtures :projects, :users, :members, :member_roles, :roles,
22 22 :groups_users,
23 23 :trackers, :projects_trackers,
24 24 :enabled_modules,
25 25 :versions,
26 26 :issue_statuses, :issue_categories, :issue_relations, :workflows,
27 27 :enumerations,
28 28 :issues, :journals, :journal_details,
29 29 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
30 30 :time_entries
31 31
32 32 include Redmine::I18n
33 33
34 34 def teardown
35 35 User.current = nil
36 36 end
37 37
38 38 def test_create
39 39 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
40 40 :status_id => 1, :priority => IssuePriority.all.first,
41 41 :subject => 'test_create',
42 42 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
43 43 assert issue.save
44 44 issue.reload
45 45 assert_equal 1.5, issue.estimated_hours
46 46 end
47 47
48 48 def test_create_minimal
49 49 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
50 50 :status_id => 1, :priority => IssuePriority.all.first,
51 51 :subject => 'test_create')
52 52 assert issue.save
53 53 assert issue.description.nil?
54 54 assert_nil issue.estimated_hours
55 55 end
56 56
57 57 def test_start_date_format_should_be_validated
58 58 set_language_if_valid 'en'
59 59 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
60 60 issue = Issue.new(:start_date => invalid_date)
61 61 assert !issue.valid?
62 62 assert_include 'Start date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
63 63 end
64 64 end
65 65
66 66 def test_due_date_format_should_be_validated
67 67 set_language_if_valid 'en'
68 68 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
69 69 issue = Issue.new(:due_date => invalid_date)
70 70 assert !issue.valid?
71 71 assert_include 'Due date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
72 72 end
73 73 end
74 74
75 75 def test_due_date_lesser_than_start_date_should_not_validate
76 76 set_language_if_valid 'en'
77 77 issue = Issue.new(:start_date => '2012-10-06', :due_date => '2012-10-02')
78 78 assert !issue.valid?
79 79 assert_include 'Due date must be greater than start date', issue.errors.full_messages
80 80 end
81 81
82 82 def test_create_with_required_custom_field
83 83 set_language_if_valid 'en'
84 84 field = IssueCustomField.find_by_name('Database')
85 85 field.update_attribute(:is_required, true)
86 86
87 87 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
88 88 :status_id => 1, :subject => 'test_create',
89 89 :description => 'IssueTest#test_create_with_required_custom_field')
90 90 assert issue.available_custom_fields.include?(field)
91 91 # No value for the custom field
92 92 assert !issue.save
93 93 assert_equal ["Database can't be blank"], issue.errors.full_messages
94 94 # Blank value
95 95 issue.custom_field_values = { field.id => '' }
96 96 assert !issue.save
97 97 assert_equal ["Database can't be blank"], issue.errors.full_messages
98 98 # Invalid value
99 99 issue.custom_field_values = { field.id => 'SQLServer' }
100 100 assert !issue.save
101 101 assert_equal ["Database is not included in the list"], issue.errors.full_messages
102 102 # Valid value
103 103 issue.custom_field_values = { field.id => 'PostgreSQL' }
104 104 assert issue.save
105 105 issue.reload
106 106 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
107 107 end
108 108
109 109 def test_create_with_group_assignment
110 110 with_settings :issue_group_assignment => '1' do
111 111 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
112 112 :subject => 'Group assignment',
113 113 :assigned_to_id => 11).save
114 114 issue = Issue.first(:order => 'id DESC')
115 115 assert_kind_of Group, issue.assigned_to
116 116 assert_equal Group.find(11), issue.assigned_to
117 117 end
118 118 end
119 119
120 120 def test_create_with_parent_issue_id
121 121 issue = Issue.new(:project_id => 1, :tracker_id => 1,
122 122 :author_id => 1, :subject => 'Group assignment',
123 123 :parent_issue_id => 1)
124 124 assert_save issue
125 125 assert_equal 1, issue.parent_issue_id
126 126 assert_equal Issue.find(1), issue.parent
127 127 end
128 128
129 129 def test_create_with_sharp_parent_issue_id
130 130 issue = Issue.new(:project_id => 1, :tracker_id => 1,
131 131 :author_id => 1, :subject => 'Group assignment',
132 132 :parent_issue_id => "#1")
133 133 assert_save issue
134 134 assert_equal 1, issue.parent_issue_id
135 135 assert_equal Issue.find(1), issue.parent
136 136 end
137 137
138 138 def test_create_with_invalid_parent_issue_id
139 139 set_language_if_valid 'en'
140 140 issue = Issue.new(:project_id => 1, :tracker_id => 1,
141 141 :author_id => 1, :subject => 'Group assignment',
142 142 :parent_issue_id => '01ABC')
143 143 assert !issue.save
144 144 assert_equal '01ABC', issue.parent_issue_id
145 145 assert_include 'Parent task is invalid', issue.errors.full_messages
146 146 end
147 147
148 148 def test_create_with_invalid_sharp_parent_issue_id
149 149 set_language_if_valid 'en'
150 150 issue = Issue.new(:project_id => 1, :tracker_id => 1,
151 151 :author_id => 1, :subject => 'Group assignment',
152 152 :parent_issue_id => '#01ABC')
153 153 assert !issue.save
154 154 assert_equal '#01ABC', issue.parent_issue_id
155 155 assert_include 'Parent task is invalid', issue.errors.full_messages
156 156 end
157 157
158 158 def assert_visibility_match(user, issues)
159 159 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
160 160 end
161 161
162 162 def test_visible_scope_for_anonymous
163 163 # Anonymous user should see issues of public projects only
164 164 issues = Issue.visible(User.anonymous).all
165 165 assert issues.any?
166 166 assert_nil issues.detect {|issue| !issue.project.is_public?}
167 167 assert_nil issues.detect {|issue| issue.is_private?}
168 168 assert_visibility_match User.anonymous, issues
169 169 end
170 170
171 171 def test_visible_scope_for_anonymous_without_view_issues_permissions
172 172 # Anonymous user should not see issues without permission
173 173 Role.anonymous.remove_permission!(:view_issues)
174 174 issues = Issue.visible(User.anonymous).all
175 175 assert issues.empty?
176 176 assert_visibility_match User.anonymous, issues
177 177 end
178 178
179 179 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
180 180 assert Role.anonymous.update_attribute(:issues_visibility, 'default')
181 181 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
182 182 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
183 183 assert !issue.visible?(User.anonymous)
184 184 end
185 185
186 186 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
187 187 assert Role.anonymous.update_attribute(:issues_visibility, 'own')
188 188 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
189 189 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
190 190 assert !issue.visible?(User.anonymous)
191 191 end
192 192
193 193 def test_visible_scope_for_non_member
194 194 user = User.find(9)
195 195 assert user.projects.empty?
196 196 # Non member user should see issues of public projects only
197 197 issues = Issue.visible(user).all
198 198 assert issues.any?
199 199 assert_nil issues.detect {|issue| !issue.project.is_public?}
200 200 assert_nil issues.detect {|issue| issue.is_private?}
201 201 assert_visibility_match user, issues
202 202 end
203 203
204 204 def test_visible_scope_for_non_member_with_own_issues_visibility
205 205 Role.non_member.update_attribute :issues_visibility, 'own'
206 206 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
207 207 user = User.find(9)
208 208
209 209 issues = Issue.visible(user).all
210 210 assert issues.any?
211 211 assert_nil issues.detect {|issue| issue.author != user}
212 212 assert_visibility_match user, issues
213 213 end
214 214
215 215 def test_visible_scope_for_non_member_without_view_issues_permissions
216 216 # Non member user should not see issues without permission
217 217 Role.non_member.remove_permission!(:view_issues)
218 218 user = User.find(9)
219 219 assert user.projects.empty?
220 220 issues = Issue.visible(user).all
221 221 assert issues.empty?
222 222 assert_visibility_match user, issues
223 223 end
224 224
225 225 def test_visible_scope_for_member
226 226 user = User.find(9)
227 227 # User should see issues of projects for which he has view_issues permissions only
228 228 Role.non_member.remove_permission!(:view_issues)
229 229 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
230 230 issues = Issue.visible(user).all
231 231 assert issues.any?
232 232 assert_nil issues.detect {|issue| issue.project_id != 3}
233 233 assert_nil issues.detect {|issue| issue.is_private?}
234 234 assert_visibility_match user, issues
235 235 end
236 236
237 237 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
238 238 user = User.find(8)
239 239 assert user.groups.any?
240 240 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
241 241 Role.non_member.remove_permission!(:view_issues)
242 242
243 243 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
244 244 :status_id => 1, :priority => IssuePriority.all.first,
245 245 :subject => 'Assignment test',
246 246 :assigned_to => user.groups.first,
247 247 :is_private => true)
248 248
249 249 Role.find(2).update_attribute :issues_visibility, 'default'
250 250 issues = Issue.visible(User.find(8)).all
251 251 assert issues.any?
252 252 assert issues.include?(issue)
253 253
254 254 Role.find(2).update_attribute :issues_visibility, 'own'
255 255 issues = Issue.visible(User.find(8)).all
256 256 assert issues.any?
257 257 assert issues.include?(issue)
258 258 end
259 259
260 260 def test_visible_scope_for_admin
261 261 user = User.find(1)
262 262 user.members.each(&:destroy)
263 263 assert user.projects.empty?
264 264 issues = Issue.visible(user).all
265 265 assert issues.any?
266 266 # Admin should see issues on private projects that he does not belong to
267 267 assert issues.detect {|issue| !issue.project.is_public?}
268 268 # Admin should see private issues of other users
269 269 assert issues.detect {|issue| issue.is_private? && issue.author != user}
270 270 assert_visibility_match user, issues
271 271 end
272 272
273 273 def test_visible_scope_with_project
274 274 project = Project.find(1)
275 275 issues = Issue.visible(User.find(2), :project => project).all
276 276 projects = issues.collect(&:project).uniq
277 277 assert_equal 1, projects.size
278 278 assert_equal project, projects.first
279 279 end
280 280
281 281 def test_visible_scope_with_project_and_subprojects
282 282 project = Project.find(1)
283 283 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).all
284 284 projects = issues.collect(&:project).uniq
285 285 assert projects.size > 1
286 286 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
287 287 end
288 288
289 289 def test_visible_and_nested_set_scopes
290 290 assert_equal 0, Issue.find(1).descendants.visible.all.size
291 291 end
292 292
293 293 def test_open_scope
294 294 issues = Issue.open.all
295 295 assert_nil issues.detect(&:closed?)
296 296 end
297 297
298 298 def test_open_scope_with_arg
299 299 issues = Issue.open(false).all
300 300 assert_equal issues, issues.select(&:closed?)
301 301 end
302 302
303 303 def test_errors_full_messages_should_include_custom_fields_errors
304 304 field = IssueCustomField.find_by_name('Database')
305 305
306 306 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
307 307 :status_id => 1, :subject => 'test_create',
308 308 :description => 'IssueTest#test_create_with_required_custom_field')
309 309 assert issue.available_custom_fields.include?(field)
310 310 # Invalid value
311 311 issue.custom_field_values = { field.id => 'SQLServer' }
312 312
313 313 assert !issue.valid?
314 314 assert_equal 1, issue.errors.full_messages.size
315 315 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
316 316 issue.errors.full_messages.first
317 317 end
318 318
319 319 def test_update_issue_with_required_custom_field
320 320 field = IssueCustomField.find_by_name('Database')
321 321 field.update_attribute(:is_required, true)
322 322
323 323 issue = Issue.find(1)
324 324 assert_nil issue.custom_value_for(field)
325 325 assert issue.available_custom_fields.include?(field)
326 326 # No change to custom values, issue can be saved
327 327 assert issue.save
328 328 # Blank value
329 329 issue.custom_field_values = { field.id => '' }
330 330 assert !issue.save
331 331 # Valid value
332 332 issue.custom_field_values = { field.id => 'PostgreSQL' }
333 333 assert issue.save
334 334 issue.reload
335 335 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
336 336 end
337 337
338 338 def test_should_not_update_attributes_if_custom_fields_validation_fails
339 339 issue = Issue.find(1)
340 340 field = IssueCustomField.find_by_name('Database')
341 341 assert issue.available_custom_fields.include?(field)
342 342
343 343 issue.custom_field_values = { field.id => 'Invalid' }
344 344 issue.subject = 'Should be not be saved'
345 345 assert !issue.save
346 346
347 347 issue.reload
348 348 assert_equal "Can't print recipes", issue.subject
349 349 end
350 350
351 351 def test_should_not_recreate_custom_values_objects_on_update
352 352 field = IssueCustomField.find_by_name('Database')
353 353
354 354 issue = Issue.find(1)
355 355 issue.custom_field_values = { field.id => 'PostgreSQL' }
356 356 assert issue.save
357 357 custom_value = issue.custom_value_for(field)
358 358 issue.reload
359 359 issue.custom_field_values = { field.id => 'MySQL' }
360 360 assert issue.save
361 361 issue.reload
362 362 assert_equal custom_value.id, issue.custom_value_for(field).id
363 363 end
364 364
365 365 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
366 366 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1,
367 367 :status_id => 1, :subject => 'Test',
368 368 :custom_field_values => {'2' => 'Test'})
369 369 assert !Tracker.find(2).custom_field_ids.include?(2)
370 370
371 371 issue = Issue.find(issue.id)
372 372 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
373 373
374 374 issue = Issue.find(issue.id)
375 375 custom_value = issue.custom_value_for(2)
376 376 assert_not_nil custom_value
377 377 assert_equal 'Test', custom_value.value
378 378 end
379 379
380 380 def test_assigning_tracker_id_should_reload_custom_fields_values
381 381 issue = Issue.new(:project => Project.find(1))
382 382 assert issue.custom_field_values.empty?
383 383 issue.tracker_id = 1
384 384 assert issue.custom_field_values.any?
385 385 end
386 386
387 387 def test_assigning_attributes_should_assign_project_and_tracker_first
388 388 seq = sequence('seq')
389 389 issue = Issue.new
390 390 issue.expects(:project_id=).in_sequence(seq)
391 391 issue.expects(:tracker_id=).in_sequence(seq)
392 392 issue.expects(:subject=).in_sequence(seq)
393 393 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
394 394 end
395 395
396 396 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
397 397 attributes = ActiveSupport::OrderedHash.new
398 398 attributes['custom_field_values'] = { '1' => 'MySQL' }
399 399 attributes['tracker_id'] = '1'
400 400 issue = Issue.new(:project => Project.find(1))
401 401 issue.attributes = attributes
402 402 assert_equal 'MySQL', issue.custom_field_value(1)
403 403 end
404 404
405 405 def test_should_update_issue_with_disabled_tracker
406 406 p = Project.find(1)
407 407 issue = Issue.find(1)
408 408
409 409 p.trackers.delete(issue.tracker)
410 410 assert !p.trackers.include?(issue.tracker)
411 411
412 412 issue.reload
413 413 issue.subject = 'New subject'
414 414 assert issue.save
415 415 end
416 416
417 417 def test_should_not_set_a_disabled_tracker
418 418 p = Project.find(1)
419 419 p.trackers.delete(Tracker.find(2))
420 420
421 421 issue = Issue.find(1)
422 422 issue.tracker_id = 2
423 423 issue.subject = 'New subject'
424 424 assert !issue.save
425 425 assert_not_nil issue.errors[:tracker_id]
426 426 end
427 427
428 428 def test_category_based_assignment
429 429 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
430 430 :status_id => 1, :priority => IssuePriority.all.first,
431 431 :subject => 'Assignment test',
432 432 :description => 'Assignment test', :category_id => 1)
433 433 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
434 434 end
435 435
436 436 def test_new_statuses_allowed_to
437 437 WorkflowTransition.delete_all
438 438 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
439 439 :old_status_id => 1, :new_status_id => 2,
440 440 :author => false, :assignee => false)
441 441 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
442 442 :old_status_id => 1, :new_status_id => 3,
443 443 :author => true, :assignee => false)
444 444 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1,
445 445 :new_status_id => 4, :author => false,
446 446 :assignee => true)
447 447 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
448 448 :old_status_id => 1, :new_status_id => 5,
449 449 :author => true, :assignee => true)
450 450 status = IssueStatus.find(1)
451 451 role = Role.find(1)
452 452 tracker = Tracker.find(1)
453 453 user = User.find(2)
454 454
455 455 issue = Issue.generate!(:tracker => tracker, :status => status,
456 456 :project_id => 1, :author_id => 1)
457 457 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
458 458
459 459 issue = Issue.generate!(:tracker => tracker, :status => status,
460 460 :project_id => 1, :author => user)
461 461 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
462 462
463 463 issue = Issue.generate!(:tracker => tracker, :status => status,
464 464 :project_id => 1, :author_id => 1,
465 465 :assigned_to => user)
466 466 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
467 467
468 468 issue = Issue.generate!(:tracker => tracker, :status => status,
469 469 :project_id => 1, :author => user,
470 470 :assigned_to => user)
471 471 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
472 472 end
473 473
474 474 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
475 475 admin = User.find(1)
476 476 issue = Issue.find(1)
477 477 assert !admin.member_of?(issue.project)
478 478 expected_statuses = [issue.status] +
479 479 WorkflowTransition.find_all_by_old_status_id(
480 480 issue.status_id).map(&:new_status).uniq.sort
481 481 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
482 482 end
483 483
484 484 def test_new_statuses_allowed_to_should_return_default_and_current_status_when_copying
485 485 issue = Issue.find(1).copy
486 486 assert_equal [1], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
487 487
488 488 issue = Issue.find(2).copy
489 489 assert_equal [1, 2], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
490 490 end
491 491
492 492 def test_safe_attributes_names_should_not_include_disabled_field
493 493 tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
494 494
495 495 issue = Issue.new(:tracker => tracker)
496 496 assert_include 'tracker_id', issue.safe_attribute_names
497 497 assert_include 'status_id', issue.safe_attribute_names
498 498 assert_include 'subject', issue.safe_attribute_names
499 499 assert_include 'description', issue.safe_attribute_names
500 500 assert_include 'custom_field_values', issue.safe_attribute_names
501 501 assert_include 'custom_fields', issue.safe_attribute_names
502 502 assert_include 'lock_version', issue.safe_attribute_names
503 503
504 504 tracker.core_fields.each do |field|
505 505 assert_include field, issue.safe_attribute_names
506 506 end
507 507
508 508 tracker.disabled_core_fields.each do |field|
509 509 assert_not_include field, issue.safe_attribute_names
510 510 end
511 511 end
512 512
513 513 def test_safe_attributes_should_ignore_disabled_fields
514 514 tracker = Tracker.find(1)
515 515 tracker.core_fields = %w(assigned_to_id due_date)
516 516 tracker.save!
517 517
518 518 issue = Issue.new(:tracker => tracker)
519 519 issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
520 520 assert_nil issue.start_date
521 521 assert_equal Date.parse('2012-07-14'), issue.due_date
522 522 end
523 523
524 524 def test_safe_attributes_should_accept_target_tracker_enabled_fields
525 525 source = Tracker.find(1)
526 526 source.core_fields = []
527 527 source.save!
528 528 target = Tracker.find(2)
529 529 target.core_fields = %w(assigned_to_id due_date)
530 530 target.save!
531 531
532 532 issue = Issue.new(:tracker => source)
533 533 issue.safe_attributes = {'tracker_id' => 2, 'due_date' => '2012-07-14'}
534 534 assert_equal target, issue.tracker
535 535 assert_equal Date.parse('2012-07-14'), issue.due_date
536 536 end
537 537
538 538 def test_safe_attributes_should_not_include_readonly_fields
539 539 WorkflowPermission.delete_all
540 540 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
541 541 :role_id => 1, :field_name => 'due_date',
542 542 :rule => 'readonly')
543 543 user = User.find(2)
544 544
545 545 issue = Issue.new(:project_id => 1, :tracker_id => 1)
546 546 assert_equal %w(due_date), issue.read_only_attribute_names(user)
547 547 assert_not_include 'due_date', issue.safe_attribute_names(user)
548 548
549 549 issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
550 550 assert_equal Date.parse('2012-07-14'), issue.start_date
551 551 assert_nil issue.due_date
552 552 end
553 553
554 554 def test_safe_attributes_should_not_include_readonly_custom_fields
555 555 cf1 = IssueCustomField.create!(:name => 'Writable field',
556 556 :field_format => 'string',
557 557 :is_for_all => true, :tracker_ids => [1])
558 558 cf2 = IssueCustomField.create!(:name => 'Readonly field',
559 559 :field_format => 'string',
560 560 :is_for_all => true, :tracker_ids => [1])
561 561 WorkflowPermission.delete_all
562 562 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
563 563 :role_id => 1, :field_name => cf2.id.to_s,
564 564 :rule => 'readonly')
565 565 user = User.find(2)
566 566 issue = Issue.new(:project_id => 1, :tracker_id => 1)
567 567 assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
568 568 assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
569 569
570 570 issue.send :safe_attributes=, {'custom_field_values' => {
571 571 cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'
572 572 }}, user
573 573 assert_equal 'value1', issue.custom_field_value(cf1)
574 574 assert_nil issue.custom_field_value(cf2)
575 575
576 576 issue.send :safe_attributes=, {'custom_fields' => [
577 577 {'id' => cf1.id.to_s, 'value' => 'valuea'},
578 578 {'id' => cf2.id.to_s, 'value' => 'valueb'}
579 579 ]}, user
580 580 assert_equal 'valuea', issue.custom_field_value(cf1)
581 581 assert_nil issue.custom_field_value(cf2)
582 582 end
583 583
584 584 def test_editable_custom_field_values_should_return_non_readonly_custom_values
585 585 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string',
586 586 :is_for_all => true, :tracker_ids => [1, 2])
587 587 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string',
588 588 :is_for_all => true, :tracker_ids => [1, 2])
589 589 WorkflowPermission.delete_all
590 590 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
591 591 :field_name => cf2.id.to_s, :rule => 'readonly')
592 592 user = User.find(2)
593 593
594 594 issue = Issue.new(:project_id => 1, :tracker_id => 1)
595 595 values = issue.editable_custom_field_values(user)
596 596 assert values.detect {|value| value.custom_field == cf1}
597 597 assert_nil values.detect {|value| value.custom_field == cf2}
598 598
599 599 issue.tracker_id = 2
600 600 values = issue.editable_custom_field_values(user)
601 601 assert values.detect {|value| value.custom_field == cf1}
602 602 assert values.detect {|value| value.custom_field == cf2}
603 603 end
604 604
605 605 def test_safe_attributes_should_accept_target_tracker_writable_fields
606 606 WorkflowPermission.delete_all
607 607 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
608 608 :role_id => 1, :field_name => 'due_date',
609 609 :rule => 'readonly')
610 610 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
611 611 :role_id => 1, :field_name => 'start_date',
612 612 :rule => 'readonly')
613 613 user = User.find(2)
614 614
615 615 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
616 616
617 617 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
618 618 'due_date' => '2012-07-14'}, user
619 619 assert_equal Date.parse('2012-07-12'), issue.start_date
620 620 assert_nil issue.due_date
621 621
622 622 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
623 623 'due_date' => '2012-07-16',
624 624 'tracker_id' => 2}, user
625 625 assert_equal Date.parse('2012-07-12'), issue.start_date
626 626 assert_equal Date.parse('2012-07-16'), issue.due_date
627 627 end
628 628
629 629 def test_safe_attributes_should_accept_target_status_writable_fields
630 630 WorkflowPermission.delete_all
631 631 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
632 632 :role_id => 1, :field_name => 'due_date',
633 633 :rule => 'readonly')
634 634 WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1,
635 635 :role_id => 1, :field_name => 'start_date',
636 636 :rule => 'readonly')
637 637 user = User.find(2)
638 638
639 639 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
640 640
641 641 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
642 642 'due_date' => '2012-07-14'},
643 643 user
644 644 assert_equal Date.parse('2012-07-12'), issue.start_date
645 645 assert_nil issue.due_date
646 646
647 647 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
648 648 'due_date' => '2012-07-16',
649 649 'status_id' => 2},
650 650 user
651 651 assert_equal Date.parse('2012-07-12'), issue.start_date
652 652 assert_equal Date.parse('2012-07-16'), issue.due_date
653 653 end
654 654
655 655 def test_required_attributes_should_be_validated
656 656 cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
657 657 :is_for_all => true, :tracker_ids => [1, 2])
658 658
659 659 WorkflowPermission.delete_all
660 660 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
661 661 :role_id => 1, :field_name => 'due_date',
662 662 :rule => 'required')
663 663 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
664 664 :role_id => 1, :field_name => 'category_id',
665 665 :rule => 'required')
666 666 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
667 667 :role_id => 1, :field_name => cf.id.to_s,
668 668 :rule => 'required')
669 669
670 670 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
671 671 :role_id => 1, :field_name => 'start_date',
672 672 :rule => 'required')
673 673 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
674 674 :role_id => 1, :field_name => cf.id.to_s,
675 675 :rule => 'required')
676 676 user = User.find(2)
677 677
678 678 issue = Issue.new(:project_id => 1, :tracker_id => 1,
679 679 :status_id => 1, :subject => 'Required fields',
680 680 :author => user)
681 681 assert_equal [cf.id.to_s, "category_id", "due_date"],
682 682 issue.required_attribute_names(user).sort
683 683 assert !issue.save, "Issue was saved"
684 684 assert_equal ["Category can't be blank", "Due date can't be blank", "Foo can't be blank"],
685 685 issue.errors.full_messages.sort
686 686
687 687 issue.tracker_id = 2
688 688 assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
689 689 assert !issue.save, "Issue was saved"
690 690 assert_equal ["Foo can't be blank", "Start date can't be blank"],
691 691 issue.errors.full_messages.sort
692 692
693 693 issue.start_date = Date.today
694 694 issue.custom_field_values = {cf.id.to_s => 'bar'}
695 695 assert issue.save
696 696 end
697 697
698 698 def test_required_attribute_names_for_multiple_roles_should_intersect_rules
699 699 WorkflowPermission.delete_all
700 700 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
701 701 :role_id => 1, :field_name => 'due_date',
702 702 :rule => 'required')
703 703 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
704 704 :role_id => 1, :field_name => 'start_date',
705 705 :rule => 'required')
706 706 user = User.find(2)
707 707 member = Member.find(1)
708 708 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
709 709
710 710 assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
711 711
712 712 member.role_ids = [1, 2]
713 713 member.save!
714 714 assert_equal [], issue.required_attribute_names(user.reload)
715 715
716 716 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
717 717 :role_id => 2, :field_name => 'due_date',
718 718 :rule => 'required')
719 719 assert_equal %w(due_date), issue.required_attribute_names(user)
720 720
721 721 member.role_ids = [1, 2, 3]
722 722 member.save!
723 723 assert_equal [], issue.required_attribute_names(user.reload)
724 724
725 725 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
726 726 :role_id => 2, :field_name => 'due_date',
727 727 :rule => 'readonly')
728 728 # required + readonly => required
729 729 assert_equal %w(due_date), issue.required_attribute_names(user)
730 730 end
731 731
732 732 def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
733 733 WorkflowPermission.delete_all
734 734 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
735 735 :role_id => 1, :field_name => 'due_date',
736 736 :rule => 'readonly')
737 737 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
738 738 :role_id => 1, :field_name => 'start_date',
739 739 :rule => 'readonly')
740 740 user = User.find(2)
741 741 member = Member.find(1)
742 742 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
743 743
744 744 assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
745 745
746 746 member.role_ids = [1, 2]
747 747 member.save!
748 748 assert_equal [], issue.read_only_attribute_names(user.reload)
749 749
750 750 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
751 751 :role_id => 2, :field_name => 'due_date',
752 752 :rule => 'readonly')
753 753 assert_equal %w(due_date), issue.read_only_attribute_names(user)
754 754 end
755 755
756 756 def test_copy
757 757 issue = Issue.new.copy_from(1)
758 758 assert issue.copy?
759 759 assert issue.save
760 760 issue.reload
761 761 orig = Issue.find(1)
762 762 assert_equal orig.subject, issue.subject
763 763 assert_equal orig.tracker, issue.tracker
764 764 assert_equal "125", issue.custom_value_for(2).value
765 765 end
766 766
767 767 def test_copy_should_copy_status
768 768 orig = Issue.find(8)
769 769 assert orig.status != IssueStatus.default
770 770
771 771 issue = Issue.new.copy_from(orig)
772 772 assert issue.save
773 773 issue.reload
774 774 assert_equal orig.status, issue.status
775 775 end
776 776
777 777 def test_copy_should_add_relation_with_copied_issue
778 778 copied = Issue.find(1)
779 779 issue = Issue.new.copy_from(copied)
780 780 assert issue.save
781 781 issue.reload
782 782
783 783 assert_equal 1, issue.relations.size
784 784 relation = issue.relations.first
785 785 assert_equal 'copied_to', relation.relation_type
786 786 assert_equal copied, relation.issue_from
787 787 assert_equal issue, relation.issue_to
788 788 end
789 789
790 790 def test_copy_should_copy_subtasks
791 791 issue = Issue.generate_with_descendants!
792 792
793 793 copy = issue.reload.copy
794 794 copy.author = User.find(7)
795 795 assert_difference 'Issue.count', 1+issue.descendants.count do
796 796 assert copy.save
797 797 end
798 798 copy.reload
799 799 assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
800 800 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
801 801 assert_equal %w(Child11), child_copy.children.map(&:subject).sort
802 802 assert_equal copy.author, child_copy.author
803 803 end
804 804
805 805 def test_copy_should_copy_subtasks_to_target_project
806 806 issue = Issue.generate_with_descendants!
807 807
808 808 copy = issue.copy(:project_id => 3)
809 809 assert_difference 'Issue.count', 1+issue.descendants.count do
810 810 assert copy.save
811 811 end
812 812 assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
813 813 end
814 814
815 815 def test_copy_should_not_copy_subtasks_twice_when_saving_twice
816 816 issue = Issue.generate_with_descendants!
817 817
818 818 copy = issue.reload.copy
819 819 assert_difference 'Issue.count', 1+issue.descendants.count do
820 820 assert copy.save
821 821 assert copy.save
822 822 end
823 823 end
824 824
825 825 def test_should_not_call_after_project_change_on_creation
826 826 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
827 827 :subject => 'Test', :author_id => 1)
828 828 issue.expects(:after_project_change).never
829 829 issue.save!
830 830 end
831 831
832 832 def test_should_not_call_after_project_change_on_update
833 833 issue = Issue.find(1)
834 834 issue.project = Project.find(1)
835 835 issue.subject = 'No project change'
836 836 issue.expects(:after_project_change).never
837 837 issue.save!
838 838 end
839 839
840 840 def test_should_call_after_project_change_on_project_change
841 841 issue = Issue.find(1)
842 842 issue.project = Project.find(2)
843 843 issue.expects(:after_project_change).once
844 844 issue.save!
845 845 end
846 846
847 847 def test_adding_journal_should_update_timestamp
848 848 issue = Issue.find(1)
849 849 updated_on_was = issue.updated_on
850 850
851 851 issue.init_journal(User.first, "Adding notes")
852 852 assert_difference 'Journal.count' do
853 853 assert issue.save
854 854 end
855 855 issue.reload
856 856
857 857 assert_not_equal updated_on_was, issue.updated_on
858 858 end
859 859
860 860 def test_should_close_duplicates
861 861 # Create 3 issues
862 862 issue1 = Issue.generate!
863 863 issue2 = Issue.generate!
864 864 issue3 = Issue.generate!
865 865
866 866 # 2 is a dupe of 1
867 867 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1,
868 868 :relation_type => IssueRelation::TYPE_DUPLICATES)
869 869 # And 3 is a dupe of 2
870 870 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
871 871 :relation_type => IssueRelation::TYPE_DUPLICATES)
872 872 # And 3 is a dupe of 1 (circular duplicates)
873 873 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1,
874 874 :relation_type => IssueRelation::TYPE_DUPLICATES)
875 875
876 876 assert issue1.reload.duplicates.include?(issue2)
877 877
878 878 # Closing issue 1
879 879 issue1.init_journal(User.find(:first), "Closing issue1")
880 880 issue1.status = IssueStatus.find :first, :conditions => {:is_closed => true}
881 881 assert issue1.save
882 882 # 2 and 3 should be also closed
883 883 assert issue2.reload.closed?
884 884 assert issue3.reload.closed?
885 885 end
886 886
887 887 def test_should_not_close_duplicated_issue
888 888 issue1 = Issue.generate!
889 889 issue2 = Issue.generate!
890 890
891 891 # 2 is a dupe of 1
892 892 IssueRelation.create(:issue_from => issue2, :issue_to => issue1,
893 893 :relation_type => IssueRelation::TYPE_DUPLICATES)
894 894 # 2 is a dup of 1 but 1 is not a duplicate of 2
895 895 assert !issue2.reload.duplicates.include?(issue1)
896 896
897 897 # Closing issue 2
898 898 issue2.init_journal(User.find(:first), "Closing issue2")
899 899 issue2.status = IssueStatus.find :first, :conditions => {:is_closed => true}
900 900 assert issue2.save
901 901 # 1 should not be also closed
902 902 assert !issue1.reload.closed?
903 903 end
904 904
905 905 def test_assignable_versions
906 906 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
907 907 :status_id => 1, :fixed_version_id => 1,
908 908 :subject => 'New issue')
909 909 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
910 910 end
911 911
912 912 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
913 913 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
914 914 :status_id => 1, :fixed_version_id => 1,
915 915 :subject => 'New issue')
916 916 assert !issue.save
917 917 assert_not_nil issue.errors[:fixed_version_id]
918 918 end
919 919
920 920 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
921 921 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
922 922 :status_id => 1, :fixed_version_id => 2,
923 923 :subject => 'New issue')
924 924 assert !issue.save
925 925 assert_not_nil issue.errors[:fixed_version_id]
926 926 end
927 927
928 928 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
929 929 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
930 930 :status_id => 1, :fixed_version_id => 3,
931 931 :subject => 'New issue')
932 932 assert issue.save
933 933 end
934 934
935 935 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
936 936 issue = Issue.find(11)
937 937 assert_equal 'closed', issue.fixed_version.status
938 938 issue.subject = 'Subject changed'
939 939 assert issue.save
940 940 end
941 941
942 942 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
943 943 issue = Issue.find(11)
944 944 issue.status_id = 1
945 945 assert !issue.save
946 946 assert_not_nil issue.errors[:base]
947 947 end
948 948
949 949 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
950 950 issue = Issue.find(11)
951 951 issue.status_id = 1
952 952 issue.fixed_version_id = 3
953 953 assert issue.save
954 954 end
955 955
956 956 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
957 957 issue = Issue.find(12)
958 958 assert_equal 'locked', issue.fixed_version.status
959 959 issue.status_id = 1
960 960 assert issue.save
961 961 end
962 962
963 963 def test_should_not_be_able_to_keep_unshared_version_when_changing_project
964 964 issue = Issue.find(2)
965 965 assert_equal 2, issue.fixed_version_id
966 966 issue.project_id = 3
967 967 assert_nil issue.fixed_version_id
968 968 issue.fixed_version_id = 2
969 969 assert !issue.save
970 970 assert_include 'Target version is not included in the list', issue.errors.full_messages
971 971 end
972 972
973 973 def test_should_keep_shared_version_when_changing_project
974 974 Version.find(2).update_attribute :sharing, 'tree'
975 975
976 976 issue = Issue.find(2)
977 977 assert_equal 2, issue.fixed_version_id
978 978 issue.project_id = 3
979 979 assert_equal 2, issue.fixed_version_id
980 980 assert issue.save
981 981 end
982 982
983 983 def test_allowed_target_projects_on_move_should_include_projects_with_issue_tracking_enabled
984 984 assert_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
985 985 end
986 986
987 987 def test_allowed_target_projects_on_move_should_not_include_projects_with_issue_tracking_disabled
988 988 Project.find(2).disable_module! :issue_tracking
989 989 assert_not_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
990 990 end
991 991
992 992 def test_move_to_another_project_with_same_category
993 993 issue = Issue.find(1)
994 994 issue.project = Project.find(2)
995 995 assert issue.save
996 996 issue.reload
997 997 assert_equal 2, issue.project_id
998 998 # Category changes
999 999 assert_equal 4, issue.category_id
1000 1000 # Make sure time entries were move to the target project
1001 1001 assert_equal 2, issue.time_entries.first.project_id
1002 1002 end
1003 1003
1004 1004 def test_move_to_another_project_without_same_category
1005 1005 issue = Issue.find(2)
1006 1006 issue.project = Project.find(2)
1007 1007 assert issue.save
1008 1008 issue.reload
1009 1009 assert_equal 2, issue.project_id
1010 1010 # Category cleared
1011 1011 assert_nil issue.category_id
1012 1012 end
1013 1013
1014 1014 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
1015 1015 issue = Issue.find(1)
1016 1016 issue.update_attribute(:fixed_version_id, 1)
1017 1017 issue.project = Project.find(2)
1018 1018 assert issue.save
1019 1019 issue.reload
1020 1020 assert_equal 2, issue.project_id
1021 1021 # Cleared fixed_version
1022 1022 assert_equal nil, issue.fixed_version
1023 1023 end
1024 1024
1025 1025 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
1026 1026 issue = Issue.find(1)
1027 1027 issue.update_attribute(:fixed_version_id, 4)
1028 1028 issue.project = Project.find(5)
1029 1029 assert issue.save
1030 1030 issue.reload
1031 1031 assert_equal 5, issue.project_id
1032 1032 # Keep fixed_version
1033 1033 assert_equal 4, issue.fixed_version_id
1034 1034 end
1035 1035
1036 1036 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
1037 1037 issue = Issue.find(1)
1038 1038 issue.update_attribute(:fixed_version_id, 1)
1039 1039 issue.project = Project.find(5)
1040 1040 assert issue.save
1041 1041 issue.reload
1042 1042 assert_equal 5, issue.project_id
1043 1043 # Cleared fixed_version
1044 1044 assert_equal nil, issue.fixed_version
1045 1045 end
1046 1046
1047 1047 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
1048 1048 issue = Issue.find(1)
1049 1049 issue.update_attribute(:fixed_version_id, 7)
1050 1050 issue.project = Project.find(2)
1051 1051 assert issue.save
1052 1052 issue.reload
1053 1053 assert_equal 2, issue.project_id
1054 1054 # Keep fixed_version
1055 1055 assert_equal 7, issue.fixed_version_id
1056 1056 end
1057 1057
1058 1058 def test_move_to_another_project_should_keep_parent_if_valid
1059 1059 issue = Issue.find(1)
1060 1060 issue.update_attribute(:parent_issue_id, 2)
1061 1061 issue.project = Project.find(3)
1062 1062 assert issue.save
1063 1063 issue.reload
1064 1064 assert_equal 2, issue.parent_id
1065 1065 end
1066 1066
1067 1067 def test_move_to_another_project_should_clear_parent_if_not_valid
1068 1068 issue = Issue.find(1)
1069 1069 issue.update_attribute(:parent_issue_id, 2)
1070 1070 issue.project = Project.find(2)
1071 1071 assert issue.save
1072 1072 issue.reload
1073 1073 assert_nil issue.parent_id
1074 1074 end
1075 1075
1076 1076 def test_move_to_another_project_with_disabled_tracker
1077 1077 issue = Issue.find(1)
1078 1078 target = Project.find(2)
1079 1079 target.tracker_ids = [3]
1080 1080 target.save
1081 1081 issue.project = target
1082 1082 assert issue.save
1083 1083 issue.reload
1084 1084 assert_equal 2, issue.project_id
1085 1085 assert_equal 3, issue.tracker_id
1086 1086 end
1087 1087
1088 1088 def test_copy_to_the_same_project
1089 1089 issue = Issue.find(1)
1090 1090 copy = issue.copy
1091 1091 assert_difference 'Issue.count' do
1092 1092 copy.save!
1093 1093 end
1094 1094 assert_kind_of Issue, copy
1095 1095 assert_equal issue.project, copy.project
1096 1096 assert_equal "125", copy.custom_value_for(2).value
1097 1097 end
1098 1098
1099 1099 def test_copy_to_another_project_and_tracker
1100 1100 issue = Issue.find(1)
1101 1101 copy = issue.copy(:project_id => 3, :tracker_id => 2)
1102 1102 assert_difference 'Issue.count' do
1103 1103 copy.save!
1104 1104 end
1105 1105 copy.reload
1106 1106 assert_kind_of Issue, copy
1107 1107 assert_equal Project.find(3), copy.project
1108 1108 assert_equal Tracker.find(2), copy.tracker
1109 1109 # Custom field #2 is not associated with target tracker
1110 1110 assert_nil copy.custom_value_for(2)
1111 1111 end
1112 1112
1113 1113 context "#copy" do
1114 1114 setup do
1115 1115 @issue = Issue.find(1)
1116 1116 end
1117 1117
1118 1118 should "not create a journal" do
1119 1119 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1120 1120 copy.save!
1121 1121 assert_equal 0, copy.reload.journals.size
1122 1122 end
1123 1123
1124 1124 should "allow assigned_to changes" do
1125 1125 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1126 1126 assert_equal 3, copy.assigned_to_id
1127 1127 end
1128 1128
1129 1129 should "allow status changes" do
1130 1130 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
1131 1131 assert_equal 2, copy.status_id
1132 1132 end
1133 1133
1134 1134 should "allow start date changes" do
1135 1135 date = Date.today
1136 1136 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1137 1137 assert_equal date, copy.start_date
1138 1138 end
1139 1139
1140 1140 should "allow due date changes" do
1141 1141 date = Date.today
1142 1142 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :due_date => date)
1143 1143 assert_equal date, copy.due_date
1144 1144 end
1145 1145
1146 1146 should "set current user as author" do
1147 1147 User.current = User.find(9)
1148 1148 copy = @issue.copy(:project_id => 3, :tracker_id => 2)
1149 1149 assert_equal User.current, copy.author
1150 1150 end
1151 1151
1152 1152 should "create a journal with notes" do
1153 1153 date = Date.today
1154 1154 notes = "Notes added when copying"
1155 1155 copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1156 1156 copy.init_journal(User.current, notes)
1157 1157 copy.save!
1158 1158
1159 1159 assert_equal 1, copy.journals.size
1160 1160 journal = copy.journals.first
1161 1161 assert_equal 0, journal.details.size
1162 1162 assert_equal notes, journal.notes
1163 1163 end
1164 1164 end
1165 1165
1166 1166 def test_valid_parent_project
1167 1167 issue = Issue.find(1)
1168 1168 issue_in_same_project = Issue.find(2)
1169 1169 issue_in_child_project = Issue.find(5)
1170 1170 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1171 1171 issue_in_other_child_project = Issue.find(6)
1172 1172 issue_in_different_tree = Issue.find(4)
1173 1173
1174 1174 with_settings :cross_project_subtasks => '' do
1175 1175 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1176 1176 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1177 1177 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1178 1178 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1179 1179 end
1180 1180
1181 1181 with_settings :cross_project_subtasks => 'system' do
1182 1182 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1183 1183 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1184 1184 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1185 1185 end
1186 1186
1187 1187 with_settings :cross_project_subtasks => 'tree' do
1188 1188 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1189 1189 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1190 1190 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1191 1191 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1192 1192
1193 1193 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1194 1194 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1195 1195 end
1196 1196
1197 1197 with_settings :cross_project_subtasks => 'descendants' do
1198 1198 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1199 1199 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1200 1200 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1201 1201 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1202 1202
1203 1203 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1204 1204 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1205 1205 end
1206 1206 end
1207 1207
1208 1208 def test_recipients_should_include_previous_assignee
1209 1209 user = User.find(3)
1210 1210 user.members.update_all ["mail_notification = ?", false]
1211 1211 user.update_attribute :mail_notification, 'only_assigned'
1212 1212
1213 1213 issue = Issue.find(2)
1214 1214 issue.assigned_to = nil
1215 1215 assert_include user.mail, issue.recipients
1216 1216 issue.save!
1217 1217 assert !issue.recipients.include?(user.mail)
1218 1218 end
1219 1219
1220 1220 def test_recipients_should_not_include_users_that_cannot_view_the_issue
1221 1221 issue = Issue.find(12)
1222 1222 assert issue.recipients.include?(issue.author.mail)
1223 1223 # copy the issue to a private project
1224 1224 copy = issue.copy(:project_id => 5, :tracker_id => 2)
1225 1225 # author is not a member of project anymore
1226 1226 assert !copy.recipients.include?(copy.author.mail)
1227 1227 end
1228 1228
1229 1229 def test_recipients_should_include_the_assigned_group_members
1230 1230 group_member = User.generate!
1231 1231 group = Group.generate!
1232 1232 group.users << group_member
1233 1233
1234 1234 issue = Issue.find(12)
1235 1235 issue.assigned_to = group
1236 1236 assert issue.recipients.include?(group_member.mail)
1237 1237 end
1238 1238
1239 1239 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1240 1240 user = User.find(3)
1241 1241 issue = Issue.find(9)
1242 1242 Watcher.create!(:user => user, :watchable => issue)
1243 1243 assert issue.watched_by?(user)
1244 1244 assert !issue.watcher_recipients.include?(user.mail)
1245 1245 end
1246 1246
1247 1247 def test_issue_destroy
1248 1248 Issue.find(1).destroy
1249 1249 assert_nil Issue.find_by_id(1)
1250 1250 assert_nil TimeEntry.find_by_issue_id(1)
1251 1251 end
1252 1252
1253 1253 def test_destroying_a_deleted_issue_should_not_raise_an_error
1254 1254 issue = Issue.find(1)
1255 1255 Issue.find(1).destroy
1256 1256
1257 1257 assert_nothing_raised do
1258 1258 assert_no_difference 'Issue.count' do
1259 1259 issue.destroy
1260 1260 end
1261 1261 assert issue.destroyed?
1262 1262 end
1263 1263 end
1264 1264
1265 1265 def test_destroying_a_stale_issue_should_not_raise_an_error
1266 1266 issue = Issue.find(1)
1267 1267 Issue.find(1).update_attribute :subject, "Updated"
1268 1268
1269 1269 assert_nothing_raised do
1270 1270 assert_difference 'Issue.count', -1 do
1271 1271 issue.destroy
1272 1272 end
1273 1273 assert issue.destroyed?
1274 1274 end
1275 1275 end
1276 1276
1277 1277 def test_blocked
1278 1278 blocked_issue = Issue.find(9)
1279 1279 blocking_issue = Issue.find(10)
1280 1280
1281 1281 assert blocked_issue.blocked?
1282 1282 assert !blocking_issue.blocked?
1283 1283 end
1284 1284
1285 1285 def test_blocked_issues_dont_allow_closed_statuses
1286 1286 blocked_issue = Issue.find(9)
1287 1287
1288 1288 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1289 1289 assert !allowed_statuses.empty?
1290 1290 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1291 1291 assert closed_statuses.empty?
1292 1292 end
1293 1293
1294 1294 def test_unblocked_issues_allow_closed_statuses
1295 1295 blocking_issue = Issue.find(10)
1296 1296
1297 1297 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1298 1298 assert !allowed_statuses.empty?
1299 1299 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1300 1300 assert !closed_statuses.empty?
1301 1301 end
1302 1302
1303 1303 def test_reschedule_an_issue_without_dates
1304 1304 with_settings :non_working_week_days => [] do
1305 1305 issue = Issue.new(:start_date => nil, :due_date => nil)
1306 1306 issue.reschedule_on '2012-10-09'.to_date
1307 1307 assert_equal '2012-10-09'.to_date, issue.start_date
1308 1308 assert_equal '2012-10-09'.to_date, issue.due_date
1309 1309 end
1310 1310
1311 1311 with_settings :non_working_week_days => %w(6 7) do
1312 1312 issue = Issue.new(:start_date => nil, :due_date => nil)
1313 1313 issue.reschedule_on '2012-10-09'.to_date
1314 1314 assert_equal '2012-10-09'.to_date, issue.start_date
1315 1315 assert_equal '2012-10-09'.to_date, issue.due_date
1316 1316
1317 1317 issue = Issue.new(:start_date => nil, :due_date => nil)
1318 1318 issue.reschedule_on '2012-10-13'.to_date
1319 1319 assert_equal '2012-10-15'.to_date, issue.start_date
1320 1320 assert_equal '2012-10-15'.to_date, issue.due_date
1321 1321 end
1322 1322 end
1323 1323
1324 1324 def test_reschedule_an_issue_with_start_date
1325 1325 with_settings :non_working_week_days => [] do
1326 1326 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1327 1327 issue.reschedule_on '2012-10-13'.to_date
1328 1328 assert_equal '2012-10-13'.to_date, issue.start_date
1329 1329 assert_equal '2012-10-13'.to_date, issue.due_date
1330 1330 end
1331 1331
1332 1332 with_settings :non_working_week_days => %w(6 7) do
1333 1333 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1334 1334 issue.reschedule_on '2012-10-11'.to_date
1335 1335 assert_equal '2012-10-11'.to_date, issue.start_date
1336 1336 assert_equal '2012-10-11'.to_date, issue.due_date
1337 1337
1338 1338 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1339 1339 issue.reschedule_on '2012-10-13'.to_date
1340 1340 assert_equal '2012-10-15'.to_date, issue.start_date
1341 1341 assert_equal '2012-10-15'.to_date, issue.due_date
1342 1342 end
1343 1343 end
1344 1344
1345 1345 def test_reschedule_an_issue_with_start_and_due_dates
1346 1346 with_settings :non_working_week_days => [] do
1347 1347 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-15')
1348 1348 issue.reschedule_on '2012-10-13'.to_date
1349 1349 assert_equal '2012-10-13'.to_date, issue.start_date
1350 1350 assert_equal '2012-10-19'.to_date, issue.due_date
1351 1351 end
1352 1352
1353 1353 with_settings :non_working_week_days => %w(6 7) do
1354 1354 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19') # 8 working days
1355 1355 issue.reschedule_on '2012-10-11'.to_date
1356 1356 assert_equal '2012-10-11'.to_date, issue.start_date
1357 1357 assert_equal '2012-10-23'.to_date, issue.due_date
1358 1358
1359 1359 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19')
1360 1360 issue.reschedule_on '2012-10-13'.to_date
1361 1361 assert_equal '2012-10-15'.to_date, issue.start_date
1362 1362 assert_equal '2012-10-25'.to_date, issue.due_date
1363 1363 end
1364 1364 end
1365 1365
1366 1366 def test_rescheduling_an_issue_should_reschedule_following_issue
1367 1367 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1368 1368 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1369 1369 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1370 1370 :relation_type => IssueRelation::TYPE_PRECEDES)
1371 1371 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1372 1372
1373 1373 issue1.due_date = '2012-10-23'
1374 1374 issue1.save!
1375 1375 issue2.reload
1376 1376 assert_equal Date.parse('2012-10-24'), issue2.start_date
1377 1377 assert_equal Date.parse('2012-10-26'), issue2.due_date
1378 1378 end
1379 1379
1380 1380 def test_rescheduling_a_stale_issue_should_not_raise_an_error
1381 1381 with_settings :non_working_week_days => [] do
1382 1382 stale = Issue.find(1)
1383 1383 issue = Issue.find(1)
1384 1384 issue.subject = "Updated"
1385 1385 issue.save!
1386 1386 date = 10.days.from_now.to_date
1387 1387 assert_nothing_raised do
1388 1388 stale.reschedule_on!(date)
1389 1389 end
1390 1390 assert_equal date, stale.reload.start_date
1391 1391 end
1392 1392 end
1393 1393
1394 1394 def test_overdue
1395 1395 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
1396 1396 assert !Issue.new(:due_date => Date.today).overdue?
1397 1397 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
1398 1398 assert !Issue.new(:due_date => nil).overdue?
1399 1399 assert !Issue.new(:due_date => 1.day.ago.to_date,
1400 1400 :status => IssueStatus.find(:first,
1401 1401 :conditions => {:is_closed => true})
1402 1402 ).overdue?
1403 1403 end
1404 1404
1405 1405 context "#behind_schedule?" do
1406 1406 should "be false if the issue has no start_date" do
1407 1407 assert !Issue.new(:start_date => nil,
1408 1408 :due_date => 1.day.from_now.to_date,
1409 1409 :done_ratio => 0).behind_schedule?
1410 1410 end
1411 1411
1412 1412 should "be false if the issue has no end_date" do
1413 1413 assert !Issue.new(:start_date => 1.day.from_now.to_date,
1414 1414 :due_date => nil,
1415 1415 :done_ratio => 0).behind_schedule?
1416 1416 end
1417 1417
1418 1418 should "be false if the issue has more done than it's calendar time" do
1419 1419 assert !Issue.new(:start_date => 50.days.ago.to_date,
1420 1420 :due_date => 50.days.from_now.to_date,
1421 1421 :done_ratio => 90).behind_schedule?
1422 1422 end
1423 1423
1424 1424 should "be true if the issue hasn't been started at all" do
1425 1425 assert Issue.new(:start_date => 1.day.ago.to_date,
1426 1426 :due_date => 1.day.from_now.to_date,
1427 1427 :done_ratio => 0).behind_schedule?
1428 1428 end
1429 1429
1430 1430 should "be true if the issue has used more calendar time than it's done ratio" do
1431 1431 assert Issue.new(:start_date => 100.days.ago.to_date,
1432 1432 :due_date => Date.today,
1433 1433 :done_ratio => 90).behind_schedule?
1434 1434 end
1435 1435 end
1436 1436
1437 1437 context "#assignable_users" do
1438 1438 should "be Users" do
1439 1439 assert_kind_of User, Issue.find(1).assignable_users.first
1440 1440 end
1441 1441
1442 1442 should "include the issue author" do
1443 1443 non_project_member = User.generate!
1444 1444 issue = Issue.generate!(:author => non_project_member)
1445 1445
1446 1446 assert issue.assignable_users.include?(non_project_member)
1447 1447 end
1448 1448
1449 1449 should "include the current assignee" do
1450 1450 user = User.generate!
1451 1451 issue = Issue.generate!(:assigned_to => user)
1452 1452 user.lock!
1453 1453
1454 1454 assert Issue.find(issue.id).assignable_users.include?(user)
1455 1455 end
1456 1456
1457 1457 should "not show the issue author twice" do
1458 1458 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
1459 1459 assert_equal 2, assignable_user_ids.length
1460 1460
1461 1461 assignable_user_ids.each do |user_id|
1462 1462 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length,
1463 1463 "User #{user_id} appears more or less than once"
1464 1464 end
1465 1465 end
1466 1466
1467 1467 context "with issue_group_assignment" do
1468 1468 should "include groups" do
1469 1469 issue = Issue.new(:project => Project.find(2))
1470 1470
1471 1471 with_settings :issue_group_assignment => '1' do
1472 1472 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1473 1473 assert issue.assignable_users.include?(Group.find(11))
1474 1474 end
1475 1475 end
1476 1476 end
1477 1477
1478 1478 context "without issue_group_assignment" do
1479 1479 should "not include groups" do
1480 1480 issue = Issue.new(:project => Project.find(2))
1481 1481
1482 1482 with_settings :issue_group_assignment => '0' do
1483 1483 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1484 1484 assert !issue.assignable_users.include?(Group.find(11))
1485 1485 end
1486 1486 end
1487 1487 end
1488 1488 end
1489 1489
1490 1490 def test_create_should_send_email_notification
1491 1491 ActionMailer::Base.deliveries.clear
1492 1492 issue = Issue.new(:project_id => 1, :tracker_id => 1,
1493 1493 :author_id => 3, :status_id => 1,
1494 1494 :priority => IssuePriority.all.first,
1495 1495 :subject => 'test_create', :estimated_hours => '1:30')
1496 1496
1497 1497 assert issue.save
1498 1498 assert_equal 1, ActionMailer::Base.deliveries.size
1499 1499 end
1500 1500
1501 1501 def test_stale_issue_should_not_send_email_notification
1502 1502 ActionMailer::Base.deliveries.clear
1503 1503 issue = Issue.find(1)
1504 1504 stale = Issue.find(1)
1505 1505
1506 1506 issue.init_journal(User.find(1))
1507 1507 issue.subject = 'Subjet update'
1508 1508 assert issue.save
1509 1509 assert_equal 1, ActionMailer::Base.deliveries.size
1510 1510 ActionMailer::Base.deliveries.clear
1511 1511
1512 1512 stale.init_journal(User.find(1))
1513 1513 stale.subject = 'Another subjet update'
1514 1514 assert_raise ActiveRecord::StaleObjectError do
1515 1515 stale.save
1516 1516 end
1517 1517 assert ActionMailer::Base.deliveries.empty?
1518 1518 end
1519 1519
1520 1520 def test_journalized_description
1521 1521 IssueCustomField.delete_all
1522 1522
1523 1523 i = Issue.first
1524 1524 old_description = i.description
1525 1525 new_description = "This is the new description"
1526 1526
1527 1527 i.init_journal(User.find(2))
1528 1528 i.description = new_description
1529 1529 assert_difference 'Journal.count', 1 do
1530 1530 assert_difference 'JournalDetail.count', 1 do
1531 1531 i.save!
1532 1532 end
1533 1533 end
1534 1534
1535 1535 detail = JournalDetail.first(:order => 'id DESC')
1536 1536 assert_equal i, detail.journal.journalized
1537 1537 assert_equal 'attr', detail.property
1538 1538 assert_equal 'description', detail.prop_key
1539 1539 assert_equal old_description, detail.old_value
1540 1540 assert_equal new_description, detail.value
1541 1541 end
1542 1542
1543 1543 def test_blank_descriptions_should_not_be_journalized
1544 1544 IssueCustomField.delete_all
1545 1545 Issue.update_all("description = NULL", "id=1")
1546 1546
1547 1547 i = Issue.find(1)
1548 1548 i.init_journal(User.find(2))
1549 1549 i.subject = "blank description"
1550 1550 i.description = "\r\n"
1551 1551
1552 1552 assert_difference 'Journal.count', 1 do
1553 1553 assert_difference 'JournalDetail.count', 1 do
1554 1554 i.save!
1555 1555 end
1556 1556 end
1557 1557 end
1558 1558
1559 1559 def test_journalized_multi_custom_field
1560 1560 field = IssueCustomField.create!(:name => 'filter', :field_format => 'list',
1561 1561 :is_filter => true, :is_for_all => true,
1562 1562 :tracker_ids => [1],
1563 1563 :possible_values => ['value1', 'value2', 'value3'],
1564 1564 :multiple => true)
1565 1565
1566 1566 issue = Issue.create!(:project_id => 1, :tracker_id => 1,
1567 1567 :subject => 'Test', :author_id => 1)
1568 1568
1569 1569 assert_difference 'Journal.count' do
1570 1570 assert_difference 'JournalDetail.count' do
1571 1571 issue.init_journal(User.first)
1572 1572 issue.custom_field_values = {field.id => ['value1']}
1573 1573 issue.save!
1574 1574 end
1575 1575 assert_difference 'JournalDetail.count' do
1576 1576 issue.init_journal(User.first)
1577 1577 issue.custom_field_values = {field.id => ['value1', 'value2']}
1578 1578 issue.save!
1579 1579 end
1580 1580 assert_difference 'JournalDetail.count', 2 do
1581 1581 issue.init_journal(User.first)
1582 1582 issue.custom_field_values = {field.id => ['value3', 'value2']}
1583 1583 issue.save!
1584 1584 end
1585 1585 assert_difference 'JournalDetail.count', 2 do
1586 1586 issue.init_journal(User.first)
1587 1587 issue.custom_field_values = {field.id => nil}
1588 1588 issue.save!
1589 1589 end
1590 1590 end
1591 1591 end
1592 1592
1593 1593 def test_description_eol_should_be_normalized
1594 1594 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
1595 1595 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
1596 1596 end
1597 1597
1598 1598 def test_saving_twice_should_not_duplicate_journal_details
1599 1599 i = Issue.find(:first)
1600 1600 i.init_journal(User.find(2), 'Some notes')
1601 1601 # initial changes
1602 1602 i.subject = 'New subject'
1603 1603 i.done_ratio = i.done_ratio + 10
1604 1604 assert_difference 'Journal.count' do
1605 1605 assert i.save
1606 1606 end
1607 1607 # 1 more change
1608 1608 i.priority = IssuePriority.find(:first, :conditions => ["id <> ?", i.priority_id])
1609 1609 assert_no_difference 'Journal.count' do
1610 1610 assert_difference 'JournalDetail.count', 1 do
1611 1611 i.save
1612 1612 end
1613 1613 end
1614 1614 # no more change
1615 1615 assert_no_difference 'Journal.count' do
1616 1616 assert_no_difference 'JournalDetail.count' do
1617 1617 i.save
1618 1618 end
1619 1619 end
1620 1620 end
1621 1621
1622 1622 def test_all_dependent_issues
1623 1623 IssueRelation.delete_all
1624 1624 assert IssueRelation.create!(:issue_from => Issue.find(1),
1625 1625 :issue_to => Issue.find(2),
1626 1626 :relation_type => IssueRelation::TYPE_PRECEDES)
1627 1627 assert IssueRelation.create!(:issue_from => Issue.find(2),
1628 1628 :issue_to => Issue.find(3),
1629 1629 :relation_type => IssueRelation::TYPE_PRECEDES)
1630 1630 assert IssueRelation.create!(:issue_from => Issue.find(3),
1631 1631 :issue_to => Issue.find(8),
1632 1632 :relation_type => IssueRelation::TYPE_PRECEDES)
1633 1633
1634 1634 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1635 1635 end
1636 1636
1637 1637 def test_all_dependent_issues_with_persistent_circular_dependency
1638 1638 IssueRelation.delete_all
1639 1639 assert IssueRelation.create!(:issue_from => Issue.find(1),
1640 1640 :issue_to => Issue.find(2),
1641 1641 :relation_type => IssueRelation::TYPE_PRECEDES)
1642 1642 assert IssueRelation.create!(:issue_from => Issue.find(2),
1643 1643 :issue_to => Issue.find(3),
1644 1644 :relation_type => IssueRelation::TYPE_PRECEDES)
1645 1645
1646 1646 r = IssueRelation.create!(:issue_from => Issue.find(3),
1647 1647 :issue_to => Issue.find(7),
1648 1648 :relation_type => IssueRelation::TYPE_PRECEDES)
1649 1649 IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
1650 1650
1651 1651 assert_equal [2, 3], Issue.find(1).all_dependent_issues.collect(&:id).sort
1652 1652 end
1653 1653
1654 1654 def test_all_dependent_issues_with_persistent_multiple_circular_dependencies
1655 1655 IssueRelation.delete_all
1656 1656 assert IssueRelation.create!(:issue_from => Issue.find(1),
1657 1657 :issue_to => Issue.find(2),
1658 1658 :relation_type => IssueRelation::TYPE_RELATES)
1659 1659 assert IssueRelation.create!(:issue_from => Issue.find(2),
1660 1660 :issue_to => Issue.find(3),
1661 1661 :relation_type => IssueRelation::TYPE_RELATES)
1662 1662 assert IssueRelation.create!(:issue_from => Issue.find(3),
1663 1663 :issue_to => Issue.find(8),
1664 1664 :relation_type => IssueRelation::TYPE_RELATES)
1665 1665
1666 1666 r = IssueRelation.create!(:issue_from => Issue.find(8),
1667 1667 :issue_to => Issue.find(7),
1668 1668 :relation_type => IssueRelation::TYPE_RELATES)
1669 1669 IssueRelation.update_all("issue_to_id = 2", ["id = ?", r.id])
1670 1670
1671 1671 r = IssueRelation.create!(:issue_from => Issue.find(3),
1672 1672 :issue_to => Issue.find(7),
1673 1673 :relation_type => IssueRelation::TYPE_RELATES)
1674 1674 IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
1675 1675
1676 1676 assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1677 1677 end
1678 1678
1679 1679 context "#done_ratio" do
1680 1680 setup do
1681 1681 @issue = Issue.find(1)
1682 1682 @issue_status = IssueStatus.find(1)
1683 1683 @issue_status.update_attribute(:default_done_ratio, 50)
1684 1684 @issue2 = Issue.find(2)
1685 1685 @issue_status2 = IssueStatus.find(2)
1686 1686 @issue_status2.update_attribute(:default_done_ratio, 0)
1687 1687 end
1688 1688
1689 1689 teardown do
1690 1690 Setting.issue_done_ratio = 'issue_field'
1691 1691 end
1692 1692
1693 1693 context "with Setting.issue_done_ratio using the issue_field" do
1694 1694 setup do
1695 1695 Setting.issue_done_ratio = 'issue_field'
1696 1696 end
1697 1697
1698 1698 should "read the issue's field" do
1699 1699 assert_equal 0, @issue.done_ratio
1700 1700 assert_equal 30, @issue2.done_ratio
1701 1701 end
1702 1702 end
1703 1703
1704 1704 context "with Setting.issue_done_ratio using the issue_status" do
1705 1705 setup do
1706 1706 Setting.issue_done_ratio = 'issue_status'
1707 1707 end
1708 1708
1709 1709 should "read the Issue Status's default done ratio" do
1710 1710 assert_equal 50, @issue.done_ratio
1711 1711 assert_equal 0, @issue2.done_ratio
1712 1712 end
1713 1713 end
1714 1714 end
1715 1715
1716 1716 context "#update_done_ratio_from_issue_status" do
1717 1717 setup do
1718 1718 @issue = Issue.find(1)
1719 1719 @issue_status = IssueStatus.find(1)
1720 1720 @issue_status.update_attribute(:default_done_ratio, 50)
1721 1721 @issue2 = Issue.find(2)
1722 1722 @issue_status2 = IssueStatus.find(2)
1723 1723 @issue_status2.update_attribute(:default_done_ratio, 0)
1724 1724 end
1725 1725
1726 1726 context "with Setting.issue_done_ratio using the issue_field" do
1727 1727 setup do
1728 1728 Setting.issue_done_ratio = 'issue_field'
1729 1729 end
1730 1730
1731 1731 should "not change the issue" do
1732 1732 @issue.update_done_ratio_from_issue_status
1733 1733 @issue2.update_done_ratio_from_issue_status
1734 1734
1735 1735 assert_equal 0, @issue.read_attribute(:done_ratio)
1736 1736 assert_equal 30, @issue2.read_attribute(:done_ratio)
1737 1737 end
1738 1738 end
1739 1739
1740 1740 context "with Setting.issue_done_ratio using the issue_status" do
1741 1741 setup do
1742 1742 Setting.issue_done_ratio = 'issue_status'
1743 1743 end
1744 1744
1745 1745 should "change the issue's done ratio" do
1746 1746 @issue.update_done_ratio_from_issue_status
1747 1747 @issue2.update_done_ratio_from_issue_status
1748 1748
1749 1749 assert_equal 50, @issue.read_attribute(:done_ratio)
1750 1750 assert_equal 0, @issue2.read_attribute(:done_ratio)
1751 1751 end
1752 1752 end
1753 1753 end
1754 1754
1755 1755 test "#by_tracker" do
1756 1756 User.current = User.anonymous
1757 1757 groups = Issue.by_tracker(Project.find(1))
1758 1758 assert_equal 3, groups.size
1759 1759 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1760 1760 end
1761 1761
1762 1762 test "#by_version" do
1763 1763 User.current = User.anonymous
1764 1764 groups = Issue.by_version(Project.find(1))
1765 1765 assert_equal 3, groups.size
1766 1766 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1767 1767 end
1768 1768
1769 1769 test "#by_priority" do
1770 1770 User.current = User.anonymous
1771 1771 groups = Issue.by_priority(Project.find(1))
1772 1772 assert_equal 4, groups.size
1773 1773 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1774 1774 end
1775 1775
1776 1776 test "#by_category" do
1777 1777 User.current = User.anonymous
1778 1778 groups = Issue.by_category(Project.find(1))
1779 1779 assert_equal 2, groups.size
1780 1780 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1781 1781 end
1782 1782
1783 1783 test "#by_assigned_to" do
1784 1784 User.current = User.anonymous
1785 1785 groups = Issue.by_assigned_to(Project.find(1))
1786 1786 assert_equal 2, groups.size
1787 1787 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1788 1788 end
1789 1789
1790 1790 test "#by_author" do
1791 1791 User.current = User.anonymous
1792 1792 groups = Issue.by_author(Project.find(1))
1793 1793 assert_equal 4, groups.size
1794 1794 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1795 1795 end
1796 1796
1797 1797 test "#by_subproject" do
1798 1798 User.current = User.anonymous
1799 1799 groups = Issue.by_subproject(Project.find(1))
1800 1800 # Private descendant not visible
1801 1801 assert_equal 1, groups.size
1802 1802 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1803 1803 end
1804 1804
1805 1805 def test_recently_updated_scope
1806 1806 #should return the last updated issue
1807 1807 assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
1808 1808 end
1809 1809
1810 1810 def test_on_active_projects_scope
1811 1811 assert Project.find(2).archive
1812 1812
1813 1813 before = Issue.on_active_project.length
1814 1814 # test inclusion to results
1815 1815 issue = Issue.generate!(:tracker => Project.find(2).trackers.first)
1816 1816 assert_equal before + 1, Issue.on_active_project.length
1817 1817
1818 1818 # Move to an archived project
1819 1819 issue.project = Project.find(2)
1820 1820 assert issue.save
1821 1821 assert_equal before, Issue.on_active_project.length
1822 1822 end
1823 1823
1824 1824 context "Issue#recipients" do
1825 1825 setup do
1826 1826 @project = Project.find(1)
1827 1827 @author = User.generate!
1828 1828 @assignee = User.generate!
1829 1829 @issue = Issue.generate!(:project => @project, :assigned_to => @assignee, :author => @author)
1830 1830 end
1831 1831
1832 1832 should "include project recipients" do
1833 1833 assert @project.recipients.present?
1834 1834 @project.recipients.each do |project_recipient|
1835 1835 assert @issue.recipients.include?(project_recipient)
1836 1836 end
1837 1837 end
1838 1838
1839 1839 should "include the author if the author is active" do
1840 1840 assert @issue.author, "No author set for Issue"
1841 1841 assert @issue.recipients.include?(@issue.author.mail)
1842 1842 end
1843 1843
1844 1844 should "include the assigned to user if the assigned to user is active" do
1845 1845 assert @issue.assigned_to, "No assigned_to set for Issue"
1846 1846 assert @issue.recipients.include?(@issue.assigned_to.mail)
1847 1847 end
1848 1848
1849 1849 should "not include users who opt out of all email" do
1850 1850 @author.update_attribute(:mail_notification, :none)
1851 1851
1852 1852 assert !@issue.recipients.include?(@issue.author.mail)
1853 1853 end
1854 1854
1855 1855 should "not include the issue author if they are only notified of assigned issues" do
1856 1856 @author.update_attribute(:mail_notification, :only_assigned)
1857 1857
1858 1858 assert !@issue.recipients.include?(@issue.author.mail)
1859 1859 end
1860 1860
1861 1861 should "not include the assigned user if they are only notified of owned issues" do
1862 1862 @assignee.update_attribute(:mail_notification, :only_owner)
1863 1863
1864 1864 assert !@issue.recipients.include?(@issue.assigned_to.mail)
1865 1865 end
1866 1866 end
1867 1867
1868 1868 def test_last_journal_id_with_journals_should_return_the_journal_id
1869 1869 assert_equal 2, Issue.find(1).last_journal_id
1870 1870 end
1871 1871
1872 1872 def test_last_journal_id_without_journals_should_return_nil
1873 1873 assert_nil Issue.find(3).last_journal_id
1874 1874 end
1875 1875
1876 1876 def test_journals_after_should_return_journals_with_greater_id
1877 1877 assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
1878 1878 assert_equal [], Issue.find(1).journals_after('2')
1879 1879 end
1880 1880
1881 1881 def test_journals_after_with_blank_arg_should_return_all_journals
1882 1882 assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
1883 1883 end
1884 1884
1885 1885 def test_css_classes_should_include_priority
1886 1886 issue = Issue.new(:priority => IssuePriority.find(8))
1887 1887 classes = issue.css_classes.split(' ')
1888 1888 assert_include 'priority-8', classes
1889 1889 assert_include 'priority-highest', classes
1890 1890 end
1891
1892 def test_save_attachments_with_hash_should_save_attachments_in_keys_order
1893 set_tmp_attachments_directory
1894 issue = Issue.generate!
1895 issue.save_attachments({
1896 'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')},
1897 '3' => {'file' => mock_file_with_options(:original_filename => 'bar')},
1898 '1' => {'file' => mock_file_with_options(:original_filename => 'foo')}
1899 })
1900 issue.attach_saved_attachments
1901
1902 assert_equal 3, issue.reload.attachments.count
1903 assert_equal %w(upload foo bar), issue.attachments.map(&:filename)
1904 end
1891 1905 end
General Comments 0
You need to be logged in to leave comments. Login now