To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / .svn / pristine / 4b / 4b6ad4ee97c77f142f2ea07d73404f963b296d82.svn-base @ 1298:4f746d8966dd

History | View | Annotate | Download (71.2 KB)

1
# Redmine - project management software
2
# Copyright (C) 2006-2012  Jean-Philippe Lang
3
#
4
# This program is free software; you can redistribute it and/or
5
# modify it under the terms of the GNU General Public License
6
# as published by the Free Software Foundation; either version 2
7
# of the License, or (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17

    
18
require File.expand_path('../../test_helper', __FILE__)
19

    
20
class IssueTest < ActiveSupport::TestCase
21
  fixtures :projects, :users, :members, :member_roles, :roles,
22
           :groups_users,
23
           :trackers, :projects_trackers,
24
           :enabled_modules,
25
           :versions,
26
           :issue_statuses, :issue_categories, :issue_relations, :workflows,
27
           :enumerations,
28
           :issues, :journals, :journal_details,
29
           :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
30
           :time_entries
31

    
32
  include Redmine::I18n
33

    
34
  def teardown
35
    User.current = nil
36
  end
37

    
38
  def test_create
39
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
40
                      :status_id => 1, :priority => IssuePriority.all.first,
41
                      :subject => 'test_create',
42
                      :description => 'IssueTest#test_create', :estimated_hours => '1:30')
43
    assert issue.save
44
    issue.reload
45
    assert_equal 1.5, issue.estimated_hours
46
  end
47

    
48
  def test_create_minimal
49
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
50
                      :status_id => 1, :priority => IssuePriority.all.first,
51
                      :subject => 'test_create')
52
    assert issue.save
53
    assert issue.description.nil?
54
    assert_nil issue.estimated_hours
55
  end
56

    
57
  def test_start_date_format_should_be_validated
58
    set_language_if_valid 'en'
59
    ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
60
      issue = Issue.new(:start_date => invalid_date)
61
      assert !issue.valid?
62
      assert_include 'Start date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
63
    end
64
  end
65

    
66
  def test_due_date_format_should_be_validated
67
    set_language_if_valid 'en'
68
    ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
69
      issue = Issue.new(:due_date => invalid_date)
70
      assert !issue.valid?
71
      assert_include 'Due date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
72
    end
73
  end
74

    
75
  def test_due_date_lesser_than_start_date_should_not_validate
76
    set_language_if_valid 'en'
77
    issue = Issue.new(:start_date => '2012-10-06', :due_date => '2012-10-02')
78
    assert !issue.valid?
79
    assert_include 'Due date must be greater than start date', issue.errors.full_messages
80
  end
81

    
82
  def test_create_with_required_custom_field
83
    set_language_if_valid 'en'
84
    field = IssueCustomField.find_by_name('Database')
85
    field.update_attribute(:is_required, true)
86

    
87
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
88
                      :status_id => 1, :subject => 'test_create',
89
                      :description => 'IssueTest#test_create_with_required_custom_field')
90
    assert issue.available_custom_fields.include?(field)
91
    # No value for the custom field
92
    assert !issue.save
93
    assert_equal ["Database can't be blank"], issue.errors.full_messages
94
    # Blank value
95
    issue.custom_field_values = { field.id => '' }
96
    assert !issue.save
97
    assert_equal ["Database can't be blank"], issue.errors.full_messages
98
    # Invalid value
99
    issue.custom_field_values = { field.id => 'SQLServer' }
100
    assert !issue.save
101
    assert_equal ["Database is not included in the list"], issue.errors.full_messages
102
    # Valid value
103
    issue.custom_field_values = { field.id => 'PostgreSQL' }
104
    assert issue.save
105
    issue.reload
106
    assert_equal 'PostgreSQL', issue.custom_value_for(field).value
107
  end
108

    
109
  def test_create_with_group_assignment
110
    with_settings :issue_group_assignment => '1' do
111
      assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
112
                       :subject => 'Group assignment',
113
                       :assigned_to_id => 11).save
114
      issue = Issue.first(:order => 'id DESC')
115
      assert_kind_of Group, issue.assigned_to
116
      assert_equal Group.find(11), issue.assigned_to
117
    end
118
  end
119

    
120
  def test_create_with_parent_issue_id
121
    issue = Issue.new(:project_id => 1, :tracker_id => 1,
122
                      :author_id => 1, :subject => 'Group assignment',
123
                      :parent_issue_id => 1)
124
    assert_save issue
125
    assert_equal 1, issue.parent_issue_id
126
    assert_equal Issue.find(1), issue.parent
127
  end
128

    
129
  def test_create_with_sharp_parent_issue_id
130
    issue = Issue.new(:project_id => 1, :tracker_id => 1,
131
                      :author_id => 1, :subject => 'Group assignment',
132
                      :parent_issue_id => "#1")
133
    assert_save issue
134
    assert_equal 1, issue.parent_issue_id
135
    assert_equal Issue.find(1), issue.parent
136
  end
137

    
138
  def test_create_with_invalid_parent_issue_id
139
    set_language_if_valid 'en'
140
    issue = Issue.new(:project_id => 1, :tracker_id => 1,
141
                      :author_id => 1, :subject => 'Group assignment',
142
                      :parent_issue_id => '01ABC')
143
    assert !issue.save
144
    assert_equal '01ABC', issue.parent_issue_id
145
    assert_include 'Parent task is invalid', issue.errors.full_messages
146
  end
147

    
148
  def test_create_with_invalid_sharp_parent_issue_id
149
    set_language_if_valid 'en'
150
    issue = Issue.new(:project_id => 1, :tracker_id => 1,
151
                      :author_id => 1, :subject => 'Group assignment',
152
                      :parent_issue_id => '#01ABC')
153
    assert !issue.save
154
    assert_equal '#01ABC', issue.parent_issue_id
155
    assert_include 'Parent task is invalid', issue.errors.full_messages
156
  end
157

    
158
  def assert_visibility_match(user, issues)
159
    assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
160
  end
161

    
162
  def test_visible_scope_for_anonymous
163
    # Anonymous user should see issues of public projects only
164
    issues = Issue.visible(User.anonymous).all
165
    assert issues.any?
166
    assert_nil issues.detect {|issue| !issue.project.is_public?}
167
    assert_nil issues.detect {|issue| issue.is_private?}
168
    assert_visibility_match User.anonymous, issues
169
  end
170

    
171
  def test_visible_scope_for_anonymous_without_view_issues_permissions
172
    # Anonymous user should not see issues without permission
173
    Role.anonymous.remove_permission!(:view_issues)
174
    issues = Issue.visible(User.anonymous).all
175
    assert issues.empty?
176
    assert_visibility_match User.anonymous, issues
177
  end
178

    
179
  def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
180
    assert Role.anonymous.update_attribute(:issues_visibility, 'default')
181
    issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
182
    assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
183
    assert !issue.visible?(User.anonymous)
184
  end
185

    
186
  def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
187
    assert Role.anonymous.update_attribute(:issues_visibility, 'own')
188
    issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
189
    assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
190
    assert !issue.visible?(User.anonymous)
191
  end
192

    
193
  def test_visible_scope_for_non_member
194
    user = User.find(9)
195
    assert user.projects.empty?
196
    # Non member user should see issues of public projects only
197
    issues = Issue.visible(user).all
198
    assert issues.any?
199
    assert_nil issues.detect {|issue| !issue.project.is_public?}
200
    assert_nil issues.detect {|issue| issue.is_private?}
201
    assert_visibility_match user, issues
202
  end
203

    
204
  def test_visible_scope_for_non_member_with_own_issues_visibility
205
    Role.non_member.update_attribute :issues_visibility, 'own'
206
    Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
207
    user = User.find(9)
208

    
209
    issues = Issue.visible(user).all
210
    assert issues.any?
211
    assert_nil issues.detect {|issue| issue.author != user}
212
    assert_visibility_match user, issues
213
  end
214

    
215
  def test_visible_scope_for_non_member_without_view_issues_permissions
216
    # Non member user should not see issues without permission
217
    Role.non_member.remove_permission!(:view_issues)
218
    user = User.find(9)
219
    assert user.projects.empty?
220
    issues = Issue.visible(user).all
221
    assert issues.empty?
222
    assert_visibility_match user, issues
223
  end
224

    
225
  def test_visible_scope_for_member
226
    user = User.find(9)
227
    # User should see issues of projects for which he has view_issues permissions only
228
    Role.non_member.remove_permission!(:view_issues)
229
    Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
230
    issues = Issue.visible(user).all
231
    assert issues.any?
232
    assert_nil issues.detect {|issue| issue.project_id != 3}
233
    assert_nil issues.detect {|issue| issue.is_private?}
234
    assert_visibility_match user, issues
235
  end
236

    
237
  def test_visible_scope_for_member_with_groups_should_return_assigned_issues
238
    user = User.find(8)
239
    assert user.groups.any?
240
    Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
241
    Role.non_member.remove_permission!(:view_issues)
242
    
243
    issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
244
      :status_id => 1, :priority => IssuePriority.all.first,
