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