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