245
      :subject => 'Assignment test',
246
      :assigned_to => user.groups.first,
247
      :is_private => true)
248
    
249
    Role.find(2).update_attribute :issues_visibility, 'default'
250
    issues = Issue.visible(User.find(8)).all
251
    assert issues.any?
252
    assert issues.include?(issue)
253
    
254
    Role.find(2).update_attribute :issues_visibility, 'own'
255
    issues = Issue.visible(User.find(8)).all
256
    assert issues.any?
257
    assert issues.include?(issue)
258
  end
259

    
260
  def test_visible_scope_for_admin
261
    user = User.find(1)
262
    user.members.each(&:destroy)
263
    assert user.projects.empty?
264
    issues = Issue.visible(user).all
265
    assert issues.any?
266
    # Admin should see issues on private projects that he does not belong to
267
    assert issues.detect {|issue| !issue.project.is_public?}
268
    # Admin should see private issues of other users
269
    assert issues.detect {|issue| issue.is_private? && issue.author != user}
270
    assert_visibility_match user, issues
271
  end
272

    
273
  def test_visible_scope_with_project
274
    project = Project.find(1)
275
    issues = Issue.visible(User.find(2), :project => project).all
276
    projects = issues.collect(&:project).uniq
277
    assert_equal 1, projects.size
278
    assert_equal project, projects.first
279
  end
280

    
281
  def test_visible_scope_with_project_and_subprojects
282
    project = Project.find(1)
283
    issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).all
284
    projects = issues.collect(&:project).uniq
285
    assert projects.size > 1
286
    assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
287
  end
288

    
289
  def test_visible_and_nested_set_scopes
290
    assert_equal 0, Issue.find(1).descendants.visible.all.size
291
  end
292

    
293
  def test_open_scope
294
    issues = Issue.open.all
295
    assert_nil issues.detect(&:closed?)
296
  end
297

    
298
  def test_open_scope_with_arg
299
    issues = Issue.open(false).all
300
    assert_equal issues, issues.select(&:closed?)
301
  end
302

    
303
  def test_errors_full_messages_should_include_custom_fields_errors
304
    field = IssueCustomField.find_by_name('Database')
305

    
306
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
307
                      :status_id => 1, :subject => 'test_create',
308
                      :description => 'IssueTest#test_create_with_required_custom_field')
309
    assert issue.available_custom_fields.include?(field)
310
    # Invalid value
311
    issue.custom_field_values = { field.id => 'SQLServer' }
312

    
313
    assert !issue.valid?
314
    assert_equal 1, issue.errors.full_messages.size
315
    assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
316
                 issue.errors.full_messages.first
317
  end
318

    
319
  def test_update_issue_with_required_custom_field
320
    field = IssueCustomField.find_by_name('Database')
321
    field.update_attribute(:is_required, true)
322

    
323
    issue = Issue.find(1)
324
    assert_nil issue.custom_value_for(field)
325
    assert issue.available_custom_fields.include?(field)
326
    # No change to custom values, issue can be saved
327
    assert issue.save
328
    # Blank value
329
    issue.custom_field_values = { field.id => '' }
330
    assert !issue.save
331
    # Valid value
332
    issue.custom_field_values = { field.id => 'PostgreSQL' }
333
    assert issue.save
334
    issue.reload
335
    assert_equal 'PostgreSQL', issue.custom_value_for(field).value
336
  end
337

    
338
  def test_should_not_update_attributes_if_custom_fields_validation_fails
339
    issue = Issue.find(1)
340
    field = IssueCustomField.find_by_name('Database')
341
    assert issue.available_custom_fields.include?(field)
342

    
343
    issue.custom_field_values = { field.id => 'Invalid' }
344
    issue.subject = 'Should be not be saved'
345
    assert !issue.save
346

    
347
    issue.reload
348
    assert_equal "Can't print recipes", issue.subject
349
  end
350

    
351
  def test_should_not_recreate_custom_values_objects_on_update
352
    field = IssueCustomField.find_by_name('Database')
353

    
354
    issue = Issue.find(1)
355
    issue.custom_field_values = { field.id => 'PostgreSQL' }
356
    assert issue.save
357
    custom_value = issue.custom_value_for(field)
358
    issue.reload
359
    issue.custom_field_values = { field.id => 'MySQL' }
360
    assert issue.save
361
    issue.reload
362
    assert_equal custom_value.id, issue.custom_value_for(field).id
363
  end
364

    
365
  def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
366
    issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1,
367
                          :status_id => 1, :subject => 'Test',
368
                          :custom_field_values => {'2' => 'Test'})
369
    assert !Tracker.find(2).custom_field_ids.include?(2)
370

    
371
    issue = Issue.find(issue.id)
372
    issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
373

    
374
    issue = Issue.find(issue.id)
375
    custom_value = issue.custom_value_for(2)
376
    assert_not_nil custom_value
377
    assert_equal 'Test', custom_value.value
378
  end
379

    
380
  def test_assigning_tracker_id_should_reload_custom_fields_values
381
    issue = Issue.new(:project => Project.find(1))
382
    assert issue.custom_field_values.empty?
383
    issue.tracker_id = 1
384
    assert issue.custom_field_values.any?
385
  end
386

    
387
  def test_assigning_attributes_should_assign_project_and_tracker_first
388
    seq = sequence('seq')
389
    issue = Issue.new
390
    issue.expects(:project_id=).in_sequence(seq)
391
    issue.expects(:tracker_id=).in_sequence(seq)
392
    issue.expects(:subject=).in_sequence(seq)
393
    issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
394
  end
395

    
396
  def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
397
    attributes = ActiveSupport::OrderedHash.new
398
    attributes['custom_field_values'] = { '1' => 'MySQL' }
399
    attributes['tracker_id'] = '1'
400
    issue = Issue.new(:project => Project.find(1))
401
    issue.attributes = attributes
402
    assert_equal 'MySQL', issue.custom_field_value(1)
403
  end
404

    
405
  def test_should_update_issue_with_disabled_tracker
406
    p = Project.find(1)
407
    issue = Issue.find(1)
408

    
409
    p.trackers.delete(issue.tracker)
410
    assert !p.trackers.include?(issue.tracker)
411

    
412
    issue.reload
413
    issue.subject = 'New subject'
414
    assert issue.save
415
  end
416

    
417
  def test_should_not_set_a_disabled_tracker
418
    p = Project.find(1)
419
    p.trackers.delete(Tracker.find(2))
420

    
421
    issue = Issue.find(1)
422
    issue.tracker_id = 2
423
    issue.subject = 'New subject'
424
    assert !issue.save
425
    assert_not_nil issue.errors[:tracker_id]
426
  end
427

    
428
  def test_category_based_assignment
429
    issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
430
                         :status_id => 1, :priority => IssuePriority.all.first,
431
                         :subject => 'Assignment test',
432
                         :description => 'Assignment test', :category_id => 1)
433
    assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
434
  end
435

    
436
  def test_new_statuses_allowed_to
437
    WorkflowTransition.delete_all
438
    WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
439
                               :old_status_id => 1, :new_status_id => 2,
440
                               :author => false, :assignee => false)
441
    WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
442
                               :old_status_id => 1, :new_status_id => 3,
443
                               :author => true, :assignee => false)
444
    WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1,
445
                               :new_status_id => 4, :author => false,
446
                               :assignee => true)
447
    WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
448
                               :old_status_id => 1, :new_status_id => 5,
449
                               :author => true, :assignee => true)
450
    status = IssueStatus.find(1)
451
    role = Role.find(1)
452
    tracker = Tracker.find(1)
453
    user = User.find(2)
454

    
455
    issue = Issue.generate!(:tracker => tracker, :status => status,
456
                            :project_id => 1, :author_id => 1)
457
    assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
458

    
459
    issue = Issue.generate!(:tracker => tracker, :status => status,
460
                            :project_id => 1, :author => user)
461
    assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
462

    
463
    issue = Issue.generate!(:tracker => tracker, :status => status,
464
                            :project_id => 1, :author_id => 1,
465
                            :assigned_to => user)
466
    assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
467

    
468
    issue = Issue.generate!(:tracker => tracker, :status => status,
469
                            :project_id => 1, :author => user,
470
                            :assigned_to => user)
471
    assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
472
  end
473

    
474
  def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
475
    admin = User.find(1)
476
    issue = Issue.find(1)
477
    assert !admin.member_of?(issue.project)
478
    expected_statuses = [issue.status] + 
479
                            WorkflowTransition.find_all_by_old_status_id(
480
                                issue.status_id).map(&:new_status).uniq.sort
481
    assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
482
  end
483

    
484
  def test_new_statuses_allowed_to_should_return_default_and_current_status_when_copying
485
    issue = Issue.find(1).copy
486
    assert_equal [1], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
487

    
488
    issue = Issue.find(2).copy
489
    assert_equal [1, 2], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
490
  end
491

    
492
  def test_safe_attributes_names_should_not_include_disabled_field
493
    tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
494

    
495
    issue = Issue.new(:tracker => tracker)
