##// END OF EJS Templates
Journalize values that are cleared after project or tracker change (#21623)....
Jean-Philippe Lang -
r15429:55be3ef1727e
parent child
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,312 +1,320
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class Journal < ActiveRecord::Base
18 class Journal < ActiveRecord::Base
19 include Redmine::SafeAttributes
19 include Redmine::SafeAttributes
20
20
21 belongs_to :journalized, :polymorphic => true
21 belongs_to :journalized, :polymorphic => true
22 # added as a quick fix to allow eager loading of the polymorphic association
22 # added as a quick fix to allow eager loading of the polymorphic association
23 # since always associated to an issue, for now
23 # since always associated to an issue, for now
24 belongs_to :issue, :foreign_key => :journalized_id
24 belongs_to :issue, :foreign_key => :journalized_id
25
25
26 belongs_to :user
26 belongs_to :user
27 has_many :details, :class_name => "JournalDetail", :dependent => :delete_all, :inverse_of => :journal
27 has_many :details, :class_name => "JournalDetail", :dependent => :delete_all, :inverse_of => :journal
28 attr_accessor :indice
28 attr_accessor :indice
29 attr_protected :id
29 attr_protected :id
30
30
31 acts_as_event :title => Proc.new {|o| status = ((s = o.new_status) ? " (#{s})" : nil); "#{o.issue.tracker} ##{o.issue.id}#{status}: #{o.issue.subject}" },
31 acts_as_event :title => Proc.new {|o| status = ((s = o.new_status) ? " (#{s})" : nil); "#{o.issue.tracker} ##{o.issue.id}#{status}: #{o.issue.subject}" },
32 :description => :notes,
32 :description => :notes,
33 :author => :user,
33 :author => :user,
34 :group => :issue,
34 :group => :issue,
35 :type => Proc.new {|o| (s = o.new_status) ? (s.is_closed? ? 'issue-closed' : 'issue-edit') : 'issue-note' },
35 :type => Proc.new {|o| (s = o.new_status) ? (s.is_closed? ? 'issue-closed' : 'issue-edit') : 'issue-note' },
36 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.issue.id, :anchor => "change-#{o.id}"}}
36 :url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.issue.id, :anchor => "change-#{o.id}"}}
37
37
38 acts_as_activity_provider :type => 'issues',
38 acts_as_activity_provider :type => 'issues',
39 :author_key => :user_id,
39 :author_key => :user_id,
40 :scope => preload({:issue => :project}, :user).
40 :scope => preload({:issue => :project}, :user).
41 joins("LEFT OUTER JOIN #{JournalDetail.table_name} ON #{JournalDetail.table_name}.journal_id = #{Journal.table_name}.id").
41 joins("LEFT OUTER JOIN #{JournalDetail.table_name} ON #{JournalDetail.table_name}.journal_id = #{Journal.table_name}.id").
42 where("#{Journal.table_name}.journalized_type = 'Issue' AND" +
42 where("#{Journal.table_name}.journalized_type = 'Issue' AND" +
43 " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')").distinct
43 " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')").distinct
44
44
45 before_create :split_private_notes
45 before_create :split_private_notes
46 after_create :send_notification
46 after_create :send_notification
47
47
48 scope :visible, lambda {|*args|
48 scope :visible, lambda {|*args|
49 user = args.shift || User.current
49 user = args.shift || User.current
50 joins(:issue => :project).
50 joins(:issue => :project).
51 where(Issue.visible_condition(user, *args)).
51 where(Issue.visible_condition(user, *args)).
52 where("(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(user, :view_private_notes, *args)}))", false)
52 where("(#{Journal.table_name}.private_notes = ? OR (#{Project.allowed_to_condition(user, :view_private_notes, *args)}))", false)
53 }
53 }
54
54
55 safe_attributes 'notes',
55 safe_attributes 'notes',
56 :if => lambda {|journal, user| journal.new_record? || journal.editable_by?(user)}
56 :if => lambda {|journal, user| journal.new_record? || journal.editable_by?(user)}
57 safe_attributes 'private_notes',
57 safe_attributes 'private_notes',
58 :if => lambda {|journal, user| user.allowed_to?(:set_notes_private, journal.project)}
58 :if => lambda {|journal, user| user.allowed_to?(:set_notes_private, journal.project)}
59
59
60 def initialize(*args)
60 def initialize(*args)
61 super
61 super
62 if journalized
62 if journalized
63 if journalized.new_record?
63 if journalized.new_record?
64 self.notify = false
64 self.notify = false
65 else
65 else
66 start
66 start
67 end
67 end
68 end
68 end
69 end
69 end
70
70
71 def save(*args)
71 def save(*args)
72 journalize_changes
72 journalize_changes
73 # Do not save an empty journal
73 # Do not save an empty journal
74 (details.empty? && notes.blank?) ? false : super
74 (details.empty? && notes.blank?) ? false : super
75 end
75 end
76
76
77 # Returns journal details that are visible to user
77 # Returns journal details that are visible to user
78 def visible_details(user=User.current)
78 def visible_details(user=User.current)
79 details.select do |detail|
79 details.select do |detail|
80 if detail.property == 'cf'
80 if detail.property == 'cf'
81 detail.custom_field && detail.custom_field.visible_by?(project, user)
81 detail.custom_field && detail.custom_field.visible_by?(project, user)
82 elsif detail.property == 'relation'
82 elsif detail.property == 'relation'
83 Issue.find_by_id(detail.value || detail.old_value).try(:visible?, user)
83 Issue.find_by_id(detail.value || detail.old_value).try(:visible?, user)
84 else
84 else
85 true
85 true
86 end
86 end
87 end
87 end
88 end
88 end
89
89
90 def each_notification(users, &block)
90 def each_notification(users, &block)
91 if users.any?
91 if users.any?
92 users_by_details_visibility = users.group_by do |user|
92 users_by_details_visibility = users.group_by do |user|
93 visible_details(user)
93 visible_details(user)
94 end
94 end
95 users_by_details_visibility.each do |visible_details, users|
95 users_by_details_visibility.each do |visible_details, users|
96 if notes? || visible_details.any?
96 if notes? || visible_details.any?
97 yield(users)
97 yield(users)
98 end
98 end
99 end
99 end
100 end
100 end
101 end
101 end
102
102
103 # Returns the JournalDetail for the given attribute, or nil if the attribute
103 # Returns the JournalDetail for the given attribute, or nil if the attribute
104 # was not updated
104 # was not updated
105 def detail_for_attribute(attribute)
105 def detail_for_attribute(attribute)
106 details.detect {|detail| detail.prop_key == attribute}
106 details.detect {|detail| detail.prop_key == attribute}
107 end
107 end
108
108
109 # Returns the new status if the journal contains a status change, otherwise nil
109 # Returns the new status if the journal contains a status change, otherwise nil
110 def new_status
110 def new_status
111 s = new_value_for('status_id')
111 s = new_value_for('status_id')
112 s ? IssueStatus.find_by_id(s.to_i) : nil
112 s ? IssueStatus.find_by_id(s.to_i) : nil
113 end
113 end
114
114
115 def new_value_for(prop)
115 def new_value_for(prop)
116 detail_for_attribute(prop).try(:value)
116 detail_for_attribute(prop).try(:value)
117 end
117 end
118
118
119 def editable_by?(usr)
119 def editable_by?(usr)
120 usr && usr.logged? && (usr.allowed_to?(:edit_issue_notes, project) || (self.user == usr && usr.allowed_to?(:edit_own_issue_notes, project)))
120 usr && usr.logged? && (usr.allowed_to?(:edit_issue_notes, project) || (self.user == usr && usr.allowed_to?(:edit_own_issue_notes, project)))
121 end
121 end
122
122
123 def project
123 def project
124 journalized.respond_to?(:project) ? journalized.project : nil
124 journalized.respond_to?(:project) ? journalized.project : nil
125 end
125 end
126
126
127 def attachments
127 def attachments
128 journalized.respond_to?(:attachments) ? journalized.attachments : nil
128 journalized.respond_to?(:attachments) ? journalized.attachments : nil
129 end
129 end
130
130
131 # Returns a string of css classes
131 # Returns a string of css classes
132 def css_classes
132 def css_classes
133 s = 'journal'
133 s = 'journal'
134 s << ' has-notes' unless notes.blank?
134 s << ' has-notes' unless notes.blank?
135 s << ' has-details' unless details.blank?
135 s << ' has-details' unless details.blank?
136 s << ' private-notes' if private_notes?
136 s << ' private-notes' if private_notes?
137 s
137 s
138 end
138 end
139
139
140 def notify?
140 def notify?
141 @notify != false
141 @notify != false
142 end
142 end
143
143
144 def notify=(arg)
144 def notify=(arg)
145 @notify = arg
145 @notify = arg
146 end
146 end
147
147
148 def notified_users
148 def notified_users
149 notified = journalized.notified_users
149 notified = journalized.notified_users
150 if private_notes?
150 if private_notes?
151 notified = notified.select {|user| user.allowed_to?(:view_private_notes, journalized.project)}
151 notified = notified.select {|user| user.allowed_to?(:view_private_notes, journalized.project)}
152 end
152 end
153 notified
153 notified
154 end
154 end
155
155
156 def recipients
156 def recipients
157 notified_users.map(&:mail)
157 notified_users.map(&:mail)
158 end
158 end
159
159
160 def notified_watchers
160 def notified_watchers
161 notified = journalized.notified_watchers
161 notified = journalized.notified_watchers
162 if private_notes?
162 if private_notes?
163 notified = notified.select {|user| user.allowed_to?(:view_private_notes, journalized.project)}
163 notified = notified.select {|user| user.allowed_to?(:view_private_notes, journalized.project)}
164 end
164 end
165 notified
165 notified
166 end
166 end
167
167
168 def watcher_recipients
168 def watcher_recipients
169 notified_watchers.map(&:mail)
169 notified_watchers.map(&:mail)
170 end
170 end
171
171
172 # Sets @custom_field instance variable on journals details using a single query
172 # Sets @custom_field instance variable on journals details using a single query
173 def self.preload_journals_details_custom_fields(journals)
173 def self.preload_journals_details_custom_fields(journals)
174 field_ids = journals.map(&:details).flatten.select {|d| d.property == 'cf'}.map(&:prop_key).uniq
174 field_ids = journals.map(&:details).flatten.select {|d| d.property == 'cf'}.map(&:prop_key).uniq
175 if field_ids.any?
175 if field_ids.any?
176 fields_by_id = CustomField.where(:id => field_ids).inject({}) {|h, f| h[f.id] = f; h}
176 fields_by_id = CustomField.where(:id => field_ids).inject({}) {|h, f| h[f.id] = f; h}
177 journals.each do |journal|
177 journals.each do |journal|
178 journal.details.each do |detail|
178 journal.details.each do |detail|
179 if detail.property == 'cf'
179 if detail.property == 'cf'
180 detail.instance_variable_set "@custom_field", fields_by_id[detail.prop_key.to_i]
180 detail.instance_variable_set "@custom_field", fields_by_id[detail.prop_key.to_i]
181 end
181 end
182 end
182 end
183 end
183 end
184 end
184 end
185 journals
185 journals
186 end
186 end
187
187
188 # Stores the values of the attributes and custom fields of the journalized object
188 # Stores the values of the attributes and custom fields of the journalized object
189 def start
189 def start
190 if journalized
190 if journalized
191 @attributes_before_change = journalized.journalized_attribute_names.inject({}) do |h, attribute|
191 @attributes_before_change = journalized.journalized_attribute_names.inject({}) do |h, attribute|
192 h[attribute] = journalized.send(attribute)
192 h[attribute] = journalized.send(attribute)
193 h
193 h
194 end
194 end
195 @custom_values_before_change = journalized.custom_field_values.inject({}) do |h, c|
195 @custom_values_before_change = journalized.custom_field_values.inject({}) do |h, c|
196 h[c.custom_field_id] = c.value
196 h[c.custom_field_id] = c.value
197 h
197 h
198 end
198 end
199 end
199 end
200 self
200 self
201 end
201 end
202
202
203 # Adds a journal detail for an attachment that was added or removed
203 # Adds a journal detail for an attachment that was added or removed
204 def journalize_attachment(attachment, added_or_removed)
204 def journalize_attachment(attachment, added_or_removed)
205 key = (added_or_removed == :removed ? :old_value : :value)
205 key = (added_or_removed == :removed ? :old_value : :value)
206 details << JournalDetail.new(
206 details << JournalDetail.new(
207 :property => 'attachment',
207 :property => 'attachment',
208 :prop_key => attachment.id,
208 :prop_key => attachment.id,
209 key => attachment.filename
209 key => attachment.filename
210 )
210 )
211 end
211 end
212
212
213 # Adds a journal detail for an issue relation that was added or removed
213 # Adds a journal detail for an issue relation that was added or removed
214 def journalize_relation(relation, added_or_removed)
214 def journalize_relation(relation, added_or_removed)
215 key = (added_or_removed == :removed ? :old_value : :value)
215 key = (added_or_removed == :removed ? :old_value : :value)
216 details << JournalDetail.new(
216 details << JournalDetail.new(
217 :property => 'relation',
217 :property => 'relation',
218 :prop_key => relation.relation_type_for(journalized),
218 :prop_key => relation.relation_type_for(journalized),
219 key => relation.other_issue(journalized).try(:id)
219 key => relation.other_issue(journalized).try(:id)
220 )
220 )
221 end
221 end
222
222
223 private
223 private
224
224
225 # Generates journal details for attribute and custom field changes
225 # Generates journal details for attribute and custom field changes
226 def journalize_changes
226 def journalize_changes
227 # attributes changes
227 # attributes changes
228 if @attributes_before_change
228 if @attributes_before_change
229 journalized.journalized_attribute_names.each {|attribute|
229 attrs = (journalized.journalized_attribute_names + @attributes_before_change.keys).uniq
230 attrs.each do |attribute|
230 before = @attributes_before_change[attribute]
231 before = @attributes_before_change[attribute]
231 after = journalized.send(attribute)
232 after = journalized.send(attribute)
232 next if before == after || (before.blank? && after.blank?)
233 next if before == after || (before.blank? && after.blank?)
233 add_attribute_detail(attribute, before, after)
234 add_attribute_detail(attribute, before, after)
234 }
235 end
235 end
236 end
237 # custom fields changes
236 if @custom_values_before_change
238 if @custom_values_before_change
237 # custom fields changes
239 values_by_custom_field_id = {}
238 journalized.custom_field_values.each {|c|
240 @custom_values_before_change.each do |custom_field_id, value|
239 before = @custom_values_before_change[c.custom_field_id]
241 values_by_custom_field_id[custom_field_id] = nil
240 after = c.value
242 end
243 journalized.custom_field_values.each do |c|
244 values_by_custom_field_id[c.custom_field_id] = c.value
245 end
246
247 values_by_custom_field_id.each do |custom_field_id, after|
248 before = @custom_values_before_change[custom_field_id]
241 next if before == after || (before.blank? && after.blank?)
249 next if before == after || (before.blank? && after.blank?)
242
250
243 if before.is_a?(Array) || after.is_a?(Array)
251 if before.is_a?(Array) || after.is_a?(Array)
244 before = [before] unless before.is_a?(Array)
252 before = [before] unless before.is_a?(Array)
245 after = [after] unless after.is_a?(Array)
253 after = [after] unless after.is_a?(Array)
246
254
247 # values removed
255 # values removed
248 (before - after).reject(&:blank?).each do |value|
256 (before - after).reject(&:blank?).each do |value|
249 add_custom_value_detail(c, value, nil)
257 add_custom_field_detail(custom_field_id, value, nil)
250 end
258 end
251 # values added
259 # values added
252 (after - before).reject(&:blank?).each do |value|
260 (after - before).reject(&:blank?).each do |value|
253 add_custom_value_detail(c, nil, value)
261 add_custom_field_detail(custom_field_id, nil, value)
254 end
262 end
255 else
263 else
256 add_custom_value_detail(c, before, after)
264 add_custom_field_detail(custom_field_id, before, after)
257 end
265 end
258 }
266 end
259 end
267 end
260 start
268 start
261 end
269 end
262
270
263 # Adds a journal detail for an attribute change
271 # Adds a journal detail for an attribute change
264 def add_attribute_detail(attribute, old_value, value)
272 def add_attribute_detail(attribute, old_value, value)
265 add_detail('attr', attribute, old_value, value)
273 add_detail('attr', attribute, old_value, value)
266 end
274 end
267
275
268 # Adds a journal detail for a custom field value change
276 # Adds a journal detail for a custom field value change
269 def add_custom_value_detail(custom_value, old_value, value)
277 def add_custom_field_detail(custom_field_id, old_value, value)
270 add_detail('cf', custom_value.custom_field_id, old_value, value)
278 add_detail('cf', custom_field_id, old_value, value)
271 end
279 end
272
280
273 # Adds a journal detail
281 # Adds a journal detail
274 def add_detail(property, prop_key, old_value, value)
282 def add_detail(property, prop_key, old_value, value)
275 details << JournalDetail.new(
283 details << JournalDetail.new(
276 :property => property,
284 :property => property,
277 :prop_key => prop_key,
285 :prop_key => prop_key,
278 :old_value => old_value,
286 :old_value => old_value,
279 :value => value
287 :value => value
280 )
288 )
281 end
289 end
282
290
283 def split_private_notes
291 def split_private_notes
284 if private_notes?
292 if private_notes?
285 if notes.present?
293 if notes.present?
286 if details.any?
294 if details.any?
287 # Split the journal (notes/changes) so we don't have half-private journals
295 # Split the journal (notes/changes) so we don't have half-private journals
288 journal = Journal.new(:journalized => journalized, :user => user, :notes => nil, :private_notes => false)
296 journal = Journal.new(:journalized => journalized, :user => user, :notes => nil, :private_notes => false)
289 journal.details = details
297 journal.details = details
290 journal.save
298 journal.save
291 self.details = []
299 self.details = []
292 self.created_on = journal.created_on
300 self.created_on = journal.created_on
293 end
301 end
294 else
302 else
295 # Blank notes should not be private
303 # Blank notes should not be private
296 self.private_notes = false
304 self.private_notes = false
297 end
305 end
298 end
306 end
299 true
307 true
300 end
308 end
301
309
302 def send_notification
310 def send_notification
303 if notify? && (Setting.notified_events.include?('issue_updated') ||
311 if notify? && (Setting.notified_events.include?('issue_updated') ||
304 (Setting.notified_events.include?('issue_note_added') && notes.present?) ||
312 (Setting.notified_events.include?('issue_note_added') && notes.present?) ||
305 (Setting.notified_events.include?('issue_status_updated') && new_status.present?) ||
313 (Setting.notified_events.include?('issue_status_updated') && new_status.present?) ||
306 (Setting.notified_events.include?('issue_assigned_to_updated') && detail_for_attribute('assigned_to_id').present?) ||
314 (Setting.notified_events.include?('issue_assigned_to_updated') && detail_for_attribute('assigned_to_id').present?) ||
307 (Setting.notified_events.include?('issue_priority_updated') && new_value_for('priority_id').present?)
315 (Setting.notified_events.include?('issue_priority_updated') && new_value_for('priority_id').present?)
308 )
316 )
309 Mailer.deliver_issue_edit(self)
317 Mailer.deliver_issue_edit(self)
310 end
318 end
311 end
319 end
312 end
320 end
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,2970 +1,2988
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class IssueTest < ActiveSupport::TestCase
20 class IssueTest < ActiveSupport::TestCase
21 fixtures :projects, :users, :email_addresses, :user_preferences, :members, :member_roles, :roles,
21 fixtures :projects, :users, :email_addresses, :user_preferences, :members, :member_roles, :roles,
22 :groups_users,
22 :groups_users,
23 :trackers, :projects_trackers,
23 :trackers, :projects_trackers,
24 :enabled_modules,
24 :enabled_modules,
25 :versions,
25 :versions,
26 :issue_statuses, :issue_categories, :issue_relations, :workflows,
26 :issue_statuses, :issue_categories, :issue_relations, :workflows,
27 :enumerations,
27 :enumerations,
28 :issues, :journals, :journal_details,
28 :issues, :journals, :journal_details,
29 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
29 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
30 :time_entries
30 :time_entries
31
31
32 include Redmine::I18n
32 include Redmine::I18n
33
33
34 def setup
34 def setup
35 set_language_if_valid 'en'
35 set_language_if_valid 'en'
36 end
36 end
37
37
38 def teardown
38 def teardown
39 User.current = nil
39 User.current = nil
40 end
40 end
41
41
42 def test_initialize
42 def test_initialize
43 issue = Issue.new
43 issue = Issue.new
44
44
45 assert_nil issue.project_id
45 assert_nil issue.project_id
46 assert_nil issue.tracker_id
46 assert_nil issue.tracker_id
47 assert_nil issue.status_id
47 assert_nil issue.status_id
48 assert_nil issue.author_id
48 assert_nil issue.author_id
49 assert_nil issue.assigned_to_id
49 assert_nil issue.assigned_to_id
50 assert_nil issue.category_id
50 assert_nil issue.category_id
51
51
52 assert_equal IssuePriority.default, issue.priority
52 assert_equal IssuePriority.default, issue.priority
53 end
53 end
54
54
55 def test_create
55 def test_create
56 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
56 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
57 :status_id => 1, :priority => IssuePriority.all.first,
57 :status_id => 1, :priority => IssuePriority.all.first,
58 :subject => 'test_create',
58 :subject => 'test_create',
59 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
59 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
60 assert issue.save
60 assert issue.save
61 issue.reload
61 issue.reload
62 assert_equal 1.5, issue.estimated_hours
62 assert_equal 1.5, issue.estimated_hours
63 end
63 end
64
64
65 def test_create_minimal
65 def test_create_minimal
66 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create')
66 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create')
67 assert issue.save
67 assert issue.save
68 assert_equal issue.tracker.default_status, issue.status
68 assert_equal issue.tracker.default_status, issue.status
69 assert issue.description.nil?
69 assert issue.description.nil?
70 assert_nil issue.estimated_hours
70 assert_nil issue.estimated_hours
71 end
71 end
72
72
73 def test_create_with_all_fields_disabled
73 def test_create_with_all_fields_disabled
74 tracker = Tracker.find(1)
74 tracker = Tracker.find(1)
75 tracker.core_fields = []
75 tracker.core_fields = []
76 tracker.save!
76 tracker.save!
77
77
78 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create_with_all_fields_disabled')
78 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create_with_all_fields_disabled')
79 assert_save issue
79 assert_save issue
80 end
80 end
81
81
82 def test_start_date_format_should_be_validated
82 def test_start_date_format_should_be_validated
83 set_language_if_valid 'en'
83 set_language_if_valid 'en'
84 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
84 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
85 issue = Issue.new(:start_date => invalid_date)
85 issue = Issue.new(:start_date => invalid_date)
86 assert !issue.valid?
86 assert !issue.valid?
87 assert_include 'Start date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
87 assert_include 'Start date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
88 end
88 end
89 end
89 end
90
90
91 def test_due_date_format_should_be_validated
91 def test_due_date_format_should_be_validated
92 set_language_if_valid 'en'
92 set_language_if_valid 'en'
93 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
93 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
94 issue = Issue.new(:due_date => invalid_date)
94 issue = Issue.new(:due_date => invalid_date)
95 assert !issue.valid?
95 assert !issue.valid?
96 assert_include 'Due date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
96 assert_include 'Due date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
97 end
97 end
98 end
98 end
99
99
100 def test_due_date_lesser_than_start_date_should_not_validate
100 def test_due_date_lesser_than_start_date_should_not_validate
101 set_language_if_valid 'en'
101 set_language_if_valid 'en'
102 issue = Issue.new(:start_date => '2012-10-06', :due_date => '2012-10-02')
102 issue = Issue.new(:start_date => '2012-10-06', :due_date => '2012-10-02')
103 assert !issue.valid?
103 assert !issue.valid?
104 assert_include 'Due date must be greater than start date', issue.errors.full_messages
104 assert_include 'Due date must be greater than start date', issue.errors.full_messages
105 end
105 end
106
106
107 def test_start_date_lesser_than_soonest_start_should_not_validate_on_create
107 def test_start_date_lesser_than_soonest_start_should_not_validate_on_create
108 issue = Issue.generate(:start_date => '2013-06-04')
108 issue = Issue.generate(:start_date => '2013-06-04')
109 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
109 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
110 assert !issue.valid?
110 assert !issue.valid?
111 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
111 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
112 end
112 end
113
113
114 def test_start_date_lesser_than_soonest_start_should_not_validate_on_update_if_changed
114 def test_start_date_lesser_than_soonest_start_should_not_validate_on_update_if_changed
115 issue = Issue.generate!(:start_date => '2013-06-04')
115 issue = Issue.generate!(:start_date => '2013-06-04')
116 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
116 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
117 issue.start_date = '2013-06-07'
117 issue.start_date = '2013-06-07'
118 assert !issue.valid?
118 assert !issue.valid?
119 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
119 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
120 end
120 end
121
121
122 def test_start_date_lesser_than_soonest_start_should_validate_on_update_if_unchanged
122 def test_start_date_lesser_than_soonest_start_should_validate_on_update_if_unchanged
123 issue = Issue.generate!(:start_date => '2013-06-04')
123 issue = Issue.generate!(:start_date => '2013-06-04')
124 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
124 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
125 assert issue.valid?
125 assert issue.valid?
126 end
126 end
127
127
128 def test_estimated_hours_should_be_validated
128 def test_estimated_hours_should_be_validated
129 set_language_if_valid 'en'
129 set_language_if_valid 'en'
130 ['-2'].each do |invalid|
130 ['-2'].each do |invalid|
131 issue = Issue.new(:estimated_hours => invalid)
131 issue = Issue.new(:estimated_hours => invalid)
132 assert !issue.valid?
132 assert !issue.valid?
133 assert_include 'Estimated time is invalid', issue.errors.full_messages
133 assert_include 'Estimated time is invalid', issue.errors.full_messages
134 end
134 end
135 end
135 end
136
136
137 def test_create_with_required_custom_field
137 def test_create_with_required_custom_field
138 set_language_if_valid 'en'
138 set_language_if_valid 'en'
139 field = IssueCustomField.find_by_name('Database')
139 field = IssueCustomField.find_by_name('Database')
140 field.update!(:is_required => true)
140 field.update!(:is_required => true)
141
141
142 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
142 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
143 :status_id => 1, :subject => 'test_create',
143 :status_id => 1, :subject => 'test_create',
144 :description => 'IssueTest#test_create_with_required_custom_field')
144 :description => 'IssueTest#test_create_with_required_custom_field')
145 assert issue.available_custom_fields.include?(field)
145 assert issue.available_custom_fields.include?(field)
146 # No value for the custom field
146 # No value for the custom field
147 assert !issue.save
147 assert !issue.save
148 assert_equal ["Database cannot be blank"], issue.errors.full_messages
148 assert_equal ["Database cannot be blank"], issue.errors.full_messages
149 # Blank value
149 # Blank value
150 issue.custom_field_values = { field.id => '' }
150 issue.custom_field_values = { field.id => '' }
151 assert !issue.save
151 assert !issue.save
152 assert_equal ["Database cannot be blank"], issue.errors.full_messages
152 assert_equal ["Database cannot be blank"], issue.errors.full_messages
153 # Invalid value
153 # Invalid value
154 issue.custom_field_values = { field.id => 'SQLServer' }
154 issue.custom_field_values = { field.id => 'SQLServer' }
155 assert !issue.save
155 assert !issue.save
156 assert_equal ["Database is not included in the list"], issue.errors.full_messages
156 assert_equal ["Database is not included in the list"], issue.errors.full_messages
157 # Valid value
157 # Valid value
158 issue.custom_field_values = { field.id => 'PostgreSQL' }
158 issue.custom_field_values = { field.id => 'PostgreSQL' }
159 assert issue.save
159 assert issue.save
160 issue.reload
160 issue.reload
161 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
161 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
162 end
162 end
163
163
164 def test_create_with_group_assignment
164 def test_create_with_group_assignment
165 with_settings :issue_group_assignment => '1' do
165 with_settings :issue_group_assignment => '1' do
166 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
166 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
167 :subject => 'Group assignment',
167 :subject => 'Group assignment',
168 :assigned_to_id => 11).save
168 :assigned_to_id => 11).save
169 issue = Issue.order('id DESC').first
169 issue = Issue.order('id DESC').first
170 assert_kind_of Group, issue.assigned_to
170 assert_kind_of Group, issue.assigned_to
171 assert_equal Group.find(11), issue.assigned_to
171 assert_equal Group.find(11), issue.assigned_to
172 end
172 end
173 end
173 end
174
174
175 def test_create_with_parent_issue_id
175 def test_create_with_parent_issue_id
176 issue = Issue.new(:project_id => 1, :tracker_id => 1,
176 issue = Issue.new(:project_id => 1, :tracker_id => 1,
177 :author_id => 1, :subject => 'Group assignment',
177 :author_id => 1, :subject => 'Group assignment',
178 :parent_issue_id => 1)
178 :parent_issue_id => 1)
179 assert_save issue
179 assert_save issue
180 assert_equal 1, issue.parent_issue_id
180 assert_equal 1, issue.parent_issue_id
181 assert_equal Issue.find(1), issue.parent
181 assert_equal Issue.find(1), issue.parent
182 end
182 end
183
183
184 def test_create_with_sharp_parent_issue_id
184 def test_create_with_sharp_parent_issue_id
185 issue = Issue.new(:project_id => 1, :tracker_id => 1,
185 issue = Issue.new(:project_id => 1, :tracker_id => 1,
186 :author_id => 1, :subject => 'Group assignment',
186 :author_id => 1, :subject => 'Group assignment',
187 :parent_issue_id => "#1")
187 :parent_issue_id => "#1")
188 assert_save issue
188 assert_save issue
189 assert_equal 1, issue.parent_issue_id
189 assert_equal 1, issue.parent_issue_id
190 assert_equal Issue.find(1), issue.parent
190 assert_equal Issue.find(1), issue.parent
191 end
191 end
192
192
193 def test_create_with_invalid_parent_issue_id
193 def test_create_with_invalid_parent_issue_id
194 set_language_if_valid 'en'
194 set_language_if_valid 'en'
195 issue = Issue.new(:project_id => 1, :tracker_id => 1,
195 issue = Issue.new(:project_id => 1, :tracker_id => 1,
196 :author_id => 1, :subject => 'Group assignment',
196 :author_id => 1, :subject => 'Group assignment',
197 :parent_issue_id => '01ABC')
197 :parent_issue_id => '01ABC')
198 assert !issue.save
198 assert !issue.save
199 assert_equal '01ABC', issue.parent_issue_id
199 assert_equal '01ABC', issue.parent_issue_id
200 assert_include 'Parent task is invalid', issue.errors.full_messages
200 assert_include 'Parent task is invalid', issue.errors.full_messages
201 end
201 end
202
202
203 def test_create_with_invalid_sharp_parent_issue_id
203 def test_create_with_invalid_sharp_parent_issue_id
204 set_language_if_valid 'en'
204 set_language_if_valid 'en'
205 issue = Issue.new(:project_id => 1, :tracker_id => 1,
205 issue = Issue.new(:project_id => 1, :tracker_id => 1,
206 :author_id => 1, :subject => 'Group assignment',
206 :author_id => 1, :subject => 'Group assignment',
207 :parent_issue_id => '#01ABC')
207 :parent_issue_id => '#01ABC')
208 assert !issue.save
208 assert !issue.save
209 assert_equal '#01ABC', issue.parent_issue_id
209 assert_equal '#01ABC', issue.parent_issue_id
210 assert_include 'Parent task is invalid', issue.errors.full_messages
210 assert_include 'Parent task is invalid', issue.errors.full_messages
211 end
211 end
212
212
213 def assert_visibility_match(user, issues)
213 def assert_visibility_match(user, issues)
214 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
214 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
215 end
215 end
216
216
217 def test_visible_scope_for_anonymous
217 def test_visible_scope_for_anonymous
218 # Anonymous user should see issues of public projects only
218 # Anonymous user should see issues of public projects only
219 issues = Issue.visible(User.anonymous).to_a
219 issues = Issue.visible(User.anonymous).to_a
220 assert issues.any?
220 assert issues.any?
221 assert_nil issues.detect {|issue| !issue.project.is_public?}
221 assert_nil issues.detect {|issue| !issue.project.is_public?}
222 assert_nil issues.detect {|issue| issue.is_private?}
222 assert_nil issues.detect {|issue| issue.is_private?}
223 assert_visibility_match User.anonymous, issues
223 assert_visibility_match User.anonymous, issues
224 end
224 end
225
225
226 def test_visible_scope_for_anonymous_without_view_issues_permissions
226 def test_visible_scope_for_anonymous_without_view_issues_permissions
227 # Anonymous user should not see issues without permission
227 # Anonymous user should not see issues without permission
228 Role.anonymous.remove_permission!(:view_issues)
228 Role.anonymous.remove_permission!(:view_issues)
229 issues = Issue.visible(User.anonymous).to_a
229 issues = Issue.visible(User.anonymous).to_a
230 assert issues.empty?
230 assert issues.empty?
231 assert_visibility_match User.anonymous, issues
231 assert_visibility_match User.anonymous, issues
232 end
232 end
233
233
234 def test_visible_scope_for_anonymous_without_view_issues_permissions_and_membership
234 def test_visible_scope_for_anonymous_without_view_issues_permissions_and_membership
235 Role.anonymous.remove_permission!(:view_issues)
235 Role.anonymous.remove_permission!(:view_issues)
236 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [2])
236 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [2])
237
237
238 issues = Issue.visible(User.anonymous).all
238 issues = Issue.visible(User.anonymous).all
239 assert issues.any?
239 assert issues.any?
240 assert_equal [1], issues.map(&:project_id).uniq.sort
240 assert_equal [1], issues.map(&:project_id).uniq.sort
241 assert_visibility_match User.anonymous, issues
241 assert_visibility_match User.anonymous, issues
242 end
242 end
243
243
244 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
244 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
245 Role.anonymous.update!(:issues_visibility => 'default')
245 Role.anonymous.update!(:issues_visibility => 'default')
246 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
246 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
247 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
247 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
248 assert !issue.visible?(User.anonymous)
248 assert !issue.visible?(User.anonymous)
249 end
249 end
250
250
251 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
251 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
252 assert Role.anonymous.update!(:issues_visibility => 'own')
252 assert Role.anonymous.update!(:issues_visibility => 'own')
253 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
253 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
254 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
254 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
255 assert !issue.visible?(User.anonymous)
255 assert !issue.visible?(User.anonymous)
256 end
256 end
257
257
258 def test_visible_scope_for_non_member
258 def test_visible_scope_for_non_member
259 user = User.find(9)
259 user = User.find(9)
260 assert user.projects.empty?
260 assert user.projects.empty?
261 # Non member user should see issues of public projects only
261 # Non member user should see issues of public projects only
262 issues = Issue.visible(user).to_a
262 issues = Issue.visible(user).to_a
263 assert issues.any?
263 assert issues.any?
264 assert_nil issues.detect {|issue| !issue.project.is_public?}
264 assert_nil issues.detect {|issue| !issue.project.is_public?}
265 assert_nil issues.detect {|issue| issue.is_private?}
265 assert_nil issues.detect {|issue| issue.is_private?}
266 assert_visibility_match user, issues
266 assert_visibility_match user, issues
267 end
267 end
268
268
269 def test_visible_scope_for_non_member_with_own_issues_visibility
269 def test_visible_scope_for_non_member_with_own_issues_visibility
270 Role.non_member.update! :issues_visibility => 'own'
270 Role.non_member.update! :issues_visibility => 'own'
271 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
271 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
272 user = User.find(9)
272 user = User.find(9)
273
273
274 issues = Issue.visible(user).to_a
274 issues = Issue.visible(user).to_a
275 assert issues.any?
275 assert issues.any?
276 assert_nil issues.detect {|issue| issue.author != user}
276 assert_nil issues.detect {|issue| issue.author != user}
277 assert_visibility_match user, issues
277 assert_visibility_match user, issues
278 end
278 end
279
279
280 def test_visible_scope_for_non_member_without_view_issues_permissions
280 def test_visible_scope_for_non_member_without_view_issues_permissions
281 # Non member user should not see issues without permission
281 # Non member user should not see issues without permission
282 Role.non_member.remove_permission!(:view_issues)
282 Role.non_member.remove_permission!(:view_issues)
283 user = User.find(9)
283 user = User.find(9)
284 assert user.projects.empty?
284 assert user.projects.empty?
285 issues = Issue.visible(user).to_a
285 issues = Issue.visible(user).to_a
286 assert issues.empty?
286 assert issues.empty?
287 assert_visibility_match user, issues
287 assert_visibility_match user, issues
288 end
288 end
289
289
290 def test_visible_scope_for_non_member_without_view_issues_permissions_and_membership
290 def test_visible_scope_for_non_member_without_view_issues_permissions_and_membership
291 Role.non_member.remove_permission!(:view_issues)
291 Role.non_member.remove_permission!(:view_issues)
292 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [2])
292 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [2])
293 user = User.find(9)
293 user = User.find(9)
294
294
295 issues = Issue.visible(user).all
295 issues = Issue.visible(user).all
296 assert issues.any?
296 assert issues.any?
297 assert_equal [1], issues.map(&:project_id).uniq.sort
297 assert_equal [1], issues.map(&:project_id).uniq.sort
298 assert_visibility_match user, issues
298 assert_visibility_match user, issues
299 end
299 end
300
300
301 def test_visible_scope_for_member
301 def test_visible_scope_for_member
302 user = User.find(9)
302 user = User.find(9)
303 # User should see issues of projects for which user has view_issues permissions only
303 # User should see issues of projects for which user has view_issues permissions only
304 Role.non_member.remove_permission!(:view_issues)
304 Role.non_member.remove_permission!(:view_issues)
305 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
305 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
306 issues = Issue.visible(user).to_a
306 issues = Issue.visible(user).to_a
307 assert issues.any?
307 assert issues.any?
308 assert_nil issues.detect {|issue| issue.project_id != 3}
308 assert_nil issues.detect {|issue| issue.project_id != 3}
309 assert_nil issues.detect {|issue| issue.is_private?}
309 assert_nil issues.detect {|issue| issue.is_private?}
310 assert_visibility_match user, issues
310 assert_visibility_match user, issues
311 end
311 end
312
312
313 def test_visible_scope_for_member_without_view_issues_permission_and_non_member_role_having_the_permission
313 def test_visible_scope_for_member_without_view_issues_permission_and_non_member_role_having_the_permission
314 Role.non_member.add_permission!(:view_issues)
314 Role.non_member.add_permission!(:view_issues)
315 Role.find(1).remove_permission!(:view_issues)
315 Role.find(1).remove_permission!(:view_issues)
316 user = User.find(2)
316 user = User.find(2)
317
317
318 assert_equal 0, Issue.where(:project_id => 1).visible(user).count
318 assert_equal 0, Issue.where(:project_id => 1).visible(user).count
319 assert_equal false, Issue.where(:project_id => 1).first.visible?(user)
319 assert_equal false, Issue.where(:project_id => 1).first.visible?(user)
320 end
320 end
321
321
322 def test_visible_scope_with_custom_non_member_role_having_restricted_permission
322 def test_visible_scope_with_custom_non_member_role_having_restricted_permission
323 role = Role.generate!(:permissions => [:view_project])
323 role = Role.generate!(:permissions => [:view_project])
324 assert Role.non_member.has_permission?(:view_issues)
324 assert Role.non_member.has_permission?(:view_issues)
325 user = User.generate!
325 user = User.generate!
326 Member.create!(:principal => Group.non_member, :project_id => 1, :roles => [role])
326 Member.create!(:principal => Group.non_member, :project_id => 1, :roles => [role])
327
327
328 issues = Issue.visible(user).to_a
328 issues = Issue.visible(user).to_a
329 assert issues.any?
329 assert issues.any?
330 assert_nil issues.detect {|issue| issue.project_id == 1}
330 assert_nil issues.detect {|issue| issue.project_id == 1}
331 end
331 end
332
332
333 def test_visible_scope_with_custom_non_member_role_having_extended_permission
333 def test_visible_scope_with_custom_non_member_role_having_extended_permission
334 role = Role.generate!(:permissions => [:view_project, :view_issues])
334 role = Role.generate!(:permissions => [:view_project, :view_issues])
335 Role.non_member.remove_permission!(:view_issues)
335 Role.non_member.remove_permission!(:view_issues)
336 user = User.generate!
336 user = User.generate!
337 Member.create!(:principal => Group.non_member, :project_id => 1, :roles => [role])
337 Member.create!(:principal => Group.non_member, :project_id => 1, :roles => [role])
338
338
339 issues = Issue.visible(user).to_a
339 issues = Issue.visible(user).to_a
340 assert issues.any?
340 assert issues.any?
341 assert_not_nil issues.detect {|issue| issue.project_id == 1}
341 assert_not_nil issues.detect {|issue| issue.project_id == 1}
342 end
342 end
343
343
344 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
344 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
345 user = User.find(8)
345 user = User.find(8)
346 assert user.groups.any?
346 assert user.groups.any?
347 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
347 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
348 Role.non_member.remove_permission!(:view_issues)
348 Role.non_member.remove_permission!(:view_issues)
349
349
350 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 3,
350 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 3,
351 :status_id => 1, :priority => IssuePriority.all.first,
351 :status_id => 1, :priority => IssuePriority.all.first,
352 :subject => 'Assignment test',
352 :subject => 'Assignment test',
353 :assigned_to => user.groups.first,
353 :assigned_to => user.groups.first,
354 :is_private => true)
354 :is_private => true)
355
355
356 Role.find(2).update! :issues_visibility => 'default'
356 Role.find(2).update! :issues_visibility => 'default'
357 issues = Issue.visible(User.find(8)).to_a
357 issues = Issue.visible(User.find(8)).to_a
358 assert issues.any?
358 assert issues.any?
359 assert issues.include?(issue)
359 assert issues.include?(issue)
360
360
361 Role.find(2).update! :issues_visibility => 'own'
361 Role.find(2).update! :issues_visibility => 'own'
362 issues = Issue.visible(User.find(8)).to_a
362 issues = Issue.visible(User.find(8)).to_a
363 assert issues.any?
363 assert issues.any?
364 assert_include issue, issues
364 assert_include issue, issues
365 end
365 end
366
366
367 def test_visible_scope_for_member_with_limited_tracker_ids
367 def test_visible_scope_for_member_with_limited_tracker_ids
368 role = Role.find(1)
368 role = Role.find(1)
369 role.set_permission_trackers :view_issues, [2]
369 role.set_permission_trackers :view_issues, [2]
370 role.save!
370 role.save!
371 user = User.find(2)
371 user = User.find(2)
372
372
373 issues = Issue.where(:project_id => 1).visible(user).to_a
373 issues = Issue.where(:project_id => 1).visible(user).to_a
374 assert issues.any?
374 assert issues.any?
375 assert_equal [2], issues.map(&:tracker_id).uniq
375 assert_equal [2], issues.map(&:tracker_id).uniq
376
376
377 assert Issue.where(:project_id => 1).all? {|issue| issue.visible?(user) ^ issue.tracker_id != 2}
377 assert Issue.where(:project_id => 1).all? {|issue| issue.visible?(user) ^ issue.tracker_id != 2}
378 end
378 end
379
379
380 def test_visible_scope_should_consider_tracker_ids_on_each_project
380 def test_visible_scope_should_consider_tracker_ids_on_each_project
381 user = User.generate!
381 user = User.generate!
382
382
383 project1 = Project.generate!
383 project1 = Project.generate!
384 role1 = Role.generate!
384 role1 = Role.generate!
385 role1.add_permission! :view_issues
385 role1.add_permission! :view_issues
386 role1.set_permission_trackers :view_issues, :all
386 role1.set_permission_trackers :view_issues, :all
387 role1.save!
387 role1.save!
388 User.add_to_project(user, project1, role1)
388 User.add_to_project(user, project1, role1)
389
389
390 project2 = Project.generate!
390 project2 = Project.generate!
391 role2 = Role.generate!
391 role2 = Role.generate!
392 role2.add_permission! :view_issues
392 role2.add_permission! :view_issues
393 role2.set_permission_trackers :view_issues, [2]
393 role2.set_permission_trackers :view_issues, [2]
394 role2.save!
394 role2.save!
395 User.add_to_project(user, project2, role2)
395 User.add_to_project(user, project2, role2)
396
396
397 visible_issues = [
397 visible_issues = [
398 Issue.generate!(:project => project1, :tracker_id => 1),
398 Issue.generate!(:project => project1, :tracker_id => 1),
399 Issue.generate!(:project => project1, :tracker_id => 2),
399 Issue.generate!(:project => project1, :tracker_id => 2),
400 Issue.generate!(:project => project2, :tracker_id => 2)
400 Issue.generate!(:project => project2, :tracker_id => 2)
401 ]
401 ]
402 hidden_issue = Issue.generate!(:project => project2, :tracker_id => 1)
402 hidden_issue = Issue.generate!(:project => project2, :tracker_id => 1)
403
403
404 issues = Issue.where(:project_id => [project1.id, project2.id]).visible(user)
404 issues = Issue.where(:project_id => [project1.id, project2.id]).visible(user)
405 assert_equal visible_issues.map(&:id), issues.ids.sort
405 assert_equal visible_issues.map(&:id), issues.ids.sort
406
406
407 assert visible_issues.all? {|issue| issue.visible?(user)}
407 assert visible_issues.all? {|issue| issue.visible?(user)}
408 assert !hidden_issue.visible?(user)
408 assert !hidden_issue.visible?(user)
409 end
409 end
410
410
411 def test_visible_scope_should_not_consider_roles_without_view_issues_permission
411 def test_visible_scope_should_not_consider_roles_without_view_issues_permission
412 user = User.generate!
412 user = User.generate!
413 role1 = Role.generate!
413 role1 = Role.generate!
414 role1.remove_permission! :view_issues
414 role1.remove_permission! :view_issues
415 role1.set_permission_trackers :view_issues, :all
415 role1.set_permission_trackers :view_issues, :all
416 role1.save!
416 role1.save!
417 role2 = Role.generate!
417 role2 = Role.generate!
418 role2.add_permission! :view_issues
418 role2.add_permission! :view_issues
419 role2.set_permission_trackers :view_issues, [2]
419 role2.set_permission_trackers :view_issues, [2]
420 role2.save!
420 role2.save!
421 User.add_to_project(user, Project.find(1), [role1, role2])
421 User.add_to_project(user, Project.find(1), [role1, role2])
422
422
423 issues = Issue.where(:project_id => 1).visible(user).to_a
423 issues = Issue.where(:project_id => 1).visible(user).to_a
424 assert issues.any?
424 assert issues.any?
425 assert_equal [2], issues.map(&:tracker_id).uniq
425 assert_equal [2], issues.map(&:tracker_id).uniq
426
426
427 assert Issue.where(:project_id => 1).all? {|issue| issue.visible?(user) ^ issue.tracker_id != 2}
427 assert Issue.where(:project_id => 1).all? {|issue| issue.visible?(user) ^ issue.tracker_id != 2}
428 end
428 end
429
429
430 def test_visible_scope_for_admin
430 def test_visible_scope_for_admin
431 user = User.find(1)
431 user = User.find(1)
432 user.members.each(&:destroy)
432 user.members.each(&:destroy)
433 assert user.projects.empty?
433 assert user.projects.empty?
434 issues = Issue.visible(user).to_a
434 issues = Issue.visible(user).to_a
435 assert issues.any?
435 assert issues.any?
436 # Admin should see issues on private projects that admin does not belong to
436 # Admin should see issues on private projects that admin does not belong to
437 assert issues.detect {|issue| !issue.project.is_public?}
437 assert issues.detect {|issue| !issue.project.is_public?}
438 # Admin should see private issues of other users
438 # Admin should see private issues of other users
439 assert issues.detect {|issue| issue.is_private? && issue.author != user}
439 assert issues.detect {|issue| issue.is_private? && issue.author != user}
440 assert_visibility_match user, issues
440 assert_visibility_match user, issues
441 end
441 end
442
442
443 def test_visible_scope_with_project
443 def test_visible_scope_with_project
444 project = Project.find(1)
444 project = Project.find(1)
445 issues = Issue.visible(User.find(2), :project => project).to_a
445 issues = Issue.visible(User.find(2), :project => project).to_a
446 projects = issues.collect(&:project).uniq
446 projects = issues.collect(&:project).uniq
447 assert_equal 1, projects.size
447 assert_equal 1, projects.size
448 assert_equal project, projects.first
448 assert_equal project, projects.first
449 end
449 end
450
450
451 def test_visible_scope_with_project_and_subprojects
451 def test_visible_scope_with_project_and_subprojects
452 project = Project.find(1)
452 project = Project.find(1)
453 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).to_a
453 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).to_a
454 projects = issues.collect(&:project).uniq
454 projects = issues.collect(&:project).uniq
455 assert projects.size > 1
455 assert projects.size > 1
456 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
456 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
457 end
457 end
458
458
459 def test_visible_and_nested_set_scopes
459 def test_visible_and_nested_set_scopes
460 user = User.generate!
460 user = User.generate!
461 parent = Issue.generate!(:assigned_to => user)
461 parent = Issue.generate!(:assigned_to => user)
462 assert parent.visible?(user)
462 assert parent.visible?(user)
463 child1 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
463 child1 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
464 child2 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
464 child2 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
465 parent.reload
465 parent.reload
466 child1.reload
466 child1.reload
467 child2.reload
467 child2.reload
468 assert child1.visible?(user)
468 assert child1.visible?(user)
469 assert child2.visible?(user)
469 assert child2.visible?(user)
470 assert_equal 2, parent.descendants.count
470 assert_equal 2, parent.descendants.count
471 assert_equal 2, parent.descendants.visible(user).count
471 assert_equal 2, parent.descendants.visible(user).count
472 # awesome_nested_set 2-1-stable branch has regression.
472 # awesome_nested_set 2-1-stable branch has regression.
473 # https://github.com/collectiveidea/awesome_nested_set/commit/3d5ac746542b564f6586c2316180254b088bebb6
473 # https://github.com/collectiveidea/awesome_nested_set/commit/3d5ac746542b564f6586c2316180254b088bebb6
474 # ActiveRecord::StatementInvalid: SQLite3::SQLException: ambiguous column name: lft:
474 # ActiveRecord::StatementInvalid: SQLite3::SQLException: ambiguous column name: lft:
475 assert_equal 2, parent.descendants.collect{|i| i}.size
475 assert_equal 2, parent.descendants.collect{|i| i}.size
476 assert_equal 2, parent.descendants.visible(user).collect{|i| i}.size
476 assert_equal 2, parent.descendants.visible(user).collect{|i| i}.size
477 end
477 end
478
478
479 def test_visible_scope_with_unsaved_user_should_not_raise_an_error
479 def test_visible_scope_with_unsaved_user_should_not_raise_an_error
480 user = User.new
480 user = User.new
481 assert_nothing_raised do
481 assert_nothing_raised do
482 Issue.visible(user).to_a
482 Issue.visible(user).to_a
483 end
483 end
484 end
484 end
485
485
486 def test_open_scope
486 def test_open_scope
487 issues = Issue.open.to_a
487 issues = Issue.open.to_a
488 assert_nil issues.detect(&:closed?)
488 assert_nil issues.detect(&:closed?)
489 end
489 end
490
490
491 def test_open_scope_with_arg
491 def test_open_scope_with_arg
492 issues = Issue.open(false).to_a
492 issues = Issue.open(false).to_a
493 assert_equal issues, issues.select(&:closed?)
493 assert_equal issues, issues.select(&:closed?)
494 end
494 end
495
495
496 def test_fixed_version_scope_with_a_version_should_return_its_fixed_issues
496 def test_fixed_version_scope_with_a_version_should_return_its_fixed_issues
497 version = Version.find(2)
497 version = Version.find(2)
498 assert version.fixed_issues.any?
498 assert version.fixed_issues.any?
499 assert_equal version.fixed_issues.to_a.sort, Issue.fixed_version(version).to_a.sort
499 assert_equal version.fixed_issues.to_a.sort, Issue.fixed_version(version).to_a.sort
500 end
500 end
501
501
502 def test_fixed_version_scope_with_empty_array_should_return_no_result
502 def test_fixed_version_scope_with_empty_array_should_return_no_result
503 assert_equal 0, Issue.fixed_version([]).count
503 assert_equal 0, Issue.fixed_version([]).count
504 end
504 end
505
505
506 def test_assigned_to_scope_should_return_issues_assigned_to_the_user
506 def test_assigned_to_scope_should_return_issues_assigned_to_the_user
507 user = User.generate!
507 user = User.generate!
508 issue = Issue.generate!
508 issue = Issue.generate!
509 Issue.where(:id => issue.id).update_all :assigned_to_id => user.id
509 Issue.where(:id => issue.id).update_all :assigned_to_id => user.id
510 assert_equal [issue], Issue.assigned_to(user).to_a
510 assert_equal [issue], Issue.assigned_to(user).to_a
511 end
511 end
512
512
513 def test_assigned_to_scope_should_return_issues_assigned_to_the_user_groups
513 def test_assigned_to_scope_should_return_issues_assigned_to_the_user_groups
514 group = Group.generate!
514 group = Group.generate!
515 user = User.generate!
515 user = User.generate!
516 group.users << user
516 group.users << user
517 issue = Issue.generate!
517 issue = Issue.generate!
518 Issue.where(:id => issue.id).update_all :assigned_to_id => group.id
518 Issue.where(:id => issue.id).update_all :assigned_to_id => group.id
519 assert_equal [issue], Issue.assigned_to(user).to_a
519 assert_equal [issue], Issue.assigned_to(user).to_a
520 end
520 end
521
521
522 def test_errors_full_messages_should_include_custom_fields_errors
522 def test_errors_full_messages_should_include_custom_fields_errors
523 field = IssueCustomField.find_by_name('Database')
523 field = IssueCustomField.find_by_name('Database')
524
524
525 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
525 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
526 :status_id => 1, :subject => 'test_create',
526 :status_id => 1, :subject => 'test_create',
527 :description => 'IssueTest#test_create_with_required_custom_field')
527 :description => 'IssueTest#test_create_with_required_custom_field')
528 assert issue.available_custom_fields.include?(field)
528 assert issue.available_custom_fields.include?(field)
529 # Invalid value
529 # Invalid value
530 issue.custom_field_values = { field.id => 'SQLServer' }
530 issue.custom_field_values = { field.id => 'SQLServer' }
531
531
532 assert !issue.valid?
532 assert !issue.valid?
533 assert_equal 1, issue.errors.full_messages.size
533 assert_equal 1, issue.errors.full_messages.size
534 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
534 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
535 issue.errors.full_messages.first
535 issue.errors.full_messages.first
536 end
536 end
537
537
538 def test_update_issue_with_required_custom_field
538 def test_update_issue_with_required_custom_field
539 field = IssueCustomField.find_by_name('Database')
539 field = IssueCustomField.find_by_name('Database')
540 field.update!(:is_required => true)
540 field.update!(:is_required => true)
541
541
542 issue = Issue.find(1)
542 issue = Issue.find(1)
543 assert_nil issue.custom_value_for(field)
543 assert_nil issue.custom_value_for(field)
544 assert issue.available_custom_fields.include?(field)
544 assert issue.available_custom_fields.include?(field)
545 # No change to custom values, issue can be saved
545 # No change to custom values, issue can be saved
546 assert issue.save
546 assert issue.save
547 # Blank value
547 # Blank value
548 issue.custom_field_values = { field.id => '' }
548 issue.custom_field_values = { field.id => '' }
549 assert !issue.save
549 assert !issue.save
550 # Valid value
550 # Valid value
551 issue.custom_field_values = { field.id => 'PostgreSQL' }
551 issue.custom_field_values = { field.id => 'PostgreSQL' }
552 assert issue.save
552 assert issue.save
553 issue.reload
553 issue.reload
554 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
554 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
555 end
555 end
556
556
557 def test_should_not_update_attributes_if_custom_fields_validation_fails
557 def test_should_not_update_attributes_if_custom_fields_validation_fails
558 issue = Issue.find(1)
558 issue = Issue.find(1)
559 field = IssueCustomField.find_by_name('Database')
559 field = IssueCustomField.find_by_name('Database')
560 assert issue.available_custom_fields.include?(field)
560 assert issue.available_custom_fields.include?(field)
561
561
562 issue.custom_field_values = { field.id => 'Invalid' }
562 issue.custom_field_values = { field.id => 'Invalid' }
563 issue.subject = 'Should be not be saved'
563 issue.subject = 'Should be not be saved'
564 assert !issue.save
564 assert !issue.save
565
565
566 issue.reload
566 issue.reload
567 assert_equal "Cannot print recipes", issue.subject
567 assert_equal "Cannot print recipes", issue.subject
568 end
568 end
569
569
570 def test_should_not_recreate_custom_values_objects_on_update
570 def test_should_not_recreate_custom_values_objects_on_update
571 field = IssueCustomField.find_by_name('Database')
571 field = IssueCustomField.find_by_name('Database')
572
572
573 issue = Issue.find(1)
573 issue = Issue.find(1)
574 issue.custom_field_values = { field.id => 'PostgreSQL' }
574 issue.custom_field_values = { field.id => 'PostgreSQL' }
575 assert issue.save
575 assert issue.save
576 custom_value = issue.custom_value_for(field)
576 custom_value = issue.custom_value_for(field)
577 issue.reload
577 issue.reload
578 issue.custom_field_values = { field.id => 'MySQL' }
578 issue.custom_field_values = { field.id => 'MySQL' }
579 assert issue.save
579 assert issue.save
580 issue.reload
580 issue.reload
581 assert_equal custom_value.id, issue.custom_value_for(field).id
581 assert_equal custom_value.id, issue.custom_value_for(field).id
582 end
582 end
583
583
584 def test_setting_project_should_set_version_to_default_version
584 def test_setting_project_should_set_version_to_default_version
585 version = Version.generate!(:project_id => 1)
585 version = Version.generate!(:project_id => 1)
586 Project.find(1).update!(:default_version_id => version.id)
586 Project.find(1).update!(:default_version_id => version.id)
587
587
588 issue = Issue.new(:project_id => 1)
588 issue = Issue.new(:project_id => 1)
589 assert_equal version, issue.fixed_version
589 assert_equal version, issue.fixed_version
590 end
590 end
591
591
592 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
592 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
593 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1,
593 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1,
594 :status_id => 1, :subject => 'Test',
594 :status_id => 1, :subject => 'Test',
595 :custom_field_values => {'2' => 'Test'})
595 :custom_field_values => {'2' => 'Test'})
596 assert !Tracker.find(2).custom_field_ids.include?(2)
596 assert !Tracker.find(2).custom_field_ids.include?(2)
597
597
598 issue = Issue.find(issue.id)
598 issue = Issue.find(issue.id)
599 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
599 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
600
600
601 issue = Issue.find(issue.id)
601 issue = Issue.find(issue.id)
602 custom_value = issue.custom_value_for(2)
602 custom_value = issue.custom_value_for(2)
603 assert_not_nil custom_value
603 assert_not_nil custom_value
604 assert_equal 'Test', custom_value.value
604 assert_equal 'Test', custom_value.value
605 end
605 end
606
606
607 def test_assigning_tracker_id_should_reload_custom_fields_values
607 def test_assigning_tracker_id_should_reload_custom_fields_values
608 issue = Issue.new(:project => Project.find(1))
608 issue = Issue.new(:project => Project.find(1))
609 assert issue.custom_field_values.empty?
609 assert issue.custom_field_values.empty?
610 issue.tracker_id = 1
610 issue.tracker_id = 1
611 assert issue.custom_field_values.any?
611 assert issue.custom_field_values.any?
612 end
612 end
613
613
614 def test_assigning_attributes_should_assign_project_and_tracker_first
614 def test_assigning_attributes_should_assign_project_and_tracker_first
615 seq = sequence('seq')
615 seq = sequence('seq')
616 issue = Issue.new
616 issue = Issue.new
617 issue.expects(:project_id=).in_sequence(seq)
617 issue.expects(:project_id=).in_sequence(seq)
618 issue.expects(:tracker_id=).in_sequence(seq)
618 issue.expects(:tracker_id=).in_sequence(seq)
619 issue.expects(:subject=).in_sequence(seq)
619 issue.expects(:subject=).in_sequence(seq)
620 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
620 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
621 end
621 end
622
622
623 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
623 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
624 attributes = ActiveSupport::OrderedHash.new
624 attributes = ActiveSupport::OrderedHash.new
625 attributes['custom_field_values'] = { '1' => 'MySQL' }
625 attributes['custom_field_values'] = { '1' => 'MySQL' }
626 attributes['tracker_id'] = '1'
626 attributes['tracker_id'] = '1'
627 issue = Issue.new(:project => Project.find(1))
627 issue = Issue.new(:project => Project.find(1))
628 issue.attributes = attributes
628 issue.attributes = attributes
629 assert_equal 'MySQL', issue.custom_field_value(1)
629 assert_equal 'MySQL', issue.custom_field_value(1)
630 end
630 end
631
631
632 def test_changing_tracker_should_clear_disabled_core_fields
632 def test_changing_tracker_should_clear_disabled_core_fields
633 tracker = Tracker.find(2)
633 tracker = Tracker.find(2)
634 tracker.core_fields = tracker.core_fields - %w(due_date)
634 tracker.core_fields = tracker.core_fields - %w(due_date)
635 tracker.save!
635 tracker.save!
636
636
637 issue = Issue.generate!(:tracker_id => 1, :start_date => Date.today, :due_date => Date.today)
637 issue = Issue.generate!(:tracker_id => 1, :start_date => Date.today, :due_date => Date.today)
638 issue.save!
638 issue.save!
639
639
640 issue.tracker_id = 2
640 issue.tracker_id = 2
641 issue.save!
641 issue.save!
642 assert_not_nil issue.start_date
642 assert_not_nil issue.start_date
643 assert_nil issue.due_date
643 assert_nil issue.due_date
644 end
644 end
645
645
646 def test_changing_tracker_should_not_add_cleared_fields_to_journal
646 def test_attribute_cleared_on_tracker_change_should_be_journalized
647 CustomField.delete_all
647 tracker = Tracker.find(2)
648 tracker = Tracker.find(2)
648 tracker.core_fields = tracker.core_fields - %w(due_date)
649 tracker.core_fields = tracker.core_fields - %w(due_date)
649 tracker.save!
650 tracker.save!
650
651
651 issue = Issue.generate!(:tracker_id => 1, :due_date => Date.today)
652 issue = Issue.generate!(:tracker_id => 1, :due_date => Date.today)
652 issue.save!
653 issue.save!
653
654
654 assert_difference 'Journal.count' do
655 assert_difference 'Journal.count' do
655 issue.init_journal User.find(1)
656 issue.init_journal User.find(1)
656 issue.tracker_id = 2
657 issue.tracker_id = 2
657 issue.save!
658 issue.save!
658 assert_nil issue.due_date
659 assert_nil issue.due_date
659 end
660 end
660 journal = Journal.order('id DESC').first
661 journal = Journal.order('id DESC').first
661 assert_equal 1, journal.details.count
662 details = journal.details.select {|d| d.prop_key == 'due_date'}
663 assert_equal 1, details.count
662 end
664 end
663
665
664 def test_reload_should_reload_custom_field_values
666 def test_reload_should_reload_custom_field_values
665 issue = Issue.generate!
667 issue = Issue.generate!
666 issue.custom_field_values = {'2' => 'Foo'}
668 issue.custom_field_values = {'2' => 'Foo'}
667 issue.save!
669 issue.save!
668
670
669 issue = Issue.order('id desc').first
671 issue = Issue.order('id desc').first
670 assert_equal 'Foo', issue.custom_field_value(2)
672 assert_equal 'Foo', issue.custom_field_value(2)
671
673
672 issue.custom_field_values = {'2' => 'Bar'}
674 issue.custom_field_values = {'2' => 'Bar'}
673 assert_equal 'Bar', issue.custom_field_value(2)
675 assert_equal 'Bar', issue.custom_field_value(2)
674
676
675 issue.reload
677 issue.reload
676 assert_equal 'Foo', issue.custom_field_value(2)
678 assert_equal 'Foo', issue.custom_field_value(2)
677 end
679 end
678
680
679 def test_should_update_issue_with_disabled_tracker
681 def test_should_update_issue_with_disabled_tracker
680 p = Project.find(1)
682 p = Project.find(1)
681 issue = Issue.find(1)
683 issue = Issue.find(1)
682
684
683 p.trackers.delete(issue.tracker)
685 p.trackers.delete(issue.tracker)
684 assert !p.trackers.include?(issue.tracker)
686 assert !p.trackers.include?(issue.tracker)
685
687
686 issue.reload
688 issue.reload
687 issue.subject = 'New subject'
689 issue.subject = 'New subject'
688 assert issue.save
690 assert issue.save
689 end
691 end
690
692
691 def test_should_not_set_a_disabled_tracker
693 def test_should_not_set_a_disabled_tracker
692 p = Project.find(1)
694 p = Project.find(1)
693 p.trackers.delete(Tracker.find(2))
695 p.trackers.delete(Tracker.find(2))
694
696
695 issue = Issue.find(1)
697 issue = Issue.find(1)
696 issue.tracker_id = 2
698 issue.tracker_id = 2
697 issue.subject = 'New subject'
699 issue.subject = 'New subject'
698 assert !issue.save
700 assert !issue.save
699 assert_not_equal [], issue.errors[:tracker_id]
701 assert_not_equal [], issue.errors[:tracker_id]
700 end
702 end
701
703
702 def test_category_based_assignment
704 def test_category_based_assignment
703 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
705 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
704 :status_id => 1, :priority => IssuePriority.all.first,
706 :status_id => 1, :priority => IssuePriority.all.first,
705 :subject => 'Assignment test',
707 :subject => 'Assignment test',
706 :description => 'Assignment test', :category_id => 1)
708 :description => 'Assignment test', :category_id => 1)
707 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
709 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
708 end
710 end
709
711
710 def test_new_statuses_allowed_to
712 def test_new_statuses_allowed_to
711 WorkflowTransition.delete_all
713 WorkflowTransition.delete_all
712 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
714 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
713 :old_status_id => 1, :new_status_id => 2,
715 :old_status_id => 1, :new_status_id => 2,
714 :author => false, :assignee => false)
716 :author => false, :assignee => false)
715 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
717 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
716 :old_status_id => 1, :new_status_id => 3,
718 :old_status_id => 1, :new_status_id => 3,
717 :author => true, :assignee => false)
719 :author => true, :assignee => false)
718 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
720 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
719 :old_status_id => 1, :new_status_id => 4,
721 :old_status_id => 1, :new_status_id => 4,
720 :author => false, :assignee => true)
722 :author => false, :assignee => true)
721 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
723 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
722 :old_status_id => 1, :new_status_id => 5,
724 :old_status_id => 1, :new_status_id => 5,
723 :author => true, :assignee => true)
725 :author => true, :assignee => true)
724 status = IssueStatus.find(1)
726 status = IssueStatus.find(1)
725 role = Role.find(1)
727 role = Role.find(1)
726 tracker = Tracker.find(1)
728 tracker = Tracker.find(1)
727 user = User.find(2)
729 user = User.find(2)
728
730
729 issue = Issue.generate!(:tracker => tracker, :status => status,
731 issue = Issue.generate!(:tracker => tracker, :status => status,
730 :project_id => 1, :author_id => 1)
732 :project_id => 1, :author_id => 1)
731 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
733 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
732
734
733 issue = Issue.generate!(:tracker => tracker, :status => status,
735 issue = Issue.generate!(:tracker => tracker, :status => status,
734 :project_id => 1, :author => user)
736 :project_id => 1, :author => user)
735 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
737 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
736
738
737 issue = Issue.generate!(:tracker => tracker, :status => status,
739 issue = Issue.generate!(:tracker => tracker, :status => status,
738 :project_id => 1, :author_id => 1,
740 :project_id => 1, :author_id => 1,
739 :assigned_to => user)
741 :assigned_to => user)
740 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
742 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
741
743
742 issue = Issue.generate!(:tracker => tracker, :status => status,
744 issue = Issue.generate!(:tracker => tracker, :status => status,
743 :project_id => 1, :author => user,
745 :project_id => 1, :author => user,
744 :assigned_to => user)
746 :assigned_to => user)
745 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
747 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
746
748
747 group = Group.generate!
749 group = Group.generate!
748 group.users << user
750 group.users << user
749 issue = Issue.generate!(:tracker => tracker, :status => status,
751 issue = Issue.generate!(:tracker => tracker, :status => status,
750 :project_id => 1, :author => user,
752 :project_id => 1, :author => user,
751 :assigned_to => group)
753 :assigned_to => group)
752 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
754 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
753 end
755 end
754
756
755 def test_new_statuses_allowed_to_should_consider_group_assignment
757 def test_new_statuses_allowed_to_should_consider_group_assignment
756 WorkflowTransition.delete_all
758 WorkflowTransition.delete_all
757 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
759 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
758 :old_status_id => 1, :new_status_id => 4,
760 :old_status_id => 1, :new_status_id => 4,
759 :author => false, :assignee => true)
761 :author => false, :assignee => true)
760 user = User.find(2)
762 user = User.find(2)
761 group = Group.generate!
763 group = Group.generate!
762 group.users << user
764 group.users << user
763
765
764 issue = Issue.generate!(:author_id => 1, :assigned_to => group)
766 issue = Issue.generate!(:author_id => 1, :assigned_to => group)
765 assert_include 4, issue.new_statuses_allowed_to(user).map(&:id)
767 assert_include 4, issue.new_statuses_allowed_to(user).map(&:id)
766 end
768 end
767
769
768 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
770 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
769 admin = User.find(1)
771 admin = User.find(1)
770 issue = Issue.find(1)
772 issue = Issue.find(1)
771 assert !admin.member_of?(issue.project)
773 assert !admin.member_of?(issue.project)
772 expected_statuses = [issue.status] +
774 expected_statuses = [issue.status] +
773 WorkflowTransition.where(:old_status_id => issue.status_id).
775 WorkflowTransition.where(:old_status_id => issue.status_id).
774 map(&:new_status).uniq.sort
776 map(&:new_status).uniq.sort
775 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
777 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
776 end
778 end
777
779
778 def test_new_statuses_allowed_to_should_return_allowed_statuses_and_current_status_when_copying
780 def test_new_statuses_allowed_to_should_return_allowed_statuses_and_current_status_when_copying
779 Tracker.find(1).generate_transitions! :role_id => 1, :clear => true, 0 => [1, 3]
781 Tracker.find(1).generate_transitions! :role_id => 1, :clear => true, 0 => [1, 3]
780
782
781 orig = Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 4)
783 orig = Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 4)
782 issue = orig.copy
784 issue = orig.copy
783 assert_equal [1, 3, 4], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
785 assert_equal [1, 3, 4], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
784 assert_equal 4, issue.status_id
786 assert_equal 4, issue.status_id
785 end
787 end
786
788
787 def test_safe_attributes_names_should_not_include_disabled_field
789 def test_safe_attributes_names_should_not_include_disabled_field
788 tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
790 tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
789
791
790 issue = Issue.new(:tracker => tracker)
792 issue = Issue.new(:tracker => tracker)
791 assert_include 'tracker_id', issue.safe_attribute_names
793 assert_include 'tracker_id', issue.safe_attribute_names
792 assert_include 'status_id', issue.safe_attribute_names
794 assert_include 'status_id', issue.safe_attribute_names
793 assert_include 'subject', issue.safe_attribute_names
795 assert_include 'subject', issue.safe_attribute_names
794 assert_include 'description', issue.safe_attribute_names
796 assert_include 'description', issue.safe_attribute_names
795 assert_include 'custom_field_values', issue.safe_attribute_names
797 assert_include 'custom_field_values', issue.safe_attribute_names
796 assert_include 'custom_fields', issue.safe_attribute_names
798 assert_include 'custom_fields', issue.safe_attribute_names
797 assert_include 'lock_version', issue.safe_attribute_names
799 assert_include 'lock_version', issue.safe_attribute_names
798
800
799 tracker.core_fields.each do |field|
801 tracker.core_fields.each do |field|
800 assert_include field, issue.safe_attribute_names
802 assert_include field, issue.safe_attribute_names
801 end
803 end
802
804
803 tracker.disabled_core_fields.each do |field|
805 tracker.disabled_core_fields.each do |field|
804 assert_not_include field, issue.safe_attribute_names
806 assert_not_include field, issue.safe_attribute_names
805 end
807 end
806 end
808 end
807
809
808 def test_safe_attributes_should_ignore_disabled_fields
810 def test_safe_attributes_should_ignore_disabled_fields
809 tracker = Tracker.find(1)
811 tracker = Tracker.find(1)
810 tracker.core_fields = %w(assigned_to_id due_date)
812 tracker.core_fields = %w(assigned_to_id due_date)
811 tracker.save!
813 tracker.save!
812
814
813 issue = Issue.new(:tracker => tracker)
815 issue = Issue.new(:tracker => tracker)
814 issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
816 issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
815 assert_nil issue.start_date
817 assert_nil issue.start_date
816 assert_equal Date.parse('2012-07-14'), issue.due_date
818 assert_equal Date.parse('2012-07-14'), issue.due_date
817 end
819 end
818
820
819 def test_safe_attributes_should_accept_target_tracker_enabled_fields
821 def test_safe_attributes_should_accept_target_tracker_enabled_fields
820 source = Tracker.find(1)
822 source = Tracker.find(1)
821 source.core_fields = []
823 source.core_fields = []
822 source.save!
824 source.save!
823 target = Tracker.find(2)
825 target = Tracker.find(2)
824 target.core_fields = %w(assigned_to_id due_date)
826 target.core_fields = %w(assigned_to_id due_date)
825 target.save!
827 target.save!
826 user = User.find(2)
828 user = User.find(2)
827
829
828 issue = Issue.new(:project => Project.find(1), :tracker => source)
830 issue = Issue.new(:project => Project.find(1), :tracker => source)
829 issue.send :safe_attributes=, {'tracker_id' => 2, 'due_date' => '2012-07-14'}, user
831 issue.send :safe_attributes=, {'tracker_id' => 2, 'due_date' => '2012-07-14'}, user
830 assert_equal target, issue.tracker
832 assert_equal target, issue.tracker
831 assert_equal Date.parse('2012-07-14'), issue.due_date
833 assert_equal Date.parse('2012-07-14'), issue.due_date
832 end
834 end
833
835
834 def test_safe_attributes_should_not_include_readonly_fields
836 def test_safe_attributes_should_not_include_readonly_fields
835 WorkflowPermission.delete_all
837 WorkflowPermission.delete_all
836 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
838 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
837 :role_id => 1, :field_name => 'due_date',
839 :role_id => 1, :field_name => 'due_date',
838 :rule => 'readonly')
840 :rule => 'readonly')
839 user = User.find(2)
841 user = User.find(2)
840
842
841 issue = Issue.new(:project_id => 1, :tracker_id => 1)
843 issue = Issue.new(:project_id => 1, :tracker_id => 1)
842 assert_equal %w(due_date), issue.read_only_attribute_names(user)
844 assert_equal %w(due_date), issue.read_only_attribute_names(user)
843 assert_not_include 'due_date', issue.safe_attribute_names(user)
845 assert_not_include 'due_date', issue.safe_attribute_names(user)
844
846
845 issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
847 issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
846 assert_equal Date.parse('2012-07-14'), issue.start_date
848 assert_equal Date.parse('2012-07-14'), issue.start_date
847 assert_nil issue.due_date
849 assert_nil issue.due_date
848 end
850 end
849
851
850 def test_safe_attributes_should_not_include_readonly_custom_fields
852 def test_safe_attributes_should_not_include_readonly_custom_fields
851 cf1 = IssueCustomField.create!(:name => 'Writable field',
853 cf1 = IssueCustomField.create!(:name => 'Writable field',
852 :field_format => 'string',
854 :field_format => 'string',
853 :is_for_all => true, :tracker_ids => [1])
855 :is_for_all => true, :tracker_ids => [1])
854 cf2 = IssueCustomField.create!(:name => 'Readonly field',
856 cf2 = IssueCustomField.create!(:name => 'Readonly field',
855 :field_format => 'string',
857 :field_format => 'string',
856 :is_for_all => true, :tracker_ids => [1])
858 :is_for_all => true, :tracker_ids => [1])
857 WorkflowPermission.delete_all
859 WorkflowPermission.delete_all
858 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
860 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
859 :role_id => 1, :field_name => cf2.id.to_s,
861 :role_id => 1, :field_name => cf2.id.to_s,
860 :rule => 'readonly')
862 :rule => 'readonly')
861 user = User.find(2)
863 user = User.find(2)
862 issue = Issue.new(:project_id => 1, :tracker_id => 1)
864 issue = Issue.new(:project_id => 1, :tracker_id => 1)
863 assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
865 assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
864 assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
866 assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
865
867
866 issue.send :safe_attributes=, {'custom_field_values' => {
868 issue.send :safe_attributes=, {'custom_field_values' => {
867 cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'
869 cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'
868 }}, user
870 }}, user
869 assert_equal 'value1', issue.custom_field_value(cf1)
871 assert_equal 'value1', issue.custom_field_value(cf1)
870 assert_nil issue.custom_field_value(cf2)
872 assert_nil issue.custom_field_value(cf2)
871
873
872 issue.send :safe_attributes=, {'custom_fields' => [
874 issue.send :safe_attributes=, {'custom_fields' => [
873 {'id' => cf1.id.to_s, 'value' => 'valuea'},
875 {'id' => cf1.id.to_s, 'value' => 'valuea'},
874 {'id' => cf2.id.to_s, 'value' => 'valueb'}
876 {'id' => cf2.id.to_s, 'value' => 'valueb'}
875 ]}, user
877 ]}, user
876 assert_equal 'valuea', issue.custom_field_value(cf1)
878 assert_equal 'valuea', issue.custom_field_value(cf1)
877 assert_nil issue.custom_field_value(cf2)
879 assert_nil issue.custom_field_value(cf2)
878 end
880 end
879
881
880 def test_safe_attributes_should_ignore_unassignable_assignee
882 def test_safe_attributes_should_ignore_unassignable_assignee
881 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
883 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
882 :status_id => 1, :priority => IssuePriority.all.first,
884 :status_id => 1, :priority => IssuePriority.all.first,
883 :subject => 'test_create')
885 :subject => 'test_create')
884 assert issue.valid?
886 assert issue.valid?
885
887
886 # locked user, not allowed
888 # locked user, not allowed
887 issue.safe_attributes=({'assigned_to_id' => '5'})
889 issue.safe_attributes=({'assigned_to_id' => '5'})
888 assert_nil issue.assigned_to_id
890 assert_nil issue.assigned_to_id
889 # no member
891 # no member
890 issue.safe_attributes=({'assigned_to_id' => '1'})
892 issue.safe_attributes=({'assigned_to_id' => '1'})
891 assert_nil issue.assigned_to_id
893 assert_nil issue.assigned_to_id
892 # user 2 is ok
894 # user 2 is ok
893 issue.safe_attributes=({'assigned_to_id' => '2'})
895 issue.safe_attributes=({'assigned_to_id' => '2'})
894 assert_equal 2, issue.assigned_to_id
896 assert_equal 2, issue.assigned_to_id
895 assert issue.save
897 assert issue.save
896
898
897 issue.reload
899 issue.reload
898 assert_equal 2, issue.assigned_to_id
900 assert_equal 2, issue.assigned_to_id
899 issue.safe_attributes=({'assigned_to_id' => '5'})
901 issue.safe_attributes=({'assigned_to_id' => '5'})
900 assert_equal 2, issue.assigned_to_id
902 assert_equal 2, issue.assigned_to_id
901 issue.safe_attributes=({'assigned_to_id' => '1'})
903 issue.safe_attributes=({'assigned_to_id' => '1'})
902 assert_equal 2, issue.assigned_to_id
904 assert_equal 2, issue.assigned_to_id
903 # user 3 is also ok
905 # user 3 is also ok
904 issue.safe_attributes=({'assigned_to_id' => '3'})
906 issue.safe_attributes=({'assigned_to_id' => '3'})
905 assert_equal 3, issue.assigned_to_id
907 assert_equal 3, issue.assigned_to_id
906 assert issue.save
908 assert issue.save
907
909
908 # removal of assignee
910 # removal of assignee
909 issue.safe_attributes=({'assigned_to_id' => ''})
911 issue.safe_attributes=({'assigned_to_id' => ''})
910 assert_nil issue.assigned_to_id
912 assert_nil issue.assigned_to_id
911 assert issue.save
913 assert issue.save
912 end
914 end
913
915
914 def test_editable_custom_field_values_should_return_non_readonly_custom_values
916 def test_editable_custom_field_values_should_return_non_readonly_custom_values
915 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string',
917 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string',
916 :is_for_all => true, :tracker_ids => [1, 2])
918 :is_for_all => true, :tracker_ids => [1, 2])
917 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string',
919 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string',
918 :is_for_all => true, :tracker_ids => [1, 2])
920 :is_for_all => true, :tracker_ids => [1, 2])
919 WorkflowPermission.delete_all
921 WorkflowPermission.delete_all
920 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
922 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
921 :field_name => cf2.id.to_s, :rule => 'readonly')
923 :field_name => cf2.id.to_s, :rule => 'readonly')
922 user = User.find(2)
924 user = User.find(2)
923
925
924 issue = Issue.new(:project_id => 1, :tracker_id => 1)
926 issue = Issue.new(:project_id => 1, :tracker_id => 1)
925 values = issue.editable_custom_field_values(user)
927 values = issue.editable_custom_field_values(user)
926 assert values.detect {|value| value.custom_field == cf1}
928 assert values.detect {|value| value.custom_field == cf1}
927 assert_nil values.detect {|value| value.custom_field == cf2}
929 assert_nil values.detect {|value| value.custom_field == cf2}
928
930
929 issue.tracker_id = 2
931 issue.tracker_id = 2
930 values = issue.editable_custom_field_values(user)
932 values = issue.editable_custom_field_values(user)
931 assert values.detect {|value| value.custom_field == cf1}
933 assert values.detect {|value| value.custom_field == cf1}
932 assert values.detect {|value| value.custom_field == cf2}
934 assert values.detect {|value| value.custom_field == cf2}
933 end
935 end
934
936
935 def test_editable_custom_fields_should_return_custom_field_that_is_enabled_for_the_role_only
937 def test_editable_custom_fields_should_return_custom_field_that_is_enabled_for_the_role_only
936 enabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [1,2])
938 enabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [1,2])
937 disabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [2])
939 disabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [2])
938 user = User.find(2)
940 user = User.find(2)
939 issue = Issue.new(:project_id => 1, :tracker_id => 1)
941 issue = Issue.new(:project_id => 1, :tracker_id => 1)
940
942
941 assert_include enabled_cf, issue.editable_custom_fields(user)
943 assert_include enabled_cf, issue.editable_custom_fields(user)
942 assert_not_include disabled_cf, issue.editable_custom_fields(user)
944 assert_not_include disabled_cf, issue.editable_custom_fields(user)
943 end
945 end
944
946
945 def test_safe_attributes_should_accept_target_tracker_writable_fields
947 def test_safe_attributes_should_accept_target_tracker_writable_fields
946 WorkflowPermission.delete_all
948 WorkflowPermission.delete_all
947 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
949 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
948 :role_id => 1, :field_name => 'due_date',
950 :role_id => 1, :field_name => 'due_date',
949 :rule => 'readonly')
951 :rule => 'readonly')
950 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
952 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
951 :role_id => 1, :field_name => 'start_date',
953 :role_id => 1, :field_name => 'start_date',
952 :rule => 'readonly')
954 :rule => 'readonly')
953 user = User.find(2)
955 user = User.find(2)
954
956
955 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
957 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
956
958
957 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
959 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
958 'due_date' => '2012-07-14'}, user
960 'due_date' => '2012-07-14'}, user
959 assert_equal Date.parse('2012-07-12'), issue.start_date
961 assert_equal Date.parse('2012-07-12'), issue.start_date
960 assert_nil issue.due_date
962 assert_nil issue.due_date
961
963
962 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
964 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
963 'due_date' => '2012-07-16',
965 'due_date' => '2012-07-16',
964 'tracker_id' => 2}, user
966 'tracker_id' => 2}, user
965 assert_equal Date.parse('2012-07-12'), issue.start_date
967 assert_equal Date.parse('2012-07-12'), issue.start_date
966 assert_equal Date.parse('2012-07-16'), issue.due_date
968 assert_equal Date.parse('2012-07-16'), issue.due_date
967 end
969 end
968
970
969 def test_safe_attributes_should_accept_target_status_writable_fields
971 def test_safe_attributes_should_accept_target_status_writable_fields
970 WorkflowPermission.delete_all
972 WorkflowPermission.delete_all
971 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
973 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
972 :role_id => 1, :field_name => 'due_date',
974 :role_id => 1, :field_name => 'due_date',
973 :rule => 'readonly')
975 :rule => 'readonly')
974 WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1,
976 WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1,
975 :role_id => 1, :field_name => 'start_date',
977 :role_id => 1, :field_name => 'start_date',
976 :rule => 'readonly')
978 :rule => 'readonly')
977 user = User.find(2)
979 user = User.find(2)
978
980
979 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
981 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
980
982
981 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
983 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
982 'due_date' => '2012-07-14'},
984 'due_date' => '2012-07-14'},
983 user
985 user
984 assert_equal Date.parse('2012-07-12'), issue.start_date
986 assert_equal Date.parse('2012-07-12'), issue.start_date
985 assert_nil issue.due_date
987 assert_nil issue.due_date
986
988
987 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
989 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
988 'due_date' => '2012-07-16',
990 'due_date' => '2012-07-16',
989 'status_id' => 2},
991 'status_id' => 2},
990 user
992 user
991 assert_equal Date.parse('2012-07-12'), issue.start_date
993 assert_equal Date.parse('2012-07-12'), issue.start_date
992 assert_equal Date.parse('2012-07-16'), issue.due_date
994 assert_equal Date.parse('2012-07-16'), issue.due_date
993 end
995 end
994
996
995 def test_required_attributes_should_be_validated
997 def test_required_attributes_should_be_validated
996 cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
998 cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
997 :is_for_all => true, :tracker_ids => [1, 2])
999 :is_for_all => true, :tracker_ids => [1, 2])
998
1000
999 WorkflowPermission.delete_all
1001 WorkflowPermission.delete_all
1000 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1002 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1001 :role_id => 1, :field_name => 'due_date',
1003 :role_id => 1, :field_name => 'due_date',
1002 :rule => 'required')
1004 :rule => 'required')
1003 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1005 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1004 :role_id => 1, :field_name => 'category_id',
1006 :role_id => 1, :field_name => 'category_id',
1005 :rule => 'required')
1007 :rule => 'required')
1006 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1008 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1007 :role_id => 1, :field_name => cf.id.to_s,
1009 :role_id => 1, :field_name => cf.id.to_s,
1008 :rule => 'required')
1010 :rule => 'required')
1009
1011
1010 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
1012 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
1011 :role_id => 1, :field_name => 'start_date',
1013 :role_id => 1, :field_name => 'start_date',
1012 :rule => 'required')
1014 :rule => 'required')
1013 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
1015 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
1014 :role_id => 1, :field_name => cf.id.to_s,
1016 :role_id => 1, :field_name => cf.id.to_s,
1015 :rule => 'required')
1017 :rule => 'required')
1016 user = User.find(2)
1018 user = User.find(2)
1017
1019
1018 issue = Issue.new(:project_id => 1, :tracker_id => 1,
1020 issue = Issue.new(:project_id => 1, :tracker_id => 1,
1019 :status_id => 1, :subject => 'Required fields',
1021 :status_id => 1, :subject => 'Required fields',
1020 :author => user)
1022 :author => user)
1021 assert_equal [cf.id.to_s, "category_id", "due_date"],
1023 assert_equal [cf.id.to_s, "category_id", "due_date"],
1022 issue.required_attribute_names(user).sort
1024 issue.required_attribute_names(user).sort
1023 assert !issue.save, "Issue was saved"
1025 assert !issue.save, "Issue was saved"
1024 assert_equal ["Category cannot be blank", "Due date cannot be blank", "Foo cannot be blank"],
1026 assert_equal ["Category cannot be blank", "Due date cannot be blank", "Foo cannot be blank"],
1025 issue.errors.full_messages.sort
1027 issue.errors.full_messages.sort
1026
1028
1027 issue.tracker_id = 2
1029 issue.tracker_id = 2
1028 assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
1030 assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
1029 assert !issue.save, "Issue was saved"
1031 assert !issue.save, "Issue was saved"
1030 assert_equal ["Foo cannot be blank", "Start date cannot be blank"],
1032 assert_equal ["Foo cannot be blank", "Start date cannot be blank"],
1031 issue.errors.full_messages.sort
1033 issue.errors.full_messages.sort
1032
1034
1033 issue.start_date = Date.today
1035 issue.start_date = Date.today
1034 issue.custom_field_values = {cf.id.to_s => 'bar'}
1036 issue.custom_field_values = {cf.id.to_s => 'bar'}
1035 assert issue.save
1037 assert issue.save
1036 end
1038 end
1037
1039
1038 def test_required_attribute_that_is_disabled_for_the_tracker_should_not_be_required
1040 def test_required_attribute_that_is_disabled_for_the_tracker_should_not_be_required
1039 WorkflowPermission.delete_all
1041 WorkflowPermission.delete_all
1040 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1042 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1041 :role_id => 1, :field_name => 'start_date',
1043 :role_id => 1, :field_name => 'start_date',
1042 :rule => 'required')
1044 :rule => 'required')
1043 user = User.find(2)
1045 user = User.find(2)
1044
1046
1045 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1047 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1046 :subject => 'Required fields', :author => user)
1048 :subject => 'Required fields', :author => user)
1047 assert !issue.save
1049 assert !issue.save
1048 assert_include "Start date cannot be blank", issue.errors.full_messages
1050 assert_include "Start date cannot be blank", issue.errors.full_messages
1049
1051
1050 tracker = Tracker.find(1)
1052 tracker = Tracker.find(1)
1051 tracker.core_fields -= %w(start_date)
1053 tracker.core_fields -= %w(start_date)
1052 tracker.save!
1054 tracker.save!
1053 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1055 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1054 :subject => 'Required fields', :author => user)
1056 :subject => 'Required fields', :author => user)
1055 assert issue.save
1057 assert issue.save
1056 end
1058 end
1057
1059
1058 def test_category_should_not_be_required_if_project_has_no_categories
1060 def test_category_should_not_be_required_if_project_has_no_categories
1059 Project.find(1).issue_categories.delete_all
1061 Project.find(1).issue_categories.delete_all
1060 WorkflowPermission.delete_all
1062 WorkflowPermission.delete_all
1061 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1063 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1062 :role_id => 1, :field_name => 'category_id',:rule => 'required')
1064 :role_id => 1, :field_name => 'category_id',:rule => 'required')
1063 user = User.find(2)
1065 user = User.find(2)
1064
1066
1065 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1067 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1066 :subject => 'Required fields', :author => user)
1068 :subject => 'Required fields', :author => user)
1067 assert_save issue
1069 assert_save issue
1068 end
1070 end
1069
1071
1070 def test_fixed_version_should_not_be_required_no_assignable_versions
1072 def test_fixed_version_should_not_be_required_no_assignable_versions
1071 Version.delete_all
1073 Version.delete_all
1072 WorkflowPermission.delete_all
1074 WorkflowPermission.delete_all
1073 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1075 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1074 :role_id => 1, :field_name => 'fixed_version_id',:rule => 'required')
1076 :role_id => 1, :field_name => 'fixed_version_id',:rule => 'required')
1075 user = User.find(2)
1077 user = User.find(2)
1076
1078
1077 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1079 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1078 :subject => 'Required fields', :author => user)
1080 :subject => 'Required fields', :author => user)
1079 assert_save issue
1081 assert_save issue
1080 end
1082 end
1081
1083
1082 def test_required_custom_field_that_is_not_visible_for_the_user_should_not_be_required
1084 def test_required_custom_field_that_is_not_visible_for_the_user_should_not_be_required
1083 CustomField.delete_all
1085 CustomField.delete_all
1084 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
1086 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
1085 user = User.generate!
1087 user = User.generate!
1086 User.add_to_project(user, Project.find(1), Role.find(2))
1088 User.add_to_project(user, Project.find(1), Role.find(2))
1087
1089
1088 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1090 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1089 :subject => 'Required fields', :author => user)
1091 :subject => 'Required fields', :author => user)
1090 assert_save issue
1092 assert_save issue
1091 end
1093 end
1092
1094
1093 def test_required_custom_field_that_is_visible_for_the_user_should_be_required
1095 def test_required_custom_field_that_is_visible_for_the_user_should_be_required
1094 CustomField.delete_all
1096 CustomField.delete_all
1095 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
1097 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
1096 user = User.generate!
1098 user = User.generate!
1097 User.add_to_project(user, Project.find(1), Role.find(1))
1099 User.add_to_project(user, Project.find(1), Role.find(1))
1098
1100
1099 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1101 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1100 :subject => 'Required fields', :author => user)
1102 :subject => 'Required fields', :author => user)
1101 assert !issue.save
1103 assert !issue.save
1102 assert_include "#{field.name} cannot be blank", issue.errors.full_messages
1104 assert_include "#{field.name} cannot be blank", issue.errors.full_messages
1103 end
1105 end
1104
1106
1105 def test_required_attribute_names_for_multiple_roles_should_intersect_rules
1107 def test_required_attribute_names_for_multiple_roles_should_intersect_rules
1106 WorkflowPermission.delete_all
1108 WorkflowPermission.delete_all
1107 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1109 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1108 :role_id => 1, :field_name => 'due_date',
1110 :role_id => 1, :field_name => 'due_date',
1109 :rule => 'required')
1111 :rule => 'required')
1110 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1112 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1111 :role_id => 1, :field_name => 'start_date',
1113 :role_id => 1, :field_name => 'start_date',
1112 :rule => 'required')
1114 :rule => 'required')
1113 user = User.find(2)
1115 user = User.find(2)
1114 member = Member.find(1)
1116 member = Member.find(1)
1115 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1117 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1116
1118
1117 assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
1119 assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
1118
1120
1119 member.role_ids = [1, 2]
1121 member.role_ids = [1, 2]
1120 member.save!
1122 member.save!
1121 assert_equal [], issue.required_attribute_names(user.reload)
1123 assert_equal [], issue.required_attribute_names(user.reload)
1122
1124
1123 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1125 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1124 :role_id => 2, :field_name => 'due_date',
1126 :role_id => 2, :field_name => 'due_date',
1125 :rule => 'required')
1127 :rule => 'required')
1126 assert_equal %w(due_date), issue.required_attribute_names(user)
1128 assert_equal %w(due_date), issue.required_attribute_names(user)
1127
1129
1128 member.role_ids = [1, 2, 3]
1130 member.role_ids = [1, 2, 3]
1129 member.save!
1131 member.save!
1130 assert_equal [], issue.required_attribute_names(user.reload)
1132 assert_equal [], issue.required_attribute_names(user.reload)
1131
1133
1132 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1134 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1133 :role_id => 3, :field_name => 'due_date',
1135 :role_id => 3, :field_name => 'due_date',
1134 :rule => 'readonly')
1136 :rule => 'readonly')
1135 # required + readonly => required
1137 # required + readonly => required
1136 assert_equal %w(due_date), issue.required_attribute_names(user)
1138 assert_equal %w(due_date), issue.required_attribute_names(user)
1137 end
1139 end
1138
1140
1139 def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
1141 def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
1140 WorkflowPermission.delete_all
1142 WorkflowPermission.delete_all
1141 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1143 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1142 :role_id => 1, :field_name => 'due_date',
1144 :role_id => 1, :field_name => 'due_date',
1143 :rule => 'readonly')
1145 :rule => 'readonly')
1144 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1146 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1145 :role_id => 1, :field_name => 'start_date',
1147 :role_id => 1, :field_name => 'start_date',
1146 :rule => 'readonly')
1148 :rule => 'readonly')
1147 user = User.find(2)
1149 user = User.find(2)
1148 member = Member.find(1)
1150 member = Member.find(1)
1149 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1151 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1150
1152
1151 assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
1153 assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
1152
1154
1153 member.role_ids = [1, 2]
1155 member.role_ids = [1, 2]
1154 member.save!
1156 member.save!
1155 assert_equal [], issue.read_only_attribute_names(user.reload)
1157 assert_equal [], issue.read_only_attribute_names(user.reload)
1156
1158
1157 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1159 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1158 :role_id => 2, :field_name => 'due_date',
1160 :role_id => 2, :field_name => 'due_date',
1159 :rule => 'readonly')
1161 :rule => 'readonly')
1160 assert_equal %w(due_date), issue.read_only_attribute_names(user)
1162 assert_equal %w(due_date), issue.read_only_attribute_names(user)
1161 end
1163 end
1162
1164
1163 # A field that is not visible by role 2 and readonly by role 1 should be readonly for user with role 1 and 2
1165 # A field that is not visible by role 2 and readonly by role 1 should be readonly for user with role 1 and 2
1164 def test_read_only_attribute_names_should_include_custom_fields_that_combine_readonly_and_not_visible_for_roles
1166 def test_read_only_attribute_names_should_include_custom_fields_that_combine_readonly_and_not_visible_for_roles
1165 field = IssueCustomField.generate!(
1167 field = IssueCustomField.generate!(
1166 :is_for_all => true, :trackers => Tracker.all, :visible => false, :role_ids => [1]
1168 :is_for_all => true, :trackers => Tracker.all, :visible => false, :role_ids => [1]
1167 )
1169 )
1168 WorkflowPermission.delete_all
1170 WorkflowPermission.delete_all
1169 WorkflowPermission.create!(
1171 WorkflowPermission.create!(
1170 :old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => field.id, :rule => 'readonly'
1172 :old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => field.id, :rule => 'readonly'
1171 )
1173 )
1172 user = User.generate!
1174 user = User.generate!
1173 project = Project.find(1)
1175 project = Project.find(1)
1174 User.add_to_project(user, project, Role.where(:id => [1, 2]))
1176 User.add_to_project(user, project, Role.where(:id => [1, 2]))
1175
1177
1176 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1178 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1177 assert_equal [field.id.to_s], issue.read_only_attribute_names(user)
1179 assert_equal [field.id.to_s], issue.read_only_attribute_names(user)
1178 end
1180 end
1179
1181
1180 def test_workflow_rules_should_ignore_roles_without_issue_permissions
1182 def test_workflow_rules_should_ignore_roles_without_issue_permissions
1181 role = Role.generate! :permissions => [:view_issues, :edit_issues]
1183 role = Role.generate! :permissions => [:view_issues, :edit_issues]
1182 ignored_role = Role.generate! :permissions => [:view_issues]
1184 ignored_role = Role.generate! :permissions => [:view_issues]
1183
1185
1184 WorkflowPermission.delete_all
1186 WorkflowPermission.delete_all
1185 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1187 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1186 :role => role, :field_name => 'due_date',
1188 :role => role, :field_name => 'due_date',
1187 :rule => 'required')
1189 :rule => 'required')
1188 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1190 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1189 :role => role, :field_name => 'start_date',
1191 :role => role, :field_name => 'start_date',
1190 :rule => 'readonly')
1192 :rule => 'readonly')
1191 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1193 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1192 :role => role, :field_name => 'done_ratio',
1194 :role => role, :field_name => 'done_ratio',
1193 :rule => 'readonly')
1195 :rule => 'readonly')
1194 user = User.generate!
1196 user = User.generate!
1195 User.add_to_project user, Project.find(1), [role, ignored_role]
1197 User.add_to_project user, Project.find(1), [role, ignored_role]
1196
1198
1197 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1199 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1198
1200
1199 assert_equal %w(due_date), issue.required_attribute_names(user)
1201 assert_equal %w(due_date), issue.required_attribute_names(user)
1200 assert_equal %w(done_ratio start_date), issue.read_only_attribute_names(user).sort
1202 assert_equal %w(done_ratio start_date), issue.read_only_attribute_names(user).sort
1201 end
1203 end
1202
1204
1203 def test_workflow_rules_should_work_for_member_with_duplicate_role
1205 def test_workflow_rules_should_work_for_member_with_duplicate_role
1204 WorkflowPermission.delete_all
1206 WorkflowPermission.delete_all
1205 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1207 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1206 :role_id => 1, :field_name => 'due_date',
1208 :role_id => 1, :field_name => 'due_date',
1207 :rule => 'required')
1209 :rule => 'required')
1208 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1210 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1209 :role_id => 1, :field_name => 'start_date',
1211 :role_id => 1, :field_name => 'start_date',
1210 :rule => 'readonly')
1212 :rule => 'readonly')
1211
1213
1212 user = User.generate!
1214 user = User.generate!
1213 m = Member.new(:user_id => user.id, :project_id => 1)
1215 m = Member.new(:user_id => user.id, :project_id => 1)
1214 m.member_roles.build(:role_id => 1)
1216 m.member_roles.build(:role_id => 1)
1215 m.member_roles.build(:role_id => 1)
1217 m.member_roles.build(:role_id => 1)
1216 m.save!
1218 m.save!
1217
1219
1218 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1220 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1219
1221
1220 assert_equal %w(due_date), issue.required_attribute_names(user)
1222 assert_equal %w(due_date), issue.required_attribute_names(user)
1221 assert_equal %w(start_date), issue.read_only_attribute_names(user)
1223 assert_equal %w(start_date), issue.read_only_attribute_names(user)
1222 end
1224 end
1223
1225
1224 def test_copy
1226 def test_copy
1225 issue = Issue.new.copy_from(1)
1227 issue = Issue.new.copy_from(1)
1226 assert issue.copy?
1228 assert issue.copy?
1227 assert issue.save
1229 assert issue.save
1228 issue.reload
1230 issue.reload
1229 orig = Issue.find(1)
1231 orig = Issue.find(1)
1230 assert_equal orig.subject, issue.subject
1232 assert_equal orig.subject, issue.subject
1231 assert_equal orig.tracker, issue.tracker
1233 assert_equal orig.tracker, issue.tracker
1232 assert_equal "125", issue.custom_value_for(2).value
1234 assert_equal "125", issue.custom_value_for(2).value
1233 end
1235 end
1234
1236
1235 def test_copy_should_copy_status
1237 def test_copy_should_copy_status
1236 orig = Issue.find(8)
1238 orig = Issue.find(8)
1237 assert orig.status != orig.default_status
1239 assert orig.status != orig.default_status
1238
1240
1239 issue = Issue.new.copy_from(orig)
1241 issue = Issue.new.copy_from(orig)
1240 assert issue.save
1242 assert issue.save
1241 issue.reload
1243 issue.reload
1242 assert_equal orig.status, issue.status
1244 assert_equal orig.status, issue.status
1243 end
1245 end
1244
1246
1245 def test_copy_should_add_relation_with_copied_issue
1247 def test_copy_should_add_relation_with_copied_issue
1246 copied = Issue.find(1)
1248 copied = Issue.find(1)
1247 issue = Issue.new.copy_from(copied)
1249 issue = Issue.new.copy_from(copied)
1248 assert issue.save
1250 assert issue.save
1249 issue.reload
1251 issue.reload
1250
1252
1251 assert_equal 1, issue.relations.size
1253 assert_equal 1, issue.relations.size
1252 relation = issue.relations.first
1254 relation = issue.relations.first
1253 assert_equal 'copied_to', relation.relation_type
1255 assert_equal 'copied_to', relation.relation_type
1254 assert_equal copied, relation.issue_from
1256 assert_equal copied, relation.issue_from
1255 assert_equal issue, relation.issue_to
1257 assert_equal issue, relation.issue_to
1256 end
1258 end
1257
1259
1258 def test_copy_should_copy_subtasks
1260 def test_copy_should_copy_subtasks
1259 issue = Issue.generate_with_descendants!
1261 issue = Issue.generate_with_descendants!
1260
1262
1261 copy = issue.reload.copy
1263 copy = issue.reload.copy
1262 copy.author = User.find(7)
1264 copy.author = User.find(7)
1263 assert_difference 'Issue.count', 1+issue.descendants.count do
1265 assert_difference 'Issue.count', 1+issue.descendants.count do
1264 assert copy.save
1266 assert copy.save
1265 end
1267 end
1266 copy.reload
1268 copy.reload
1267 assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
1269 assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
1268 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
1270 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
1269 assert_equal %w(Child11), child_copy.children.map(&:subject).sort
1271 assert_equal %w(Child11), child_copy.children.map(&:subject).sort
1270 assert_equal copy.author, child_copy.author
1272 assert_equal copy.author, child_copy.author
1271 end
1273 end
1272
1274
1273 def test_copy_as_a_child_of_copied_issue_should_not_copy_itself
1275 def test_copy_as_a_child_of_copied_issue_should_not_copy_itself
1274 parent = Issue.generate!
1276 parent = Issue.generate!
1275 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1277 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1276 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1278 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1277
1279
1278 copy = parent.reload.copy
1280 copy = parent.reload.copy
1279 copy.parent_issue_id = parent.id
1281 copy.parent_issue_id = parent.id
1280 copy.author = User.find(7)
1282 copy.author = User.find(7)
1281 assert_difference 'Issue.count', 3 do
1283 assert_difference 'Issue.count', 3 do
1282 assert copy.save
1284 assert copy.save
1283 end
1285 end
1284 parent.reload
1286 parent.reload
1285 copy.reload
1287 copy.reload
1286 assert_equal parent, copy.parent
1288 assert_equal parent, copy.parent
1287 assert_equal 3, parent.children.count
1289 assert_equal 3, parent.children.count
1288 assert_equal 5, parent.descendants.count
1290 assert_equal 5, parent.descendants.count
1289 assert_equal 2, copy.children.count
1291 assert_equal 2, copy.children.count
1290 assert_equal 2, copy.descendants.count
1292 assert_equal 2, copy.descendants.count
1291 end
1293 end
1292
1294
1293 def test_copy_as_a_descendant_of_copied_issue_should_not_copy_itself
1295 def test_copy_as_a_descendant_of_copied_issue_should_not_copy_itself
1294 parent = Issue.generate!
1296 parent = Issue.generate!
1295 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1297 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1296 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1298 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1297
1299
1298 copy = parent.reload.copy
1300 copy = parent.reload.copy
1299 copy.parent_issue_id = child1.id
1301 copy.parent_issue_id = child1.id
1300 copy.author = User.find(7)
1302 copy.author = User.find(7)
1301 assert_difference 'Issue.count', 3 do
1303 assert_difference 'Issue.count', 3 do
1302 assert copy.save
1304 assert copy.save
1303 end
1305 end
1304 parent.reload
1306 parent.reload
1305 child1.reload
1307 child1.reload
1306 copy.reload
1308 copy.reload
1307 assert_equal child1, copy.parent
1309 assert_equal child1, copy.parent
1308 assert_equal 2, parent.children.count
1310 assert_equal 2, parent.children.count
1309 assert_equal 5, parent.descendants.count
1311 assert_equal 5, parent.descendants.count
1310 assert_equal 1, child1.children.count
1312 assert_equal 1, child1.children.count
1311 assert_equal 3, child1.descendants.count
1313 assert_equal 3, child1.descendants.count
1312 assert_equal 2, copy.children.count
1314 assert_equal 2, copy.children.count
1313 assert_equal 2, copy.descendants.count
1315 assert_equal 2, copy.descendants.count
1314 end
1316 end
1315
1317
1316 def test_copy_should_copy_subtasks_to_target_project
1318 def test_copy_should_copy_subtasks_to_target_project
1317 issue = Issue.generate_with_descendants!
1319 issue = Issue.generate_with_descendants!
1318
1320
1319 copy = issue.copy(:project_id => 3)
1321 copy = issue.copy(:project_id => 3)
1320 assert_difference 'Issue.count', 1+issue.descendants.count do
1322 assert_difference 'Issue.count', 1+issue.descendants.count do
1321 assert copy.save
1323 assert copy.save
1322 end
1324 end
1323 assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
1325 assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
1324 end
1326 end
1325
1327
1326 def test_copy_should_not_copy_subtasks_twice_when_saving_twice
1328 def test_copy_should_not_copy_subtasks_twice_when_saving_twice
1327 issue = Issue.generate_with_descendants!
1329 issue = Issue.generate_with_descendants!
1328
1330
1329 copy = issue.reload.copy
1331 copy = issue.reload.copy
1330 assert_difference 'Issue.count', 1+issue.descendants.count do
1332 assert_difference 'Issue.count', 1+issue.descendants.count do
1331 assert copy.save
1333 assert copy.save
1332 assert copy.save
1334 assert copy.save
1333 end
1335 end
1334 end
1336 end
1335
1337
1336 def test_should_not_call_after_project_change_on_creation
1338 def test_should_not_call_after_project_change_on_creation
1337 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1339 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1338 :subject => 'Test', :author_id => 1)
1340 :subject => 'Test', :author_id => 1)
1339 issue.expects(:after_project_change).never
1341 issue.expects(:after_project_change).never
1340 issue.save!
1342 issue.save!
1341 end
1343 end
1342
1344
1343 def test_should_not_call_after_project_change_on_update
1345 def test_should_not_call_after_project_change_on_update
1344 issue = Issue.find(1)
1346 issue = Issue.find(1)
1345 issue.project = Project.find(1)
1347 issue.project = Project.find(1)
1346 issue.subject = 'No project change'
1348 issue.subject = 'No project change'
1347 issue.expects(:after_project_change).never
1349 issue.expects(:after_project_change).never
1348 issue.save!
1350 issue.save!
1349 end
1351 end
1350
1352
1351 def test_should_call_after_project_change_on_project_change
1353 def test_should_call_after_project_change_on_project_change
1352 issue = Issue.find(1)
1354 issue = Issue.find(1)
1353 issue.project = Project.find(2)
1355 issue.project = Project.find(2)
1354 issue.expects(:after_project_change).once
1356 issue.expects(:after_project_change).once
1355 issue.save!
1357 issue.save!
1356 end
1358 end
1357
1359
1358 def test_adding_journal_should_update_timestamp
1360 def test_adding_journal_should_update_timestamp
1359 issue = Issue.find(1)
1361 issue = Issue.find(1)
1360 updated_on_was = issue.updated_on
1362 updated_on_was = issue.updated_on
1361
1363
1362 issue.init_journal(User.first, "Adding notes")
1364 issue.init_journal(User.first, "Adding notes")
1363 assert_difference 'Journal.count' do
1365 assert_difference 'Journal.count' do
1364 assert issue.save
1366 assert issue.save
1365 end
1367 end
1366 issue.reload
1368 issue.reload
1367
1369
1368 assert_not_equal updated_on_was, issue.updated_on
1370 assert_not_equal updated_on_was, issue.updated_on
1369 end
1371 end
1370
1372
1371 def test_should_close_duplicates
1373 def test_should_close_duplicates
1372 # Create 3 issues
1374 # Create 3 issues
1373 issue1 = Issue.generate!
1375 issue1 = Issue.generate!
1374 issue2 = Issue.generate!
1376 issue2 = Issue.generate!
1375 issue3 = Issue.generate!
1377 issue3 = Issue.generate!
1376
1378
1377 # 2 is a dupe of 1
1379 # 2 is a dupe of 1
1378 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1,
1380 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1,
1379 :relation_type => IssueRelation::TYPE_DUPLICATES)
1381 :relation_type => IssueRelation::TYPE_DUPLICATES)
1380 # And 3 is a dupe of 2
1382 # And 3 is a dupe of 2
1381 # IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1383 # IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1382 # :relation_type => IssueRelation::TYPE_DUPLICATES)
1384 # :relation_type => IssueRelation::TYPE_DUPLICATES)
1383 # And 3 is a dupe of 1 (circular duplicates)
1385 # And 3 is a dupe of 1 (circular duplicates)
1384 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1,
1386 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1,
1385 :relation_type => IssueRelation::TYPE_DUPLICATES)
1387 :relation_type => IssueRelation::TYPE_DUPLICATES)
1386
1388
1387 assert issue1.reload.duplicates.include?(issue2)
1389 assert issue1.reload.duplicates.include?(issue2)
1388
1390
1389 # Closing issue 1
1391 # Closing issue 1
1390 issue1.init_journal(User.first, "Closing issue1")
1392 issue1.init_journal(User.first, "Closing issue1")
1391 issue1.status = IssueStatus.where(:is_closed => true).first
1393 issue1.status = IssueStatus.where(:is_closed => true).first
1392 assert issue1.save
1394 assert issue1.save
1393 # 2 and 3 should be also closed
1395 # 2 and 3 should be also closed
1394 assert issue2.reload.closed?
1396 assert issue2.reload.closed?
1395 assert issue3.reload.closed?
1397 assert issue3.reload.closed?
1396 end
1398 end
1397
1399
1398 def test_should_close_duplicates_with_private_notes
1400 def test_should_close_duplicates_with_private_notes
1399 issue = Issue.generate!
1401 issue = Issue.generate!
1400 duplicate = Issue.generate!
1402 duplicate = Issue.generate!
1401 IssueRelation.create!(:issue_from => duplicate, :issue_to => issue,
1403 IssueRelation.create!(:issue_from => duplicate, :issue_to => issue,
1402 :relation_type => IssueRelation::TYPE_DUPLICATES)
1404 :relation_type => IssueRelation::TYPE_DUPLICATES)
1403 assert issue.reload.duplicates.include?(duplicate)
1405 assert issue.reload.duplicates.include?(duplicate)
1404
1406
1405 # Closing issue with private notes
1407 # Closing issue with private notes
1406 issue.init_journal(User.first, "Private notes")
1408 issue.init_journal(User.first, "Private notes")
1407 issue.private_notes = true
1409 issue.private_notes = true
1408 issue.status = IssueStatus.where(:is_closed => true).first
1410 issue.status = IssueStatus.where(:is_closed => true).first
1409 assert_save issue
1411 assert_save issue
1410
1412
1411 duplicate.reload
1413 duplicate.reload
1412 assert journal = duplicate.journals.detect {|journal| journal.notes == "Private notes"}
1414 assert journal = duplicate.journals.detect {|journal| journal.notes == "Private notes"}
1413 assert_equal true, journal.private_notes
1415 assert_equal true, journal.private_notes
1414 end
1416 end
1415
1417
1416 def test_should_not_close_duplicated_issue
1418 def test_should_not_close_duplicated_issue
1417 issue1 = Issue.generate!
1419 issue1 = Issue.generate!
1418 issue2 = Issue.generate!
1420 issue2 = Issue.generate!
1419
1421
1420 # 2 is a dupe of 1
1422 # 2 is a dupe of 1
1421 IssueRelation.create(:issue_from => issue2, :issue_to => issue1,
1423 IssueRelation.create(:issue_from => issue2, :issue_to => issue1,
1422 :relation_type => IssueRelation::TYPE_DUPLICATES)
1424 :relation_type => IssueRelation::TYPE_DUPLICATES)
1423 # 2 is a dup of 1 but 1 is not a duplicate of 2
1425 # 2 is a dup of 1 but 1 is not a duplicate of 2
1424 assert !issue2.reload.duplicates.include?(issue1)
1426 assert !issue2.reload.duplicates.include?(issue1)
1425
1427
1426 # Closing issue 2
1428 # Closing issue 2
1427 issue2.init_journal(User.first, "Closing issue2")
1429 issue2.init_journal(User.first, "Closing issue2")
1428 issue2.status = IssueStatus.where(:is_closed => true).first
1430 issue2.status = IssueStatus.where(:is_closed => true).first
1429 assert issue2.save
1431 assert issue2.save
1430 # 1 should not be also closed
1432 # 1 should not be also closed
1431 assert !issue1.reload.closed?
1433 assert !issue1.reload.closed?
1432 end
1434 end
1433
1435
1434 def test_assignable_versions
1436 def test_assignable_versions
1435 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1437 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1436 :status_id => 1, :fixed_version_id => 1,
1438 :status_id => 1, :fixed_version_id => 1,
1437 :subject => 'New issue')
1439 :subject => 'New issue')
1438 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
1440 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
1439 end
1441 end
1440
1442
1441 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
1443 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
1442 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1444 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1443 :status_id => 1, :fixed_version_id => 1,
1445 :status_id => 1, :fixed_version_id => 1,
1444 :subject => 'New issue')
1446 :subject => 'New issue')
1445 assert !issue.save
1447 assert !issue.save
1446 assert_not_equal [], issue.errors[:fixed_version_id]
1448 assert_not_equal [], issue.errors[:fixed_version_id]
1447 end
1449 end
1448
1450
1449 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
1451 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
1450 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1452 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1451 :status_id => 1, :fixed_version_id => 2,
1453 :status_id => 1, :fixed_version_id => 2,
1452 :subject => 'New issue')
1454 :subject => 'New issue')
1453 assert !issue.save
1455 assert !issue.save
1454 assert_not_equal [], issue.errors[:fixed_version_id]
1456 assert_not_equal [], issue.errors[:fixed_version_id]
1455 end
1457 end
1456
1458
1457 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
1459 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
1458 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1460 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1459 :status_id => 1, :fixed_version_id => 3,
1461 :status_id => 1, :fixed_version_id => 3,
1460 :subject => 'New issue')
1462 :subject => 'New issue')
1461 assert issue.save
1463 assert issue.save
1462 end
1464 end
1463
1465
1464 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
1466 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
1465 issue = Issue.find(11)
1467 issue = Issue.find(11)
1466 assert_equal 'closed', issue.fixed_version.status
1468 assert_equal 'closed', issue.fixed_version.status
1467 issue.subject = 'Subject changed'
1469 issue.subject = 'Subject changed'
1468 assert issue.save
1470 assert issue.save
1469 end
1471 end
1470
1472
1471 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
1473 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
1472 issue = Issue.find(11)
1474 issue = Issue.find(11)
1473 issue.status_id = 1
1475 issue.status_id = 1
1474 assert !issue.save
1476 assert !issue.save
1475 assert_not_equal [], issue.errors[:base]
1477 assert_not_equal [], issue.errors[:base]
1476 end
1478 end
1477
1479
1478 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
1480 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
1479 issue = Issue.find(11)
1481 issue = Issue.find(11)
1480 issue.status_id = 1
1482 issue.status_id = 1
1481 issue.fixed_version_id = 3
1483 issue.fixed_version_id = 3
1482 assert issue.save
1484 assert issue.save
1483 end
1485 end
1484
1486
1485 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
1487 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
1486 issue = Issue.find(12)
1488 issue = Issue.find(12)
1487 assert_equal 'locked', issue.fixed_version.status
1489 assert_equal 'locked', issue.fixed_version.status
1488 issue.status_id = 1
1490 issue.status_id = 1
1489 assert issue.save
1491 assert issue.save
1490 end
1492 end
1491
1493
1492 def test_should_not_be_able_to_keep_unshared_version_when_changing_project
1494 def test_should_not_be_able_to_keep_unshared_version_when_changing_project
1493 issue = Issue.find(2)
1495 issue = Issue.find(2)
1494 assert_equal 2, issue.fixed_version_id
1496 assert_equal 2, issue.fixed_version_id
1495 issue.project_id = 3
1497 issue.project_id = 3
1496 assert_nil issue.fixed_version_id
1498 assert_nil issue.fixed_version_id
1497 issue.fixed_version_id = 2
1499 issue.fixed_version_id = 2
1498 assert !issue.save
1500 assert !issue.save
1499 assert_include 'Target version is not included in the list', issue.errors.full_messages
1501 assert_include 'Target version is not included in the list', issue.errors.full_messages
1500 end
1502 end
1501
1503
1502 def test_should_keep_shared_version_when_changing_project
1504 def test_should_keep_shared_version_when_changing_project
1503 Version.find(2).update! :sharing => 'tree'
1505 Version.find(2).update! :sharing => 'tree'
1504
1506
1505 issue = Issue.find(2)
1507 issue = Issue.find(2)
1506 assert_equal 2, issue.fixed_version_id
1508 assert_equal 2, issue.fixed_version_id
1507 issue.project_id = 3
1509 issue.project_id = 3
1508 assert_equal 2, issue.fixed_version_id
1510 assert_equal 2, issue.fixed_version_id
1509 assert issue.save
1511 assert issue.save
1510 end
1512 end
1511
1513
1512 def test_allowed_target_projects_should_include_projects_with_issue_tracking_enabled
1514 def test_allowed_target_projects_should_include_projects_with_issue_tracking_enabled
1513 assert_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1515 assert_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1514 end
1516 end
1515
1517
1516 def test_allowed_target_projects_should_not_include_projects_with_issue_tracking_disabled
1518 def test_allowed_target_projects_should_not_include_projects_with_issue_tracking_disabled
1517 Project.find(2).disable_module! :issue_tracking
1519 Project.find(2).disable_module! :issue_tracking
1518 assert_not_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1520 assert_not_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1519 end
1521 end
1520
1522
1521 def test_allowed_target_projects_should_not_include_projects_without_trackers
1523 def test_allowed_target_projects_should_not_include_projects_without_trackers
1522 project = Project.generate!(:tracker_ids => [])
1524 project = Project.generate!(:tracker_ids => [])
1523 assert project.trackers.empty?
1525 assert project.trackers.empty?
1524 assert_not_include project, Issue.allowed_target_projects(User.find(1))
1526 assert_not_include project, Issue.allowed_target_projects(User.find(1))
1525 end
1527 end
1526
1528
1527 def test_allowed_target_trackers_with_one_role_allowed_on_all_trackers
1529 def test_allowed_target_trackers_with_one_role_allowed_on_all_trackers
1528 user = User.generate!
1530 user = User.generate!
1529 role = Role.generate!
1531 role = Role.generate!
1530 role.add_permission! :add_issues
1532 role.add_permission! :add_issues
1531 role.set_permission_trackers :add_issues, :all
1533 role.set_permission_trackers :add_issues, :all
1532 role.save!
1534 role.save!
1533 User.add_to_project(user, Project.find(1), role)
1535 User.add_to_project(user, Project.find(1), role)
1534
1536
1535 assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1537 assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1536 end
1538 end
1537
1539
1538 def test_allowed_target_trackers_with_one_role_allowed_on_some_trackers
1540 def test_allowed_target_trackers_with_one_role_allowed_on_some_trackers
1539 user = User.generate!
1541 user = User.generate!
1540 role = Role.generate!
1542 role = Role.generate!
1541 role.add_permission! :add_issues
1543 role.add_permission! :add_issues
1542 role.set_permission_trackers :add_issues, [1, 3]
1544 role.set_permission_trackers :add_issues, [1, 3]
1543 role.save!
1545 role.save!
1544 User.add_to_project(user, Project.find(1), role)
1546 User.add_to_project(user, Project.find(1), role)
1545
1547
1546 assert_equal [1, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1548 assert_equal [1, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1547 end
1549 end
1548
1550
1549 def test_allowed_target_trackers_with_two_roles_allowed_on_some_trackers
1551 def test_allowed_target_trackers_with_two_roles_allowed_on_some_trackers
1550 user = User.generate!
1552 user = User.generate!
1551 role1 = Role.generate!
1553 role1 = Role.generate!
1552 role1.add_permission! :add_issues
1554 role1.add_permission! :add_issues
1553 role1.set_permission_trackers :add_issues, [1]
1555 role1.set_permission_trackers :add_issues, [1]
1554 role1.save!
1556 role1.save!
1555 role2 = Role.generate!
1557 role2 = Role.generate!
1556 role2.add_permission! :add_issues
1558 role2.add_permission! :add_issues
1557 role2.set_permission_trackers :add_issues, [3]
1559 role2.set_permission_trackers :add_issues, [3]
1558 role2.save!
1560 role2.save!
1559 User.add_to_project(user, Project.find(1), [role1, role2])
1561 User.add_to_project(user, Project.find(1), [role1, role2])
1560
1562
1561 assert_equal [1, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1563 assert_equal [1, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1562 end
1564 end
1563
1565
1564 def test_allowed_target_trackers_with_two_roles_allowed_on_all_trackers_and_some_trackers
1566 def test_allowed_target_trackers_with_two_roles_allowed_on_all_trackers_and_some_trackers
1565 user = User.generate!
1567 user = User.generate!
1566 role1 = Role.generate!
1568 role1 = Role.generate!
1567 role1.add_permission! :add_issues
1569 role1.add_permission! :add_issues
1568 role1.set_permission_trackers :add_issues, :all
1570 role1.set_permission_trackers :add_issues, :all
1569 role1.save!
1571 role1.save!
1570 role2 = Role.generate!
1572 role2 = Role.generate!
1571 role2.add_permission! :add_issues
1573 role2.add_permission! :add_issues
1572 role2.set_permission_trackers :add_issues, [1, 3]
1574 role2.set_permission_trackers :add_issues, [1, 3]
1573 role2.save!
1575 role2.save!
1574 User.add_to_project(user, Project.find(1), [role1, role2])
1576 User.add_to_project(user, Project.find(1), [role1, role2])
1575
1577
1576 assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1578 assert_equal [1, 2, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1577 end
1579 end
1578
1580
1579 def test_allowed_target_trackers_should_not_consider_roles_without_add_issues_permission
1581 def test_allowed_target_trackers_should_not_consider_roles_without_add_issues_permission
1580 user = User.generate!
1582 user = User.generate!
1581 role1 = Role.generate!
1583 role1 = Role.generate!
1582 role1.remove_permission! :add_issues
1584 role1.remove_permission! :add_issues
1583 role1.set_permission_trackers :add_issues, :all
1585 role1.set_permission_trackers :add_issues, :all
1584 role1.save!
1586 role1.save!
1585 role2 = Role.generate!
1587 role2 = Role.generate!
1586 role2.add_permission! :add_issues
1588 role2.add_permission! :add_issues
1587 role2.set_permission_trackers :add_issues, [1, 3]
1589 role2.set_permission_trackers :add_issues, [1, 3]
1588 role2.save!
1590 role2.save!
1589 User.add_to_project(user, Project.find(1), [role1, role2])
1591 User.add_to_project(user, Project.find(1), [role1, role2])
1590
1592
1591 assert_equal [1, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1593 assert_equal [1, 3], Issue.new(:project => Project.find(1)).allowed_target_trackers(user).ids.sort
1592 end
1594 end
1593
1595
1594 def test_allowed_target_trackers_without_project_should_be_empty
1596 def test_allowed_target_trackers_without_project_should_be_empty
1595 issue = Issue.new
1597 issue = Issue.new
1596 assert_nil issue.project
1598 assert_nil issue.project
1597 assert_equal [], issue.allowed_target_trackers(User.find(2)).ids
1599 assert_equal [], issue.allowed_target_trackers(User.find(2)).ids
1598 end
1600 end
1599
1601
1600 def test_allowed_target_trackers_should_include_current_tracker
1602 def test_allowed_target_trackers_should_include_current_tracker
1601 user = User.generate!
1603 user = User.generate!
1602 role = Role.generate!
1604 role = Role.generate!
1603 role.add_permission! :add_issues
1605 role.add_permission! :add_issues
1604 role.set_permission_trackers :add_issues, [3]
1606 role.set_permission_trackers :add_issues, [3]
1605 role.save!
1607 role.save!
1606 User.add_to_project(user, Project.find(1), role)
1608 User.add_to_project(user, Project.find(1), role)
1607
1609
1608 issue = Issue.generate!(:project => Project.find(1), :tracker => Tracker.find(1))
1610 issue = Issue.generate!(:project => Project.find(1), :tracker => Tracker.find(1))
1609 assert_equal [1, 3], issue.allowed_target_trackers(user).ids.sort
1611 assert_equal [1, 3], issue.allowed_target_trackers(user).ids.sort
1610 end
1612 end
1611
1613
1612 def test_move_to_another_project_with_same_category
1614 def test_move_to_another_project_with_same_category
1613 issue = Issue.find(1)
1615 issue = Issue.find(1)
1614 issue.project = Project.find(2)
1616 issue.project = Project.find(2)
1615 assert issue.save
1617 assert issue.save
1616 issue.reload
1618 issue.reload
1617 assert_equal 2, issue.project_id
1619 assert_equal 2, issue.project_id
1618 # Category changes
1620 # Category changes
1619 assert_equal 4, issue.category_id
1621 assert_equal 4, issue.category_id
1620 # Make sure time entries were move to the target project
1622 # Make sure time entries were move to the target project
1621 assert_equal 2, issue.time_entries.first.project_id
1623 assert_equal 2, issue.time_entries.first.project_id
1622 end
1624 end
1623
1625
1624 def test_move_to_another_project_without_same_category
1626 def test_move_to_another_project_without_same_category
1625 issue = Issue.find(2)
1627 issue = Issue.find(2)
1626 issue.project = Project.find(2)
1628 issue.project = Project.find(2)
1627 assert issue.save
1629 assert issue.save
1628 issue.reload
1630 issue.reload
1629 assert_equal 2, issue.project_id
1631 assert_equal 2, issue.project_id
1630 # Category cleared
1632 # Category cleared
1631 assert_nil issue.category_id
1633 assert_nil issue.category_id
1632 end
1634 end
1633
1635
1634 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
1636 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
1635 issue = Issue.find(1)
1637 issue = Issue.find(1)
1636 issue.update!(:fixed_version_id => 3)
1638 issue.update!(:fixed_version_id => 3)
1637 issue.project = Project.find(2)
1639 issue.project = Project.find(2)
1638 assert issue.save
1640 assert issue.save
1639 issue.reload
1641 issue.reload
1640 assert_equal 2, issue.project_id
1642 assert_equal 2, issue.project_id
1641 # Cleared fixed_version
1643 # Cleared fixed_version
1642 assert_equal nil, issue.fixed_version
1644 assert_equal nil, issue.fixed_version
1643 end
1645 end
1644
1646
1645 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
1647 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
1646 issue = Issue.find(1)
1648 issue = Issue.find(1)
1647 issue.update!(:fixed_version_id => 4)
1649 issue.update!(:fixed_version_id => 4)
1648 issue.project = Project.find(5)
1650 issue.project = Project.find(5)
1649 assert issue.save
1651 assert issue.save
1650 issue.reload
1652 issue.reload
1651 assert_equal 5, issue.project_id
1653 assert_equal 5, issue.project_id
1652 # Keep fixed_version
1654 # Keep fixed_version
1653 assert_equal 4, issue.fixed_version_id
1655 assert_equal 4, issue.fixed_version_id
1654 end
1656 end
1655
1657
1656 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
1658 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
1657 issue = Issue.find(1)
1659 issue = Issue.find(1)
1658 issue.update!(:fixed_version_id => 3)
1660 issue.update!(:fixed_version_id => 3)
1659 issue.project = Project.find(5)
1661 issue.project = Project.find(5)
1660 assert issue.save
1662 assert issue.save
1661 issue.reload
1663 issue.reload
1662 assert_equal 5, issue.project_id
1664 assert_equal 5, issue.project_id
1663 # Cleared fixed_version
1665 # Cleared fixed_version
1664 assert_equal nil, issue.fixed_version
1666 assert_equal nil, issue.fixed_version
1665 end
1667 end
1666
1668
1667 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
1669 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
1668 issue = Issue.find(1)
1670 issue = Issue.find(1)
1669 issue.update!(:fixed_version_id => 7)
1671 issue.update!(:fixed_version_id => 7)
1670 issue.project = Project.find(2)
1672 issue.project = Project.find(2)
1671 assert issue.save
1673 assert issue.save
1672 issue.reload
1674 issue.reload
1673 assert_equal 2, issue.project_id
1675 assert_equal 2, issue.project_id
1674 # Keep fixed_version
1676 # Keep fixed_version
1675 assert_equal 7, issue.fixed_version_id
1677 assert_equal 7, issue.fixed_version_id
1676 end
1678 end
1677
1679
1678 def test_move_to_another_project_should_keep_parent_if_valid
1680 def test_move_to_another_project_should_keep_parent_if_valid
1679 issue = Issue.find(1)
1681 issue = Issue.find(1)
1680 issue.update! :parent_issue_id => 2
1682 issue.update! :parent_issue_id => 2
1681 issue.project = Project.find(3)
1683 issue.project = Project.find(3)
1682 assert issue.save
1684 assert issue.save
1683 issue.reload
1685 issue.reload
1684 assert_equal 2, issue.parent_id
1686 assert_equal 2, issue.parent_id
1685 end
1687 end
1686
1688
1687 def test_move_to_another_project_should_clear_parent_if_not_valid
1689 def test_move_to_another_project_should_clear_parent_if_not_valid
1688 issue = Issue.find(1)
1690 issue = Issue.find(1)
1689 issue.update! :parent_issue_id => 2
1691 issue.update! :parent_issue_id => 2
1690 issue.project = Project.find(2)
1692 issue.project = Project.find(2)
1691 assert issue.save
1693 assert issue.save
1692 issue.reload
1694 issue.reload
1693 assert_nil issue.parent_id
1695 assert_nil issue.parent_id
1694 end
1696 end
1695
1697
1696 def test_move_to_another_project_with_disabled_tracker
1698 def test_move_to_another_project_with_disabled_tracker
1697 issue = Issue.find(1)
1699 issue = Issue.find(1)
1698 target = Project.find(2)
1700 target = Project.find(2)
1699 target.tracker_ids = [3]
1701 target.tracker_ids = [3]
1700 target.save
1702 target.save
1701 issue.project = target
1703 issue.project = target
1702 assert issue.save
1704 assert issue.save
1703 issue.reload
1705 issue.reload
1704 assert_equal 2, issue.project_id
1706 assert_equal 2, issue.project_id
1705 assert_equal 3, issue.tracker_id
1707 assert_equal 3, issue.tracker_id
1706 end
1708 end
1707
1709
1708 def test_copy_to_the_same_project
1710 def test_copy_to_the_same_project
1709 issue = Issue.find(1)
1711 issue = Issue.find(1)
1710 copy = issue.copy
1712 copy = issue.copy
1711 assert_difference 'Issue.count' do
1713 assert_difference 'Issue.count' do
1712 copy.save!
1714 copy.save!
1713 end
1715 end
1714 assert_kind_of Issue, copy
1716 assert_kind_of Issue, copy
1715 assert_equal issue.project, copy.project
1717 assert_equal issue.project, copy.project
1716 assert_equal "125", copy.custom_value_for(2).value
1718 assert_equal "125", copy.custom_value_for(2).value
1717 end
1719 end
1718
1720
1719 def test_copy_to_another_project_and_tracker
1721 def test_copy_to_another_project_and_tracker
1720 issue = Issue.find(1)
1722 issue = Issue.find(1)
1721 copy = issue.copy(:project_id => 3, :tracker_id => 2)
1723 copy = issue.copy(:project_id => 3, :tracker_id => 2)
1722 assert_difference 'Issue.count' do
1724 assert_difference 'Issue.count' do
1723 copy.save!
1725 copy.save!
1724 end
1726 end
1725 copy.reload
1727 copy.reload
1726 assert_kind_of Issue, copy
1728 assert_kind_of Issue, copy
1727 assert_equal Project.find(3), copy.project
1729 assert_equal Project.find(3), copy.project
1728 assert_equal Tracker.find(2), copy.tracker
1730 assert_equal Tracker.find(2), copy.tracker
1729 # Custom field #2 is not associated with target tracker
1731 # Custom field #2 is not associated with target tracker
1730 assert_nil copy.custom_value_for(2)
1732 assert_nil copy.custom_value_for(2)
1731 end
1733 end
1732
1734
1733 test "#copy should not create a journal" do
1735 test "#copy should not create a journal" do
1734 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :assigned_to_id => 3}, :link => false)
1736 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :assigned_to_id => 3}, :link => false)
1735 copy.save!
1737 copy.save!
1736 assert_equal 0, copy.reload.journals.size
1738 assert_equal 0, copy.reload.journals.size
1737 end
1739 end
1738
1740
1739 test "#copy should allow assigned_to changes" do
1741 test "#copy should allow assigned_to changes" do
1740 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1742 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1741 assert_equal 3, copy.assigned_to_id
1743 assert_equal 3, copy.assigned_to_id
1742 end
1744 end
1743
1745
1744 test "#copy should allow status changes" do
1746 test "#copy should allow status changes" do
1745 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
1747 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
1746 assert_equal 2, copy.status_id
1748 assert_equal 2, copy.status_id
1747 end
1749 end
1748
1750
1749 test "#copy should allow start date changes" do
1751 test "#copy should allow start date changes" do
1750 date = Date.today
1752 date = Date.today
1751 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1753 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1752 assert_equal date, copy.start_date
1754 assert_equal date, copy.start_date
1753 end
1755 end
1754
1756
1755 test "#copy should allow due date changes" do
1757 test "#copy should allow due date changes" do
1756 date = Date.today
1758 date = Date.today
1757 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :due_date => date)
1759 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :due_date => date)
1758 assert_equal date, copy.due_date
1760 assert_equal date, copy.due_date
1759 end
1761 end
1760
1762
1761 test "#copy should set current user as author" do
1763 test "#copy should set current user as author" do
1762 User.current = User.find(9)
1764 User.current = User.find(9)
1763 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2)
1765 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2)
1764 assert_equal User.current, copy.author
1766 assert_equal User.current, copy.author
1765 end
1767 end
1766
1768
1767 test "#copy should create a journal with notes" do
1769 test "#copy should create a journal with notes" do
1768 date = Date.today
1770 date = Date.today
1769 notes = "Notes added when copying"
1771 notes = "Notes added when copying"
1770 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :start_date => date}, :link => false)
1772 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :start_date => date}, :link => false)
1771 copy.init_journal(User.current, notes)
1773 copy.init_journal(User.current, notes)
1772 copy.save!
1774 copy.save!
1773
1775
1774 assert_equal 1, copy.journals.size
1776 assert_equal 1, copy.journals.size
1775 journal = copy.journals.first
1777 journal = copy.journals.first
1776 assert_equal 0, journal.details.size
1778 assert_equal 0, journal.details.size
1777 assert_equal notes, journal.notes
1779 assert_equal notes, journal.notes
1778 end
1780 end
1779
1781
1780 def test_valid_parent_project
1782 def test_valid_parent_project
1781 issue = Issue.find(1)
1783 issue = Issue.find(1)
1782 issue_in_same_project = Issue.find(2)
1784 issue_in_same_project = Issue.find(2)
1783 issue_in_child_project = Issue.find(5)
1785 issue_in_child_project = Issue.find(5)
1784 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1786 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1785 issue_in_other_child_project = Issue.find(6)
1787 issue_in_other_child_project = Issue.find(6)
1786 issue_in_different_tree = Issue.find(4)
1788 issue_in_different_tree = Issue.find(4)
1787
1789
1788 with_settings :cross_project_subtasks => '' do
1790 with_settings :cross_project_subtasks => '' do
1789 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1791 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1790 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1792 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1791 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1793 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1792 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1794 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1793 end
1795 end
1794
1796
1795 with_settings :cross_project_subtasks => 'system' do
1797 with_settings :cross_project_subtasks => 'system' do
1796 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1798 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1797 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1799 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1798 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1800 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1799 end
1801 end
1800
1802
1801 with_settings :cross_project_subtasks => 'tree' do
1803 with_settings :cross_project_subtasks => 'tree' do
1802 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1804 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1803 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1805 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1804 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1806 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1805 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1807 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1806
1808
1807 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1809 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1808 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1810 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1809 end
1811 end
1810
1812
1811 with_settings :cross_project_subtasks => 'descendants' do
1813 with_settings :cross_project_subtasks => 'descendants' do
1812 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1814 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1813 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1815 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1814 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1816 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1815 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1817 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1816
1818
1817 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1819 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1818 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1820 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1819 end
1821 end
1820 end
1822 end
1821
1823
1822 def test_recipients_should_include_previous_assignee
1824 def test_recipients_should_include_previous_assignee
1823 user = User.find(3)
1825 user = User.find(3)
1824 user.members.update_all ["mail_notification = ?", false]
1826 user.members.update_all ["mail_notification = ?", false]
1825 user.update! :mail_notification => 'only_assigned'
1827 user.update! :mail_notification => 'only_assigned'
1826
1828
1827 issue = Issue.find(2)
1829 issue = Issue.find(2)
1828 issue.assigned_to = nil
1830 issue.assigned_to = nil
1829 assert_include user.mail, issue.recipients
1831 assert_include user.mail, issue.recipients
1830 issue.save!
1832 issue.save!
1831 assert !issue.recipients.include?(user.mail)
1833 assert !issue.recipients.include?(user.mail)
1832 end
1834 end
1833
1835
1834 def test_recipients_should_not_include_users_that_cannot_view_the_issue
1836 def test_recipients_should_not_include_users_that_cannot_view_the_issue
1835 issue = Issue.find(12)
1837 issue = Issue.find(12)
1836 assert issue.recipients.include?(issue.author.mail)
1838 assert issue.recipients.include?(issue.author.mail)
1837 # copy the issue to a private project
1839 # copy the issue to a private project
1838 copy = issue.copy(:project_id => 5, :tracker_id => 2)
1840 copy = issue.copy(:project_id => 5, :tracker_id => 2)
1839 # author is not a member of project anymore
1841 # author is not a member of project anymore
1840 assert !copy.recipients.include?(copy.author.mail)
1842 assert !copy.recipients.include?(copy.author.mail)
1841 end
1843 end
1842
1844
1843 def test_recipients_should_include_the_assigned_group_members
1845 def test_recipients_should_include_the_assigned_group_members
1844 group_member = User.generate!
1846 group_member = User.generate!
1845 group = Group.generate!
1847 group = Group.generate!
1846 group.users << group_member
1848 group.users << group_member
1847
1849
1848 issue = Issue.find(12)
1850 issue = Issue.find(12)
1849 issue.assigned_to = group
1851 issue.assigned_to = group
1850 assert issue.recipients.include?(group_member.mail)
1852 assert issue.recipients.include?(group_member.mail)
1851 end
1853 end
1852
1854
1853 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1855 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1854 user = User.find(3)
1856 user = User.find(3)
1855 issue = Issue.find(9)
1857 issue = Issue.find(9)
1856 Watcher.create!(:user => user, :watchable => issue)
1858 Watcher.create!(:user => user, :watchable => issue)
1857 assert issue.watched_by?(user)
1859 assert issue.watched_by?(user)
1858 assert !issue.watcher_recipients.include?(user.mail)
1860 assert !issue.watcher_recipients.include?(user.mail)
1859 end
1861 end
1860
1862
1861 def test_issue_destroy
1863 def test_issue_destroy
1862 Issue.find(1).destroy
1864 Issue.find(1).destroy
1863 assert_nil Issue.find_by_id(1)
1865 assert_nil Issue.find_by_id(1)
1864 assert_nil TimeEntry.find_by_issue_id(1)
1866 assert_nil TimeEntry.find_by_issue_id(1)
1865 end
1867 end
1866
1868
1867 def test_destroy_should_delete_time_entries_custom_values
1869 def test_destroy_should_delete_time_entries_custom_values
1868 issue = Issue.generate!
1870 issue = Issue.generate!
1869 time_entry = TimeEntry.generate!(:issue => issue, :custom_field_values => {10 => '1'})
1871 time_entry = TimeEntry.generate!(:issue => issue, :custom_field_values => {10 => '1'})
1870
1872
1871 assert_difference 'CustomValue.where(:customized_type => "TimeEntry").count', -1 do
1873 assert_difference 'CustomValue.where(:customized_type => "TimeEntry").count', -1 do
1872 assert issue.destroy
1874 assert issue.destroy
1873 end
1875 end
1874 end
1876 end
1875
1877
1876 def test_destroying_a_deleted_issue_should_not_raise_an_error
1878 def test_destroying_a_deleted_issue_should_not_raise_an_error
1877 issue = Issue.find(1)
1879 issue = Issue.find(1)
1878 Issue.find(1).destroy
1880 Issue.find(1).destroy
1879
1881
1880 assert_nothing_raised do
1882 assert_nothing_raised do
1881 assert_no_difference 'Issue.count' do
1883 assert_no_difference 'Issue.count' do
1882 issue.destroy
1884 issue.destroy
1883 end
1885 end
1884 assert issue.destroyed?
1886 assert issue.destroyed?
1885 end
1887 end
1886 end
1888 end
1887
1889
1888 def test_destroying_a_stale_issue_should_not_raise_an_error
1890 def test_destroying_a_stale_issue_should_not_raise_an_error
1889 issue = Issue.find(1)
1891 issue = Issue.find(1)
1890 Issue.find(1).update! :subject => "Updated"
1892 Issue.find(1).update! :subject => "Updated"
1891
1893
1892 assert_nothing_raised do
1894 assert_nothing_raised do
1893 assert_difference 'Issue.count', -1 do
1895 assert_difference 'Issue.count', -1 do
1894 issue.destroy
1896 issue.destroy
1895 end
1897 end
1896 assert issue.destroyed?
1898 assert issue.destroyed?
1897 end
1899 end
1898 end
1900 end
1899
1901
1900 def test_blocked
1902 def test_blocked
1901 blocked_issue = Issue.find(9)
1903 blocked_issue = Issue.find(9)
1902 blocking_issue = Issue.find(10)
1904 blocking_issue = Issue.find(10)
1903
1905
1904 assert blocked_issue.blocked?
1906 assert blocked_issue.blocked?
1905 assert !blocking_issue.blocked?
1907 assert !blocking_issue.blocked?
1906 end
1908 end
1907
1909
1908 def test_blocked_issues_dont_allow_closed_statuses
1910 def test_blocked_issues_dont_allow_closed_statuses
1909 blocked_issue = Issue.find(9)
1911 blocked_issue = Issue.find(9)
1910
1912
1911 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1913 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1912 assert !allowed_statuses.empty?
1914 assert !allowed_statuses.empty?
1913 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1915 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1914 assert closed_statuses.empty?
1916 assert closed_statuses.empty?
1915 end
1917 end
1916
1918
1917 def test_unblocked_issues_allow_closed_statuses
1919 def test_unblocked_issues_allow_closed_statuses
1918 blocking_issue = Issue.find(10)
1920 blocking_issue = Issue.find(10)
1919
1921
1920 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1922 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1921 assert !allowed_statuses.empty?
1923 assert !allowed_statuses.empty?
1922 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1924 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1923 assert !closed_statuses.empty?
1925 assert !closed_statuses.empty?
1924 end
1926 end
1925
1927
1926 def test_reschedule_an_issue_without_dates
1928 def test_reschedule_an_issue_without_dates
1927 with_settings :non_working_week_days => [] do
1929 with_settings :non_working_week_days => [] do
1928 issue = Issue.new(:start_date => nil, :due_date => nil)
1930 issue = Issue.new(:start_date => nil, :due_date => nil)
1929 issue.reschedule_on '2012-10-09'.to_date
1931 issue.reschedule_on '2012-10-09'.to_date
1930 assert_equal '2012-10-09'.to_date, issue.start_date
1932 assert_equal '2012-10-09'.to_date, issue.start_date
1931 assert_equal '2012-10-09'.to_date, issue.due_date
1933 assert_equal '2012-10-09'.to_date, issue.due_date
1932 end
1934 end
1933
1935
1934 with_settings :non_working_week_days => %w(6 7) do
1936 with_settings :non_working_week_days => %w(6 7) do
1935 issue = Issue.new(:start_date => nil, :due_date => nil)
1937 issue = Issue.new(:start_date => nil, :due_date => nil)
1936 issue.reschedule_on '2012-10-09'.to_date
1938 issue.reschedule_on '2012-10-09'.to_date
1937 assert_equal '2012-10-09'.to_date, issue.start_date
1939 assert_equal '2012-10-09'.to_date, issue.start_date
1938 assert_equal '2012-10-09'.to_date, issue.due_date
1940 assert_equal '2012-10-09'.to_date, issue.due_date
1939
1941
1940 issue = Issue.new(:start_date => nil, :due_date => nil)
1942 issue = Issue.new(:start_date => nil, :due_date => nil)
1941 issue.reschedule_on '2012-10-13'.to_date
1943 issue.reschedule_on '2012-10-13'.to_date
1942 assert_equal '2012-10-15'.to_date, issue.start_date
1944 assert_equal '2012-10-15'.to_date, issue.start_date
1943 assert_equal '2012-10-15'.to_date, issue.due_date
1945 assert_equal '2012-10-15'.to_date, issue.due_date
1944 end
1946 end
1945 end
1947 end
1946
1948
1947 def test_reschedule_an_issue_with_start_date
1949 def test_reschedule_an_issue_with_start_date
1948 with_settings :non_working_week_days => [] do
1950 with_settings :non_working_week_days => [] do
1949 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1951 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1950 issue.reschedule_on '2012-10-13'.to_date
1952 issue.reschedule_on '2012-10-13'.to_date
1951 assert_equal '2012-10-13'.to_date, issue.start_date
1953 assert_equal '2012-10-13'.to_date, issue.start_date
1952 assert_equal '2012-10-13'.to_date, issue.due_date
1954 assert_equal '2012-10-13'.to_date, issue.due_date
1953 end
1955 end
1954
1956
1955 with_settings :non_working_week_days => %w(6 7) do
1957 with_settings :non_working_week_days => %w(6 7) do
1956 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1958 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1957 issue.reschedule_on '2012-10-11'.to_date
1959 issue.reschedule_on '2012-10-11'.to_date
1958 assert_equal '2012-10-11'.to_date, issue.start_date
1960 assert_equal '2012-10-11'.to_date, issue.start_date
1959 assert_equal '2012-10-11'.to_date, issue.due_date
1961 assert_equal '2012-10-11'.to_date, issue.due_date
1960
1962
1961 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1963 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1962 issue.reschedule_on '2012-10-13'.to_date
1964 issue.reschedule_on '2012-10-13'.to_date
1963 assert_equal '2012-10-15'.to_date, issue.start_date
1965 assert_equal '2012-10-15'.to_date, issue.start_date
1964 assert_equal '2012-10-15'.to_date, issue.due_date
1966 assert_equal '2012-10-15'.to_date, issue.due_date
1965 end
1967 end
1966 end
1968 end
1967
1969
1968 def test_reschedule_an_issue_with_start_and_due_dates
1970 def test_reschedule_an_issue_with_start_and_due_dates
1969 with_settings :non_working_week_days => [] do
1971 with_settings :non_working_week_days => [] do
1970 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-15')
1972 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-15')
1971 issue.reschedule_on '2012-10-13'.to_date
1973 issue.reschedule_on '2012-10-13'.to_date
1972 assert_equal '2012-10-13'.to_date, issue.start_date
1974 assert_equal '2012-10-13'.to_date, issue.start_date
1973 assert_equal '2012-10-19'.to_date, issue.due_date
1975 assert_equal '2012-10-19'.to_date, issue.due_date
1974 end
1976 end
1975
1977
1976 with_settings :non_working_week_days => %w(6 7) do
1978 with_settings :non_working_week_days => %w(6 7) do
1977 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19') # 8 working days
1979 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19') # 8 working days
1978 issue.reschedule_on '2012-10-11'.to_date
1980 issue.reschedule_on '2012-10-11'.to_date
1979 assert_equal '2012-10-11'.to_date, issue.start_date
1981 assert_equal '2012-10-11'.to_date, issue.start_date
1980 assert_equal '2012-10-23'.to_date, issue.due_date
1982 assert_equal '2012-10-23'.to_date, issue.due_date
1981
1983
1982 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19')
1984 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19')
1983 issue.reschedule_on '2012-10-13'.to_date
1985 issue.reschedule_on '2012-10-13'.to_date
1984 assert_equal '2012-10-15'.to_date, issue.start_date
1986 assert_equal '2012-10-15'.to_date, issue.start_date
1985 assert_equal '2012-10-25'.to_date, issue.due_date
1987 assert_equal '2012-10-25'.to_date, issue.due_date
1986 end
1988 end
1987 end
1989 end
1988
1990
1989 def test_rescheduling_an_issue_to_a_later_due_date_should_reschedule_following_issue
1991 def test_rescheduling_an_issue_to_a_later_due_date_should_reschedule_following_issue
1990 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1992 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1991 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1993 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1992 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1994 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1993 :relation_type => IssueRelation::TYPE_PRECEDES)
1995 :relation_type => IssueRelation::TYPE_PRECEDES)
1994 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1996 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1995
1997
1996 issue1.reload
1998 issue1.reload
1997 issue1.due_date = '2012-10-23'
1999 issue1.due_date = '2012-10-23'
1998 issue1.save!
2000 issue1.save!
1999 issue2.reload
2001 issue2.reload
2000 assert_equal Date.parse('2012-10-24'), issue2.start_date
2002 assert_equal Date.parse('2012-10-24'), issue2.start_date
2001 assert_equal Date.parse('2012-10-26'), issue2.due_date
2003 assert_equal Date.parse('2012-10-26'), issue2.due_date
2002 end
2004 end
2003
2005
2004 def test_rescheduling_an_issue_to_an_earlier_due_date_should_reschedule_following_issue
2006 def test_rescheduling_an_issue_to_an_earlier_due_date_should_reschedule_following_issue
2005 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2007 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2006 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2008 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2007 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
2009 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
2008 :relation_type => IssueRelation::TYPE_PRECEDES)
2010 :relation_type => IssueRelation::TYPE_PRECEDES)
2009 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
2011 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
2010
2012
2011 issue1.reload
2013 issue1.reload
2012 issue1.start_date = '2012-09-17'
2014 issue1.start_date = '2012-09-17'
2013 issue1.due_date = '2012-09-18'
2015 issue1.due_date = '2012-09-18'
2014 issue1.save!
2016 issue1.save!
2015 issue2.reload
2017 issue2.reload
2016 assert_equal Date.parse('2012-09-19'), issue2.start_date
2018 assert_equal Date.parse('2012-09-19'), issue2.start_date
2017 assert_equal Date.parse('2012-09-21'), issue2.due_date
2019 assert_equal Date.parse('2012-09-21'), issue2.due_date
2018 end
2020 end
2019
2021
2020 def test_rescheduling_reschedule_following_issue_earlier_should_consider_other_preceding_issues
2022 def test_rescheduling_reschedule_following_issue_earlier_should_consider_other_preceding_issues
2021 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2023 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2022 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2024 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2023 issue3 = Issue.generate!(:start_date => '2012-10-01', :due_date => '2012-10-02')
2025 issue3 = Issue.generate!(:start_date => '2012-10-01', :due_date => '2012-10-02')
2024 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
2026 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
2025 :relation_type => IssueRelation::TYPE_PRECEDES)
2027 :relation_type => IssueRelation::TYPE_PRECEDES)
2026 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
2028 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
2027 :relation_type => IssueRelation::TYPE_PRECEDES)
2029 :relation_type => IssueRelation::TYPE_PRECEDES)
2028 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
2030 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
2029
2031
2030 issue1.reload
2032 issue1.reload
2031 issue1.start_date = '2012-09-17'
2033 issue1.start_date = '2012-09-17'
2032 issue1.due_date = '2012-09-18'
2034 issue1.due_date = '2012-09-18'
2033 issue1.save!
2035 issue1.save!
2034 issue2.reload
2036 issue2.reload
2035 # Issue 2 must start after Issue 3
2037 # Issue 2 must start after Issue 3
2036 assert_equal Date.parse('2012-10-03'), issue2.start_date
2038 assert_equal Date.parse('2012-10-03'), issue2.start_date
2037 assert_equal Date.parse('2012-10-05'), issue2.due_date
2039 assert_equal Date.parse('2012-10-05'), issue2.due_date
2038 end
2040 end
2039
2041
2040 def test_rescheduling_a_stale_issue_should_not_raise_an_error
2042 def test_rescheduling_a_stale_issue_should_not_raise_an_error
2041 with_settings :non_working_week_days => [] do
2043 with_settings :non_working_week_days => [] do
2042 stale = Issue.find(1)
2044 stale = Issue.find(1)
2043 issue = Issue.find(1)
2045 issue = Issue.find(1)
2044 issue.subject = "Updated"
2046 issue.subject = "Updated"
2045 issue.save!
2047 issue.save!
2046 date = 10.days.from_now.to_date
2048 date = 10.days.from_now.to_date
2047 assert_nothing_raised do
2049 assert_nothing_raised do
2048 stale.reschedule_on!(date)
2050 stale.reschedule_on!(date)
2049 end
2051 end
2050 assert_equal date, stale.reload.start_date
2052 assert_equal date, stale.reload.start_date
2051 end
2053 end
2052 end
2054 end
2053
2055
2054 def test_child_issue_should_consider_parent_soonest_start_on_create
2056 def test_child_issue_should_consider_parent_soonest_start_on_create
2055 set_language_if_valid 'en'
2057 set_language_if_valid 'en'
2056 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2058 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
2057 issue2 = Issue.generate!(:start_date => '2012-10-18', :due_date => '2012-10-20')
2059 issue2 = Issue.generate!(:start_date => '2012-10-18', :due_date => '2012-10-20')
2058 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
2060 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
2059 :relation_type => IssueRelation::TYPE_PRECEDES)
2061 :relation_type => IssueRelation::TYPE_PRECEDES)
2060 issue1.reload
2062 issue1.reload
2061 issue2.reload
2063 issue2.reload
2062 assert_equal Date.parse('2012-10-18'), issue2.start_date
2064 assert_equal Date.parse('2012-10-18'), issue2.start_date
2063
2065
2064 with_settings :date_format => '%m/%d/%Y' do
2066 with_settings :date_format => '%m/%d/%Y' do
2065 child = Issue.new(:parent_issue_id => issue2.id, :start_date => '2012-10-16',
2067 child = Issue.new(:parent_issue_id => issue2.id, :start_date => '2012-10-16',
2066 :project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Child', :author_id => 1)
2068 :project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Child', :author_id => 1)
2067 assert !child.valid?
2069 assert !child.valid?
2068 assert_include 'Start date cannot be earlier than 10/18/2012 because of preceding issues', child.errors.full_messages
2070 assert_include 'Start date cannot be earlier than 10/18/2012 because of preceding issues', child.errors.full_messages
2069 assert_equal Date.parse('2012-10-18'), child.soonest_start
2071 assert_equal Date.parse('2012-10-18'), child.soonest_start
2070 child.start_date = '2012-10-18'
2072 child.start_date = '2012-10-18'
2071 assert child.save
2073 assert child.save
2072 end
2074 end
2073 end
2075 end
2074
2076
2075 def test_setting_parent_to_a_an_issue_that_precedes_should_not_validate
2077 def test_setting_parent_to_a_an_issue_that_precedes_should_not_validate
2076 # tests that 3 cannot have 1 as parent:
2078 # tests that 3 cannot have 1 as parent:
2077 #
2079 #
2078 # 1 -> 2 -> 3
2080 # 1 -> 2 -> 3
2079 #
2081 #
2080 set_language_if_valid 'en'
2082 set_language_if_valid 'en'
2081 issue1 = Issue.generate!
2083 issue1 = Issue.generate!
2082 issue2 = Issue.generate!
2084 issue2 = Issue.generate!
2083 issue3 = Issue.generate!
2085 issue3 = Issue.generate!
2084 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
2086 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
2085 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
2087 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
2086 issue3.reload
2088 issue3.reload
2087 issue3.parent_issue_id = issue1.id
2089 issue3.parent_issue_id = issue1.id
2088 assert !issue3.valid?
2090 assert !issue3.valid?
2089 assert_include 'Parent task is invalid', issue3.errors.full_messages
2091 assert_include 'Parent task is invalid', issue3.errors.full_messages
2090 end
2092 end
2091
2093
2092 def test_setting_parent_to_a_an_issue_that_follows_should_not_validate
2094 def test_setting_parent_to_a_an_issue_that_follows_should_not_validate
2093 # tests that 1 cannot have 3 as parent:
2095 # tests that 1 cannot have 3 as parent:
2094 #
2096 #
2095 # 1 -> 2 -> 3
2097 # 1 -> 2 -> 3
2096 #
2098 #
2097 set_language_if_valid 'en'
2099 set_language_if_valid 'en'
2098 issue1 = Issue.generate!
2100 issue1 = Issue.generate!
2099 issue2 = Issue.generate!
2101 issue2 = Issue.generate!
2100 issue3 = Issue.generate!
2102 issue3 = Issue.generate!
2101 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
2103 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
2102 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
2104 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
2103 issue1.reload
2105 issue1.reload
2104 issue1.parent_issue_id = issue3.id
2106 issue1.parent_issue_id = issue3.id
2105 assert !issue1.valid?
2107 assert !issue1.valid?
2106 assert_include 'Parent task is invalid', issue1.errors.full_messages
2108 assert_include 'Parent task is invalid', issue1.errors.full_messages
2107 end
2109 end
2108
2110
2109 def test_setting_parent_to_a_an_issue_that_precedes_through_hierarchy_should_not_validate
2111 def test_setting_parent_to_a_an_issue_that_precedes_through_hierarchy_should_not_validate
2110 # tests that 4 cannot have 1 as parent:
2112 # tests that 4 cannot have 1 as parent:
2111 # changing the due date of 4 would update the end date of 1 which would reschedule 2
2113 # changing the due date of 4 would update the end date of 1 which would reschedule 2
2112 # which would change the end date of 3 which would reschedule 4 and so on...
2114 # which would change the end date of 3 which would reschedule 4 and so on...
2113 #
2115 #
2114 # 3 -> 4
2116 # 3 -> 4
2115 # ^
2117 # ^
2116 # 1 -> 2
2118 # 1 -> 2
2117 #
2119 #
2118 set_language_if_valid 'en'
2120 set_language_if_valid 'en'
2119 issue1 = Issue.generate!
2121 issue1 = Issue.generate!
2120 issue2 = Issue.generate!
2122 issue2 = Issue.generate!
2121 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
2123 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
2122 issue3 = Issue.generate!
2124 issue3 = Issue.generate!
2123 issue2.reload
2125 issue2.reload
2124 issue2.parent_issue_id = issue3.id
2126 issue2.parent_issue_id = issue3.id
2125 issue2.save!
2127 issue2.save!
2126 issue4 = Issue.generate!
2128 issue4 = Issue.generate!
2127 IssueRelation.create!(:issue_from => issue3, :issue_to => issue4, :relation_type => IssueRelation::TYPE_PRECEDES)
2129 IssueRelation.create!(:issue_from => issue3, :issue_to => issue4, :relation_type => IssueRelation::TYPE_PRECEDES)
2128 issue4.reload
2130 issue4.reload
2129 issue4.parent_issue_id = issue1.id
2131 issue4.parent_issue_id = issue1.id
2130 assert !issue4.valid?
2132 assert !issue4.valid?
2131 assert_include 'Parent task is invalid', issue4.errors.full_messages
2133 assert_include 'Parent task is invalid', issue4.errors.full_messages
2132 end
2134 end
2133
2135
2134 def test_issue_and_following_issue_should_be_able_to_be_moved_to_the_same_parent
2136 def test_issue_and_following_issue_should_be_able_to_be_moved_to_the_same_parent
2135 set_language_if_valid 'en'
2137 set_language_if_valid 'en'
2136 issue1 = Issue.generate!
2138 issue1 = Issue.generate!
2137 issue2 = Issue.generate!
2139 issue2 = Issue.generate!
2138 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_FOLLOWS)
2140 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_FOLLOWS)
2139 parent = Issue.generate!
2141 parent = Issue.generate!
2140 issue1.reload.parent_issue_id = parent.id
2142 issue1.reload.parent_issue_id = parent.id
2141 assert_save issue1
2143 assert_save issue1
2142 parent.reload
2144 parent.reload
2143 issue2.reload.parent_issue_id = parent.id
2145 issue2.reload.parent_issue_id = parent.id
2144 assert_save issue2
2146 assert_save issue2
2145 assert IssueRelation.exists?(relation.id)
2147 assert IssueRelation.exists?(relation.id)
2146 end
2148 end
2147
2149
2148 def test_issue_and_preceding_issue_should_be_able_to_be_moved_to_the_same_parent
2150 def test_issue_and_preceding_issue_should_be_able_to_be_moved_to_the_same_parent
2149 set_language_if_valid 'en'
2151 set_language_if_valid 'en'
2150 issue1 = Issue.generate!
2152 issue1 = Issue.generate!
2151 issue2 = Issue.generate!
2153 issue2 = Issue.generate!
2152 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_PRECEDES)
2154 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_PRECEDES)
2153 parent = Issue.generate!
2155 parent = Issue.generate!
2154 issue1.reload.parent_issue_id = parent.id
2156 issue1.reload.parent_issue_id = parent.id
2155 assert_save issue1
2157 assert_save issue1
2156 parent.reload
2158 parent.reload
2157 issue2.reload.parent_issue_id = parent.id
2159 issue2.reload.parent_issue_id = parent.id
2158 assert_save issue2
2160 assert_save issue2
2159 assert IssueRelation.exists?(relation.id)
2161 assert IssueRelation.exists?(relation.id)
2160 end
2162 end
2161
2163
2162 def test_issue_and_blocked_issue_should_be_able_to_be_moved_to_the_same_parent
2164 def test_issue_and_blocked_issue_should_be_able_to_be_moved_to_the_same_parent
2163 set_language_if_valid 'en'
2165 set_language_if_valid 'en'
2164 issue1 = Issue.generate!
2166 issue1 = Issue.generate!
2165 issue2 = Issue.generate!
2167 issue2 = Issue.generate!
2166 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKED)
2168 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKED)
2167 parent = Issue.generate!
2169 parent = Issue.generate!
2168 issue1.reload.parent_issue_id = parent.id
2170 issue1.reload.parent_issue_id = parent.id
2169 assert_save issue1
2171 assert_save issue1
2170 parent.reload
2172 parent.reload
2171 issue2.reload.parent_issue_id = parent.id
2173 issue2.reload.parent_issue_id = parent.id
2172 assert_save issue2
2174 assert_save issue2
2173 assert IssueRelation.exists?(relation.id)
2175 assert IssueRelation.exists?(relation.id)
2174 end
2176 end
2175
2177
2176 def test_issue_and_blocking_issue_should_be_able_to_be_moved_to_the_same_parent
2178 def test_issue_and_blocking_issue_should_be_able_to_be_moved_to_the_same_parent
2177 set_language_if_valid 'en'
2179 set_language_if_valid 'en'
2178 issue1 = Issue.generate!
2180 issue1 = Issue.generate!
2179 issue2 = Issue.generate!
2181 issue2 = Issue.generate!
2180 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKS)
2182 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKS)
2181 parent = Issue.generate!
2183 parent = Issue.generate!
2182 issue1.reload.parent_issue_id = parent.id
2184 issue1.reload.parent_issue_id = parent.id
2183 assert_save issue1
2185 assert_save issue1
2184 parent.reload
2186 parent.reload
2185 issue2.reload.parent_issue_id = parent.id
2187 issue2.reload.parent_issue_id = parent.id
2186 assert_save issue2
2188 assert_save issue2
2187 assert IssueRelation.exists?(relation.id)
2189 assert IssueRelation.exists?(relation.id)
2188 end
2190 end
2189
2191
2190 def test_issue_copy_should_be_able_to_be_moved_to_the_same_parent_as_copied_issue
2192 def test_issue_copy_should_be_able_to_be_moved_to_the_same_parent_as_copied_issue
2191 issue = Issue.generate!
2193 issue = Issue.generate!
2192 parent = Issue.generate!
2194 parent = Issue.generate!
2193 issue.parent_issue_id = parent.id
2195 issue.parent_issue_id = parent.id
2194 issue.save!
2196 issue.save!
2195 issue.reload
2197 issue.reload
2196
2198
2197 copy = Issue.new.copy_from(issue, :link => true)
2199 copy = Issue.new.copy_from(issue, :link => true)
2198 relation = new_record(IssueRelation) do
2200 relation = new_record(IssueRelation) do
2199 copy.save!
2201 copy.save!
2200 end
2202 end
2201
2203
2202 copy.parent_issue_id = parent.id
2204 copy.parent_issue_id = parent.id
2203 assert_save copy
2205 assert_save copy
2204 assert IssueRelation.exists?(relation.id)
2206 assert IssueRelation.exists?(relation.id)
2205 end
2207 end
2206
2208
2207 def test_overdue
2209 def test_overdue
2208 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
2210 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
2209 assert !Issue.new(:due_date => Date.today).overdue?
2211 assert !Issue.new(:due_date => Date.today).overdue?
2210 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
2212 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
2211 assert !Issue.new(:due_date => nil).overdue?
2213 assert !Issue.new(:due_date => nil).overdue?
2212 assert !Issue.new(:due_date => 1.day.ago.to_date,
2214 assert !Issue.new(:due_date => 1.day.ago.to_date,
2213 :status => IssueStatus.where(:is_closed => true).first
2215 :status => IssueStatus.where(:is_closed => true).first
2214 ).overdue?
2216 ).overdue?
2215 end
2217 end
2216
2218
2217 test "#behind_schedule? should be false if the issue has no start_date" do
2219 test "#behind_schedule? should be false if the issue has no start_date" do
2218 assert !Issue.new(:start_date => nil,
2220 assert !Issue.new(:start_date => nil,
2219 :due_date => 1.day.from_now.to_date,
2221 :due_date => 1.day.from_now.to_date,
2220 :done_ratio => 0).behind_schedule?
2222 :done_ratio => 0).behind_schedule?
2221 end
2223 end
2222
2224
2223 test "#behind_schedule? should be false if the issue has no end_date" do
2225 test "#behind_schedule? should be false if the issue has no end_date" do
2224 assert !Issue.new(:start_date => 1.day.from_now.to_date,
2226 assert !Issue.new(:start_date => 1.day.from_now.to_date,
2225 :due_date => nil,
2227 :due_date => nil,
2226 :done_ratio => 0).behind_schedule?
2228 :done_ratio => 0).behind_schedule?
2227 end
2229 end
2228
2230
2229 test "#behind_schedule? should be false if the issue has more done than it's calendar time" do
2231 test "#behind_schedule? should be false if the issue has more done than it's calendar time" do
2230 assert !Issue.new(:start_date => 50.days.ago.to_date,
2232 assert !Issue.new(:start_date => 50.days.ago.to_date,
2231 :due_date => 50.days.from_now.to_date,
2233 :due_date => 50.days.from_now.to_date,
2232 :done_ratio => 90).behind_schedule?
2234 :done_ratio => 90).behind_schedule?
2233 end
2235 end
2234
2236
2235 test "#behind_schedule? should be true if the issue hasn't been started at all" do
2237 test "#behind_schedule? should be true if the issue hasn't been started at all" do
2236 assert Issue.new(:start_date => 1.day.ago.to_date,
2238 assert Issue.new(:start_date => 1.day.ago.to_date,
2237 :due_date => 1.day.from_now.to_date,
2239 :due_date => 1.day.from_now.to_date,
2238 :done_ratio => 0).behind_schedule?
2240 :done_ratio => 0).behind_schedule?
2239 end
2241 end
2240
2242
2241 test "#behind_schedule? should be true if the issue has used more calendar time than it's done ratio" do
2243 test "#behind_schedule? should be true if the issue has used more calendar time than it's done ratio" do
2242 assert Issue.new(:start_date => 100.days.ago.to_date,
2244 assert Issue.new(:start_date => 100.days.ago.to_date,
2243 :due_date => Date.today,
2245 :due_date => Date.today,
2244 :done_ratio => 90).behind_schedule?
2246 :done_ratio => 90).behind_schedule?
2245 end
2247 end
2246
2248
2247 test "#assignable_users should be Users" do
2249 test "#assignable_users should be Users" do
2248 assert_kind_of User, Issue.find(1).assignable_users.first
2250 assert_kind_of User, Issue.find(1).assignable_users.first
2249 end
2251 end
2250
2252
2251 test "#assignable_users should include the issue author" do
2253 test "#assignable_users should include the issue author" do
2252 non_project_member = User.generate!
2254 non_project_member = User.generate!
2253 issue = Issue.generate!(:author => non_project_member)
2255 issue = Issue.generate!(:author => non_project_member)
2254
2256
2255 assert issue.assignable_users.include?(non_project_member)
2257 assert issue.assignable_users.include?(non_project_member)
2256 end
2258 end
2257
2259
2258 def test_assignable_users_should_not_include_anonymous_user
2260 def test_assignable_users_should_not_include_anonymous_user
2259 issue = Issue.generate!(:author => User.anonymous)
2261 issue = Issue.generate!(:author => User.anonymous)
2260
2262
2261 assert !issue.assignable_users.include?(User.anonymous)
2263 assert !issue.assignable_users.include?(User.anonymous)
2262 end
2264 end
2263
2265
2264 def test_assignable_users_should_not_include_locked_user
2266 def test_assignable_users_should_not_include_locked_user
2265 user = User.generate!
2267 user = User.generate!
2266 issue = Issue.generate!(:author => user)
2268 issue = Issue.generate!(:author => user)
2267 user.lock!
2269 user.lock!
2268
2270
2269 assert !issue.assignable_users.include?(user)
2271 assert !issue.assignable_users.include?(user)
2270 end
2272 end
2271
2273
2272 test "#assignable_users should include the current assignee" do
2274 test "#assignable_users should include the current assignee" do
2273 user = User.generate!
2275 user = User.generate!
2274 issue = Issue.generate!(:assigned_to => user)
2276 issue = Issue.generate!(:assigned_to => user)
2275 user.lock!
2277 user.lock!
2276
2278
2277 assert Issue.find(issue.id).assignable_users.include?(user)
2279 assert Issue.find(issue.id).assignable_users.include?(user)
2278 end
2280 end
2279
2281
2280 test "#assignable_users should not show the issue author twice" do
2282 test "#assignable_users should not show the issue author twice" do
2281 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
2283 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
2282 assert_equal 2, assignable_user_ids.length
2284 assert_equal 2, assignable_user_ids.length
2283
2285
2284 assignable_user_ids.each do |user_id|
2286 assignable_user_ids.each do |user_id|
2285 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length,
2287 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length,
2286 "User #{user_id} appears more or less than once"
2288 "User #{user_id} appears more or less than once"
2287 end
2289 end
2288 end
2290 end
2289
2291
2290 test "#assignable_users with issue_group_assignment should include groups" do
2292 test "#assignable_users with issue_group_assignment should include groups" do
2291 issue = Issue.new(:project => Project.find(2))
2293 issue = Issue.new(:project => Project.find(2))
2292
2294
2293 with_settings :issue_group_assignment => '1' do
2295 with_settings :issue_group_assignment => '1' do
2294 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2296 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2295 assert issue.assignable_users.include?(Group.find(11))
2297 assert issue.assignable_users.include?(Group.find(11))
2296 end
2298 end
2297 end
2299 end
2298
2300
2299 test "#assignable_users without issue_group_assignment should not include groups" do
2301 test "#assignable_users without issue_group_assignment should not include groups" do
2300 issue = Issue.new(:project => Project.find(2))
2302 issue = Issue.new(:project => Project.find(2))
2301
2303
2302 with_settings :issue_group_assignment => '0' do
2304 with_settings :issue_group_assignment => '0' do
2303 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2305 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2304 assert !issue.assignable_users.include?(Group.find(11))
2306 assert !issue.assignable_users.include?(Group.find(11))
2305 end
2307 end
2306 end
2308 end
2307
2309
2308 def test_assignable_users_should_not_include_builtin_groups
2310 def test_assignable_users_should_not_include_builtin_groups
2309 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [1])
2311 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [1])
2310 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [1])
2312 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [1])
2311 issue = Issue.new(:project => Project.find(1))
2313 issue = Issue.new(:project => Project.find(1))
2312
2314
2313 with_settings :issue_group_assignment => '1' do
2315 with_settings :issue_group_assignment => '1' do
2314 assert_nil issue.assignable_users.detect {|u| u.is_a?(GroupBuiltin)}
2316 assert_nil issue.assignable_users.detect {|u| u.is_a?(GroupBuiltin)}
2315 end
2317 end
2316 end
2318 end
2317
2319
2318 def test_assignable_users_should_not_include_users_that_cannot_view_the_tracker
2320 def test_assignable_users_should_not_include_users_that_cannot_view_the_tracker
2319 user = User.find(3)
2321 user = User.find(3)
2320 role = Role.find(2)
2322 role = Role.find(2)
2321 role.set_permission_trackers :view_issues, [1, 3]
2323 role.set_permission_trackers :view_issues, [1, 3]
2322 role.save!
2324 role.save!
2323
2325
2324 issue1 = Issue.new(:project_id => 1, :tracker_id => 1)
2326 issue1 = Issue.new(:project_id => 1, :tracker_id => 1)
2325 issue2 = Issue.new(:project_id => 1, :tracker_id => 2)
2327 issue2 = Issue.new(:project_id => 1, :tracker_id => 2)
2326
2328
2327 assert_include user, issue1.assignable_users
2329 assert_include user, issue1.assignable_users
2328 assert_not_include user, issue2.assignable_users
2330 assert_not_include user, issue2.assignable_users
2329 end
2331 end
2330
2332
2331 def test_create_should_send_email_notification
2333 def test_create_should_send_email_notification
2332 ActionMailer::Base.deliveries.clear
2334 ActionMailer::Base.deliveries.clear
2333 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2335 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2334 :author_id => 3, :status_id => 1,
2336 :author_id => 3, :status_id => 1,
2335 :priority => IssuePriority.all.first,
2337 :priority => IssuePriority.all.first,
2336 :subject => 'test_create', :estimated_hours => '1:30')
2338 :subject => 'test_create', :estimated_hours => '1:30')
2337 with_settings :notified_events => %w(issue_added) do
2339 with_settings :notified_events => %w(issue_added) do
2338 assert issue.save
2340 assert issue.save
2339 assert_equal 1, ActionMailer::Base.deliveries.size
2341 assert_equal 1, ActionMailer::Base.deliveries.size
2340 end
2342 end
2341 end
2343 end
2342
2344
2343 def test_create_should_send_one_email_notification_with_both_settings
2345 def test_create_should_send_one_email_notification_with_both_settings
2344 ActionMailer::Base.deliveries.clear
2346 ActionMailer::Base.deliveries.clear
2345 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2347 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2346 :author_id => 3, :status_id => 1,
2348 :author_id => 3, :status_id => 1,
2347 :priority => IssuePriority.all.first,
2349 :priority => IssuePriority.all.first,
2348 :subject => 'test_create', :estimated_hours => '1:30')
2350 :subject => 'test_create', :estimated_hours => '1:30')
2349 with_settings :notified_events => %w(issue_added issue_updated) do
2351 with_settings :notified_events => %w(issue_added issue_updated) do
2350 assert issue.save
2352 assert issue.save
2351 assert_equal 1, ActionMailer::Base.deliveries.size
2353 assert_equal 1, ActionMailer::Base.deliveries.size
2352 end
2354 end
2353 end
2355 end
2354
2356
2355 def test_create_should_not_send_email_notification_with_no_setting
2357 def test_create_should_not_send_email_notification_with_no_setting
2356 ActionMailer::Base.deliveries.clear
2358 ActionMailer::Base.deliveries.clear
2357 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2359 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2358 :author_id => 3, :status_id => 1,
2360 :author_id => 3, :status_id => 1,
2359 :priority => IssuePriority.all.first,
2361 :priority => IssuePriority.all.first,
2360 :subject => 'test_create', :estimated_hours => '1:30')
2362 :subject => 'test_create', :estimated_hours => '1:30')
2361 with_settings :notified_events => [] do
2363 with_settings :notified_events => [] do
2362 assert issue.save
2364 assert issue.save
2363 assert_equal 0, ActionMailer::Base.deliveries.size
2365 assert_equal 0, ActionMailer::Base.deliveries.size
2364 end
2366 end
2365 end
2367 end
2366
2368
2367 def test_update_should_notify_previous_assignee
2369 def test_update_should_notify_previous_assignee
2368 ActionMailer::Base.deliveries.clear
2370 ActionMailer::Base.deliveries.clear
2369 user = User.find(3)
2371 user = User.find(3)
2370 user.members.update_all ["mail_notification = ?", false]
2372 user.members.update_all ["mail_notification = ?", false]
2371 user.update! :mail_notification => 'only_assigned'
2373 user.update! :mail_notification => 'only_assigned'
2372
2374
2373 with_settings :notified_events => %w(issue_updated) do
2375 with_settings :notified_events => %w(issue_updated) do
2374 issue = Issue.find(2)
2376 issue = Issue.find(2)
2375 issue.init_journal User.find(1)
2377 issue.init_journal User.find(1)
2376 issue.assigned_to = nil
2378 issue.assigned_to = nil
2377 issue.save!
2379 issue.save!
2378 assert_include user.mail, ActionMailer::Base.deliveries.last.bcc
2380 assert_include user.mail, ActionMailer::Base.deliveries.last.bcc
2379 end
2381 end
2380 end
2382 end
2381
2383
2382 def test_stale_issue_should_not_send_email_notification
2384 def test_stale_issue_should_not_send_email_notification
2383 ActionMailer::Base.deliveries.clear
2385 ActionMailer::Base.deliveries.clear
2384 issue = Issue.find(1)
2386 issue = Issue.find(1)
2385 stale = Issue.find(1)
2387 stale = Issue.find(1)
2386
2388
2387 issue.init_journal(User.find(1))
2389 issue.init_journal(User.find(1))
2388 issue.subject = 'Subjet update'
2390 issue.subject = 'Subjet update'
2389 with_settings :notified_events => %w(issue_updated) do
2391 with_settings :notified_events => %w(issue_updated) do
2390 assert issue.save
2392 assert issue.save
2391 assert_equal 1, ActionMailer::Base.deliveries.size
2393 assert_equal 1, ActionMailer::Base.deliveries.size
2392 ActionMailer::Base.deliveries.clear
2394 ActionMailer::Base.deliveries.clear
2393
2395
2394 stale.init_journal(User.find(1))
2396 stale.init_journal(User.find(1))
2395 stale.subject = 'Another subjet update'
2397 stale.subject = 'Another subjet update'
2396 assert_raise ActiveRecord::StaleObjectError do
2398 assert_raise ActiveRecord::StaleObjectError do
2397 stale.save
2399 stale.save
2398 end
2400 end
2399 assert ActionMailer::Base.deliveries.empty?
2401 assert ActionMailer::Base.deliveries.empty?
2400 end
2402 end
2401 end
2403 end
2402
2404
2403 def test_journalized_description
2405 def test_journalized_description
2404 IssueCustomField.delete_all
2406 IssueCustomField.delete_all
2405
2407
2406 i = Issue.first
2408 i = Issue.first
2407 old_description = i.description
2409 old_description = i.description
2408 new_description = "This is the new description"
2410 new_description = "This is the new description"
2409
2411
2410 i.init_journal(User.find(2))
2412 i.init_journal(User.find(2))
2411 i.description = new_description
2413 i.description = new_description
2412 assert_difference 'Journal.count', 1 do
2414 assert_difference 'Journal.count', 1 do
2413 assert_difference 'JournalDetail.count', 1 do
2415 assert_difference 'JournalDetail.count', 1 do
2414 i.save!
2416 i.save!
2415 end
2417 end
2416 end
2418 end
2417
2419
2418 detail = JournalDetail.order('id DESC').first
2420 detail = JournalDetail.order('id DESC').first
2419 assert_equal i, detail.journal.journalized
2421 assert_equal i, detail.journal.journalized
2420 assert_equal 'attr', detail.property
2422 assert_equal 'attr', detail.property
2421 assert_equal 'description', detail.prop_key
2423 assert_equal 'description', detail.prop_key
2422 assert_equal old_description, detail.old_value
2424 assert_equal old_description, detail.old_value
2423 assert_equal new_description, detail.value
2425 assert_equal new_description, detail.value
2424 end
2426 end
2425
2427
2426 def test_blank_descriptions_should_not_be_journalized
2428 def test_blank_descriptions_should_not_be_journalized
2427 IssueCustomField.delete_all
2429 IssueCustomField.delete_all
2428 Issue.where(:id => 1).update_all("description = NULL")
2430 Issue.where(:id => 1).update_all("description = NULL")
2429
2431
2430 i = Issue.find(1)
2432 i = Issue.find(1)
2431 i.init_journal(User.find(2))
2433 i.init_journal(User.find(2))
2432 i.subject = "blank description"
2434 i.subject = "blank description"
2433 i.description = "\r\n"
2435 i.description = "\r\n"
2434
2436
2435 assert_difference 'Journal.count', 1 do
2437 assert_difference 'Journal.count', 1 do
2436 assert_difference 'JournalDetail.count', 1 do
2438 assert_difference 'JournalDetail.count', 1 do
2437 i.save!
2439 i.save!
2438 end
2440 end
2439 end
2441 end
2440 end
2442 end
2441
2443
2442 def test_journalized_multi_custom_field
2444 def test_journalized_multi_custom_field
2443 field = IssueCustomField.create!(:name => 'filter', :field_format => 'list',
2445 field = IssueCustomField.create!(:name => 'filter', :field_format => 'list',
2444 :is_filter => true, :is_for_all => true,
2446 :is_filter => true, :is_for_all => true,
2445 :tracker_ids => [1],
2447 :tracker_ids => [1],
2446 :possible_values => ['value1', 'value2', 'value3'],
2448 :possible_values => ['value1', 'value2', 'value3'],
2447 :multiple => true)
2449 :multiple => true)
2448
2450
2449 issue = Issue.create!(:project_id => 1, :tracker_id => 1,
2451 issue = Issue.create!(:project_id => 1, :tracker_id => 1,
2450 :subject => 'Test', :author_id => 1)
2452 :subject => 'Test', :author_id => 1)
2451
2453
2452 assert_difference 'Journal.count' do
2454 assert_difference 'Journal.count' do
2453 assert_difference 'JournalDetail.count' do
2455 assert_difference 'JournalDetail.count' do
2454 issue.init_journal(User.first)
2456 issue.init_journal(User.first)
2455 issue.custom_field_values = {field.id => ['value1']}
2457 issue.custom_field_values = {field.id => ['value1']}
2456 issue.save!
2458 issue.save!
2457 end
2459 end
2458 assert_difference 'JournalDetail.count' do
2460 assert_difference 'JournalDetail.count' do
2459 issue.init_journal(User.first)
2461 issue.init_journal(User.first)
2460 issue.custom_field_values = {field.id => ['value1', 'value2']}
2462 issue.custom_field_values = {field.id => ['value1', 'value2']}
2461 issue.save!
2463 issue.save!
2462 end
2464 end
2463 assert_difference 'JournalDetail.count', 2 do
2465 assert_difference 'JournalDetail.count', 2 do
2464 issue.init_journal(User.first)
2466 issue.init_journal(User.first)
2465 issue.custom_field_values = {field.id => ['value3', 'value2']}
2467 issue.custom_field_values = {field.id => ['value3', 'value2']}
2466 issue.save!
2468 issue.save!
2467 end
2469 end
2468 assert_difference 'JournalDetail.count', 2 do
2470 assert_difference 'JournalDetail.count', 2 do
2469 issue.init_journal(User.first)
2471 issue.init_journal(User.first)
2470 issue.custom_field_values = {field.id => nil}
2472 issue.custom_field_values = {field.id => nil}
2471 issue.save!
2473 issue.save!
2472 end
2474 end
2473 end
2475 end
2474 end
2476 end
2475
2477
2478 def test_custom_value_cleared_on_tracker_change_should_be_journalized
2479 a = IssueCustomField.generate!(:tracker_ids => [1])
2480 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {a.id.to_s => "foo"})
2481 assert_equal "foo", issue.custom_field_value(a)
2482
2483 journal = new_record(Journal) do
2484 issue.init_journal(User.first)
2485 issue.tracker_id = 2
2486 issue.save!
2487 end
2488 details = journal.details.select {|d| d.property == 'cf' && d.prop_key == a.id.to_s}
2489 assert_equal 1, details.size
2490 assert_equal 'foo', details.first.old_value
2491 assert_nil details.first.value
2492 end
2493
2476 def test_description_eol_should_be_normalized
2494 def test_description_eol_should_be_normalized
2477 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
2495 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
2478 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
2496 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
2479 end
2497 end
2480
2498
2481 def test_saving_twice_should_not_duplicate_journal_details
2499 def test_saving_twice_should_not_duplicate_journal_details
2482 i = Issue.first
2500 i = Issue.first
2483 i.init_journal(User.find(2), 'Some notes')
2501 i.init_journal(User.find(2), 'Some notes')
2484 # initial changes
2502 # initial changes
2485 i.subject = 'New subject'
2503 i.subject = 'New subject'
2486 i.done_ratio = i.done_ratio + 10
2504 i.done_ratio = i.done_ratio + 10
2487 assert_difference 'Journal.count' do
2505 assert_difference 'Journal.count' do
2488 assert i.save
2506 assert i.save
2489 end
2507 end
2490 # 1 more change
2508 # 1 more change
2491 i.priority = IssuePriority.where("id <> ?", i.priority_id).first
2509 i.priority = IssuePriority.where("id <> ?", i.priority_id).first
2492 assert_no_difference 'Journal.count' do
2510 assert_no_difference 'Journal.count' do
2493 assert_difference 'JournalDetail.count', 1 do
2511 assert_difference 'JournalDetail.count', 1 do
2494 i.save
2512 i.save
2495 end
2513 end
2496 end
2514 end
2497 # no more change
2515 # no more change
2498 assert_no_difference 'Journal.count' do
2516 assert_no_difference 'Journal.count' do
2499 assert_no_difference 'JournalDetail.count' do
2517 assert_no_difference 'JournalDetail.count' do
2500 i.save
2518 i.save
2501 end
2519 end
2502 end
2520 end
2503 end
2521 end
2504
2522
2505 test "#done_ratio should use the issue_status according to Setting.issue_done_ratio" do
2523 test "#done_ratio should use the issue_status according to Setting.issue_done_ratio" do
2506 @issue = Issue.find(1)
2524 @issue = Issue.find(1)
2507 @issue_status = IssueStatus.find(1)
2525 @issue_status = IssueStatus.find(1)
2508 @issue_status.update!(:default_done_ratio => 50)
2526 @issue_status.update!(:default_done_ratio => 50)
2509 @issue2 = Issue.find(2)
2527 @issue2 = Issue.find(2)
2510 @issue_status2 = IssueStatus.find(2)
2528 @issue_status2 = IssueStatus.find(2)
2511 @issue_status2.update!(:default_done_ratio => 0)
2529 @issue_status2.update!(:default_done_ratio => 0)
2512
2530
2513 with_settings :issue_done_ratio => 'issue_field' do
2531 with_settings :issue_done_ratio => 'issue_field' do
2514 assert_equal 0, @issue.done_ratio
2532 assert_equal 0, @issue.done_ratio
2515 assert_equal 30, @issue2.done_ratio
2533 assert_equal 30, @issue2.done_ratio
2516 end
2534 end
2517
2535
2518 with_settings :issue_done_ratio => 'issue_status' do
2536 with_settings :issue_done_ratio => 'issue_status' do
2519 assert_equal 50, @issue.done_ratio
2537 assert_equal 50, @issue.done_ratio
2520 assert_equal 0, @issue2.done_ratio
2538 assert_equal 0, @issue2.done_ratio
2521 end
2539 end
2522 end
2540 end
2523
2541
2524 test "#update_done_ratio_from_issue_status should update done_ratio according to Setting.issue_done_ratio" do
2542 test "#update_done_ratio_from_issue_status should update done_ratio according to Setting.issue_done_ratio" do
2525 @issue = Issue.find(1)
2543 @issue = Issue.find(1)
2526 @issue_status = IssueStatus.find(1)
2544 @issue_status = IssueStatus.find(1)
2527 @issue_status.update!(:default_done_ratio => 50)
2545 @issue_status.update!(:default_done_ratio => 50)
2528 @issue2 = Issue.find(2)
2546 @issue2 = Issue.find(2)
2529 @issue_status2 = IssueStatus.find(2)
2547 @issue_status2 = IssueStatus.find(2)
2530 @issue_status2.update!(:default_done_ratio => 0)
2548 @issue_status2.update!(:default_done_ratio => 0)
2531
2549
2532 with_settings :issue_done_ratio => 'issue_field' do
2550 with_settings :issue_done_ratio => 'issue_field' do
2533 @issue.update_done_ratio_from_issue_status
2551 @issue.update_done_ratio_from_issue_status
2534 @issue2.update_done_ratio_from_issue_status
2552 @issue2.update_done_ratio_from_issue_status
2535
2553
2536 assert_equal 0, @issue.read_attribute(:done_ratio)
2554 assert_equal 0, @issue.read_attribute(:done_ratio)
2537 assert_equal 30, @issue2.read_attribute(:done_ratio)
2555 assert_equal 30, @issue2.read_attribute(:done_ratio)
2538 end
2556 end
2539
2557
2540 with_settings :issue_done_ratio => 'issue_status' do
2558 with_settings :issue_done_ratio => 'issue_status' do
2541 @issue.update_done_ratio_from_issue_status
2559 @issue.update_done_ratio_from_issue_status
2542 @issue2.update_done_ratio_from_issue_status
2560 @issue2.update_done_ratio_from_issue_status
2543
2561
2544 assert_equal 50, @issue.read_attribute(:done_ratio)
2562 assert_equal 50, @issue.read_attribute(:done_ratio)
2545 assert_equal 0, @issue2.read_attribute(:done_ratio)
2563 assert_equal 0, @issue2.read_attribute(:done_ratio)
2546 end
2564 end
2547 end
2565 end
2548
2566
2549 test "#by_tracker" do
2567 test "#by_tracker" do
2550 User.current = User.anonymous
2568 User.current = User.anonymous
2551 groups = Issue.by_tracker(Project.find(1))
2569 groups = Issue.by_tracker(Project.find(1))
2552 assert_equal 3, groups.count
2570 assert_equal 3, groups.count
2553 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2571 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2554 end
2572 end
2555
2573
2556 test "#by_version" do
2574 test "#by_version" do
2557 User.current = User.anonymous
2575 User.current = User.anonymous
2558 groups = Issue.by_version(Project.find(1))
2576 groups = Issue.by_version(Project.find(1))
2559 assert_equal 3, groups.count
2577 assert_equal 3, groups.count
2560 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2578 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2561 end
2579 end
2562
2580
2563 test "#by_priority" do
2581 test "#by_priority" do
2564 User.current = User.anonymous
2582 User.current = User.anonymous
2565 groups = Issue.by_priority(Project.find(1))
2583 groups = Issue.by_priority(Project.find(1))
2566 assert_equal 4, groups.count
2584 assert_equal 4, groups.count
2567 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2585 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2568 end
2586 end
2569
2587
2570 test "#by_category" do
2588 test "#by_category" do
2571 User.current = User.anonymous
2589 User.current = User.anonymous
2572 groups = Issue.by_category(Project.find(1))
2590 groups = Issue.by_category(Project.find(1))
2573 assert_equal 2, groups.count
2591 assert_equal 2, groups.count
2574 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2592 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2575 end
2593 end
2576
2594
2577 test "#by_assigned_to" do
2595 test "#by_assigned_to" do
2578 User.current = User.anonymous
2596 User.current = User.anonymous
2579 groups = Issue.by_assigned_to(Project.find(1))
2597 groups = Issue.by_assigned_to(Project.find(1))
2580 assert_equal 2, groups.count
2598 assert_equal 2, groups.count
2581 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2599 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2582 end
2600 end
2583
2601
2584 test "#by_author" do
2602 test "#by_author" do
2585 User.current = User.anonymous
2603 User.current = User.anonymous
2586 groups = Issue.by_author(Project.find(1))
2604 groups = Issue.by_author(Project.find(1))
2587 assert_equal 4, groups.count
2605 assert_equal 4, groups.count
2588 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2606 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2589 end
2607 end
2590
2608
2591 test "#by_subproject" do
2609 test "#by_subproject" do
2592 User.current = User.anonymous
2610 User.current = User.anonymous
2593 groups = Issue.by_subproject(Project.find(1))
2611 groups = Issue.by_subproject(Project.find(1))
2594 # Private descendant not visible
2612 # Private descendant not visible
2595 assert_equal 1, groups.count
2613 assert_equal 1, groups.count
2596 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2614 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2597 end
2615 end
2598
2616
2599 def test_recently_updated_scope
2617 def test_recently_updated_scope
2600 #should return the last updated issue
2618 #should return the last updated issue
2601 assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
2619 assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
2602 end
2620 end
2603
2621
2604 def test_on_active_projects_scope
2622 def test_on_active_projects_scope
2605 assert Project.find(2).archive
2623 assert Project.find(2).archive
2606
2624
2607 before = Issue.on_active_project.length
2625 before = Issue.on_active_project.length
2608 # test inclusion to results
2626 # test inclusion to results
2609 issue = Issue.generate!(:tracker => Project.find(2).trackers.first)
2627 issue = Issue.generate!(:tracker => Project.find(2).trackers.first)
2610 assert_equal before + 1, Issue.on_active_project.length
2628 assert_equal before + 1, Issue.on_active_project.length
2611
2629
2612 # Move to an archived project
2630 # Move to an archived project
2613 issue.project = Project.find(2)
2631 issue.project = Project.find(2)
2614 assert issue.save
2632 assert issue.save
2615 assert_equal before, Issue.on_active_project.length
2633 assert_equal before, Issue.on_active_project.length
2616 end
2634 end
2617
2635
2618 test "Issue#recipients should include project recipients" do
2636 test "Issue#recipients should include project recipients" do
2619 issue = Issue.generate!
2637 issue = Issue.generate!
2620 assert issue.project.recipients.present?
2638 assert issue.project.recipients.present?
2621 issue.project.recipients.each do |project_recipient|
2639 issue.project.recipients.each do |project_recipient|
2622 assert issue.recipients.include?(project_recipient)
2640 assert issue.recipients.include?(project_recipient)
2623 end
2641 end
2624 end
2642 end
2625
2643
2626 test "Issue#recipients should include the author if the author is active" do
2644 test "Issue#recipients should include the author if the author is active" do
2627 issue = Issue.generate!(:author => User.generate!)
2645 issue = Issue.generate!(:author => User.generate!)
2628 assert issue.author, "No author set for Issue"
2646 assert issue.author, "No author set for Issue"
2629 assert issue.recipients.include?(issue.author.mail)
2647 assert issue.recipients.include?(issue.author.mail)
2630 end
2648 end
2631
2649
2632 test "Issue#recipients should include the assigned to user if the assigned to user is active" do
2650 test "Issue#recipients should include the assigned to user if the assigned to user is active" do
2633 issue = Issue.generate!(:assigned_to => User.generate!)
2651 issue = Issue.generate!(:assigned_to => User.generate!)
2634 assert issue.assigned_to, "No assigned_to set for Issue"
2652 assert issue.assigned_to, "No assigned_to set for Issue"
2635 assert issue.recipients.include?(issue.assigned_to.mail)
2653 assert issue.recipients.include?(issue.assigned_to.mail)
2636 end
2654 end
2637
2655
2638 test "Issue#recipients should not include users who opt out of all email" do
2656 test "Issue#recipients should not include users who opt out of all email" do
2639 issue = Issue.generate!(:author => User.generate!)
2657 issue = Issue.generate!(:author => User.generate!)
2640 issue.author.update!(:mail_notification => :none)
2658 issue.author.update!(:mail_notification => :none)
2641 assert !issue.recipients.include?(issue.author.mail)
2659 assert !issue.recipients.include?(issue.author.mail)
2642 end
2660 end
2643
2661
2644 test "Issue#recipients should not include the issue author if they are only notified of assigned issues" do
2662 test "Issue#recipients should not include the issue author if they are only notified of assigned issues" do
2645 issue = Issue.generate!(:author => User.generate!)
2663 issue = Issue.generate!(:author => User.generate!)
2646 issue.author.update!(:mail_notification => :only_assigned)
2664 issue.author.update!(:mail_notification => :only_assigned)
2647 assert !issue.recipients.include?(issue.author.mail)
2665 assert !issue.recipients.include?(issue.author.mail)
2648 end
2666 end
2649
2667
2650 test "Issue#recipients should not include the assigned user if they are only notified of owned issues" do
2668 test "Issue#recipients should not include the assigned user if they are only notified of owned issues" do
2651 issue = Issue.generate!(:assigned_to => User.generate!)
2669 issue = Issue.generate!(:assigned_to => User.generate!)
2652 issue.assigned_to.update!(:mail_notification => :only_owner)
2670 issue.assigned_to.update!(:mail_notification => :only_owner)
2653 assert !issue.recipients.include?(issue.assigned_to.mail)
2671 assert !issue.recipients.include?(issue.assigned_to.mail)
2654 end
2672 end
2655
2673
2656 def test_last_journal_id_with_journals_should_return_the_journal_id
2674 def test_last_journal_id_with_journals_should_return_the_journal_id
2657 assert_equal 2, Issue.find(1).last_journal_id
2675 assert_equal 2, Issue.find(1).last_journal_id
2658 end
2676 end
2659
2677
2660 def test_last_journal_id_without_journals_should_return_nil
2678 def test_last_journal_id_without_journals_should_return_nil
2661 assert_nil Issue.find(3).last_journal_id
2679 assert_nil Issue.find(3).last_journal_id
2662 end
2680 end
2663
2681
2664 def test_journals_after_should_return_journals_with_greater_id
2682 def test_journals_after_should_return_journals_with_greater_id
2665 assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
2683 assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
2666 assert_equal [], Issue.find(1).journals_after('2')
2684 assert_equal [], Issue.find(1).journals_after('2')
2667 end
2685 end
2668
2686
2669 def test_journals_after_with_blank_arg_should_return_all_journals
2687 def test_journals_after_with_blank_arg_should_return_all_journals
2670 assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
2688 assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
2671 end
2689 end
2672
2690
2673 def test_css_classes_should_include_tracker
2691 def test_css_classes_should_include_tracker
2674 issue = Issue.new(:tracker => Tracker.find(2))
2692 issue = Issue.new(:tracker => Tracker.find(2))
2675 classes = issue.css_classes.split(' ')
2693 classes = issue.css_classes.split(' ')
2676 assert_include 'tracker-2', classes
2694 assert_include 'tracker-2', classes
2677 end
2695 end
2678
2696
2679 def test_css_classes_should_include_priority
2697 def test_css_classes_should_include_priority
2680 issue = Issue.new(:priority => IssuePriority.find(8))
2698 issue = Issue.new(:priority => IssuePriority.find(8))
2681 classes = issue.css_classes.split(' ')
2699 classes = issue.css_classes.split(' ')
2682 assert_include 'priority-8', classes
2700 assert_include 'priority-8', classes
2683 assert_include 'priority-highest', classes
2701 assert_include 'priority-highest', classes
2684 end
2702 end
2685
2703
2686 def test_css_classes_should_include_user_and_group_assignment
2704 def test_css_classes_should_include_user_and_group_assignment
2687 project = Project.first
2705 project = Project.first
2688 user = User.generate!
2706 user = User.generate!
2689 group = Group.generate!
2707 group = Group.generate!
2690 Member.create!(:principal => group, :project => project, :role_ids => [1, 2])
2708 Member.create!(:principal => group, :project => project, :role_ids => [1, 2])
2691 group.users << user
2709 group.users << user
2692 assert user.member_of?(project)
2710 assert user.member_of?(project)
2693 issue1 = Issue.generate(:assigned_to_id => group.id)
2711 issue1 = Issue.generate(:assigned_to_id => group.id)
2694 assert_include 'assigned-to-my-group', issue1.css_classes(user)
2712 assert_include 'assigned-to-my-group', issue1.css_classes(user)
2695 assert_not_include 'assigned-to-me', issue1.css_classes(user)
2713 assert_not_include 'assigned-to-me', issue1.css_classes(user)
2696 issue2 = Issue.generate(:assigned_to_id => user.id)
2714 issue2 = Issue.generate(:assigned_to_id => user.id)
2697 assert_not_include 'assigned-to-my-group', issue2.css_classes(user)
2715 assert_not_include 'assigned-to-my-group', issue2.css_classes(user)
2698 assert_include 'assigned-to-me', issue2.css_classes(user)
2716 assert_include 'assigned-to-me', issue2.css_classes(user)
2699 end
2717 end
2700
2718
2701 def test_save_attachments_with_hash_should_save_attachments_in_keys_order
2719 def test_save_attachments_with_hash_should_save_attachments_in_keys_order
2702 set_tmp_attachments_directory
2720 set_tmp_attachments_directory
2703 issue = Issue.generate!
2721 issue = Issue.generate!
2704 issue.save_attachments({
2722 issue.save_attachments({
2705 'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')},
2723 'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')},
2706 '3' => {'file' => mock_file_with_options(:original_filename => 'bar')},
2724 '3' => {'file' => mock_file_with_options(:original_filename => 'bar')},
2707 '1' => {'file' => mock_file_with_options(:original_filename => 'foo')}
2725 '1' => {'file' => mock_file_with_options(:original_filename => 'foo')}
2708 })
2726 })
2709 issue.attach_saved_attachments
2727 issue.attach_saved_attachments
2710
2728
2711 assert_equal 3, issue.reload.attachments.count
2729 assert_equal 3, issue.reload.attachments.count
2712 assert_equal %w(upload foo bar), issue.attachments.map(&:filename)
2730 assert_equal %w(upload foo bar), issue.attachments.map(&:filename)
2713 end
2731 end
2714
2732
2715 def test_save_attachments_with_array_should_warn_about_missing_tokens
2733 def test_save_attachments_with_array_should_warn_about_missing_tokens
2716 set_tmp_attachments_directory
2734 set_tmp_attachments_directory
2717 issue = Issue.generate!
2735 issue = Issue.generate!
2718 issue.save_attachments([
2736 issue.save_attachments([
2719 {'token' => 'missing'}
2737 {'token' => 'missing'}
2720 ])
2738 ])
2721 assert !issue.save
2739 assert !issue.save
2722 assert issue.errors[:base].present?
2740 assert issue.errors[:base].present?
2723 assert_equal 0, issue.reload.attachments.count
2741 assert_equal 0, issue.reload.attachments.count
2724 end
2742 end
2725
2743
2726 def test_closed_on_should_be_nil_when_creating_an_open_issue
2744 def test_closed_on_should_be_nil_when_creating_an_open_issue
2727 issue = Issue.generate!(:status_id => 1).reload
2745 issue = Issue.generate!(:status_id => 1).reload
2728 assert !issue.closed?
2746 assert !issue.closed?
2729 assert_nil issue.closed_on
2747 assert_nil issue.closed_on
2730 end
2748 end
2731
2749
2732 def test_closed_on_should_be_set_when_creating_a_closed_issue
2750 def test_closed_on_should_be_set_when_creating_a_closed_issue
2733 issue = Issue.generate!(:status_id => 5).reload
2751 issue = Issue.generate!(:status_id => 5).reload
2734 assert issue.closed?
2752 assert issue.closed?
2735 assert_not_nil issue.closed_on
2753 assert_not_nil issue.closed_on
2736 assert_equal issue.updated_on, issue.closed_on
2754 assert_equal issue.updated_on, issue.closed_on
2737 assert_equal issue.created_on, issue.closed_on
2755 assert_equal issue.created_on, issue.closed_on
2738 end
2756 end
2739
2757
2740 def test_closed_on_should_be_nil_when_updating_an_open_issue
2758 def test_closed_on_should_be_nil_when_updating_an_open_issue
2741 issue = Issue.find(1)
2759 issue = Issue.find(1)
2742 issue.subject = 'Not closed yet'
2760 issue.subject = 'Not closed yet'
2743 issue.save!
2761 issue.save!
2744 issue.reload
2762 issue.reload
2745 assert_nil issue.closed_on
2763 assert_nil issue.closed_on
2746 end
2764 end
2747
2765
2748 def test_closed_on_should_be_set_when_closing_an_open_issue
2766 def test_closed_on_should_be_set_when_closing_an_open_issue
2749 issue = Issue.find(1)
2767 issue = Issue.find(1)
2750 issue.subject = 'Now closed'
2768 issue.subject = 'Now closed'
2751 issue.status_id = 5
2769 issue.status_id = 5
2752 issue.save!
2770 issue.save!
2753 issue.reload
2771 issue.reload
2754 assert_not_nil issue.closed_on
2772 assert_not_nil issue.closed_on
2755 assert_equal issue.updated_on, issue.closed_on
2773 assert_equal issue.updated_on, issue.closed_on
2756 end
2774 end
2757
2775
2758 def test_closed_on_should_not_be_updated_when_updating_a_closed_issue
2776 def test_closed_on_should_not_be_updated_when_updating_a_closed_issue
2759 issue = Issue.open(false).first
2777 issue = Issue.open(false).first
2760 was_closed_on = issue.closed_on
2778 was_closed_on = issue.closed_on
2761 assert_not_nil was_closed_on
2779 assert_not_nil was_closed_on
2762 issue.subject = 'Updating a closed issue'
2780 issue.subject = 'Updating a closed issue'
2763 issue.save!
2781 issue.save!
2764 issue.reload
2782 issue.reload
2765 assert_equal was_closed_on, issue.closed_on
2783 assert_equal was_closed_on, issue.closed_on
2766 end
2784 end
2767
2785
2768 def test_closed_on_should_be_preserved_when_reopening_a_closed_issue
2786 def test_closed_on_should_be_preserved_when_reopening_a_closed_issue
2769 issue = Issue.open(false).first
2787 issue = Issue.open(false).first
2770 was_closed_on = issue.closed_on
2788 was_closed_on = issue.closed_on
2771 assert_not_nil was_closed_on
2789 assert_not_nil was_closed_on
2772 issue.subject = 'Reopening a closed issue'
2790 issue.subject = 'Reopening a closed issue'
2773 issue.status_id = 1
2791 issue.status_id = 1
2774 issue.save!
2792 issue.save!
2775 issue.reload
2793 issue.reload
2776 assert !issue.closed?
2794 assert !issue.closed?
2777 assert_equal was_closed_on, issue.closed_on
2795 assert_equal was_closed_on, issue.closed_on
2778 end
2796 end
2779
2797
2780 def test_status_was_should_return_nil_for_new_issue
2798 def test_status_was_should_return_nil_for_new_issue
2781 issue = Issue.new
2799 issue = Issue.new
2782 assert_nil issue.status_was
2800 assert_nil issue.status_was
2783 end
2801 end
2784
2802
2785 def test_status_was_should_return_status_before_change
2803 def test_status_was_should_return_status_before_change
2786 issue = Issue.find(1)
2804 issue = Issue.find(1)
2787 issue.status = IssueStatus.find(2)
2805 issue.status = IssueStatus.find(2)
2788 assert_equal IssueStatus.find(1), issue.status_was
2806 assert_equal IssueStatus.find(1), issue.status_was
2789 end
2807 end
2790
2808
2791 def test_status_was_should_return_status_before_change_with_status_id
2809 def test_status_was_should_return_status_before_change_with_status_id
2792 issue = Issue.find(1)
2810 issue = Issue.find(1)
2793 assert_equal IssueStatus.find(1), issue.status
2811 assert_equal IssueStatus.find(1), issue.status
2794 issue.status_id = 2
2812 issue.status_id = 2
2795 assert_equal IssueStatus.find(1), issue.status_was
2813 assert_equal IssueStatus.find(1), issue.status_was
2796 end
2814 end
2797
2815
2798 def test_status_was_should_be_reset_on_save
2816 def test_status_was_should_be_reset_on_save
2799 issue = Issue.find(1)
2817 issue = Issue.find(1)
2800 issue.status = IssueStatus.find(2)
2818 issue.status = IssueStatus.find(2)
2801 assert_equal IssueStatus.find(1), issue.status_was
2819 assert_equal IssueStatus.find(1), issue.status_was
2802 assert issue.save!
2820 assert issue.save!
2803 assert_equal IssueStatus.find(2), issue.status_was
2821 assert_equal IssueStatus.find(2), issue.status_was
2804 end
2822 end
2805
2823
2806 def test_closing_should_return_true_when_closing_an_issue
2824 def test_closing_should_return_true_when_closing_an_issue
2807 issue = Issue.find(1)
2825 issue = Issue.find(1)
2808 issue.status = IssueStatus.find(2)
2826 issue.status = IssueStatus.find(2)
2809 assert_equal false, issue.closing?
2827 assert_equal false, issue.closing?
2810 issue.status = IssueStatus.find(5)
2828 issue.status = IssueStatus.find(5)
2811 assert_equal true, issue.closing?
2829 assert_equal true, issue.closing?
2812 end
2830 end
2813
2831
2814 def test_closing_should_return_true_when_closing_an_issue_with_status_id
2832 def test_closing_should_return_true_when_closing_an_issue_with_status_id
2815 issue = Issue.find(1)
2833 issue = Issue.find(1)
2816 issue.status_id = 2
2834 issue.status_id = 2
2817 assert_equal false, issue.closing?
2835 assert_equal false, issue.closing?
2818 issue.status_id = 5
2836 issue.status_id = 5
2819 assert_equal true, issue.closing?
2837 assert_equal true, issue.closing?
2820 end
2838 end
2821
2839
2822 def test_closing_should_return_true_for_new_closed_issue
2840 def test_closing_should_return_true_for_new_closed_issue
2823 issue = Issue.new
2841 issue = Issue.new
2824 assert_equal false, issue.closing?
2842 assert_equal false, issue.closing?
2825 issue.status = IssueStatus.find(5)
2843 issue.status = IssueStatus.find(5)
2826 assert_equal true, issue.closing?
2844 assert_equal true, issue.closing?
2827 end
2845 end
2828
2846
2829 def test_closing_should_return_true_for_new_closed_issue_with_status_id
2847 def test_closing_should_return_true_for_new_closed_issue_with_status_id
2830 issue = Issue.new
2848 issue = Issue.new
2831 assert_equal false, issue.closing?
2849 assert_equal false, issue.closing?
2832 issue.status_id = 5
2850 issue.status_id = 5
2833 assert_equal true, issue.closing?
2851 assert_equal true, issue.closing?
2834 end
2852 end
2835
2853
2836 def test_closing_should_be_reset_after_save
2854 def test_closing_should_be_reset_after_save
2837 issue = Issue.find(1)
2855 issue = Issue.find(1)
2838 issue.status_id = 5
2856 issue.status_id = 5
2839 assert_equal true, issue.closing?
2857 assert_equal true, issue.closing?
2840 issue.save!
2858 issue.save!
2841 assert_equal false, issue.closing?
2859 assert_equal false, issue.closing?
2842 end
2860 end
2843
2861
2844 def test_reopening_should_return_true_when_reopening_an_issue
2862 def test_reopening_should_return_true_when_reopening_an_issue
2845 issue = Issue.find(8)
2863 issue = Issue.find(8)
2846 issue.status = IssueStatus.find(6)
2864 issue.status = IssueStatus.find(6)
2847 assert_equal false, issue.reopening?
2865 assert_equal false, issue.reopening?
2848 issue.status = IssueStatus.find(2)
2866 issue.status = IssueStatus.find(2)
2849 assert_equal true, issue.reopening?
2867 assert_equal true, issue.reopening?
2850 end
2868 end
2851
2869
2852 def test_reopening_should_return_true_when_reopening_an_issue_with_status_id
2870 def test_reopening_should_return_true_when_reopening_an_issue_with_status_id
2853 issue = Issue.find(8)
2871 issue = Issue.find(8)
2854 issue.status_id = 6
2872 issue.status_id = 6
2855 assert_equal false, issue.reopening?
2873 assert_equal false, issue.reopening?
2856 issue.status_id = 2
2874 issue.status_id = 2
2857 assert_equal true, issue.reopening?
2875 assert_equal true, issue.reopening?
2858 end
2876 end
2859
2877
2860 def test_reopening_should_return_false_for_new_open_issue
2878 def test_reopening_should_return_false_for_new_open_issue
2861 issue = Issue.new
2879 issue = Issue.new
2862 issue.status = IssueStatus.find(1)
2880 issue.status = IssueStatus.find(1)
2863 assert_equal false, issue.reopening?
2881 assert_equal false, issue.reopening?
2864 end
2882 end
2865
2883
2866 def test_reopening_should_be_reset_after_save
2884 def test_reopening_should_be_reset_after_save
2867 issue = Issue.find(8)
2885 issue = Issue.find(8)
2868 issue.status_id = 2
2886 issue.status_id = 2
2869 assert_equal true, issue.reopening?
2887 assert_equal true, issue.reopening?
2870 issue.save!
2888 issue.save!
2871 assert_equal false, issue.reopening?
2889 assert_equal false, issue.reopening?
2872 end
2890 end
2873
2891
2874 def test_default_status_without_tracker_should_be_nil
2892 def test_default_status_without_tracker_should_be_nil
2875 issue = Issue.new
2893 issue = Issue.new
2876 assert_nil issue.tracker
2894 assert_nil issue.tracker
2877 assert_nil issue.default_status
2895 assert_nil issue.default_status
2878 end
2896 end
2879
2897
2880 def test_default_status_should_be_tracker_default_status
2898 def test_default_status_should_be_tracker_default_status
2881 issue = Issue.new(:tracker_id => 1)
2899 issue = Issue.new(:tracker_id => 1)
2882 assert_not_nil issue.status
2900 assert_not_nil issue.status
2883 assert_equal issue.tracker.default_status, issue.default_status
2901 assert_equal issue.tracker.default_status, issue.default_status
2884 end
2902 end
2885
2903
2886 def test_initializing_with_tracker_should_set_default_status
2904 def test_initializing_with_tracker_should_set_default_status
2887 issue = Issue.new(:tracker => Tracker.find(1))
2905 issue = Issue.new(:tracker => Tracker.find(1))
2888 assert_not_nil issue.status
2906 assert_not_nil issue.status
2889 assert_equal issue.default_status, issue.status
2907 assert_equal issue.default_status, issue.status
2890 end
2908 end
2891
2909
2892 def test_initializing_with_tracker_id_should_set_default_status
2910 def test_initializing_with_tracker_id_should_set_default_status
2893 issue = Issue.new(:tracker_id => 1)
2911 issue = Issue.new(:tracker_id => 1)
2894 assert_not_nil issue.status
2912 assert_not_nil issue.status
2895 assert_equal issue.default_status, issue.status
2913 assert_equal issue.default_status, issue.status
2896 end
2914 end
2897
2915
2898 def test_setting_tracker_should_set_default_status
2916 def test_setting_tracker_should_set_default_status
2899 issue = Issue.new
2917 issue = Issue.new
2900 issue.tracker = Tracker.find(1)
2918 issue.tracker = Tracker.find(1)
2901 assert_not_nil issue.status
2919 assert_not_nil issue.status
2902 assert_equal issue.default_status, issue.status
2920 assert_equal issue.default_status, issue.status
2903 end
2921 end
2904
2922
2905 def test_changing_tracker_should_set_default_status_if_status_was_default
2923 def test_changing_tracker_should_set_default_status_if_status_was_default
2906 WorkflowTransition.delete_all
2924 WorkflowTransition.delete_all
2907 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1
2925 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1
2908 Tracker.find(2).update! :default_status_id => 2
2926 Tracker.find(2).update! :default_status_id => 2
2909
2927
2910 issue = Issue.new(:tracker_id => 1, :status_id => 1)
2928 issue = Issue.new(:tracker_id => 1, :status_id => 1)
2911 assert_equal IssueStatus.find(1), issue.status
2929 assert_equal IssueStatus.find(1), issue.status
2912 issue.tracker = Tracker.find(2)
2930 issue.tracker = Tracker.find(2)
2913 assert_equal IssueStatus.find(2), issue.status
2931 assert_equal IssueStatus.find(2), issue.status
2914 end
2932 end
2915
2933
2916 def test_changing_tracker_should_set_default_status_if_status_is_not_used_by_tracker
2934 def test_changing_tracker_should_set_default_status_if_status_is_not_used_by_tracker
2917 WorkflowTransition.delete_all
2935 WorkflowTransition.delete_all
2918 Tracker.find(2).update! :default_status_id => 2
2936 Tracker.find(2).update! :default_status_id => 2
2919
2937
2920 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2938 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2921 assert_equal IssueStatus.find(3), issue.status
2939 assert_equal IssueStatus.find(3), issue.status
2922 issue.tracker = Tracker.find(2)
2940 issue.tracker = Tracker.find(2)
2923 assert_equal IssueStatus.find(2), issue.status
2941 assert_equal IssueStatus.find(2), issue.status
2924 end
2942 end
2925
2943
2926 def test_changing_tracker_should_keep_status_if_status_was_not_default_and_is_used_by_tracker
2944 def test_changing_tracker_should_keep_status_if_status_was_not_default_and_is_used_by_tracker
2927 WorkflowTransition.delete_all
2945 WorkflowTransition.delete_all
2928 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3
2946 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3
2929 Tracker.find(2).update! :default_status_id => 2
2947 Tracker.find(2).update! :default_status_id => 2
2930
2948
2931 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2949 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2932 assert_equal IssueStatus.find(3), issue.status
2950 assert_equal IssueStatus.find(3), issue.status
2933 issue.tracker = Tracker.find(2)
2951 issue.tracker = Tracker.find(2)
2934 assert_equal IssueStatus.find(3), issue.status
2952 assert_equal IssueStatus.find(3), issue.status
2935 end
2953 end
2936
2954
2937 def test_assigned_to_was_with_a_group
2955 def test_assigned_to_was_with_a_group
2938 group = Group.find(10)
2956 group = Group.find(10)
2939
2957
2940 issue = Issue.generate!(:assigned_to => group)
2958 issue = Issue.generate!(:assigned_to => group)
2941 issue.reload.assigned_to = nil
2959 issue.reload.assigned_to = nil
2942 assert_equal group, issue.assigned_to_was
2960 assert_equal group, issue.assigned_to_was
2943 end
2961 end
2944
2962
2945 def test_issue_overdue_should_respect_user_timezone
2963 def test_issue_overdue_should_respect_user_timezone
2946 user_in_europe = users(:users_001)
2964 user_in_europe = users(:users_001)
2947 user_in_europe.pref.update! :time_zone => 'UTC'
2965 user_in_europe.pref.update! :time_zone => 'UTC'
2948
2966
2949 user_in_asia = users(:users_002)
2967 user_in_asia = users(:users_002)
2950 user_in_asia.pref.update! :time_zone => 'Hongkong'
2968 user_in_asia.pref.update! :time_zone => 'Hongkong'
2951
2969
2952 issue = Issue.generate! :due_date => Date.parse('2016-03-20')
2970 issue = Issue.generate! :due_date => Date.parse('2016-03-20')
2953
2971
2954 # server time is UTC
2972 # server time is UTC
2955 time = Time.parse '2016-03-20 20:00 UTC'
2973 time = Time.parse '2016-03-20 20:00 UTC'
2956 Time.stubs(:now).returns(time)
2974 Time.stubs(:now).returns(time)
2957 Date.stubs(:today).returns(time.to_date)
2975 Date.stubs(:today).returns(time.to_date)
2958
2976
2959 # for a user in the same time zone as the server the issue is not overdue
2977 # for a user in the same time zone as the server the issue is not overdue
2960 # yet
2978 # yet
2961 User.current = user_in_europe
2979 User.current = user_in_europe
2962 assert !issue.overdue?
2980 assert !issue.overdue?
2963
2981
2964 # at the same time, a user in East Asia looks at the issue - it's already
2982 # at the same time, a user in East Asia looks at the issue - it's already
2965 # March 21st and the issue should be marked overdue
2983 # March 21st and the issue should be marked overdue
2966 User.current = user_in_asia
2984 User.current = user_in_asia
2967 assert issue.overdue?
2985 assert issue.overdue?
2968
2986
2969 end
2987 end
2970 end
2988 end
General Comments 0
You need to be logged in to leave comments. Login now