496
    assert_include 'tracker_id', issue.safe_attribute_names
497
    assert_include 'status_id', issue.safe_attribute_names
498
    assert_include 'subject', issue.safe_attribute_names
499
    assert_include 'description', issue.safe_attribute_names
500
    assert_include 'custom_field_values', issue.safe_attribute_names
501
    assert_include 'custom_fields', issue.safe_attribute_names
502
    assert_include 'lock_version', issue.safe_attribute_names
503

    
504
    tracker.core_fields.each do |field|
505
      assert_include field, issue.safe_attribute_names
506
    end
507

    
508
    tracker.disabled_core_fields.each do |field|
509
      assert_not_include field, issue.safe_attribute_names
510
    end
511
  end
512

    
513
  def test_safe_attributes_should_ignore_disabled_fields
514
    tracker = Tracker.find(1)
515
    tracker.core_fields = %w(assigned_to_id due_date)
516
    tracker.save!
517

    
518
    issue = Issue.new(:tracker => tracker)
519
    issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
520
    assert_nil issue.start_date
521
    assert_equal Date.parse('2012-07-14'), issue.due_date
522
  end
523

    
524
  def test_safe_attributes_should_accept_target_tracker_enabled_fields
525
    source = Tracker.find(1)
526
    source.core_fields = []
527
    source.save!
528
    target = Tracker.find(2)
529
    target.core_fields = %w(assigned_to_id due_date)
530
    target.save!
531

    
532
    issue = Issue.new(:tracker => source)
533
    issue.safe_attributes = {'tracker_id' => 2, 'due_date' => '2012-07-14'}
534
    assert_equal target, issue.tracker
535
    assert_equal Date.parse('2012-07-14'), issue.due_date
536
  end
537

    
538
  def test_safe_attributes_should_not_include_readonly_fields
539
    WorkflowPermission.delete_all
540
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
541
                               :role_id => 1, :field_name => 'due_date',
542
                               :rule => 'readonly')
543
    user = User.find(2)
544

    
545
    issue = Issue.new(:project_id => 1, :tracker_id => 1)
546
    assert_equal %w(due_date), issue.read_only_attribute_names(user)
547
    assert_not_include 'due_date', issue.safe_attribute_names(user)
548

    
549
    issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
550
    assert_equal Date.parse('2012-07-14'), issue.start_date
551
    assert_nil issue.due_date
552
  end
553

    
554
  def test_safe_attributes_should_not_include_readonly_custom_fields
555
    cf1 = IssueCustomField.create!(:name => 'Writable field',
556
                                   :field_format => 'string',
557
                                   :is_for_all => true, :tracker_ids => [1])
558
    cf2 = IssueCustomField.create!(:name => 'Readonly field',
559
                                   :field_format => 'string',
560
                                   :is_for_all => true, :tracker_ids => [1])
561
    WorkflowPermission.delete_all
562
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
563
                               :role_id => 1, :field_name => cf2.id.to_s,
564
                               :rule => 'readonly')
565
    user = User.find(2)
566
    issue = Issue.new(:project_id => 1, :tracker_id => 1)
567
    assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
568
    assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
569

    
570
    issue.send :safe_attributes=, {'custom_field_values' => {
571
                                       cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'
572
                                     }}, user
573
    assert_equal 'value1', issue.custom_field_value(cf1)
574
    assert_nil issue.custom_field_value(cf2)
575

    
576
    issue.send :safe_attributes=, {'custom_fields' => [
577
                                      {'id' => cf1.id.to_s, 'value' => 'valuea'},
578
                                      {'id' => cf2.id.to_s, 'value' => 'valueb'}
579
                                    ]}, user
580
    assert_equal 'valuea', issue.custom_field_value(cf1)
581
    assert_nil issue.custom_field_value(cf2)
582
  end
583

    
584
  def test_editable_custom_field_values_should_return_non_readonly_custom_values
585
    cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string',
586
                                   :is_for_all => true, :tracker_ids => [1, 2])
587
    cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string',
588
                                   :is_for_all => true, :tracker_ids => [1, 2])
589
    WorkflowPermission.delete_all
590
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
591
                               :field_name => cf2.id.to_s, :rule => 'readonly')
592
    user = User.find(2)
593

    
594
    issue = Issue.new(:project_id => 1, :tracker_id => 1)
595
    values = issue.editable_custom_field_values(user)
596
    assert values.detect {|value| value.custom_field == cf1}
597
    assert_nil values.detect {|value| value.custom_field == cf2}
598

    
599
    issue.tracker_id = 2
600
    values = issue.editable_custom_field_values(user)
601
    assert values.detect {|value| value.custom_field == cf1}
602
    assert values.detect {|value| value.custom_field == cf2}
603
  end
604

    
605
  def test_safe_attributes_should_accept_target_tracker_writable_fields
606
    WorkflowPermission.delete_all
607
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
608
                               :role_id => 1, :field_name => 'due_date',
609
                               :rule => 'readonly')
610
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
611
                               :role_id => 1, :field_name => 'start_date',
612
                               :rule => 'readonly')
613
    user = User.find(2)
614

    
615
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
616

    
617
    issue.send :safe_attributes=, {'start_date' => '2012-07-12',
618
                                   'due_date' => '2012-07-14'}, user
619
    assert_equal Date.parse('2012-07-12'), issue.start_date
620
    assert_nil issue.due_date
621

    
622
    issue.send :safe_attributes=, {'start_date' => '2012-07-15',
623
                                   'due_date' => '2012-07-16',
624
                                   'tracker_id' => 2}, user
625
    assert_equal Date.parse('2012-07-12'), issue.start_date
626
    assert_equal Date.parse('2012-07-16'), issue.due_date
627
  end
628

    
629
  def test_safe_attributes_should_accept_target_status_writable_fields
630
    WorkflowPermission.delete_all
631
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
632
                               :role_id => 1, :field_name => 'due_date',
633
                               :rule => 'readonly')
634
    WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1,
635
                               :role_id => 1, :field_name => 'start_date',
636
                               :rule => 'readonly')
637
    user = User.find(2)
638

    
639
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
640

    
641
    issue.send :safe_attributes=, {'start_date' => '2012-07-12',
642
                                   'due_date' => '2012-07-14'},
643
                                   user
644
    assert_equal Date.parse('2012-07-12'), issue.start_date
645
    assert_nil issue.due_date
646

    
647
    issue.send :safe_attributes=, {'start_date' => '2012-07-15',
648
                                    'due_date' => '2012-07-16',
649
                                    'status_id' => 2},
650
                                  user
651
    assert_equal Date.parse('2012-07-12'), issue.start_date
652
    assert_equal Date.parse('2012-07-16'), issue.due_date
653
  end
654

    
655
  def test_required_attributes_should_be_validated
656
    cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
657
                                  :is_for_all => true, :tracker_ids => [1, 2])
658

    
659
    WorkflowPermission.delete_all
660
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
661
                               :role_id => 1, :field_name => 'due_date',
662
                               :rule => 'required')
663
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
664
                               :role_id => 1, :field_name => 'category_id',
665
                               :rule => 'required')
666
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
667
                               :role_id => 1, :field_name => cf.id.to_s,
668
                               :rule => 'required')
669

    
670
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
671
                               :role_id => 1, :field_name => 'start_date',
672
                               :rule => 'required')
673
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
674
                               :role_id => 1, :field_name => cf.id.to_s,
675
                               :rule => 'required')
676
    user = User.find(2)
677

    
678
    issue = Issue.new(:project_id => 1, :tracker_id => 1,
679
                      :status_id => 1, :subject => 'Required fields',
680
                      :author => user)
681
    assert_equal [cf.id.to_s, "category_id", "due_date"],
682
                 issue.required_attribute_names(user).sort
683
    assert !issue.save, "Issue was saved"
684
    assert_equal ["Category can't be blank", "Due date can't be blank", "Foo can't be blank"],
685
                  issue.errors.full_messages.sort
686

    
687
    issue.tracker_id = 2
688
    assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
689
    assert !issue.save, "Issue was saved"
690
    assert_equal ["Foo can't be blank", "Start date can't be blank"],
691
                 issue.errors.full_messages.sort
692

    
693
    issue.start_date = Date.today
694
    issue.custom_field_values = {cf.id.to_s => 'bar'}
695
    assert issue.save
696
  end
697

    
698
  def test_required_attribute_names_for_multiple_roles_should_intersect_rules
699
    WorkflowPermission.delete_all
700
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
701
                               :role_id => 1, :field_name => 'due_date',
702
                               :rule => 'required')
703
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
704
                               :role_id => 1, :field_name => 'start_date',
705
                               :rule => 'required')
706
    user = User.find(2)
707
    member = Member.find(1)
708
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
709

    
710
    assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
711

    
712
    member.role_ids = [1, 2]
713
    member.save!
714
    assert_equal [], issue.required_attribute_names(user.reload)
715

    
716
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
717
                               :role_id => 2, :field_name => 'due_date',
718
                               :rule => 'required')
719
    assert_equal %w(due_date), issue.required_attribute_names(user)
720

    
721
    member.role_ids = [1, 2, 3]
722
    member.save!
723
    assert_equal [], issue.required_attribute_names(user.reload)
724

    
725
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
726
                               :role_id => 2, :field_name => 'due_date',
727
                               :rule => 'readonly')
728
    # required + readonly => required
729
    assert_equal %w(due_date), issue.required_attribute_names(user)
730
  end
731

    
732
  def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
733
    WorkflowPermission.delete_all
734
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
735
                               :role_id => 1, :field_name => 'due_date',
736
                               :rule => 'readonly')
737
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
738
                               :role_id => 1, :field_name => 'start_date',
739
                               :rule => 'readonly')
740
    user = User.find(2)
741
    member = Member.find(1)
742
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
743

    
744
    assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
745

    
746
    member.role_ids = [1, 2]
747
    member.save!
748
    assert_equal [], issue.read_only_attribute_names(user.reload)
749

    
750
    WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
751
                              :role_id => 2, :field_name => 'due_date',
752
                              :rule => 'readonly')
753
    assert_equal %w(due_date), issue.read_only_attribute_names(user)
754
  end
755

    
756
  def test_copy
757
    issue = Issue.new.copy_from(1)
758
    assert issue.copy?
759
    assert issue.save
760
    issue.reload
761
    orig = Issue.find(1)
762
    assert_equal orig.subject, issue.subject
763
    assert_equal orig.tracker, issue.tracker
764
    assert_equal "125", issue.custom_value_for(2).value
765
  end
766

    
767
  def test_copy_should_copy_status
768
    orig = Issue.find(8)
769
    assert orig.status != IssueStatus.default
770

    
771
    issue = Issue.new.copy_from(orig)
772
    assert issue.save
773
    issue.reload
774
    assert_equal orig.status, issue.status
775
  end
776

    
777
  def test_copy_should_add_relation_with_copied_issue
778
    copied = Issue.find(1)
779
    issue = Issue.new.copy_from(copied)
780
    assert issue.save
781
    issue.reload
782

    
783
    assert_equal 1, issue.relations.size
784
    relation = issue.relations.first
785
    assert_equal 'copied_to', relation.relation_type
786
    assert_equal copied, relation.issue_from
787
    assert_equal issue, relation.issue_to
788
  end
789

    
790
  def test_copy_should_copy_subtasks
791
    issue = Issue.generate_with_descendants!
792

    
793
    copy = issue.reload.copy
794
    copy.author = User.find(7)
795
    assert_difference 'Issue.count', 1+issue.descendants.count do
796
      assert copy.save
797
    end
798
    copy.reload
799
    assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
800
    child_copy = copy.children.detect {|c| c.subject == 'Child1'}
801
    assert_equal %w(Child11), child_copy.children.map(&:subject).sort
802
    assert_equal copy.author, child_copy.author
803
  end
804

    
805
  def test_copy_should_copy_subtasks_to_target_project
806
    issue = Issue.generate_with_descendants!
807

    
808
    copy = issue.copy(:project_id => 3)
809
    assert_difference 'Issue.count', 1+issue.descendants.count do
810
      assert copy.save
811
    end
812
    assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
813
  end
814

    
815
  def test_copy_should_not_copy_subtasks_twice_when_saving_twice
816
    issue = Issue.generate_with_descendants!
817

    
818
    copy = issue.reload.copy
819
    assert_difference 'Issue.count', 1+issue.descendants.count do
820
      assert copy.save
821
      assert copy.save
822
    end
823
  end
824

    
825
  def test_should_not_call_after_project_change_on_creation
826
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
827
                      :subject => 'Test', :author_id => 1)
828
    issue.expects(:after_project_change).never
829
    issue.save!
830
  end
831

    
832
  def test_should_not_call_after_project_change_on_update
833
    issue = Issue.find(1)
834
    issue.project = Project.find(1)
835
    issue.subject = 'No project change'
836
    issue.expects(:after_project_change).never
837
    issue.save!
838
  end
839

    
840
  def test_should_call_after_project_change_on_project_change
841
    issue = Issue.find(1)
842
    issue.project = Project.find(2)
843
    issue.expects(:after_project_change).once
844
    issue.save!
845
  end
846

    
847
  def test_adding_journal_should_update_timestamp
848
    issue = Issue.find(1)
849
    updated_on_was = issue.updated_on
850

    
851
    issue.init_journal(User.first, "Adding notes")
852
    assert_difference 'Journal.count' do
853
      assert issue.save
854
    end
855
    issue.reload
856

    
857
    assert_not_equal updated_on_was, issue.updated_on
858
  end
859

    
860
  def test_should_close_duplicates
861
    # Create 3 issues
862
    issue1 = Issue.generate!
863
    issue2 = Issue.generate!
864
    issue3 = Issue.generate!
865

    
866
    # 2 is a dupe of 1
867
    IssueRelation.create!(:issue_from => issue2, :issue_to => issue1,
868
                          :relation_type => IssueRelation::TYPE_DUPLICATES)
869
    # And 3 is a dupe of 2
870
    IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
871
                          :relation_type => IssueRelation::TYPE_DUPLICATES)
872
    # And 3 is a dupe of 1 (circular duplicates)
873
    IssueRelation.create!(:issue_from => issue3, :issue_to => issue1,
874
                          :relation_type => IssueRelation::TYPE_DUPLICATES)
875

    
876
    assert issue1.reload.duplicates.include?(issue2)
877

    
878
    # Closing issue 1
879
    issue1.init_journal(User.find(:first), "Closing issue1")
880
    issue1.status = IssueStatus.find :first, :conditions => {:is_closed => true}
881
    assert issue1.save
882
    # 2 and 3 should be also closed
883
    assert issue2.reload.closed?
884
    assert issue3.reload.closed?
885
  end
886

    
887
  def test_should_not_close_duplicated_issue
888
    issue1 = Issue.generate!
889
    issue2 = Issue.generate!
890

    
891
    # 2 is a dupe of 1
892
    IssueRelation.create(:issue_from => issue2, :issue_to => issue1,
893
                         :relation_type => IssueRelation::TYPE_DUPLICATES)
894
    # 2 is a dup of 1 but 1 is not a duplicate of 2
895
    assert !issue2.reload.duplicates.include?(issue1)
896

    
897
    # Closing issue 2
898
    issue2.init_journal(User.find(:first), "Closing issue2")
899
    issue2.status = IssueStatus.find :first, :conditions => {:is_closed => true}
900
    assert issue2.save
901
    # 1 should not be also closed
902
    assert !issue1.reload.closed?
903
  end
904

    
905
  def test_assignable_versions
906
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
907
                      :status_id => 1, :fixed_version_id => 1,
908
                      :subject => 'New issue')
909
    assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
910
  end
911

    
912
  def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
913
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
914
                      :status_id => 1, :fixed_version_id => 1,
915
                      :subject => 'New issue')
916
    assert !issue.save
917
    assert_not_nil issue.errors[:fixed_version_id]
918
  end
919

    
920
  def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
921
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
922
                      :status_id => 1, :fixed_version_id => 2,
923
                      :subject => 'New issue')
924
    assert !issue.save
925
    assert_not_nil issue.errors[:fixed_version_id]
926
  end
927

    
928
  def test_should_be_able_to_assign_a_new_issue_to_an_open_version
929
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
930
                      :status_id => 1, :fixed_version_id => 3,
931
                      :subject => 'New issue')
932
    assert issue.save
933
  end
934

    
935
  def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
936
    issue = Issue.find(11)
937
    assert_equal 'closed', issue.fixed_version.status
938
    issue.subject = 'Subject changed'
939
    assert issue.save
940
  end
941

    
942
  def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
943
    issue = Issue.find(11)
944
    issue.status_id = 1
945
    assert !issue.save
946
    assert_not_nil issue.errors[:base]
947
  end
948

    
949
  def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
950
    issue = Issue.find(11)
951
    issue.status_id = 1
952
    issue.fixed_version_id = 3
953
    assert issue.save
954
  end
955

    
956
  def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
957
    issue = Issue.find(12)
958
    assert_equal 'locked', issue.fixed_version.status
959
    issue.status_id = 1
960
    assert issue.save
961
  end
962

    
963
  def test_should_not_be_able_to_keep_unshared_version_when_changing_project
964
    issue = Issue.find(2)
965
    assert_equal 2, issue.fixed_version_id
966
    issue.project_id = 3
967
    assert_nil issue.fixed_version_id
968
    issue.fixed_version_id = 2
969
    assert !issue.save
970
    assert_include 'Target version is not included in the list', issue.errors.full_messages
971
  end
972

    
973
  def test_should_keep_shared_version_when_changing_project
974
    Version.find(2).update_attribute :sharing, 'tree'
975
 
976
    issue = Issue.find(2)
977
    assert_equal 2, issue.fixed_version_id
978
    issue.project_id = 3
979
    assert_equal 2, issue.fixed_version_id
980
    assert issue.save
981
  end
982

    
983
  def test_allowed_target_projects_on_move_should_include_projects_with_issue_tracking_enabled
984
    assert_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
985
  end
986

    
987
  def test_allowed_target_projects_on_move_should_not_include_projects_with_issue_tracking_disabled
988
    Project.find(2).disable_module! :issue_tracking
989
    assert_not_include Project.find(2), Issue.allowed_target_projects_on_move(User.find(2))
990
  end
991

    
992
  def test_move_to_another_project_with_same_category
993
    issue = Issue.find(1)
994
    issue.project = Project.find(2)
995
    assert issue.save
996
    issue.reload
997
    assert_equal 2, issue.project_id
998
    # Category changes
999
    assert_equal 4, issue.category_id
1000
    # Make sure time entries were move to the target project
1001
    assert_equal 2, issue.time_entries.first.project_id
1002
  end
1003

    
1004
  def test_move_to_another_project_without_same_category
1005
    issue = Issue.find(2)
1006
    issue.project = Project.find(2)
1007
    assert issue.save
1008
    issue.reload
1009
    assert_equal 2, issue.project_id
1010
    # Category cleared
1011
    assert_nil issue.category_id
1012
  end
1013

    
1014
  def test_move_to_another_project_should_clear_fixed_version_when_not_shared
1015
    issue = Issue.find(1)
1016
    issue.update_attribute(:fixed_version_id, 1)
1017
    issue.project = Project.find(2)
1018
    assert issue.save
1019
    issue.reload
1020
    assert_equal 2, issue.project_id
1021
    # Cleared fixed_version
1022
    assert_equal nil, issue.fixed_version
1023
  end
1024

    
1025
  def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
1026
    issue = Issue.find(1)
1027
    issue.update_attribute(:fixed_version_id, 4)
1028
    issue.project = Project.find(5)
1029
    assert issue.save
1030
    issue.reload
1031
    assert_equal 5, issue.project_id
1032
    # Keep fixed_version
1033
    assert_equal 4, issue.fixed_version_id
1034
  end
1035

    
1036
  def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
1037
    issue = Issue.find(1)
1038
    issue.update_attribute(:fixed_version_id, 1)
1039
    issue.project = Project.find(5)
1040
    assert issue.save
1041
    issue.reload
1042
    assert_equal 5, issue.project_id
1043
    # Cleared fixed_version
1044
    assert_equal nil, issue.fixed_version
1045
  end
1046

    
1047
  def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
1048
    issue = Issue.find(1)
1049
    issue.update_attribute(:fixed_version_id, 7)
1050
    issue.project = Project.find(2)
1051
    assert issue.save
1052
    issue.reload
1053
    assert_equal 2, issue.project_id
1054
    # Keep fixed_version
1055
    assert_equal 7, issue.fixed_version_id
1056
  end
1057

    
1058
  def test_move_to_another_project_should_keep_parent_if_valid
1059
    issue = Issue.find(1)
1060
    issue.update_attribute(:parent_issue_id, 2)
1061
    issue.project = Project.find(3)
1062
    assert issue.save
1063
    issue.reload
1064
    assert_equal 2, issue.parent_id
1065
  end
1066

    
1067
  def test_move_to_another_project_should_clear_parent_if_not_valid
1068
    issue = Issue.find(1)
1069
    issue.update_attribute(:parent_issue_id, 2)
1070
    issue.project = Project.find(2)
1071
    assert issue.save
1072
    issue.reload
1073
    assert_nil issue.parent_id
1074
  end
1075

    
1076
  def test_move_to_another_project_with_disabled_tracker
1077
    issue = Issue.find(1)
1078
    target = Project.find(2)
1079
    target.tracker_ids = [3]
1080
    target.save
1081
    issue.project = target
1082
    assert issue.save
1083
    issue.reload
1084
    assert_equal 2, issue.project_id
1085
    assert_equal 3, issue.tracker_id
1086
  end
1087

    
1088
  def test_copy_to_the_same_project
1089
    issue = Issue.find(1)
1090
    copy = issue.copy
1091
    assert_difference 'Issue.count' do
1092
      copy.save!
1093
    end
1094
    assert_kind_of Issue, copy
1095
    assert_equal issue.project, copy.project
1096
    assert_equal "125", copy.custom_value_for(2).value
1097
  end
1098

    
1099
  def test_copy_to_another_project_and_tracker
1100
    issue = Issue.find(1)
1101
    copy = issue.copy(:project_id => 3, :tracker_id => 2)
1102
    assert_difference 'Issue.count' do
1103
      copy.save!
1104
    end
1105
    copy.reload
1106
    assert_kind_of Issue, copy
1107
    assert_equal Project.find(3), copy.project
1108
    assert_equal Tracker.find(2), copy.tracker
1109
    # Custom field #2 is not associated with target tracker
1110
    assert_nil copy.custom_value_for(2)
1111
  end
1112

    
1113
  context "#copy" do
1114
    setup do
1115
      @issue = Issue.find(1)
1116
    end
1117

    
1118
    should "not create a journal" do
1119
      copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1120
      copy.save!
1121
      assert_equal 0, copy.reload.journals.size
1122
    end
1123

    
1124
    should "allow assigned_to changes" do
1125
      copy = @issue.copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1126
      assert_equal 3, copy.assigned_to_id
1127
    end
1128

    
1129
    should "allow status changes" do
1130
      copy = @issue.copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
1131
      assert_equal 2, copy.status_id
1132
    end
1133

    
1134
    should "allow start date changes" do
1135
      date = Date.today
1136
      copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1137
      assert_equal date, copy.start_date
1138
    end
1139

    
1140
    should "allow due date changes" do
1141
      date = Date.today
1142
      copy = @issue.copy(:project_id => 3, :tracker_id => 2, :due_date => date)
1143
      assert_equal date, copy.due_date
1144
    end
1145

    
1146
    should "set current user as author" do
1147
      User.current = User.find(9)
1148
      copy = @issue.copy(:project_id => 3, :tracker_id => 2)
1149
      assert_equal User.current, copy.author
1150
    end
1151

    
1152
    should "create a journal with notes" do
1153
      date = Date.today
1154
      notes = "Notes added when copying"
1155
      copy = @issue.copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1156
      copy.init_journal(User.current, notes)
1157
      copy.save!
1158

    
1159
      assert_equal 1, copy.journals.size
1160
      journal = copy.journals.first
1161
      assert_equal 0, journal.details.size
1162
      assert_equal notes, journal.notes
1163
    end
1164
  end
1165

    
1166
  def test_valid_parent_project
1167
    issue = Issue.find(1)
1168
    issue_in_same_project = Issue.find(2)
1169
    issue_in_child_project = Issue.find(5)
1170
    issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1171
    issue_in_other_child_project = Issue.find(6)
1172
    issue_in_different_tree = Issue.find(4)
1173

    
1174
    with_settings :cross_project_subtasks => '' do
1175
      assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1176
      assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1177
      assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1178
      assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1179
    end
1180

    
1181
    with_settings :cross_project_subtasks => 'system' do
1182
      assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1183
      assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1184
      assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1185
    end
1186

    
1187
    with_settings :cross_project_subtasks => 'tree' do
1188
      assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1189
      assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1190
      assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1191
      assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1192

    
1193
      assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1194
      assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1195
    end
1196

    
1197
    with_settings :cross_project_subtasks => 'descendants' do
1198
      assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1199
      assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1200
      assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1201
      assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1202

    
1203
      assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1204
      assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1205
    end
1206
  end
1207

    
1208
  def test_recipients_should_include_previous_assignee
1209
    user = User.find(3)
1210
    user.members.update_all ["mail_notification = ?", false]
1211
    user.update_attribute :mail_notification, 'only_assigned'
1212

    
1213
    issue = Issue.find(2)
1214
    issue.assigned_to = nil
1215
    assert_include user.mail, issue.recipients
1216
    issue.save!
1217
    assert !issue.recipients.include?(user.mail)
1218
  end
1219

    
1220
  def test_recipients_should_not_include_users_that_cannot_view_the_issue
1221
    issue = Issue.find(12)
1222
    assert issue.recipients.include?(issue.author.mail)
1223
    # copy the issue to a private project
1224
    copy  = issue.copy(:project_id => 5, :tracker_id => 2)
1225
    # author is not a member of project anymore
1226
    assert !copy.recipients.include?(copy.author.mail)
1227
  end
1228

    
1229
  def test_recipients_should_include_the_assigned_group_members
1230
    group_member = User.generate!
1231
    group = Group.generate!
1232
    group.users << group_member
1233

    
1234
    issue = Issue.find(12)
1235
    issue.assigned_to = group
1236
    assert issue.recipients.include?(group_member.mail)
1237
  end
1238

    
1239
  def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1240
    user = User.find(3)
1241
    issue = Issue.find(9)
1242
    Watcher.create!(:user => user, :watchable => issue)
1243
    assert issue.watched_by?(user)
1244
    assert !issue.watcher_recipients.include?(user.mail)
1245
  end
1246

    
1247
  def test_issue_destroy
1248
    Issue.find(1).destroy
1249
    assert_nil Issue.find_by_id(1)
1250
    assert_nil TimeEntry.find_by_issue_id(1)
1251
  end
1252

    
1253
  def test_destroying_a_deleted_issue_should_not_raise_an_error
1254
    issue = Issue.find(1)
1255
    Issue.find(1).destroy
1256

    
1257
    assert_nothing_raised do
1258
      assert_no_difference 'Issue.count' do
1259
        issue.destroy
1260
      end
1261
      assert issue.destroyed?
1262
    end
1263
  end
1264

    
1265
  def test_destroying_a_stale_issue_should_not_raise_an_error
1266
    issue = Issue.find(1)
1267
    Issue.find(1).update_attribute :subject, "Updated"
1268

    
1269
    assert_nothing_raised do
1270
      assert_difference 'Issue.count', -1 do
1271
        issue.destroy
1272
      end
1273
      assert issue.destroyed?
1274
    end
1275
  end
1276

    
1277
  def test_blocked
1278
    blocked_issue = Issue.find(9)
1279
    blocking_issue = Issue.find(10)
1280

    
1281
    assert blocked_issue.blocked?
1282
    assert !blocking_issue.blocked?
1283
  end
1284

    
1285
  def test_blocked_issues_dont_allow_closed_statuses
1286
    blocked_issue = Issue.find(9)
1287

    
1288
    allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1289
    assert !allowed_statuses.empty?
1290
    closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1291
    assert closed_statuses.empty?
1292
  end
1293

    
1294
  def test_unblocked_issues_allow_closed_statuses
1295
    blocking_issue = Issue.find(10)
1296

    
1297
    allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1298
    assert !allowed_statuses.empty?
1299
    closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1300
    assert !closed_statuses.empty?
1301
  end
1302

    
1303
  def test_reschedule_an_issue_without_dates
1304
    with_settings :non_working_week_days => [] do
1305
      issue = Issue.new(:start_date => nil, :due_date => nil)
1306
      issue.reschedule_on '2012-10-09'.to_date
1307
      assert_equal '2012-10-09'.to_date, issue.start_date
1308
      assert_equal '2012-10-09'.to_date, issue.due_date
1309
    end
1310

    
1311
    with_settings :non_working_week_days => %w(6 7) do
1312
      issue = Issue.new(:start_date => nil, :due_date => nil)
1313
      issue.reschedule_on '2012-10-09'.to_date
1314
      assert_equal '2012-10-09'.to_date, issue.start_date
1315
      assert_equal '2012-10-09'.to_date, issue.due_date
1316

    
1317
      issue = Issue.new(:start_date => nil, :due_date => nil)
1318
      issue.reschedule_on '2012-10-13'.to_date
1319
      assert_equal '2012-10-15'.to_date, issue.start_date
1320
      assert_equal '2012-10-15'.to_date, issue.due_date
1321
    end
1322
  end
1323

    
1324
  def test_reschedule_an_issue_with_start_date
1325
    with_settings :non_working_week_days => [] do
1326
      issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1327
      issue.reschedule_on '2012-10-13'.to_date
1328
      assert_equal '2012-10-13'.to_date, issue.start_date
1329
      assert_equal '2012-10-13'.to_date, issue.due_date
1330
    end
1331

    
1332
    with_settings :non_working_week_days => %w(6 7) do
1333
      issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1334
      issue.reschedule_on '2012-10-11'.to_date
1335
      assert_equal '2012-10-11'.to_date, issue.start_date
1336
      assert_equal '2012-10-11'.to_date, issue.due_date
1337

    
1338
      issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1339
      issue.reschedule_on '2012-10-13'.to_date
1340
      assert_equal '2012-10-15'.to_date, issue.start_date
1341
      assert_equal '2012-10-15'.to_date, issue.due_date
1342
    end
1343
  end
1344

    
1345
  def test_reschedule_an_issue_with_start_and_due_dates
1346
    with_settings :non_working_week_days => [] do
1347
      issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-15')
1348
      issue.reschedule_on '2012-10-13'.to_date
1349
      assert_equal '2012-10-13'.to_date, issue.start_date
1350
      assert_equal '2012-10-19'.to_date, issue.due_date
1351
    end
1352

    
1353
    with_settings :non_working_week_days => %w(6 7) do
1354
      issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19') # 8 working days
1355
      issue.reschedule_on '2012-10-11'.to_date
1356
      assert_equal '2012-10-11'.to_date, issue.start_date
1357
      assert_equal '2012-10-23'.to_date, issue.due_date
1358

    
1359
      issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19')
1360
      issue.reschedule_on '2012-10-13'.to_date
1361
      assert_equal '2012-10-15'.to_date, issue.start_date
1362
      assert_equal '2012-10-25'.to_date, issue.due_date
1363
    end
1364
  end
1365

    
1366
  def test_rescheduling_an_issue_to_a_later_due_date_should_reschedule_following_issue
1367
    issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1368
    issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1369
    IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1370
                          :relation_type => IssueRelation::TYPE_PRECEDES)
1371
    assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1372

    
1373
    issue1.due_date = '2012-10-23'
1374
    issue1.save!
1375
    issue2.reload
1376
    assert_equal Date.parse('2012-10-24'), issue2.start_date
1377
    assert_equal Date.parse('2012-10-26'), issue2.due_date
1378
  end
1379

    
1380
  def test_rescheduling_an_issue_to_an_earlier_due_date_should_reschedule_following_issue
1381
    issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1382
    issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1383
    IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1384
                          :relation_type => IssueRelation::TYPE_PRECEDES)
1385
    assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1386

    
1387
    issue1.start_date = '2012-09-17'
1388
    issue1.due_date = '2012-09-18'
1389
    issue1.save!
1390
    issue2.reload
1391
    assert_equal Date.parse('2012-09-19'), issue2.start_date
1392
    assert_equal Date.parse('2012-09-21'), issue2.due_date
1393
  end
1394

    
1395
  def test_rescheduling_reschedule_following_issue_earlier_should_consider_other_preceding_issues
1396
    issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1397
    issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1398
    issue3 = Issue.generate!(:start_date => '2012-10-01', :due_date => '2012-10-02')
1399
    IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1400
                          :relation_type => IssueRelation::TYPE_PRECEDES)
1401
    IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1402
                          :relation_type => IssueRelation::TYPE_PRECEDES)
1403
    assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1404

    
1405
    issue1.start_date = '2012-09-17'
1406
    issue1.due_date = '2012-09-18'
1407
    issue1.save!
1408
    issue2.reload
1409
    # Issue 2 must start after Issue 3
1410
    assert_equal Date.parse('2012-10-03'), issue2.start_date
1411
    assert_equal Date.parse('2012-10-05'), issue2.due_date
1412
  end
1413

    
1414
  def test_rescheduling_a_stale_issue_should_not_raise_an_error
1415
    with_settings :non_working_week_days => [] do
1416
      stale = Issue.find(1)
1417
      issue = Issue.find(1)
1418
      issue.subject = "Updated"
1419
      issue.save!
1420
      date = 10.days.from_now.to_date
1421
      assert_nothing_raised do
1422
        stale.reschedule_on!(date)
1423
      end
1424
      assert_equal date, stale.reload.start_date
1425
    end
1426
  end
1427

    
1428
  def test_overdue
1429
    assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
1430
    assert !Issue.new(:due_date => Date.today).overdue?
1431
    assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
1432
    assert !Issue.new(:due_date => nil).overdue?
1433
    assert !Issue.new(:due_date => 1.day.ago.to_date,
1434
                      :status => IssueStatus.find(:first,
1435
                                                  :conditions => {:is_closed => true})
1436
                      ).overdue?
1437
  end
1438

    
1439
  context "#behind_schedule?" do
1440
    should "be false if the issue has no start_date" do
1441
      assert !Issue.new(:start_date => nil,
1442
                        :due_date => 1.day.from_now.to_date,
1443
                        :done_ratio => 0).behind_schedule?
1444
    end
1445

    
1446
    should "be false if the issue has no end_date" do
1447
      assert !Issue.new(:start_date => 1.day.from_now.to_date,
1448
                        :due_date => nil,
1449
                        :done_ratio => 0).behind_schedule?
1450
    end
1451

    
1452
    should "be false if the issue has more done than it's calendar time" do
1453
      assert !Issue.new(:start_date => 50.days.ago.to_date,
1454
                        :due_date => 50.days.from_now.to_date,
1455
                        :done_ratio => 90).behind_schedule?
1456
    end
1457

    
1458
    should "be true if the issue hasn't been started at all" do
1459
      assert Issue.new(:start_date => 1.day.ago.to_date,
1460
                       :due_date => 1.day.from_now.to_date,
1461
                       :done_ratio => 0).behind_schedule?
1462
    end
1463

    
1464
    should "be true if the issue has used more calendar time than it's done ratio" do
1465
      assert Issue.new(:start_date => 100.days.ago.to_date,
1466
                       :due_date => Date.today,
1467
                       :done_ratio => 90).behind_schedule?
1468
    end
1469
  end
1470

    
1471
  context "#assignable_users" do
1472
    should "be Users" do
1473
      assert_kind_of User, Issue.find(1).assignable_users.first
1474
    end
1475

    
1476
    should "include the issue author" do
1477
      non_project_member = User.generate!
1478
      issue = Issue.generate!(:author => non_project_member)
1479

    
1480
      assert issue.assignable_users.include?(non_project_member)
1481
    end
1482

    
1483
    should "include the current assignee" do
1484
      user = User.generate!
1485
      issue = Issue.generate!(:assigned_to => user)
1486
      user.lock!
1487

    
1488
      assert Issue.find(issue.id).assignable_users.include?(user)
1489
    end
1490

    
1491
    should "not show the issue author twice" do
1492
      assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
1493
      assert_equal 2, assignable_user_ids.length
1494

    
1495
      assignable_user_ids.each do |user_id|
1496
        assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length,
1497
                     "User #{user_id} appears more or less than once"
1498
      end
1499
    end
1500

    
1501
    context "with issue_group_assignment" do
1502
      should "include groups" do
1503
        issue = Issue.new(:project => Project.find(2))
1504

    
1505
        with_settings :issue_group_assignment => '1' do
1506
          assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1507
          assert issue.assignable_users.include?(Group.find(11))
1508
        end
1509
      end
1510
    end
1511

    
1512
    context "without issue_group_assignment" do
1513
      should "not include groups" do
1514
        issue = Issue.new(:project => Project.find(2))
1515

    
1516
        with_settings :issue_group_assignment => '0' do
1517
          assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
1518
          assert !issue.assignable_users.include?(Group.find(11))
1519
        end
1520
      end
1521
    end
1522
  end
1523

    
1524
  def test_create_should_send_email_notification
1525
    ActionMailer::Base.deliveries.clear
1526
    issue = Issue.new(:project_id => 1, :tracker_id => 1,
1527
                      :author_id => 3, :status_id => 1,
1528
                      :priority => IssuePriority.all.first,
1529
                      :subject => 'test_create', :estimated_hours => '1:30')
1530

    
1531
    assert issue.save
1532
    assert_equal 1, ActionMailer::Base.deliveries.size
1533
  end
1534

    
1535
  def test_stale_issue_should_not_send_email_notification
1536
    ActionMailer::Base.deliveries.clear
1537
    issue = Issue.find(1)
1538
    stale = Issue.find(1)
1539

    
1540
    issue.init_journal(User.find(1))
1541
    issue.subject = 'Subjet update'
1542
    assert issue.save
1543
    assert_equal 1, ActionMailer::Base.deliveries.size
1544
    ActionMailer::Base.deliveries.clear
1545

    
1546
    stale.init_journal(User.find(1))
1547
    stale.subject = 'Another subjet update'
1548
    assert_raise ActiveRecord::StaleObjectError do
1549
      stale.save
1550
    end
1551
    assert ActionMailer::Base.deliveries.empty?
1552
  end
1553

    
1554
  def test_journalized_description
1555
    IssueCustomField.delete_all
1556

    
1557
    i = Issue.first
1558
    old_description = i.description
1559
    new_description = "This is the new description"
1560

    
1561
    i.init_journal(User.find(2))
1562
    i.description = new_description
1563
    assert_difference 'Journal.count', 1 do
1564
      assert_difference 'JournalDetail.count', 1 do
1565
        i.save!
1566
      end
1567
    end
1568

    
1569
    detail = JournalDetail.first(:order => 'id DESC')
1570
    assert_equal i, detail.journal.journalized
1571
    assert_equal 'attr', detail.property
1572
    assert_equal 'description', detail.prop_key
1573
    assert_equal old_description, detail.old_value
1574
    assert_equal new_description, detail.value
1575
  end
1576

    
1577
  def test_blank_descriptions_should_not_be_journalized
1578
    IssueCustomField.delete_all
1579
    Issue.update_all("description = NULL", "id=1")
1580

    
1581
    i = Issue.find(1)
1582
    i.init_journal(User.find(2))
1583
    i.subject = "blank description"
1584
    i.description = "\r\n"
1585

    
1586
    assert_difference 'Journal.count', 1 do
1587
      assert_difference 'JournalDetail.count', 1 do
1588
        i.save!
1589
      end
1590
    end
1591
  end
1592

    
1593
  def test_journalized_multi_custom_field
1594
    field = IssueCustomField.create!(:name => 'filter', :field_format => 'list',
1595
                                     :is_filter => true, :is_for_all => true,
1596
                                     :tracker_ids => [1],
1597
                                     :possible_values => ['value1', 'value2', 'value3'],
1598
                                     :multiple => true)
1599

    
1600
    issue = Issue.create!(:project_id => 1, :tracker_id => 1,
1601
                          :subject => 'Test', :author_id => 1)
1602

    
1603
    assert_difference 'Journal.count' do
1604
      assert_difference 'JournalDetail.count' do
1605
        issue.init_journal(User.first)
1606
        issue.custom_field_values = {field.id => ['value1']}
1607
        issue.save!
1608
      end
1609
      assert_difference 'JournalDetail.count' do
1610
        issue.init_journal(User.first)
1611
        issue.custom_field_values = {field.id => ['value1', 'value2']}
1612
        issue.save!
1613
      end
1614
      assert_difference 'JournalDetail.count', 2 do
1615
        issue.init_journal(User.first)
1616
        issue.custom_field_values = {field.id => ['value3', 'value2']}
1617
        issue.save!
1618
      end
1619
      assert_difference 'JournalDetail.count', 2 do
1620
        issue.init_journal(User.first)
1621
        issue.custom_field_values = {field.id => nil}
1622
        issue.save!
1623
      end
1624
    end
1625
  end
1626

    
1627
  def test_description_eol_should_be_normalized
1628
    i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
1629
    assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
1630
  end
1631

    
1632
  def test_saving_twice_should_not_duplicate_journal_details
1633
    i = Issue.find(:first)
1634
    i.init_journal(User.find(2), 'Some notes')
1635
    # initial changes
1636
    i.subject = 'New subject'
1637
    i.done_ratio = i.done_ratio + 10
1638
    assert_difference 'Journal.count' do
1639
      assert i.save
1640
    end
1641
    # 1 more change
1642
    i.priority = IssuePriority.find(:first, :conditions => ["id <> ?", i.priority_id])
1643
    assert_no_difference 'Journal.count' do
1644
      assert_difference 'JournalDetail.count', 1 do
1645
        i.save
1646
      end
1647
    end
1648
    # no more change
1649
    assert_no_difference 'Journal.count' do
1650
      assert_no_difference 'JournalDetail.count' do
1651
        i.save
1652
      end
1653
    end
1654
  end
1655

    
1656
  def test_all_dependent_issues
1657
    IssueRelation.delete_all
1658
    assert IssueRelation.create!(:issue_from => Issue.find(1),
1659
                                 :issue_to   => Issue.find(2),
1660
                                 :relation_type => IssueRelation::TYPE_PRECEDES)
1661
    assert IssueRelation.create!(:issue_from => Issue.find(2),
1662
                                 :issue_to   => Issue.find(3),
1663
                                 :relation_type => IssueRelation::TYPE_PRECEDES)
1664
    assert IssueRelation.create!(:issue_from => Issue.find(3),
1665
                                 :issue_to   => Issue.find(8),
1666
                                 :relation_type => IssueRelation::TYPE_PRECEDES)
1667

    
1668
    assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1669
  end
1670

    
1671
  def test_all_dependent_issues_with_persistent_circular_dependency
1672
    IssueRelation.delete_all
1673
    assert IssueRelation.create!(:issue_from => Issue.find(1),
1674
                                 :issue_to   => Issue.find(2),
1675
                                 :relation_type => IssueRelation::TYPE_PRECEDES)
1676
    assert IssueRelation.create!(:issue_from => Issue.find(2),
1677
                                 :issue_to   => Issue.find(3),
1678
                                 :relation_type => IssueRelation::TYPE_PRECEDES)
1679

    
1680
    r = IssueRelation.create!(:issue_from => Issue.find(3),
1681
                             :issue_to   => Issue.find(7),
1682
                             :relation_type => IssueRelation::TYPE_PRECEDES)
1683
    IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
1684
    
1685
    assert_equal [2, 3], Issue.find(1).all_dependent_issues.collect(&:id).sort
1686
  end
1687

    
1688
  def test_all_dependent_issues_with_persistent_multiple_circular_dependencies
1689
    IssueRelation.delete_all
1690
    assert IssueRelation.create!(:issue_from => Issue.find(1),
1691
                                 :issue_to   => Issue.find(2),
1692
                                 :relation_type => IssueRelation::TYPE_RELATES)
1693
    assert IssueRelation.create!(:issue_from => Issue.find(2),
1694
                                 :issue_to   => Issue.find(3),
1695
                                 :relation_type => IssueRelation::TYPE_RELATES)
1696
    assert IssueRelation.create!(:issue_from => Issue.find(3),
1697
                                 :issue_to   => Issue.find(8),
1698
                                 :relation_type => IssueRelation::TYPE_RELATES)
1699

    
1700
    r = IssueRelation.create!(:issue_from => Issue.find(8),
1701
                             :issue_to   => Issue.find(7),
1702
                             :relation_type => IssueRelation::TYPE_RELATES)
1703
    IssueRelation.update_all("issue_to_id = 2", ["id = ?", r.id])
1704
    
1705
    r = IssueRelation.create!(:issue_from => Issue.find(3),
1706
                             :issue_to   => Issue.find(7),
1707
                             :relation_type => IssueRelation::TYPE_RELATES)
1708
    IssueRelation.update_all("issue_to_id = 1", ["id = ?", r.id])
1709

    
1710
    assert_equal [2, 3, 8], Issue.find(1).all_dependent_issues.collect(&:id).sort
1711
  end
1712

    
1713
  context "#done_ratio" do
1714
    setup do
1715
      @issue = Issue.find(1)
1716
      @issue_status = IssueStatus.find(1)
1717
      @issue_status.update_attribute(:default_done_ratio, 50)
1718
      @issue2 = Issue.find(2)
1719
      @issue_status2 = IssueStatus.find(2)
1720
      @issue_status2.update_attribute(:default_done_ratio, 0)
1721
    end
1722

    
1723
    teardown do
1724
      Setting.issue_done_ratio = 'issue_field'
1725
    end
1726

    
1727
    context "with Setting.issue_done_ratio using the issue_field" do
1728
      setup do
1729
        Setting.issue_done_ratio = 'issue_field'
1730
      end
1731

    
1732
      should "read the issue's field" do
1733
        assert_equal 0, @issue.done_ratio
1734
        assert_equal 30, @issue2.done_ratio
1735
      end
1736
    end
1737

    
1738
    context "with Setting.issue_done_ratio using the issue_status" do
1739
      setup do
1740
        Setting.issue_done_ratio = 'issue_status'
1741
      end
1742

    
1743
      should "read the Issue Status's default done ratio" do
1744
        assert_equal 50, @issue.done_ratio
1745
        assert_equal 0, @issue2.done_ratio
1746
      end
1747
    end
1748
  end
1749

    
1750
  context "#update_done_ratio_from_issue_status" do
1751
    setup do
1752
      @issue = Issue.find(1)
1753
      @issue_status = IssueStatus.find(1)
1754
      @issue_status.update_attribute(:default_done_ratio, 50)
1755
      @issue2 = Issue.find(2)
1756
      @issue_status2 = IssueStatus.find(2)
1757
      @issue_status2.update_attribute(:default_done_ratio, 0)
1758
    end
1759

    
1760
    context "with Setting.issue_done_ratio using the issue_field" do
1761
      setup do
1762
        Setting.issue_done_ratio = 'issue_field'
1763
      end
1764

    
1765
      should "not change the issue" do
1766
        @issue.update_done_ratio_from_issue_status
1767
        @issue2.update_done_ratio_from_issue_status
1768

    
1769
        assert_equal 0, @issue.read_attribute(:done_ratio)
1770
        assert_equal 30, @issue2.read_attribute(:done_ratio)
1771
      end
1772
    end
1773

    
1774
    context "with Setting.issue_done_ratio using the issue_status" do
1775
      setup do
1776
        Setting.issue_done_ratio = 'issue_status'
1777
      end
1778

    
1779
      should "change the issue's done ratio" do
1780
        @issue.update_done_ratio_from_issue_status
1781
        @issue2.update_done_ratio_from_issue_status
1782

    
1783
        assert_equal 50, @issue.read_attribute(:done_ratio)
1784
        assert_equal 0, @issue2.read_attribute(:done_ratio)
1785
      end
1786
    end
1787
  end
1788

    
1789
  test "#by_tracker" do
1790
    User.current = User.anonymous
1791
    groups = Issue.by_tracker(Project.find(1))
1792
    assert_equal 3, groups.size
1793
    assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1794
  end
1795

    
1796
  test "#by_version" do
1797
    User.current = User.anonymous
1798
    groups = Issue.by_version(Project.find(1))
1799
    assert_equal 3, groups.size
1800
    assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1801
  end
1802

    
1803
  test "#by_priority" do
1804
    User.current = User.anonymous
1805
    groups = Issue.by_priority(Project.find(1))
1806
    assert_equal 4, groups.size
1807
    assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1808
  end
1809

    
1810
  test "#by_category" do
1811
    User.current = User.anonymous
1812
    groups = Issue.by_category(Project.find(1))
1813
    assert_equal 2, groups.size
1814
    assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1815
  end
1816

    
1817
  test "#by_assigned_to" do
1818
    User.current = User.anonymous
1819
    groups = Issue.by_assigned_to(Project.find(1))
1820
    assert_equal 2, groups.size
1821
    assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1822
  end
1823

    
1824
  test "#by_author" do
1825
    User.current = User.anonymous
1826
    groups = Issue.by_author(Project.find(1))
1827
    assert_equal 4, groups.size
1828
    assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1829
  end
1830

    
1831
  test "#by_subproject" do
1832
    User.current = User.anonymous
1833
    groups = Issue.by_subproject(Project.find(1))
1834
    # Private descendant not visible
1835
    assert_equal 1, groups.size
1836
    assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
1837
  end
1838

    
1839
  def test_recently_updated_scope
1840
    #should return the last updated issue
1841
    assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
1842
  end
1843

    
1844
  def test_on_active_projects_scope
1845
    assert Project.find(2).archive
1846

    
1847
    before = Issue.on_active_project.length
1848
    # test inclusion to results
1849
    issue = Issue.generate!(:tracker => Project.find(2).trackers.first)
1850
    assert_equal before + 1, Issue.on_active_project.length
1851

    
1852
    # Move to an archived project
1853
    issue.project = Project.find(2)
1854
    assert issue.save
1855
    assert_equal before, Issue.on_active_project.length
1856
  end
1857

    
1858
  context "Issue#recipients" do
1859
    setup do
1860
      @project = Project.find(1)
1861
      @author = User.generate!
1862
      @assignee = User.generate!
1863
      @issue = Issue.generate!(:project => @project, :assigned_to => @assignee, :author => @author)
1864
    end
1865

    
1866
    should "include project recipients" do
1867
      assert @project.recipients.present?
1868
      @project.recipients.each do |project_recipient|
1869
        assert @issue.recipients.include?(project_recipient)
1870
      end
1871
    end
1872

    
1873
    should "include the author if the author is active" do
1874
      assert @issue.author, "No author set for Issue"
1875
      assert @issue.recipients.include?(@issue.author.mail)
1876
    end
1877

    
1878
    should "include the assigned to user if the assigned to user is active" do
1879
      assert @issue.assigned_to, "No assigned_to set for Issue"
1880
      assert @issue.recipients.include?(@issue.assigned_to.mail)
1881
    end
1882

    
1883
    should "not include users who opt out of all email" do
1884
      @author.update_attribute(:mail_notification, :none)
1885

    
1886
      assert !@issue.recipients.include?(@issue.author.mail)
1887
    end
1888

    
1889
    should "not include the issue author if they are only notified of assigned issues" do
1890
      @author.update_attribute(:mail_notification, :only_assigned)
1891

    
1892
      assert !@issue.recipients.include?(@issue.author.mail)
1893
    end
1894

    
1895
    should "not include the assigned user if they are only notified of owned issues" do
1896
      @assignee.update_attribute(:mail_notification, :only_owner)
1897

    
1898
      assert !@issue.recipients.include?(@issue.assigned_to.mail)
1899
    end
1900
  end
1901

    
1902
  def test_last_journal_id_with_journals_should_return_the_journal_id
1903
    assert_equal 2, Issue.find(1).last_journal_id
1904
  end
1905

    
1906
  def test_last_journal_id_without_journals_should_return_nil
1907
    assert_nil Issue.find(3).last_journal_id
1908
  end
1909

    
1910
  def test_journals_after_should_return_journals_with_greater_id
1911
    assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
1912
    assert_equal [], Issue.find(1).journals_after('2')
1913
  end
1914

    
1915
  def test_journals_after_with_blank_arg_should_return_all_journals
1916
    assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
1917
  end
1918

    
1919
  def test_css_classes_should_include_priority
1920
    issue = Issue.new(:priority => IssuePriority.find(8))
1921
    classes = issue.css_classes.split(' ')
1922
    assert_include 'priority-8', classes
1923
    assert_include 'priority-highest', classes
1924
  end
1925

    
1926
  def test_save_attachments_with_hash_should_save_attachments_in_keys_order
1927
    set_tmp_attachments_directory
1928
    issue = Issue.generate!
1929
    issue.save_attachments({
1930
      'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')},
1931
      '3' => {'file' => mock_file_with_options(:original_filename => 'bar')},
1932
      '1' => {'file' => mock_file_with_options(:original_filename => 'foo')}
1933
    })
1934
    issue.attach_saved_attachments
1935

    
1936
    assert_equal 3, issue.reload.attachments.count
1937
    assert_equal %w(upload foo bar), issue.attachments.map(&:filename)
1938
  end
1939
end