##// END OF EJS Templates
"I don't want to be notified of changes that I make myself" as Default for all User (#14574)....
Jean-Philippe Lang -
r14906:05a9275fdaab
parent child
Show More

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

@@ -1,68 +1,71
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class UserPreference < ActiveRecord::Base
19 19 belongs_to :user
20 20 serialize :others
21 21
22 22 attr_protected :others, :user_id
23 23
24 24 before_save :set_others_hash
25 25
26 26 def initialize(attributes=nil, *args)
27 27 super
28 28 if new_record? && !(attributes && attributes.key?(:hide_mail))
29 29 self.hide_mail = Setting.default_users_hide_mail?
30 30 end
31 if new_record? && !(attributes && attributes.key?(:no_self_notified))
32 self.no_self_notified = true
33 end
31 34 self.others ||= {}
32 35 end
33 36
34 37 def set_others_hash
35 38 self.others ||= {}
36 39 end
37 40
38 41 def [](attr_name)
39 42 if has_attribute? attr_name
40 43 super
41 44 else
42 45 others ? others[attr_name] : nil
43 46 end
44 47 end
45 48
46 49 def []=(attr_name, value)
47 50 if has_attribute? attr_name
48 51 super
49 52 else
50 53 h = (read_attribute(:others) || {}).dup
51 54 h.update(attr_name => value)
52 55 write_attribute(:others, h)
53 56 value
54 57 end
55 58 end
56 59
57 60 def comments_sorting; self[:comments_sorting] end
58 61 def comments_sorting=(order); self[:comments_sorting]=order end
59 62
60 63 def warn_on_leaving_unsaved; self[:warn_on_leaving_unsaved] || '1'; end
61 64 def warn_on_leaving_unsaved=(value); self[:warn_on_leaving_unsaved]=value; end
62 65
63 66 def no_self_notified; (self[:no_self_notified] == true || self[:no_self_notified] == '1'); end
64 67 def no_self_notified=(value); self[:no_self_notified]=value; end
65 68
66 69 def activity_scope; Array(self[:activity_scope]) ; end
67 70 def activity_scope=(value); self[:activity_scope]=value ; end
68 71 end
@@ -1,31 +1,41
1 1 ---
2 2 user_preferences_001:
3 3 others: |
4 4 ---
5 :no_self_notified: false
5 6 :my_page_layout:
6 7 left:
7 8 - latestnews
8 9 - documents
9 10 right:
10 11 - issuesassignedtome
11 12 top:
12 13 - calendar
13 14
14 15 id: 1
15 16 user_id: 1
16 17 hide_mail: true
17 18 user_preferences_002:
18 19 others: |
19 20 ---
21 :no_self_notified: false
22
23 id: 2
24 user_id: 2
25 hide_mail: true
26 user_preferences_003:
27 others: |
28 ---
29 :no_self_notified: false
20 30 :my_page_layout:
21 31 left:
22 32 - latestnews
23 33 - documents
24 34 right:
25 35 - issuesassignedtome
26 36 top:
27 37 - calendar
28 38
29 id: 2
39 id: 3
30 40 user_id: 3
31 41 hide_mail: false
1 NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,322 +1,322
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class IssuesCustomFieldsVisibilityTest < ActionController::TestCase
21 21 tests IssuesController
22 22 fixtures :projects,
23 :users, :email_addresses,
23 :users, :email_addresses, :user_preferences,
24 24 :roles,
25 25 :members,
26 26 :member_roles,
27 27 :issue_statuses,
28 28 :trackers,
29 29 :projects_trackers,
30 30 :enabled_modules,
31 31 :enumerations,
32 32 :workflows
33 33
34 34 def setup
35 35 CustomField.delete_all
36 36 Issue.delete_all
37 37 field_attributes = {:field_format => 'string', :is_for_all => true, :is_filter => true, :trackers => Tracker.all}
38 38 @fields = []
39 39 @fields << (@field1 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 1', :visible => true)))
40 40 @fields << (@field2 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 2', :visible => false, :role_ids => [1, 2])))
41 41 @fields << (@field3 = IssueCustomField.create!(field_attributes.merge(:name => 'Field 3', :visible => false, :role_ids => [1, 3])))
42 42 @issue = Issue.generate!(
43 43 :author_id => 1,
44 44 :project_id => 1,
45 45 :tracker_id => 1,
46 46 :custom_field_values => {@field1.id => 'Value0', @field2.id => 'Value1', @field3.id => 'Value2'}
47 47 )
48 48
49 49 @user_with_role_on_other_project = User.generate!
50 50 User.add_to_project(@user_with_role_on_other_project, Project.find(2), Role.find(3))
51 51
52 52 @users_to_test = {
53 53 User.find(1) => [@field1, @field2, @field3],
54 54 User.find(3) => [@field1, @field2],
55 55 @user_with_role_on_other_project => [@field1], # should see field1 only on Project 1
56 56 User.generate! => [@field1],
57 57 User.anonymous => [@field1]
58 58 }
59 59
60 60 Member.where(:project_id => 1).each do |member|
61 61 member.destroy unless @users_to_test.keys.include?(member.principal)
62 62 end
63 63 end
64 64
65 65 def test_show_should_show_visible_custom_fields_only
66 66 @users_to_test.each do |user, fields|
67 67 @request.session[:user_id] = user.id
68 68 get :show, :id => @issue.id
69 69 @fields.each_with_index do |field, i|
70 70 if fields.include?(field)
71 71 assert_select '.value', {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name}"
72 72 else
73 73 assert_select '.value', {:text => "Value#{i}", :count => 0}, "User #{user.id} was able to view #{field.name}"
74 74 end
75 75 end
76 76 end
77 77 end
78 78
79 79 def test_show_should_show_visible_custom_fields_only_in_api
80 80 @users_to_test.each do |user, fields|
81 81 with_settings :rest_api_enabled => '1' do
82 82 get :show, :id => @issue.id, :format => 'xml', :include => 'custom_fields', :key => user.api_key
83 83 end
84 84 @fields.each_with_index do |field, i|
85 85 if fields.include?(field)
86 86 assert_select "custom_field[id=?] value", field.id.to_s, {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name} in API"
87 87 else
88 88 assert_select "custom_field[id=?] value", field.id.to_s, {:text => "Value#{i}", :count => 0}, "User #{user.id} was not able to view #{field.name} in API"
89 89 end
90 90 end
91 91 end
92 92 end
93 93
94 94 def test_show_should_show_visible_custom_fields_only_in_history
95 95 @issue.init_journal(User.find(1))
96 96 @issue.custom_field_values = {@field1.id => 'NewValue0', @field2.id => 'NewValue1', @field3.id => 'NewValue2'}
97 97 @issue.save!
98 98
99 99 @users_to_test.each do |user, fields|
100 100 @request.session[:user_id] = user.id
101 101 get :show, :id => @issue.id
102 102 @fields.each_with_index do |field, i|
103 103 if fields.include?(field)
104 104 assert_select 'ul.details i', {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name} change"
105 105 else
106 106 assert_select 'ul.details i', {:text => "Value#{i}", :count => 0}, "User #{user.id} was able to view #{field.name} change"
107 107 end
108 108 end
109 109 end
110 110 end
111 111
112 112 def test_show_should_show_visible_custom_fields_only_in_history_api
113 113 @issue.init_journal(User.find(1))
114 114 @issue.custom_field_values = {@field1.id => 'NewValue0', @field2.id => 'NewValue1', @field3.id => 'NewValue2'}
115 115 @issue.save!
116 116
117 117 @users_to_test.each do |user, fields|
118 118 with_settings :rest_api_enabled => '1' do
119 119 get :show, :id => @issue.id, :format => 'xml', :include => 'journals', :key => user.api_key
120 120 end
121 121 @fields.each_with_index do |field, i|
122 122 if fields.include?(field)
123 123 assert_select 'details old_value', {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name} change in API"
124 124 else
125 125 assert_select 'details old_value', {:text => "Value#{i}", :count => 0}, "User #{user.id} was able to view #{field.name} change in API"
126 126 end
127 127 end
128 128 end
129 129 end
130 130
131 131 def test_edit_should_show_visible_custom_fields_only
132 132 Role.anonymous.add_permission! :edit_issues
133 133
134 134 @users_to_test.each do |user, fields|
135 135 @request.session[:user_id] = user.id
136 136 get :edit, :id => @issue.id
137 137 @fields.each_with_index do |field, i|
138 138 if fields.include?(field)
139 139 assert_select 'input[value=?]', "Value#{i}", 1, "User #{user.id} was not able to edit #{field.name}"
140 140 else
141 141 assert_select 'input[value=?]', "Value#{i}", 0, "User #{user.id} was able to edit #{field.name}"
142 142 end
143 143 end
144 144 end
145 145 end
146 146
147 147 def test_update_should_update_visible_custom_fields_only
148 148 Role.anonymous.add_permission! :edit_issues
149 149
150 150 @users_to_test.each do |user, fields|
151 151 @request.session[:user_id] = user.id
152 152 put :update, :id => @issue.id,
153 153 :issue => {:custom_field_values => {
154 154 @field1.id.to_s => "User#{user.id}Value0",
155 155 @field2.id.to_s => "User#{user.id}Value1",
156 156 @field3.id.to_s => "User#{user.id}Value2",
157 157 }}
158 158 @issue.reload
159 159 @fields.each_with_index do |field, i|
160 160 if fields.include?(field)
161 161 assert_equal "User#{user.id}Value#{i}", @issue.custom_field_value(field), "User #{user.id} was not able to update #{field.name}"
162 162 else
163 163 assert_not_equal "User#{user.id}Value#{i}", @issue.custom_field_value(field), "User #{user.id} was able to update #{field.name}"
164 164 end
165 165 end
166 166 end
167 167 end
168 168
169 169 def test_index_should_show_visible_custom_fields_only
170 170 @users_to_test.each do |user, fields|
171 171 @request.session[:user_id] = user.id
172 172 get :index, :c => (["subject"] + @fields.map{|f| "cf_#{f.id}"})
173 173 @fields.each_with_index do |field, i|
174 174 if fields.include?(field)
175 175 assert_select 'td', {:text => "Value#{i}", :count => 1}, "User #{user.id} was not able to view #{field.name}"
176 176 else
177 177 assert_select 'td', {:text => "Value#{i}", :count => 0}, "User #{user.id} was able to view #{field.name}"
178 178 end
179 179 end
180 180 end
181 181 end
182 182
183 183 def test_index_as_csv_should_show_visible_custom_fields_only
184 184 @users_to_test.each do |user, fields|
185 185 @request.session[:user_id] = user.id
186 186 get :index, :c => (["subject"] + @fields.map{|f| "cf_#{f.id}"}), :format => 'csv'
187 187 @fields.each_with_index do |field, i|
188 188 if fields.include?(field)
189 189 assert_include "Value#{i}", response.body, "User #{user.id} was not able to view #{field.name} in CSV"
190 190 else
191 191 assert_not_include "Value#{i}", response.body, "User #{user.id} was able to view #{field.name} in CSV"
192 192 end
193 193 end
194 194 end
195 195 end
196 196
197 197 def test_index_with_partial_custom_field_visibility
198 198 Issue.delete_all
199 199 p1 = Project.generate!
200 200 p2 = Project.generate!
201 201 user = User.generate!
202 202 User.add_to_project(user, p1, Role.where(:id => [1, 3]).to_a)
203 203 User.add_to_project(user, p2, Role.where(:id => 3).to_a)
204 204 Issue.generate!(:project => p1, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueA'})
205 205 Issue.generate!(:project => p2, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueB'})
206 206 Issue.generate!(:project => p1, :tracker_id => 1, :custom_field_values => {@field2.id => 'ValueC'})
207 207
208 208 @request.session[:user_id] = user.id
209 209 get :index, :c => ["subject", "cf_#{@field2.id}"]
210 210 assert_select 'td', :text => 'ValueA'
211 211 assert_select 'td', :text => 'ValueB', :count => 0
212 212 assert_select 'td', :text => 'ValueC'
213 213
214 214 get :index, :sort => "cf_#{@field2.id}"
215 215 # ValueB is not visible to user and ignored while sorting
216 216 assert_equal %w(ValueB ValueA ValueC), assigns(:issues).map{|i| i.custom_field_value(@field2)}
217 217
218 218 get :index, :set_filter => '1', "cf_#{@field2.id}" => '*'
219 219 assert_equal %w(ValueA ValueC), assigns(:issues).map{|i| i.custom_field_value(@field2)}
220 220
221 221 CustomField.update_all(:field_format => 'list')
222 222 get :index, :group => "cf_#{@field2.id}"
223 223 assert_equal %w(ValueA ValueC), assigns(:issues).map{|i| i.custom_field_value(@field2)}
224 224 end
225 225
226 226 def test_create_should_send_notifications_according_custom_fields_visibility
227 227 # anonymous user is never notified
228 228 users_to_test = @users_to_test.reject {|k,v| k.anonymous?}
229 229
230 230 ActionMailer::Base.deliveries.clear
231 231 @request.session[:user_id] = 1
232 232 with_settings :bcc_recipients => '1' do
233 233 assert_difference 'Issue.count' do
234 234 post :create,
235 235 :project_id => 1,
236 236 :issue => {
237 237 :tracker_id => 1,
238 238 :status_id => 1,
239 239 :subject => 'New issue',
240 240 :priority_id => 5,
241 241 :custom_field_values => {@field1.id.to_s => 'Value0', @field2.id.to_s => 'Value1', @field3.id.to_s => 'Value2'},
242 242 :watcher_user_ids => users_to_test.keys.map(&:id)
243 243 }
244 244 assert_response 302
245 245 end
246 246 end
247 247 assert_equal users_to_test.values.uniq.size, ActionMailer::Base.deliveries.size
248 248 # tests that each user receives 1 email with the custom fields he is allowed to see only
249 249 users_to_test.each do |user, fields|
250 250 mails = ActionMailer::Base.deliveries.select {|m| m.bcc.include? user.mail}
251 251 assert_equal 1, mails.size
252 252 mail = mails.first
253 253 @fields.each_with_index do |field, i|
254 254 if fields.include?(field)
255 255 assert_mail_body_match "Value#{i}", mail, "User #{user.id} was not able to view #{field.name} in notification"
256 256 else
257 257 assert_mail_body_no_match "Value#{i}", mail, "User #{user.id} was able to view #{field.name} in notification"
258 258 end
259 259 end
260 260 end
261 261 end
262 262
263 263 def test_update_should_send_notifications_according_custom_fields_visibility
264 264 # anonymous user is never notified
265 265 users_to_test = @users_to_test.reject {|k,v| k.anonymous?}
266 266
267 267 users_to_test.keys.each do |user|
268 268 Watcher.create!(:user => user, :watchable => @issue)
269 269 end
270 270 ActionMailer::Base.deliveries.clear
271 271 @request.session[:user_id] = 1
272 272 with_settings :bcc_recipients => '1' do
273 273 put :update,
274 274 :id => @issue.id,
275 275 :issue => {
276 276 :custom_field_values => {@field1.id.to_s => 'NewValue0', @field2.id.to_s => 'NewValue1', @field3.id.to_s => 'NewValue2'}
277 277 }
278 278 assert_response 302
279 279 end
280 280 assert_equal users_to_test.values.uniq.size, ActionMailer::Base.deliveries.size
281 281 # tests that each user receives 1 email with the custom fields he is allowed to see only
282 282 users_to_test.each do |user, fields|
283 283 mails = ActionMailer::Base.deliveries.select {|m| m.bcc.include? user.mail}
284 284 assert_equal 1, mails.size
285 285 mail = mails.first
286 286 @fields.each_with_index do |field, i|
287 287 if fields.include?(field)
288 288 assert_mail_body_match "Value#{i}", mail, "User #{user.id} was not able to view #{field.name} in notification"
289 289 else
290 290 assert_mail_body_no_match "Value#{i}", mail, "User #{user.id} was able to view #{field.name} in notification"
291 291 end
292 292 end
293 293 end
294 294 end
295 295
296 296 def test_updating_hidden_custom_fields_only_should_not_notifiy_user
297 297 # anonymous user is never notified
298 298 users_to_test = @users_to_test.reject {|k,v| k.anonymous?}
299 299
300 300 users_to_test.keys.each do |user|
301 301 Watcher.create!(:user => user, :watchable => @issue)
302 302 end
303 303 ActionMailer::Base.deliveries.clear
304 304 @request.session[:user_id] = 1
305 305 with_settings :bcc_recipients => '1' do
306 306 put :update,
307 307 :id => @issue.id,
308 308 :issue => {
309 309 :custom_field_values => {@field2.id.to_s => 'NewValue1', @field3.id.to_s => 'NewValue2'}
310 310 }
311 311 assert_response 302
312 312 end
313 313 users_to_test.each do |user, fields|
314 314 mails = ActionMailer::Base.deliveries.select {|m| m.bcc.include? user.mail}
315 315 if (fields & [@field2, @field3]).any?
316 316 assert_equal 1, mails.size, "User #{user.id} was not notified"
317 317 else
318 318 assert_equal 0, mails.size, "User #{user.id} was notified"
319 319 end
320 320 end
321 321 end
322 322 end
@@ -1,219 +1,219
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class MessagesControllerTest < ActionController::TestCase
21 fixtures :projects, :users, :email_addresses, :members, :member_roles, :roles, :boards, :messages, :enabled_modules
21 fixtures :projects, :users, :email_addresses, :user_preferences, :members, :member_roles, :roles, :boards, :messages, :enabled_modules
22 22
23 23 def setup
24 24 User.current = nil
25 25 end
26 26
27 27 def test_show
28 28 get :show, :board_id => 1, :id => 1
29 29 assert_response :success
30 30 assert_template 'show'
31 31 assert_not_nil assigns(:board)
32 32 assert_not_nil assigns(:project)
33 33 assert_not_nil assigns(:topic)
34 34 end
35 35
36 36 def test_show_should_contain_reply_field_tags_for_quoting
37 37 @request.session[:user_id] = 2
38 38 get :show, :board_id => 1, :id => 1
39 39 assert_response :success
40 40
41 41 # tags required by MessagesController#quote
42 42 assert_select 'input#message_subject'
43 43 assert_select 'textarea#message_content'
44 44 assert_select 'div#reply'
45 45 end
46 46
47 47 def test_show_with_pagination
48 48 message = Message.find(1)
49 49 assert_difference 'Message.count', 30 do
50 50 30.times do
51 51 message.children << Message.new(:subject => 'Reply',
52 52 :content => 'Reply body',
53 53 :author_id => 2,
54 54 :board_id => 1)
55 55 end
56 56 end
57 57 get :show, :board_id => 1, :id => 1, :r => message.children.order('id').last.id
58 58 assert_response :success
59 59 assert_template 'show'
60 60 replies = assigns(:replies)
61 61 assert_not_nil replies
62 62 assert_not_include message.children.reorder('id').first, replies
63 63 assert_include message.children.reorder('id').last, replies
64 64 end
65 65
66 66 def test_show_with_reply_permission
67 67 @request.session[:user_id] = 2
68 68 get :show, :board_id => 1, :id => 1
69 69 assert_response :success
70 70 assert_template 'show'
71 71 assert_select 'div#reply textarea#message_content'
72 72 end
73 73
74 74 def test_show_message_not_found
75 75 get :show, :board_id => 1, :id => 99999
76 76 assert_response 404
77 77 end
78 78
79 79 def test_show_message_from_invalid_board_should_respond_with_404
80 80 get :show, :board_id => 999, :id => 1
81 81 assert_response 404
82 82 end
83 83
84 84 def test_get_new
85 85 @request.session[:user_id] = 2
86 86 get :new, :board_id => 1
87 87 assert_response :success
88 88 assert_template 'new'
89 89 end
90 90
91 91 def test_get_new_with_invalid_board
92 92 @request.session[:user_id] = 2
93 93 get :new, :board_id => 99
94 94 assert_response 404
95 95 end
96 96
97 97 def test_post_new
98 98 @request.session[:user_id] = 2
99 99 ActionMailer::Base.deliveries.clear
100 100
101 101 with_settings :notified_events => %w(message_posted) do
102 102 post :new, :board_id => 1,
103 103 :message => { :subject => 'Test created message',
104 104 :content => 'Message body'}
105 105 end
106 106 message = Message.find_by_subject('Test created message')
107 107 assert_not_nil message
108 108 assert_redirected_to "/boards/1/topics/#{message.to_param}"
109 109 assert_equal 'Message body', message.content
110 110 assert_equal 2, message.author_id
111 111 assert_equal 1, message.board_id
112 112
113 113 mail = ActionMailer::Base.deliveries.last
114 114 assert_not_nil mail
115 115 assert_equal "[#{message.board.project.name} - #{message.board.name} - msg#{message.root.id}] Test created message", mail.subject
116 116 assert_mail_body_match 'Message body', mail
117 117 # author
118 118 assert mail.bcc.include?('jsmith@somenet.foo')
119 119 # project member
120 120 assert mail.bcc.include?('dlopper@somenet.foo')
121 121 end
122 122
123 123 def test_get_edit
124 124 @request.session[:user_id] = 2
125 125 get :edit, :board_id => 1, :id => 1
126 126 assert_response :success
127 127 assert_template 'edit'
128 128 end
129 129
130 130 def test_post_edit
131 131 @request.session[:user_id] = 2
132 132 post :edit, :board_id => 1, :id => 1,
133 133 :message => { :subject => 'New subject',
134 134 :content => 'New body'}
135 135 assert_redirected_to '/boards/1/topics/1'
136 136 message = Message.find(1)
137 137 assert_equal 'New subject', message.subject
138 138 assert_equal 'New body', message.content
139 139 end
140 140
141 141 def test_post_edit_sticky_and_locked
142 142 @request.session[:user_id] = 2
143 143 post :edit, :board_id => 1, :id => 1,
144 144 :message => { :subject => 'New subject',
145 145 :content => 'New body',
146 146 :locked => '1',
147 147 :sticky => '1'}
148 148 assert_redirected_to '/boards/1/topics/1'
149 149 message = Message.find(1)
150 150 assert_equal true, message.sticky?
151 151 assert_equal true, message.locked?
152 152 end
153 153
154 154 def test_post_edit_should_allow_to_change_board
155 155 @request.session[:user_id] = 2
156 156 post :edit, :board_id => 1, :id => 1,
157 157 :message => { :subject => 'New subject',
158 158 :content => 'New body',
159 159 :board_id => 2}
160 160 assert_redirected_to '/boards/2/topics/1'
161 161 message = Message.find(1)
162 162 assert_equal Board.find(2), message.board
163 163 end
164 164
165 165 def test_reply
166 166 @request.session[:user_id] = 2
167 167 post :reply, :board_id => 1, :id => 1, :reply => { :content => 'This is a test reply', :subject => 'Test reply' }
168 168 reply = Message.order('id DESC').first
169 169 assert_redirected_to "/boards/1/topics/1?r=#{reply.id}"
170 170 assert Message.find_by_subject('Test reply')
171 171 end
172 172
173 173 def test_destroy_topic
174 174 @request.session[:user_id] = 2
175 175 assert_difference 'Message.count', -3 do
176 176 post :destroy, :board_id => 1, :id => 1
177 177 end
178 178 assert_redirected_to '/projects/ecookbook/boards/1'
179 179 assert_nil Message.find_by_id(1)
180 180 end
181 181
182 182 def test_destroy_reply
183 183 @request.session[:user_id] = 2
184 184 assert_difference 'Message.count', -1 do
185 185 post :destroy, :board_id => 1, :id => 2
186 186 end
187 187 assert_redirected_to '/boards/1/topics/1?r=2'
188 188 assert_nil Message.find_by_id(2)
189 189 end
190 190
191 191 def test_quote
192 192 @request.session[:user_id] = 2
193 193 xhr :get, :quote, :board_id => 1, :id => 3
194 194 assert_response :success
195 195 assert_equal 'text/javascript', response.content_type
196 196 assert_template 'quote'
197 197 assert_include 'RE: First post', response.body
198 198 assert_include '> An other reply', response.body
199 199 end
200 200
201 201 def test_preview_new
202 202 @request.session[:user_id] = 2
203 203 post :preview,
204 204 :board_id => 1,
205 205 :message => {:subject => "", :content => "Previewed text"}
206 206 assert_response :success
207 207 assert_template 'common/_preview'
208 208 end
209 209
210 210 def test_preview_edit
211 211 @request.session[:user_id] = 2
212 212 post :preview,
213 213 :id => 4,
214 214 :board_id => 1,
215 215 :message => {:subject => "", :content => "Previewed text"}
216 216 assert_response :success
217 217 assert_template 'common/_preview'
218 218 end
219 219 end
@@ -1,2748 +1,2748
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class IssueTest < ActiveSupport::TestCase
21 fixtures :projects, :users, :email_addresses, :members, :member_roles, :roles,
21 fixtures :projects, :users, :email_addresses, :user_preferences, :members, :member_roles, :roles,
22 22 :groups_users,
23 23 :trackers, :projects_trackers,
24 24 :enabled_modules,
25 25 :versions,
26 26 :issue_statuses, :issue_categories, :issue_relations, :workflows,
27 27 :enumerations,
28 28 :issues, :journals, :journal_details,
29 29 :custom_fields, :custom_fields_projects, :custom_fields_trackers, :custom_values,
30 30 :time_entries
31 31
32 32 include Redmine::I18n
33 33
34 34 def setup
35 35 set_language_if_valid 'en'
36 36 end
37 37
38 38 def teardown
39 39 User.current = nil
40 40 end
41 41
42 42 def test_initialize
43 43 issue = Issue.new
44 44
45 45 assert_nil issue.project_id
46 46 assert_nil issue.tracker_id
47 47 assert_nil issue.status_id
48 48 assert_nil issue.author_id
49 49 assert_nil issue.assigned_to_id
50 50 assert_nil issue.category_id
51 51
52 52 assert_equal IssuePriority.default, issue.priority
53 53 end
54 54
55 55 def test_create
56 56 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
57 57 :status_id => 1, :priority => IssuePriority.all.first,
58 58 :subject => 'test_create',
59 59 :description => 'IssueTest#test_create', :estimated_hours => '1:30')
60 60 assert issue.save
61 61 issue.reload
62 62 assert_equal 1.5, issue.estimated_hours
63 63 end
64 64
65 65 def test_create_minimal
66 66 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create')
67 67 assert issue.save
68 68 assert_equal issue.tracker.default_status, issue.status
69 69 assert issue.description.nil?
70 70 assert_nil issue.estimated_hours
71 71 end
72 72
73 73 def test_create_with_all_fields_disabled
74 74 tracker = Tracker.find(1)
75 75 tracker.core_fields = []
76 76 tracker.save!
77 77
78 78 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3, :subject => 'test_create_with_all_fields_disabled')
79 79 assert_save issue
80 80 end
81 81
82 82 def test_start_date_format_should_be_validated
83 83 set_language_if_valid 'en'
84 84 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
85 85 issue = Issue.new(:start_date => invalid_date)
86 86 assert !issue.valid?
87 87 assert_include 'Start date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
88 88 end
89 89 end
90 90
91 91 def test_due_date_format_should_be_validated
92 92 set_language_if_valid 'en'
93 93 ['2012', 'ABC', '2012-15-20'].each do |invalid_date|
94 94 issue = Issue.new(:due_date => invalid_date)
95 95 assert !issue.valid?
96 96 assert_include 'Due date is not a valid date', issue.errors.full_messages, "No error found for invalid date #{invalid_date}"
97 97 end
98 98 end
99 99
100 100 def test_due_date_lesser_than_start_date_should_not_validate
101 101 set_language_if_valid 'en'
102 102 issue = Issue.new(:start_date => '2012-10-06', :due_date => '2012-10-02')
103 103 assert !issue.valid?
104 104 assert_include 'Due date must be greater than start date', issue.errors.full_messages
105 105 end
106 106
107 107 def test_start_date_lesser_than_soonest_start_should_not_validate_on_create
108 108 issue = Issue.generate(:start_date => '2013-06-04')
109 109 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
110 110 assert !issue.valid?
111 111 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
112 112 end
113 113
114 114 def test_start_date_lesser_than_soonest_start_should_not_validate_on_update_if_changed
115 115 issue = Issue.generate!(:start_date => '2013-06-04')
116 116 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
117 117 issue.start_date = '2013-06-07'
118 118 assert !issue.valid?
119 119 assert_include "Start date cannot be earlier than 06/10/2013 because of preceding issues", issue.errors.full_messages
120 120 end
121 121
122 122 def test_start_date_lesser_than_soonest_start_should_validate_on_update_if_unchanged
123 123 issue = Issue.generate!(:start_date => '2013-06-04')
124 124 issue.stubs(:soonest_start).returns(Date.parse('2013-06-10'))
125 125 assert issue.valid?
126 126 end
127 127
128 128 def test_estimated_hours_should_be_validated
129 129 set_language_if_valid 'en'
130 130 ['-2'].each do |invalid|
131 131 issue = Issue.new(:estimated_hours => invalid)
132 132 assert !issue.valid?
133 133 assert_include 'Estimated time is invalid', issue.errors.full_messages
134 134 end
135 135 end
136 136
137 137 def test_create_with_required_custom_field
138 138 set_language_if_valid 'en'
139 139 field = IssueCustomField.find_by_name('Database')
140 140 field.update_attribute(:is_required, true)
141 141
142 142 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
143 143 :status_id => 1, :subject => 'test_create',
144 144 :description => 'IssueTest#test_create_with_required_custom_field')
145 145 assert issue.available_custom_fields.include?(field)
146 146 # No value for the custom field
147 147 assert !issue.save
148 148 assert_equal ["Database cannot be blank"], issue.errors.full_messages
149 149 # Blank value
150 150 issue.custom_field_values = { field.id => '' }
151 151 assert !issue.save
152 152 assert_equal ["Database cannot be blank"], issue.errors.full_messages
153 153 # Invalid value
154 154 issue.custom_field_values = { field.id => 'SQLServer' }
155 155 assert !issue.save
156 156 assert_equal ["Database is not included in the list"], issue.errors.full_messages
157 157 # Valid value
158 158 issue.custom_field_values = { field.id => 'PostgreSQL' }
159 159 assert issue.save
160 160 issue.reload
161 161 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
162 162 end
163 163
164 164 def test_create_with_group_assignment
165 165 with_settings :issue_group_assignment => '1' do
166 166 assert Issue.new(:project_id => 2, :tracker_id => 1, :author_id => 1,
167 167 :subject => 'Group assignment',
168 168 :assigned_to_id => 11).save
169 169 issue = Issue.order('id DESC').first
170 170 assert_kind_of Group, issue.assigned_to
171 171 assert_equal Group.find(11), issue.assigned_to
172 172 end
173 173 end
174 174
175 175 def test_create_with_parent_issue_id
176 176 issue = Issue.new(:project_id => 1, :tracker_id => 1,
177 177 :author_id => 1, :subject => 'Group assignment',
178 178 :parent_issue_id => 1)
179 179 assert_save issue
180 180 assert_equal 1, issue.parent_issue_id
181 181 assert_equal Issue.find(1), issue.parent
182 182 end
183 183
184 184 def test_create_with_sharp_parent_issue_id
185 185 issue = Issue.new(:project_id => 1, :tracker_id => 1,
186 186 :author_id => 1, :subject => 'Group assignment',
187 187 :parent_issue_id => "#1")
188 188 assert_save issue
189 189 assert_equal 1, issue.parent_issue_id
190 190 assert_equal Issue.find(1), issue.parent
191 191 end
192 192
193 193 def test_create_with_invalid_parent_issue_id
194 194 set_language_if_valid 'en'
195 195 issue = Issue.new(:project_id => 1, :tracker_id => 1,
196 196 :author_id => 1, :subject => 'Group assignment',
197 197 :parent_issue_id => '01ABC')
198 198 assert !issue.save
199 199 assert_equal '01ABC', issue.parent_issue_id
200 200 assert_include 'Parent task is invalid', issue.errors.full_messages
201 201 end
202 202
203 203 def test_create_with_invalid_sharp_parent_issue_id
204 204 set_language_if_valid 'en'
205 205 issue = Issue.new(:project_id => 1, :tracker_id => 1,
206 206 :author_id => 1, :subject => 'Group assignment',
207 207 :parent_issue_id => '#01ABC')
208 208 assert !issue.save
209 209 assert_equal '#01ABC', issue.parent_issue_id
210 210 assert_include 'Parent task is invalid', issue.errors.full_messages
211 211 end
212 212
213 213 def assert_visibility_match(user, issues)
214 214 assert_equal issues.collect(&:id).sort, Issue.all.select {|issue| issue.visible?(user)}.collect(&:id).sort
215 215 end
216 216
217 217 def test_visible_scope_for_anonymous
218 218 # Anonymous user should see issues of public projects only
219 219 issues = Issue.visible(User.anonymous).to_a
220 220 assert issues.any?
221 221 assert_nil issues.detect {|issue| !issue.project.is_public?}
222 222 assert_nil issues.detect {|issue| issue.is_private?}
223 223 assert_visibility_match User.anonymous, issues
224 224 end
225 225
226 226 def test_visible_scope_for_anonymous_without_view_issues_permissions
227 227 # Anonymous user should not see issues without permission
228 228 Role.anonymous.remove_permission!(:view_issues)
229 229 issues = Issue.visible(User.anonymous).to_a
230 230 assert issues.empty?
231 231 assert_visibility_match User.anonymous, issues
232 232 end
233 233
234 234 def test_visible_scope_for_anonymous_without_view_issues_permissions_and_membership
235 235 Role.anonymous.remove_permission!(:view_issues)
236 236 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [2])
237 237
238 238 issues = Issue.visible(User.anonymous).all
239 239 assert issues.any?
240 240 assert_equal [1], issues.map(&:project_id).uniq.sort
241 241 assert_visibility_match User.anonymous, issues
242 242 end
243 243
244 244 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default
245 245 assert Role.anonymous.update_attribute(:issues_visibility, 'default')
246 246 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
247 247 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
248 248 assert !issue.visible?(User.anonymous)
249 249 end
250 250
251 251 def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_own
252 252 assert Role.anonymous.update_attribute(:issues_visibility, 'own')
253 253 issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true)
254 254 assert_nil Issue.where(:id => issue.id).visible(User.anonymous).first
255 255 assert !issue.visible?(User.anonymous)
256 256 end
257 257
258 258 def test_visible_scope_for_non_member
259 259 user = User.find(9)
260 260 assert user.projects.empty?
261 261 # Non member user should see issues of public projects only
262 262 issues = Issue.visible(user).to_a
263 263 assert issues.any?
264 264 assert_nil issues.detect {|issue| !issue.project.is_public?}
265 265 assert_nil issues.detect {|issue| issue.is_private?}
266 266 assert_visibility_match user, issues
267 267 end
268 268
269 269 def test_visible_scope_for_non_member_with_own_issues_visibility
270 270 Role.non_member.update_attribute :issues_visibility, 'own'
271 271 Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 9, :subject => 'Issue by non member')
272 272 user = User.find(9)
273 273
274 274 issues = Issue.visible(user).to_a
275 275 assert issues.any?
276 276 assert_nil issues.detect {|issue| issue.author != user}
277 277 assert_visibility_match user, issues
278 278 end
279 279
280 280 def test_visible_scope_for_non_member_without_view_issues_permissions
281 281 # Non member user should not see issues without permission
282 282 Role.non_member.remove_permission!(:view_issues)
283 283 user = User.find(9)
284 284 assert user.projects.empty?
285 285 issues = Issue.visible(user).to_a
286 286 assert issues.empty?
287 287 assert_visibility_match user, issues
288 288 end
289 289
290 290 def test_visible_scope_for_non_member_without_view_issues_permissions_and_membership
291 291 Role.non_member.remove_permission!(:view_issues)
292 292 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [2])
293 293 user = User.find(9)
294 294
295 295 issues = Issue.visible(user).all
296 296 assert issues.any?
297 297 assert_equal [1], issues.map(&:project_id).uniq.sort
298 298 assert_visibility_match user, issues
299 299 end
300 300
301 301 def test_visible_scope_for_member
302 302 user = User.find(9)
303 303 # User should see issues of projects for which user has view_issues permissions only
304 304 Role.non_member.remove_permission!(:view_issues)
305 305 Member.create!(:principal => user, :project_id => 3, :role_ids => [2])
306 306 issues = Issue.visible(user).to_a
307 307 assert issues.any?
308 308 assert_nil issues.detect {|issue| issue.project_id != 3}
309 309 assert_nil issues.detect {|issue| issue.is_private?}
310 310 assert_visibility_match user, issues
311 311 end
312 312
313 313 def test_visible_scope_for_member_without_view_issues_permission_and_non_member_role_having_the_permission
314 314 Role.non_member.add_permission!(:view_issues)
315 315 Role.find(1).remove_permission!(:view_issues)
316 316 user = User.find(2)
317 317
318 318 assert_equal 0, Issue.where(:project_id => 1).visible(user).count
319 319 assert_equal false, Issue.where(:project_id => 1).first.visible?(user)
320 320 end
321 321
322 322 def test_visible_scope_for_member_with_groups_should_return_assigned_issues
323 323 user = User.find(8)
324 324 assert user.groups.any?
325 325 Member.create!(:principal => user.groups.first, :project_id => 1, :role_ids => [2])
326 326 Role.non_member.remove_permission!(:view_issues)
327 327
328 328 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 3,
329 329 :status_id => 1, :priority => IssuePriority.all.first,
330 330 :subject => 'Assignment test',
331 331 :assigned_to => user.groups.first,
332 332 :is_private => true)
333 333
334 334 Role.find(2).update_attribute :issues_visibility, 'default'
335 335 issues = Issue.visible(User.find(8)).to_a
336 336 assert issues.any?
337 337 assert issues.include?(issue)
338 338
339 339 Role.find(2).update_attribute :issues_visibility, 'own'
340 340 issues = Issue.visible(User.find(8)).to_a
341 341 assert issues.any?
342 342 assert_include issue, issues
343 343 end
344 344
345 345 def test_visible_scope_for_admin
346 346 user = User.find(1)
347 347 user.members.each(&:destroy)
348 348 assert user.projects.empty?
349 349 issues = Issue.visible(user).to_a
350 350 assert issues.any?
351 351 # Admin should see issues on private projects that admin does not belong to
352 352 assert issues.detect {|issue| !issue.project.is_public?}
353 353 # Admin should see private issues of other users
354 354 assert issues.detect {|issue| issue.is_private? && issue.author != user}
355 355 assert_visibility_match user, issues
356 356 end
357 357
358 358 def test_visible_scope_with_project
359 359 project = Project.find(1)
360 360 issues = Issue.visible(User.find(2), :project => project).to_a
361 361 projects = issues.collect(&:project).uniq
362 362 assert_equal 1, projects.size
363 363 assert_equal project, projects.first
364 364 end
365 365
366 366 def test_visible_scope_with_project_and_subprojects
367 367 project = Project.find(1)
368 368 issues = Issue.visible(User.find(2), :project => project, :with_subprojects => true).to_a
369 369 projects = issues.collect(&:project).uniq
370 370 assert projects.size > 1
371 371 assert_equal [], projects.select {|p| !p.is_or_is_descendant_of?(project)}
372 372 end
373 373
374 374 def test_visible_and_nested_set_scopes
375 375 user = User.generate!
376 376 parent = Issue.generate!(:assigned_to => user)
377 377 assert parent.visible?(user)
378 378 child1 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
379 379 child2 = Issue.generate!(:parent_issue_id => parent.id, :assigned_to => user)
380 380 parent.reload
381 381 child1.reload
382 382 child2.reload
383 383 assert child1.visible?(user)
384 384 assert child2.visible?(user)
385 385 assert_equal 2, parent.descendants.count
386 386 assert_equal 2, parent.descendants.visible(user).count
387 387 # awesome_nested_set 2-1-stable branch has regression.
388 388 # https://github.com/collectiveidea/awesome_nested_set/commit/3d5ac746542b564f6586c2316180254b088bebb6
389 389 # ActiveRecord::StatementInvalid: SQLite3::SQLException: ambiguous column name: lft:
390 390 assert_equal 2, parent.descendants.collect{|i| i}.size
391 391 assert_equal 2, parent.descendants.visible(user).collect{|i| i}.size
392 392 end
393 393
394 394 def test_visible_scope_with_unsaved_user_should_not_raise_an_error
395 395 user = User.new
396 396 assert_nothing_raised do
397 397 Issue.visible(user).to_a
398 398 end
399 399 end
400 400
401 401 def test_open_scope
402 402 issues = Issue.open.to_a
403 403 assert_nil issues.detect(&:closed?)
404 404 end
405 405
406 406 def test_open_scope_with_arg
407 407 issues = Issue.open(false).to_a
408 408 assert_equal issues, issues.select(&:closed?)
409 409 end
410 410
411 411 def test_fixed_version_scope_with_a_version_should_return_its_fixed_issues
412 412 version = Version.find(2)
413 413 assert version.fixed_issues.any?
414 414 assert_equal version.fixed_issues.to_a.sort, Issue.fixed_version(version).to_a.sort
415 415 end
416 416
417 417 def test_fixed_version_scope_with_empty_array_should_return_no_result
418 418 assert_equal 0, Issue.fixed_version([]).count
419 419 end
420 420
421 421 def test_assigned_to_scope_should_return_issues_assigned_to_the_user
422 422 user = User.generate!
423 423 issue = Issue.generate!
424 424 Issue.where(:id => issue.id).update_all :assigned_to_id => user.id
425 425 assert_equal [issue], Issue.assigned_to(user).to_a
426 426 end
427 427
428 428 def test_assigned_to_scope_should_return_issues_assigned_to_the_user_groups
429 429 group = Group.generate!
430 430 user = User.generate!
431 431 group.users << user
432 432 issue = Issue.generate!
433 433 Issue.where(:id => issue.id).update_all :assigned_to_id => group.id
434 434 assert_equal [issue], Issue.assigned_to(user).to_a
435 435 end
436 436
437 437 def test_errors_full_messages_should_include_custom_fields_errors
438 438 field = IssueCustomField.find_by_name('Database')
439 439
440 440 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
441 441 :status_id => 1, :subject => 'test_create',
442 442 :description => 'IssueTest#test_create_with_required_custom_field')
443 443 assert issue.available_custom_fields.include?(field)
444 444 # Invalid value
445 445 issue.custom_field_values = { field.id => 'SQLServer' }
446 446
447 447 assert !issue.valid?
448 448 assert_equal 1, issue.errors.full_messages.size
449 449 assert_equal "Database #{I18n.translate('activerecord.errors.messages.inclusion')}",
450 450 issue.errors.full_messages.first
451 451 end
452 452
453 453 def test_update_issue_with_required_custom_field
454 454 field = IssueCustomField.find_by_name('Database')
455 455 field.update_attribute(:is_required, true)
456 456
457 457 issue = Issue.find(1)
458 458 assert_nil issue.custom_value_for(field)
459 459 assert issue.available_custom_fields.include?(field)
460 460 # No change to custom values, issue can be saved
461 461 assert issue.save
462 462 # Blank value
463 463 issue.custom_field_values = { field.id => '' }
464 464 assert !issue.save
465 465 # Valid value
466 466 issue.custom_field_values = { field.id => 'PostgreSQL' }
467 467 assert issue.save
468 468 issue.reload
469 469 assert_equal 'PostgreSQL', issue.custom_value_for(field).value
470 470 end
471 471
472 472 def test_should_not_update_attributes_if_custom_fields_validation_fails
473 473 issue = Issue.find(1)
474 474 field = IssueCustomField.find_by_name('Database')
475 475 assert issue.available_custom_fields.include?(field)
476 476
477 477 issue.custom_field_values = { field.id => 'Invalid' }
478 478 issue.subject = 'Should be not be saved'
479 479 assert !issue.save
480 480
481 481 issue.reload
482 482 assert_equal "Cannot print recipes", issue.subject
483 483 end
484 484
485 485 def test_should_not_recreate_custom_values_objects_on_update
486 486 field = IssueCustomField.find_by_name('Database')
487 487
488 488 issue = Issue.find(1)
489 489 issue.custom_field_values = { field.id => 'PostgreSQL' }
490 490 assert issue.save
491 491 custom_value = issue.custom_value_for(field)
492 492 issue.reload
493 493 issue.custom_field_values = { field.id => 'MySQL' }
494 494 assert issue.save
495 495 issue.reload
496 496 assert_equal custom_value.id, issue.custom_value_for(field).id
497 497 end
498 498
499 499 def test_setting_project_should_set_version_to_default_version
500 500 version = Version.generate!(:project_id => 1)
501 501 Project.find(1).update_attribute(:default_version_id, version.id)
502 502
503 503 issue = Issue.new(:project_id => 1)
504 504 assert_equal version, issue.fixed_version
505 505 end
506 506
507 507 def test_should_not_update_custom_fields_on_changing_tracker_with_different_custom_fields
508 508 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :author_id => 1,
509 509 :status_id => 1, :subject => 'Test',
510 510 :custom_field_values => {'2' => 'Test'})
511 511 assert !Tracker.find(2).custom_field_ids.include?(2)
512 512
513 513 issue = Issue.find(issue.id)
514 514 issue.attributes = {:tracker_id => 2, :custom_field_values => {'1' => ''}}
515 515
516 516 issue = Issue.find(issue.id)
517 517 custom_value = issue.custom_value_for(2)
518 518 assert_not_nil custom_value
519 519 assert_equal 'Test', custom_value.value
520 520 end
521 521
522 522 def test_assigning_tracker_id_should_reload_custom_fields_values
523 523 issue = Issue.new(:project => Project.find(1))
524 524 assert issue.custom_field_values.empty?
525 525 issue.tracker_id = 1
526 526 assert issue.custom_field_values.any?
527 527 end
528 528
529 529 def test_assigning_attributes_should_assign_project_and_tracker_first
530 530 seq = sequence('seq')
531 531 issue = Issue.new
532 532 issue.expects(:project_id=).in_sequence(seq)
533 533 issue.expects(:tracker_id=).in_sequence(seq)
534 534 issue.expects(:subject=).in_sequence(seq)
535 535 issue.attributes = {:tracker_id => 2, :project_id => 1, :subject => 'Test'}
536 536 end
537 537
538 538 def test_assigning_tracker_and_custom_fields_should_assign_custom_fields
539 539 attributes = ActiveSupport::OrderedHash.new
540 540 attributes['custom_field_values'] = { '1' => 'MySQL' }
541 541 attributes['tracker_id'] = '1'
542 542 issue = Issue.new(:project => Project.find(1))
543 543 issue.attributes = attributes
544 544 assert_equal 'MySQL', issue.custom_field_value(1)
545 545 end
546 546
547 547 def test_changing_tracker_should_clear_disabled_core_fields
548 548 tracker = Tracker.find(2)
549 549 tracker.core_fields = tracker.core_fields - %w(due_date)
550 550 tracker.save!
551 551
552 552 issue = Issue.generate!(:tracker_id => 1, :start_date => Date.today, :due_date => Date.today)
553 553 issue.save!
554 554
555 555 issue.tracker_id = 2
556 556 issue.save!
557 557 assert_not_nil issue.start_date
558 558 assert_nil issue.due_date
559 559 end
560 560
561 561 def test_changing_tracker_should_not_add_cleared_fields_to_journal
562 562 tracker = Tracker.find(2)
563 563 tracker.core_fields = tracker.core_fields - %w(due_date)
564 564 tracker.save!
565 565
566 566 issue = Issue.generate!(:tracker_id => 1, :due_date => Date.today)
567 567 issue.save!
568 568
569 569 assert_difference 'Journal.count' do
570 570 issue.init_journal User.find(1)
571 571 issue.tracker_id = 2
572 572 issue.save!
573 573 assert_nil issue.due_date
574 574 end
575 575 journal = Journal.order('id DESC').first
576 576 assert_equal 1, journal.details.count
577 577 end
578 578
579 579 def test_reload_should_reload_custom_field_values
580 580 issue = Issue.generate!
581 581 issue.custom_field_values = {'2' => 'Foo'}
582 582 issue.save!
583 583
584 584 issue = Issue.order('id desc').first
585 585 assert_equal 'Foo', issue.custom_field_value(2)
586 586
587 587 issue.custom_field_values = {'2' => 'Bar'}
588 588 assert_equal 'Bar', issue.custom_field_value(2)
589 589
590 590 issue.reload
591 591 assert_equal 'Foo', issue.custom_field_value(2)
592 592 end
593 593
594 594 def test_should_update_issue_with_disabled_tracker
595 595 p = Project.find(1)
596 596 issue = Issue.find(1)
597 597
598 598 p.trackers.delete(issue.tracker)
599 599 assert !p.trackers.include?(issue.tracker)
600 600
601 601 issue.reload
602 602 issue.subject = 'New subject'
603 603 assert issue.save
604 604 end
605 605
606 606 def test_should_not_set_a_disabled_tracker
607 607 p = Project.find(1)
608 608 p.trackers.delete(Tracker.find(2))
609 609
610 610 issue = Issue.find(1)
611 611 issue.tracker_id = 2
612 612 issue.subject = 'New subject'
613 613 assert !issue.save
614 614 assert_not_equal [], issue.errors[:tracker_id]
615 615 end
616 616
617 617 def test_category_based_assignment
618 618 issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3,
619 619 :status_id => 1, :priority => IssuePriority.all.first,
620 620 :subject => 'Assignment test',
621 621 :description => 'Assignment test', :category_id => 1)
622 622 assert_equal IssueCategory.find(1).assigned_to, issue.assigned_to
623 623 end
624 624
625 625 def test_new_statuses_allowed_to
626 626 WorkflowTransition.delete_all
627 627 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
628 628 :old_status_id => 1, :new_status_id => 2,
629 629 :author => false, :assignee => false)
630 630 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
631 631 :old_status_id => 1, :new_status_id => 3,
632 632 :author => true, :assignee => false)
633 633 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
634 634 :old_status_id => 1, :new_status_id => 4,
635 635 :author => false, :assignee => true)
636 636 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
637 637 :old_status_id => 1, :new_status_id => 5,
638 638 :author => true, :assignee => true)
639 639 status = IssueStatus.find(1)
640 640 role = Role.find(1)
641 641 tracker = Tracker.find(1)
642 642 user = User.find(2)
643 643
644 644 issue = Issue.generate!(:tracker => tracker, :status => status,
645 645 :project_id => 1, :author_id => 1)
646 646 assert_equal [1, 2], issue.new_statuses_allowed_to(user).map(&:id)
647 647
648 648 issue = Issue.generate!(:tracker => tracker, :status => status,
649 649 :project_id => 1, :author => user)
650 650 assert_equal [1, 2, 3, 5], issue.new_statuses_allowed_to(user).map(&:id)
651 651
652 652 issue = Issue.generate!(:tracker => tracker, :status => status,
653 653 :project_id => 1, :author_id => 1,
654 654 :assigned_to => user)
655 655 assert_equal [1, 2, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
656 656
657 657 issue = Issue.generate!(:tracker => tracker, :status => status,
658 658 :project_id => 1, :author => user,
659 659 :assigned_to => user)
660 660 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
661 661
662 662 group = Group.generate!
663 663 group.users << user
664 664 issue = Issue.generate!(:tracker => tracker, :status => status,
665 665 :project_id => 1, :author => user,
666 666 :assigned_to => group)
667 667 assert_equal [1, 2, 3, 4, 5], issue.new_statuses_allowed_to(user).map(&:id)
668 668 end
669 669
670 670 def test_new_statuses_allowed_to_should_consider_group_assignment
671 671 WorkflowTransition.delete_all
672 672 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1,
673 673 :old_status_id => 1, :new_status_id => 4,
674 674 :author => false, :assignee => true)
675 675 user = User.find(2)
676 676 group = Group.generate!
677 677 group.users << user
678 678
679 679 issue = Issue.generate!(:author_id => 1, :assigned_to => group)
680 680 assert_include 4, issue.new_statuses_allowed_to(user).map(&:id)
681 681 end
682 682
683 683 def test_new_statuses_allowed_to_should_return_all_transitions_for_admin
684 684 admin = User.find(1)
685 685 issue = Issue.find(1)
686 686 assert !admin.member_of?(issue.project)
687 687 expected_statuses = [issue.status] +
688 688 WorkflowTransition.where(:old_status_id => issue.status_id).
689 689 map(&:new_status).uniq.sort
690 690 assert_equal expected_statuses, issue.new_statuses_allowed_to(admin)
691 691 end
692 692
693 693 def test_new_statuses_allowed_to_should_return_default_and_current_status_when_copying
694 694 issue = Issue.find(1).copy
695 695 assert_equal [1], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
696 696
697 697 issue = Issue.find(2).copy
698 698 assert_equal [1, 2], issue.new_statuses_allowed_to(User.find(2)).map(&:id)
699 699 end
700 700
701 701 def test_safe_attributes_names_should_not_include_disabled_field
702 702 tracker = Tracker.new(:core_fields => %w(assigned_to_id fixed_version_id))
703 703
704 704 issue = Issue.new(:tracker => tracker)
705 705 assert_include 'tracker_id', issue.safe_attribute_names
706 706 assert_include 'status_id', issue.safe_attribute_names
707 707 assert_include 'subject', issue.safe_attribute_names
708 708 assert_include 'description', issue.safe_attribute_names
709 709 assert_include 'custom_field_values', issue.safe_attribute_names
710 710 assert_include 'custom_fields', issue.safe_attribute_names
711 711 assert_include 'lock_version', issue.safe_attribute_names
712 712
713 713 tracker.core_fields.each do |field|
714 714 assert_include field, issue.safe_attribute_names
715 715 end
716 716
717 717 tracker.disabled_core_fields.each do |field|
718 718 assert_not_include field, issue.safe_attribute_names
719 719 end
720 720 end
721 721
722 722 def test_safe_attributes_should_ignore_disabled_fields
723 723 tracker = Tracker.find(1)
724 724 tracker.core_fields = %w(assigned_to_id due_date)
725 725 tracker.save!
726 726
727 727 issue = Issue.new(:tracker => tracker)
728 728 issue.safe_attributes = {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}
729 729 assert_nil issue.start_date
730 730 assert_equal Date.parse('2012-07-14'), issue.due_date
731 731 end
732 732
733 733 def test_safe_attributes_should_accept_target_tracker_enabled_fields
734 734 source = Tracker.find(1)
735 735 source.core_fields = []
736 736 source.save!
737 737 target = Tracker.find(2)
738 738 target.core_fields = %w(assigned_to_id due_date)
739 739 target.save!
740 740
741 741 issue = Issue.new(:tracker => source)
742 742 issue.safe_attributes = {'tracker_id' => 2, 'due_date' => '2012-07-14'}
743 743 assert_equal target, issue.tracker
744 744 assert_equal Date.parse('2012-07-14'), issue.due_date
745 745 end
746 746
747 747 def test_safe_attributes_should_not_include_readonly_fields
748 748 WorkflowPermission.delete_all
749 749 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
750 750 :role_id => 1, :field_name => 'due_date',
751 751 :rule => 'readonly')
752 752 user = User.find(2)
753 753
754 754 issue = Issue.new(:project_id => 1, :tracker_id => 1)
755 755 assert_equal %w(due_date), issue.read_only_attribute_names(user)
756 756 assert_not_include 'due_date', issue.safe_attribute_names(user)
757 757
758 758 issue.send :safe_attributes=, {'start_date' => '2012-07-14', 'due_date' => '2012-07-14'}, user
759 759 assert_equal Date.parse('2012-07-14'), issue.start_date
760 760 assert_nil issue.due_date
761 761 end
762 762
763 763 def test_safe_attributes_should_not_include_readonly_custom_fields
764 764 cf1 = IssueCustomField.create!(:name => 'Writable field',
765 765 :field_format => 'string',
766 766 :is_for_all => true, :tracker_ids => [1])
767 767 cf2 = IssueCustomField.create!(:name => 'Readonly field',
768 768 :field_format => 'string',
769 769 :is_for_all => true, :tracker_ids => [1])
770 770 WorkflowPermission.delete_all
771 771 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
772 772 :role_id => 1, :field_name => cf2.id.to_s,
773 773 :rule => 'readonly')
774 774 user = User.find(2)
775 775 issue = Issue.new(:project_id => 1, :tracker_id => 1)
776 776 assert_equal [cf2.id.to_s], issue.read_only_attribute_names(user)
777 777 assert_not_include cf2.id.to_s, issue.safe_attribute_names(user)
778 778
779 779 issue.send :safe_attributes=, {'custom_field_values' => {
780 780 cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'
781 781 }}, user
782 782 assert_equal 'value1', issue.custom_field_value(cf1)
783 783 assert_nil issue.custom_field_value(cf2)
784 784
785 785 issue.send :safe_attributes=, {'custom_fields' => [
786 786 {'id' => cf1.id.to_s, 'value' => 'valuea'},
787 787 {'id' => cf2.id.to_s, 'value' => 'valueb'}
788 788 ]}, user
789 789 assert_equal 'valuea', issue.custom_field_value(cf1)
790 790 assert_nil issue.custom_field_value(cf2)
791 791 end
792 792
793 793 def test_safe_attributes_should_ignore_unassignable_assignee
794 794 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 3,
795 795 :status_id => 1, :priority => IssuePriority.all.first,
796 796 :subject => 'test_create')
797 797 assert issue.valid?
798 798
799 799 # locked user, not allowed
800 800 issue.safe_attributes=({'assigned_to_id' => '5'})
801 801 assert_nil issue.assigned_to_id
802 802 # no member
803 803 issue.safe_attributes=({'assigned_to_id' => '1'})
804 804 assert_nil issue.assigned_to_id
805 805 # user 2 is ok
806 806 issue.safe_attributes=({'assigned_to_id' => '2'})
807 807 assert_equal 2, issue.assigned_to_id
808 808 assert issue.save
809 809
810 810 issue.reload
811 811 assert_equal 2, issue.assigned_to_id
812 812 issue.safe_attributes=({'assigned_to_id' => '5'})
813 813 assert_equal 2, issue.assigned_to_id
814 814 issue.safe_attributes=({'assigned_to_id' => '1'})
815 815 assert_equal 2, issue.assigned_to_id
816 816 # user 3 is also ok
817 817 issue.safe_attributes=({'assigned_to_id' => '3'})
818 818 assert_equal 3, issue.assigned_to_id
819 819 assert issue.save
820 820
821 821 # removal of assignee
822 822 issue.safe_attributes=({'assigned_to_id' => ''})
823 823 assert_nil issue.assigned_to_id
824 824 assert issue.save
825 825 end
826 826
827 827 def test_editable_custom_field_values_should_return_non_readonly_custom_values
828 828 cf1 = IssueCustomField.create!(:name => 'Writable field', :field_format => 'string',
829 829 :is_for_all => true, :tracker_ids => [1, 2])
830 830 cf2 = IssueCustomField.create!(:name => 'Readonly field', :field_format => 'string',
831 831 :is_for_all => true, :tracker_ids => [1, 2])
832 832 WorkflowPermission.delete_all
833 833 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1,
834 834 :field_name => cf2.id.to_s, :rule => 'readonly')
835 835 user = User.find(2)
836 836
837 837 issue = Issue.new(:project_id => 1, :tracker_id => 1)
838 838 values = issue.editable_custom_field_values(user)
839 839 assert values.detect {|value| value.custom_field == cf1}
840 840 assert_nil values.detect {|value| value.custom_field == cf2}
841 841
842 842 issue.tracker_id = 2
843 843 values = issue.editable_custom_field_values(user)
844 844 assert values.detect {|value| value.custom_field == cf1}
845 845 assert values.detect {|value| value.custom_field == cf2}
846 846 end
847 847
848 848 def test_editable_custom_fields_should_return_custom_field_that_is_enabled_for_the_role_only
849 849 enabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [1,2])
850 850 disabled_cf = IssueCustomField.generate!(:is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [2])
851 851 user = User.find(2)
852 852 issue = Issue.new(:project_id => 1, :tracker_id => 1)
853 853
854 854 assert_include enabled_cf, issue.editable_custom_fields(user)
855 855 assert_not_include disabled_cf, issue.editable_custom_fields(user)
856 856 end
857 857
858 858 def test_safe_attributes_should_accept_target_tracker_writable_fields
859 859 WorkflowPermission.delete_all
860 860 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
861 861 :role_id => 1, :field_name => 'due_date',
862 862 :rule => 'readonly')
863 863 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
864 864 :role_id => 1, :field_name => 'start_date',
865 865 :rule => 'readonly')
866 866 user = User.find(2)
867 867
868 868 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
869 869
870 870 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
871 871 'due_date' => '2012-07-14'}, user
872 872 assert_equal Date.parse('2012-07-12'), issue.start_date
873 873 assert_nil issue.due_date
874 874
875 875 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
876 876 'due_date' => '2012-07-16',
877 877 'tracker_id' => 2}, user
878 878 assert_equal Date.parse('2012-07-12'), issue.start_date
879 879 assert_equal Date.parse('2012-07-16'), issue.due_date
880 880 end
881 881
882 882 def test_safe_attributes_should_accept_target_status_writable_fields
883 883 WorkflowPermission.delete_all
884 884 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
885 885 :role_id => 1, :field_name => 'due_date',
886 886 :rule => 'readonly')
887 887 WorkflowPermission.create!(:old_status_id => 2, :tracker_id => 1,
888 888 :role_id => 1, :field_name => 'start_date',
889 889 :rule => 'readonly')
890 890 user = User.find(2)
891 891
892 892 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
893 893
894 894 issue.send :safe_attributes=, {'start_date' => '2012-07-12',
895 895 'due_date' => '2012-07-14'},
896 896 user
897 897 assert_equal Date.parse('2012-07-12'), issue.start_date
898 898 assert_nil issue.due_date
899 899
900 900 issue.send :safe_attributes=, {'start_date' => '2012-07-15',
901 901 'due_date' => '2012-07-16',
902 902 'status_id' => 2},
903 903 user
904 904 assert_equal Date.parse('2012-07-12'), issue.start_date
905 905 assert_equal Date.parse('2012-07-16'), issue.due_date
906 906 end
907 907
908 908 def test_required_attributes_should_be_validated
909 909 cf = IssueCustomField.create!(:name => 'Foo', :field_format => 'string',
910 910 :is_for_all => true, :tracker_ids => [1, 2])
911 911
912 912 WorkflowPermission.delete_all
913 913 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
914 914 :role_id => 1, :field_name => 'due_date',
915 915 :rule => 'required')
916 916 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
917 917 :role_id => 1, :field_name => 'category_id',
918 918 :rule => 'required')
919 919 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
920 920 :role_id => 1, :field_name => cf.id.to_s,
921 921 :rule => 'required')
922 922
923 923 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
924 924 :role_id => 1, :field_name => 'start_date',
925 925 :rule => 'required')
926 926 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2,
927 927 :role_id => 1, :field_name => cf.id.to_s,
928 928 :rule => 'required')
929 929 user = User.find(2)
930 930
931 931 issue = Issue.new(:project_id => 1, :tracker_id => 1,
932 932 :status_id => 1, :subject => 'Required fields',
933 933 :author => user)
934 934 assert_equal [cf.id.to_s, "category_id", "due_date"],
935 935 issue.required_attribute_names(user).sort
936 936 assert !issue.save, "Issue was saved"
937 937 assert_equal ["Category cannot be blank", "Due date cannot be blank", "Foo cannot be blank"],
938 938 issue.errors.full_messages.sort
939 939
940 940 issue.tracker_id = 2
941 941 assert_equal [cf.id.to_s, "start_date"], issue.required_attribute_names(user).sort
942 942 assert !issue.save, "Issue was saved"
943 943 assert_equal ["Foo cannot be blank", "Start date cannot be blank"],
944 944 issue.errors.full_messages.sort
945 945
946 946 issue.start_date = Date.today
947 947 issue.custom_field_values = {cf.id.to_s => 'bar'}
948 948 assert issue.save
949 949 end
950 950
951 951 def test_required_attribute_that_is_disabled_for_the_tracker_should_not_be_required
952 952 WorkflowPermission.delete_all
953 953 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
954 954 :role_id => 1, :field_name => 'start_date',
955 955 :rule => 'required')
956 956 user = User.find(2)
957 957
958 958 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
959 959 :subject => 'Required fields', :author => user)
960 960 assert !issue.save
961 961 assert_include "Start date cannot be blank", issue.errors.full_messages
962 962
963 963 tracker = Tracker.find(1)
964 964 tracker.core_fields -= %w(start_date)
965 965 tracker.save!
966 966 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
967 967 :subject => 'Required fields', :author => user)
968 968 assert issue.save
969 969 end
970 970
971 971 def test_category_should_not_be_required_if_project_has_no_categories
972 972 Project.find(1).issue_categories.delete_all
973 973 WorkflowPermission.delete_all
974 974 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
975 975 :role_id => 1, :field_name => 'category_id',:rule => 'required')
976 976 user = User.find(2)
977 977
978 978 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
979 979 :subject => 'Required fields', :author => user)
980 980 assert_save issue
981 981 end
982 982
983 983 def test_fixed_version_should_not_be_required_no_assignable_versions
984 984 Version.delete_all
985 985 WorkflowPermission.delete_all
986 986 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
987 987 :role_id => 1, :field_name => 'fixed_version_id',:rule => 'required')
988 988 user = User.find(2)
989 989
990 990 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
991 991 :subject => 'Required fields', :author => user)
992 992 assert_save issue
993 993 end
994 994
995 995 def test_required_custom_field_that_is_not_visible_for_the_user_should_not_be_required
996 996 CustomField.delete_all
997 997 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
998 998 user = User.generate!
999 999 User.add_to_project(user, Project.find(1), Role.find(2))
1000 1000
1001 1001 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1002 1002 :subject => 'Required fields', :author => user)
1003 1003 assert_save issue
1004 1004 end
1005 1005
1006 1006 def test_required_custom_field_that_is_visible_for_the_user_should_be_required
1007 1007 CustomField.delete_all
1008 1008 field = IssueCustomField.generate!(:is_required => true, :visible => false, :role_ids => [1], :trackers => Tracker.all, :is_for_all => true)
1009 1009 user = User.generate!
1010 1010 User.add_to_project(user, Project.find(1), Role.find(1))
1011 1011
1012 1012 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1013 1013 :subject => 'Required fields', :author => user)
1014 1014 assert !issue.save
1015 1015 assert_include "#{field.name} cannot be blank", issue.errors.full_messages
1016 1016 end
1017 1017
1018 1018 def test_required_attribute_names_for_multiple_roles_should_intersect_rules
1019 1019 WorkflowPermission.delete_all
1020 1020 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1021 1021 :role_id => 1, :field_name => 'due_date',
1022 1022 :rule => 'required')
1023 1023 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1024 1024 :role_id => 1, :field_name => 'start_date',
1025 1025 :rule => 'required')
1026 1026 user = User.find(2)
1027 1027 member = Member.find(1)
1028 1028 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1029 1029
1030 1030 assert_equal %w(due_date start_date), issue.required_attribute_names(user).sort
1031 1031
1032 1032 member.role_ids = [1, 2]
1033 1033 member.save!
1034 1034 assert_equal [], issue.required_attribute_names(user.reload)
1035 1035
1036 1036 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1037 1037 :role_id => 2, :field_name => 'due_date',
1038 1038 :rule => 'required')
1039 1039 assert_equal %w(due_date), issue.required_attribute_names(user)
1040 1040
1041 1041 member.role_ids = [1, 2, 3]
1042 1042 member.save!
1043 1043 assert_equal [], issue.required_attribute_names(user.reload)
1044 1044
1045 1045 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1046 1046 :role_id => 3, :field_name => 'due_date',
1047 1047 :rule => 'readonly')
1048 1048 # required + readonly => required
1049 1049 assert_equal %w(due_date), issue.required_attribute_names(user)
1050 1050 end
1051 1051
1052 1052 def test_read_only_attribute_names_for_multiple_roles_should_intersect_rules
1053 1053 WorkflowPermission.delete_all
1054 1054 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1055 1055 :role_id => 1, :field_name => 'due_date',
1056 1056 :rule => 'readonly')
1057 1057 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1058 1058 :role_id => 1, :field_name => 'start_date',
1059 1059 :rule => 'readonly')
1060 1060 user = User.find(2)
1061 1061 member = Member.find(1)
1062 1062 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1063 1063
1064 1064 assert_equal %w(due_date start_date), issue.read_only_attribute_names(user).sort
1065 1065
1066 1066 member.role_ids = [1, 2]
1067 1067 member.save!
1068 1068 assert_equal [], issue.read_only_attribute_names(user.reload)
1069 1069
1070 1070 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1071 1071 :role_id => 2, :field_name => 'due_date',
1072 1072 :rule => 'readonly')
1073 1073 assert_equal %w(due_date), issue.read_only_attribute_names(user)
1074 1074 end
1075 1075
1076 1076 # A field that is not visible by role 2 and readonly by role 1 should be readonly for user with role 1 and 2
1077 1077 def test_read_only_attribute_names_should_include_custom_fields_that_combine_readonly_and_not_visible_for_roles
1078 1078 field = IssueCustomField.generate!(
1079 1079 :is_for_all => true, :trackers => Tracker.all, :visible => false, :role_ids => [1]
1080 1080 )
1081 1081 WorkflowPermission.delete_all
1082 1082 WorkflowPermission.create!(
1083 1083 :old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => field.id, :rule => 'readonly'
1084 1084 )
1085 1085 user = User.generate!
1086 1086 project = Project.find(1)
1087 1087 User.add_to_project(user, project, Role.where(:id => [1, 2]))
1088 1088
1089 1089 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1090 1090 assert_equal [field.id.to_s], issue.read_only_attribute_names(user)
1091 1091 end
1092 1092
1093 1093 def test_workflow_rules_should_ignore_roles_without_issue_permissions
1094 1094 role = Role.generate! :permissions => [:view_issues, :edit_issues]
1095 1095 ignored_role = Role.generate! :permissions => [:view_issues]
1096 1096
1097 1097 WorkflowPermission.delete_all
1098 1098 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1099 1099 :role => role, :field_name => 'due_date',
1100 1100 :rule => 'required')
1101 1101 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1102 1102 :role => role, :field_name => 'start_date',
1103 1103 :rule => 'readonly')
1104 1104 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1105 1105 :role => role, :field_name => 'done_ratio',
1106 1106 :rule => 'readonly')
1107 1107 user = User.generate!
1108 1108 User.add_to_project user, Project.find(1), [role, ignored_role]
1109 1109
1110 1110 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1111 1111
1112 1112 assert_equal %w(due_date), issue.required_attribute_names(user)
1113 1113 assert_equal %w(done_ratio start_date), issue.read_only_attribute_names(user).sort
1114 1114 end
1115 1115
1116 1116 def test_workflow_rules_should_work_for_member_with_duplicate_role
1117 1117 WorkflowPermission.delete_all
1118 1118 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1119 1119 :role_id => 1, :field_name => 'due_date',
1120 1120 :rule => 'required')
1121 1121 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1,
1122 1122 :role_id => 1, :field_name => 'start_date',
1123 1123 :rule => 'readonly')
1124 1124
1125 1125 user = User.generate!
1126 1126 m = Member.new(:user_id => user.id, :project_id => 1)
1127 1127 m.member_roles.build(:role_id => 1)
1128 1128 m.member_roles.build(:role_id => 1)
1129 1129 m.save!
1130 1130
1131 1131 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1)
1132 1132
1133 1133 assert_equal %w(due_date), issue.required_attribute_names(user)
1134 1134 assert_equal %w(start_date), issue.read_only_attribute_names(user)
1135 1135 end
1136 1136
1137 1137 def test_copy
1138 1138 issue = Issue.new.copy_from(1)
1139 1139 assert issue.copy?
1140 1140 assert issue.save
1141 1141 issue.reload
1142 1142 orig = Issue.find(1)
1143 1143 assert_equal orig.subject, issue.subject
1144 1144 assert_equal orig.tracker, issue.tracker
1145 1145 assert_equal "125", issue.custom_value_for(2).value
1146 1146 end
1147 1147
1148 1148 def test_copy_should_copy_status
1149 1149 orig = Issue.find(8)
1150 1150 assert orig.status != orig.default_status
1151 1151
1152 1152 issue = Issue.new.copy_from(orig)
1153 1153 assert issue.save
1154 1154 issue.reload
1155 1155 assert_equal orig.status, issue.status
1156 1156 end
1157 1157
1158 1158 def test_copy_should_add_relation_with_copied_issue
1159 1159 copied = Issue.find(1)
1160 1160 issue = Issue.new.copy_from(copied)
1161 1161 assert issue.save
1162 1162 issue.reload
1163 1163
1164 1164 assert_equal 1, issue.relations.size
1165 1165 relation = issue.relations.first
1166 1166 assert_equal 'copied_to', relation.relation_type
1167 1167 assert_equal copied, relation.issue_from
1168 1168 assert_equal issue, relation.issue_to
1169 1169 end
1170 1170
1171 1171 def test_copy_should_copy_subtasks
1172 1172 issue = Issue.generate_with_descendants!
1173 1173
1174 1174 copy = issue.reload.copy
1175 1175 copy.author = User.find(7)
1176 1176 assert_difference 'Issue.count', 1+issue.descendants.count do
1177 1177 assert copy.save
1178 1178 end
1179 1179 copy.reload
1180 1180 assert_equal %w(Child1 Child2), copy.children.map(&:subject).sort
1181 1181 child_copy = copy.children.detect {|c| c.subject == 'Child1'}
1182 1182 assert_equal %w(Child11), child_copy.children.map(&:subject).sort
1183 1183 assert_equal copy.author, child_copy.author
1184 1184 end
1185 1185
1186 1186 def test_copy_as_a_child_of_copied_issue_should_not_copy_itself
1187 1187 parent = Issue.generate!
1188 1188 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1189 1189 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1190 1190
1191 1191 copy = parent.reload.copy
1192 1192 copy.parent_issue_id = parent.id
1193 1193 copy.author = User.find(7)
1194 1194 assert_difference 'Issue.count', 3 do
1195 1195 assert copy.save
1196 1196 end
1197 1197 parent.reload
1198 1198 copy.reload
1199 1199 assert_equal parent, copy.parent
1200 1200 assert_equal 3, parent.children.count
1201 1201 assert_equal 5, parent.descendants.count
1202 1202 assert_equal 2, copy.children.count
1203 1203 assert_equal 2, copy.descendants.count
1204 1204 end
1205 1205
1206 1206 def test_copy_as_a_descendant_of_copied_issue_should_not_copy_itself
1207 1207 parent = Issue.generate!
1208 1208 child1 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 1')
1209 1209 child2 = Issue.generate!(:parent_issue_id => parent.id, :subject => 'Child 2')
1210 1210
1211 1211 copy = parent.reload.copy
1212 1212 copy.parent_issue_id = child1.id
1213 1213 copy.author = User.find(7)
1214 1214 assert_difference 'Issue.count', 3 do
1215 1215 assert copy.save
1216 1216 end
1217 1217 parent.reload
1218 1218 child1.reload
1219 1219 copy.reload
1220 1220 assert_equal child1, copy.parent
1221 1221 assert_equal 2, parent.children.count
1222 1222 assert_equal 5, parent.descendants.count
1223 1223 assert_equal 1, child1.children.count
1224 1224 assert_equal 3, child1.descendants.count
1225 1225 assert_equal 2, copy.children.count
1226 1226 assert_equal 2, copy.descendants.count
1227 1227 end
1228 1228
1229 1229 def test_copy_should_copy_subtasks_to_target_project
1230 1230 issue = Issue.generate_with_descendants!
1231 1231
1232 1232 copy = issue.copy(:project_id => 3)
1233 1233 assert_difference 'Issue.count', 1+issue.descendants.count do
1234 1234 assert copy.save
1235 1235 end
1236 1236 assert_equal [3], copy.reload.descendants.map(&:project_id).uniq
1237 1237 end
1238 1238
1239 1239 def test_copy_should_not_copy_subtasks_twice_when_saving_twice
1240 1240 issue = Issue.generate_with_descendants!
1241 1241
1242 1242 copy = issue.reload.copy
1243 1243 assert_difference 'Issue.count', 1+issue.descendants.count do
1244 1244 assert copy.save
1245 1245 assert copy.save
1246 1246 end
1247 1247 end
1248 1248
1249 1249 def test_should_not_call_after_project_change_on_creation
1250 1250 issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
1251 1251 :subject => 'Test', :author_id => 1)
1252 1252 issue.expects(:after_project_change).never
1253 1253 issue.save!
1254 1254 end
1255 1255
1256 1256 def test_should_not_call_after_project_change_on_update
1257 1257 issue = Issue.find(1)
1258 1258 issue.project = Project.find(1)
1259 1259 issue.subject = 'No project change'
1260 1260 issue.expects(:after_project_change).never
1261 1261 issue.save!
1262 1262 end
1263 1263
1264 1264 def test_should_call_after_project_change_on_project_change
1265 1265 issue = Issue.find(1)
1266 1266 issue.project = Project.find(2)
1267 1267 issue.expects(:after_project_change).once
1268 1268 issue.save!
1269 1269 end
1270 1270
1271 1271 def test_adding_journal_should_update_timestamp
1272 1272 issue = Issue.find(1)
1273 1273 updated_on_was = issue.updated_on
1274 1274
1275 1275 issue.init_journal(User.first, "Adding notes")
1276 1276 assert_difference 'Journal.count' do
1277 1277 assert issue.save
1278 1278 end
1279 1279 issue.reload
1280 1280
1281 1281 assert_not_equal updated_on_was, issue.updated_on
1282 1282 end
1283 1283
1284 1284 def test_should_close_duplicates
1285 1285 # Create 3 issues
1286 1286 issue1 = Issue.generate!
1287 1287 issue2 = Issue.generate!
1288 1288 issue3 = Issue.generate!
1289 1289
1290 1290 # 2 is a dupe of 1
1291 1291 IssueRelation.create!(:issue_from => issue2, :issue_to => issue1,
1292 1292 :relation_type => IssueRelation::TYPE_DUPLICATES)
1293 1293 # And 3 is a dupe of 2
1294 1294 # IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1295 1295 # :relation_type => IssueRelation::TYPE_DUPLICATES)
1296 1296 # And 3 is a dupe of 1 (circular duplicates)
1297 1297 IssueRelation.create!(:issue_from => issue3, :issue_to => issue1,
1298 1298 :relation_type => IssueRelation::TYPE_DUPLICATES)
1299 1299
1300 1300 assert issue1.reload.duplicates.include?(issue2)
1301 1301
1302 1302 # Closing issue 1
1303 1303 issue1.init_journal(User.first, "Closing issue1")
1304 1304 issue1.status = IssueStatus.where(:is_closed => true).first
1305 1305 assert issue1.save
1306 1306 # 2 and 3 should be also closed
1307 1307 assert issue2.reload.closed?
1308 1308 assert issue3.reload.closed?
1309 1309 end
1310 1310
1311 1311 def test_should_close_duplicates_with_private_notes
1312 1312 issue = Issue.generate!
1313 1313 duplicate = Issue.generate!
1314 1314 IssueRelation.create!(:issue_from => duplicate, :issue_to => issue,
1315 1315 :relation_type => IssueRelation::TYPE_DUPLICATES)
1316 1316 assert issue.reload.duplicates.include?(duplicate)
1317 1317
1318 1318 # Closing issue with private notes
1319 1319 issue.init_journal(User.first, "Private notes")
1320 1320 issue.private_notes = true
1321 1321 issue.status = IssueStatus.where(:is_closed => true).first
1322 1322 assert_save issue
1323 1323
1324 1324 duplicate.reload
1325 1325 assert journal = duplicate.journals.detect {|journal| journal.notes == "Private notes"}
1326 1326 assert_equal true, journal.private_notes
1327 1327 end
1328 1328
1329 1329 def test_should_not_close_duplicated_issue
1330 1330 issue1 = Issue.generate!
1331 1331 issue2 = Issue.generate!
1332 1332
1333 1333 # 2 is a dupe of 1
1334 1334 IssueRelation.create(:issue_from => issue2, :issue_to => issue1,
1335 1335 :relation_type => IssueRelation::TYPE_DUPLICATES)
1336 1336 # 2 is a dup of 1 but 1 is not a duplicate of 2
1337 1337 assert !issue2.reload.duplicates.include?(issue1)
1338 1338
1339 1339 # Closing issue 2
1340 1340 issue2.init_journal(User.first, "Closing issue2")
1341 1341 issue2.status = IssueStatus.where(:is_closed => true).first
1342 1342 assert issue2.save
1343 1343 # 1 should not be also closed
1344 1344 assert !issue1.reload.closed?
1345 1345 end
1346 1346
1347 1347 def test_assignable_versions
1348 1348 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1349 1349 :status_id => 1, :fixed_version_id => 1,
1350 1350 :subject => 'New issue')
1351 1351 assert_equal ['open'], issue.assignable_versions.collect(&:status).uniq
1352 1352 end
1353 1353
1354 1354 def test_should_not_be_able_to_assign_a_new_issue_to_a_closed_version
1355 1355 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1356 1356 :status_id => 1, :fixed_version_id => 1,
1357 1357 :subject => 'New issue')
1358 1358 assert !issue.save
1359 1359 assert_not_equal [], issue.errors[:fixed_version_id]
1360 1360 end
1361 1361
1362 1362 def test_should_not_be_able_to_assign_a_new_issue_to_a_locked_version
1363 1363 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1364 1364 :status_id => 1, :fixed_version_id => 2,
1365 1365 :subject => 'New issue')
1366 1366 assert !issue.save
1367 1367 assert_not_equal [], issue.errors[:fixed_version_id]
1368 1368 end
1369 1369
1370 1370 def test_should_be_able_to_assign_a_new_issue_to_an_open_version
1371 1371 issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 1,
1372 1372 :status_id => 1, :fixed_version_id => 3,
1373 1373 :subject => 'New issue')
1374 1374 assert issue.save
1375 1375 end
1376 1376
1377 1377 def test_should_be_able_to_update_an_issue_assigned_to_a_closed_version
1378 1378 issue = Issue.find(11)
1379 1379 assert_equal 'closed', issue.fixed_version.status
1380 1380 issue.subject = 'Subject changed'
1381 1381 assert issue.save
1382 1382 end
1383 1383
1384 1384 def test_should_not_be_able_to_reopen_an_issue_assigned_to_a_closed_version
1385 1385 issue = Issue.find(11)
1386 1386 issue.status_id = 1
1387 1387 assert !issue.save
1388 1388 assert_not_equal [], issue.errors[:base]
1389 1389 end
1390 1390
1391 1391 def test_should_be_able_to_reopen_and_reassign_an_issue_assigned_to_a_closed_version
1392 1392 issue = Issue.find(11)
1393 1393 issue.status_id = 1
1394 1394 issue.fixed_version_id = 3
1395 1395 assert issue.save
1396 1396 end
1397 1397
1398 1398 def test_should_be_able_to_reopen_an_issue_assigned_to_a_locked_version
1399 1399 issue = Issue.find(12)
1400 1400 assert_equal 'locked', issue.fixed_version.status
1401 1401 issue.status_id = 1
1402 1402 assert issue.save
1403 1403 end
1404 1404
1405 1405 def test_should_not_be_able_to_keep_unshared_version_when_changing_project
1406 1406 issue = Issue.find(2)
1407 1407 assert_equal 2, issue.fixed_version_id
1408 1408 issue.project_id = 3
1409 1409 assert_nil issue.fixed_version_id
1410 1410 issue.fixed_version_id = 2
1411 1411 assert !issue.save
1412 1412 assert_include 'Target version is not included in the list', issue.errors.full_messages
1413 1413 end
1414 1414
1415 1415 def test_should_keep_shared_version_when_changing_project
1416 1416 Version.find(2).update_attribute :sharing, 'tree'
1417 1417
1418 1418 issue = Issue.find(2)
1419 1419 assert_equal 2, issue.fixed_version_id
1420 1420 issue.project_id = 3
1421 1421 assert_equal 2, issue.fixed_version_id
1422 1422 assert issue.save
1423 1423 end
1424 1424
1425 1425 def test_allowed_target_projects_should_include_projects_with_issue_tracking_enabled
1426 1426 assert_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1427 1427 end
1428 1428
1429 1429 def test_allowed_target_projects_should_not_include_projects_with_issue_tracking_disabled
1430 1430 Project.find(2).disable_module! :issue_tracking
1431 1431 assert_not_include Project.find(2), Issue.allowed_target_projects(User.find(2))
1432 1432 end
1433 1433
1434 1434 def test_allowed_target_projects_should_not_include_projects_without_trackers
1435 1435 project = Project.generate!(:tracker_ids => [])
1436 1436 assert project.trackers.empty?
1437 1437 assert_not_include project, Issue.allowed_target_projects(User.find(1))
1438 1438 end
1439 1439
1440 1440 def test_move_to_another_project_with_same_category
1441 1441 issue = Issue.find(1)
1442 1442 issue.project = Project.find(2)
1443 1443 assert issue.save
1444 1444 issue.reload
1445 1445 assert_equal 2, issue.project_id
1446 1446 # Category changes
1447 1447 assert_equal 4, issue.category_id
1448 1448 # Make sure time entries were move to the target project
1449 1449 assert_equal 2, issue.time_entries.first.project_id
1450 1450 end
1451 1451
1452 1452 def test_move_to_another_project_without_same_category
1453 1453 issue = Issue.find(2)
1454 1454 issue.project = Project.find(2)
1455 1455 assert issue.save
1456 1456 issue.reload
1457 1457 assert_equal 2, issue.project_id
1458 1458 # Category cleared
1459 1459 assert_nil issue.category_id
1460 1460 end
1461 1461
1462 1462 def test_move_to_another_project_should_clear_fixed_version_when_not_shared
1463 1463 issue = Issue.find(1)
1464 1464 issue.update_attribute(:fixed_version_id, 1)
1465 1465 issue.project = Project.find(2)
1466 1466 assert issue.save
1467 1467 issue.reload
1468 1468 assert_equal 2, issue.project_id
1469 1469 # Cleared fixed_version
1470 1470 assert_equal nil, issue.fixed_version
1471 1471 end
1472 1472
1473 1473 def test_move_to_another_project_should_keep_fixed_version_when_shared_with_the_target_project
1474 1474 issue = Issue.find(1)
1475 1475 issue.update_attribute(:fixed_version_id, 4)
1476 1476 issue.project = Project.find(5)
1477 1477 assert issue.save
1478 1478 issue.reload
1479 1479 assert_equal 5, issue.project_id
1480 1480 # Keep fixed_version
1481 1481 assert_equal 4, issue.fixed_version_id
1482 1482 end
1483 1483
1484 1484 def test_move_to_another_project_should_clear_fixed_version_when_not_shared_with_the_target_project
1485 1485 issue = Issue.find(1)
1486 1486 issue.update_attribute(:fixed_version_id, 1)
1487 1487 issue.project = Project.find(5)
1488 1488 assert issue.save
1489 1489 issue.reload
1490 1490 assert_equal 5, issue.project_id
1491 1491 # Cleared fixed_version
1492 1492 assert_equal nil, issue.fixed_version
1493 1493 end
1494 1494
1495 1495 def test_move_to_another_project_should_keep_fixed_version_when_shared_systemwide
1496 1496 issue = Issue.find(1)
1497 1497 issue.update_attribute(:fixed_version_id, 7)
1498 1498 issue.project = Project.find(2)
1499 1499 assert issue.save
1500 1500 issue.reload
1501 1501 assert_equal 2, issue.project_id
1502 1502 # Keep fixed_version
1503 1503 assert_equal 7, issue.fixed_version_id
1504 1504 end
1505 1505
1506 1506 def test_move_to_another_project_should_keep_parent_if_valid
1507 1507 issue = Issue.find(1)
1508 1508 issue.update_attribute(:parent_issue_id, 2)
1509 1509 issue.project = Project.find(3)
1510 1510 assert issue.save
1511 1511 issue.reload
1512 1512 assert_equal 2, issue.parent_id
1513 1513 end
1514 1514
1515 1515 def test_move_to_another_project_should_clear_parent_if_not_valid
1516 1516 issue = Issue.find(1)
1517 1517 issue.update_attribute(:parent_issue_id, 2)
1518 1518 issue.project = Project.find(2)
1519 1519 assert issue.save
1520 1520 issue.reload
1521 1521 assert_nil issue.parent_id
1522 1522 end
1523 1523
1524 1524 def test_move_to_another_project_with_disabled_tracker
1525 1525 issue = Issue.find(1)
1526 1526 target = Project.find(2)
1527 1527 target.tracker_ids = [3]
1528 1528 target.save
1529 1529 issue.project = target
1530 1530 assert issue.save
1531 1531 issue.reload
1532 1532 assert_equal 2, issue.project_id
1533 1533 assert_equal 3, issue.tracker_id
1534 1534 end
1535 1535
1536 1536 def test_copy_to_the_same_project
1537 1537 issue = Issue.find(1)
1538 1538 copy = issue.copy
1539 1539 assert_difference 'Issue.count' do
1540 1540 copy.save!
1541 1541 end
1542 1542 assert_kind_of Issue, copy
1543 1543 assert_equal issue.project, copy.project
1544 1544 assert_equal "125", copy.custom_value_for(2).value
1545 1545 end
1546 1546
1547 1547 def test_copy_to_another_project_and_tracker
1548 1548 issue = Issue.find(1)
1549 1549 copy = issue.copy(:project_id => 3, :tracker_id => 2)
1550 1550 assert_difference 'Issue.count' do
1551 1551 copy.save!
1552 1552 end
1553 1553 copy.reload
1554 1554 assert_kind_of Issue, copy
1555 1555 assert_equal Project.find(3), copy.project
1556 1556 assert_equal Tracker.find(2), copy.tracker
1557 1557 # Custom field #2 is not associated with target tracker
1558 1558 assert_nil copy.custom_value_for(2)
1559 1559 end
1560 1560
1561 1561 test "#copy should not create a journal" do
1562 1562 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :assigned_to_id => 3}, :link => false)
1563 1563 copy.save!
1564 1564 assert_equal 0, copy.reload.journals.size
1565 1565 end
1566 1566
1567 1567 test "#copy should allow assigned_to changes" do
1568 1568 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :assigned_to_id => 3)
1569 1569 assert_equal 3, copy.assigned_to_id
1570 1570 end
1571 1571
1572 1572 test "#copy should allow status changes" do
1573 1573 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :status_id => 2)
1574 1574 assert_equal 2, copy.status_id
1575 1575 end
1576 1576
1577 1577 test "#copy should allow start date changes" do
1578 1578 date = Date.today
1579 1579 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :start_date => date)
1580 1580 assert_equal date, copy.start_date
1581 1581 end
1582 1582
1583 1583 test "#copy should allow due date changes" do
1584 1584 date = Date.today
1585 1585 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2, :due_date => date)
1586 1586 assert_equal date, copy.due_date
1587 1587 end
1588 1588
1589 1589 test "#copy should set current user as author" do
1590 1590 User.current = User.find(9)
1591 1591 copy = Issue.find(1).copy(:project_id => 3, :tracker_id => 2)
1592 1592 assert_equal User.current, copy.author
1593 1593 end
1594 1594
1595 1595 test "#copy should create a journal with notes" do
1596 1596 date = Date.today
1597 1597 notes = "Notes added when copying"
1598 1598 copy = Issue.find(1).copy({:project_id => 3, :tracker_id => 2, :start_date => date}, :link => false)
1599 1599 copy.init_journal(User.current, notes)
1600 1600 copy.save!
1601 1601
1602 1602 assert_equal 1, copy.journals.size
1603 1603 journal = copy.journals.first
1604 1604 assert_equal 0, journal.details.size
1605 1605 assert_equal notes, journal.notes
1606 1606 end
1607 1607
1608 1608 def test_valid_parent_project
1609 1609 issue = Issue.find(1)
1610 1610 issue_in_same_project = Issue.find(2)
1611 1611 issue_in_child_project = Issue.find(5)
1612 1612 issue_in_grandchild_project = Issue.generate!(:project_id => 6, :tracker_id => 1)
1613 1613 issue_in_other_child_project = Issue.find(6)
1614 1614 issue_in_different_tree = Issue.find(4)
1615 1615
1616 1616 with_settings :cross_project_subtasks => '' do
1617 1617 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1618 1618 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1619 1619 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1620 1620 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1621 1621 end
1622 1622
1623 1623 with_settings :cross_project_subtasks => 'system' do
1624 1624 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1625 1625 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1626 1626 assert_equal true, issue.valid_parent_project?(issue_in_different_tree)
1627 1627 end
1628 1628
1629 1629 with_settings :cross_project_subtasks => 'tree' do
1630 1630 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1631 1631 assert_equal true, issue.valid_parent_project?(issue_in_child_project)
1632 1632 assert_equal true, issue.valid_parent_project?(issue_in_grandchild_project)
1633 1633 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1634 1634
1635 1635 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_same_project)
1636 1636 assert_equal true, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1637 1637 end
1638 1638
1639 1639 with_settings :cross_project_subtasks => 'descendants' do
1640 1640 assert_equal true, issue.valid_parent_project?(issue_in_same_project)
1641 1641 assert_equal false, issue.valid_parent_project?(issue_in_child_project)
1642 1642 assert_equal false, issue.valid_parent_project?(issue_in_grandchild_project)
1643 1643 assert_equal false, issue.valid_parent_project?(issue_in_different_tree)
1644 1644
1645 1645 assert_equal true, issue_in_child_project.valid_parent_project?(issue)
1646 1646 assert_equal false, issue_in_child_project.valid_parent_project?(issue_in_other_child_project)
1647 1647 end
1648 1648 end
1649 1649
1650 1650 def test_recipients_should_include_previous_assignee
1651 1651 user = User.find(3)
1652 1652 user.members.update_all ["mail_notification = ?", false]
1653 1653 user.update_attribute :mail_notification, 'only_assigned'
1654 1654
1655 1655 issue = Issue.find(2)
1656 1656 issue.assigned_to = nil
1657 1657 assert_include user.mail, issue.recipients
1658 1658 issue.save!
1659 1659 assert !issue.recipients.include?(user.mail)
1660 1660 end
1661 1661
1662 1662 def test_recipients_should_not_include_users_that_cannot_view_the_issue
1663 1663 issue = Issue.find(12)
1664 1664 assert issue.recipients.include?(issue.author.mail)
1665 1665 # copy the issue to a private project
1666 1666 copy = issue.copy(:project_id => 5, :tracker_id => 2)
1667 1667 # author is not a member of project anymore
1668 1668 assert !copy.recipients.include?(copy.author.mail)
1669 1669 end
1670 1670
1671 1671 def test_recipients_should_include_the_assigned_group_members
1672 1672 group_member = User.generate!
1673 1673 group = Group.generate!
1674 1674 group.users << group_member
1675 1675
1676 1676 issue = Issue.find(12)
1677 1677 issue.assigned_to = group
1678 1678 assert issue.recipients.include?(group_member.mail)
1679 1679 end
1680 1680
1681 1681 def test_watcher_recipients_should_not_include_users_that_cannot_view_the_issue
1682 1682 user = User.find(3)
1683 1683 issue = Issue.find(9)
1684 1684 Watcher.create!(:user => user, :watchable => issue)
1685 1685 assert issue.watched_by?(user)
1686 1686 assert !issue.watcher_recipients.include?(user.mail)
1687 1687 end
1688 1688
1689 1689 def test_issue_destroy
1690 1690 Issue.find(1).destroy
1691 1691 assert_nil Issue.find_by_id(1)
1692 1692 assert_nil TimeEntry.find_by_issue_id(1)
1693 1693 end
1694 1694
1695 1695 def test_destroy_should_delete_time_entries_custom_values
1696 1696 issue = Issue.generate!
1697 1697 time_entry = TimeEntry.generate!(:issue => issue, :custom_field_values => {10 => '1'})
1698 1698
1699 1699 assert_difference 'CustomValue.where(:customized_type => "TimeEntry").count', -1 do
1700 1700 assert issue.destroy
1701 1701 end
1702 1702 end
1703 1703
1704 1704 def test_destroying_a_deleted_issue_should_not_raise_an_error
1705 1705 issue = Issue.find(1)
1706 1706 Issue.find(1).destroy
1707 1707
1708 1708 assert_nothing_raised do
1709 1709 assert_no_difference 'Issue.count' do
1710 1710 issue.destroy
1711 1711 end
1712 1712 assert issue.destroyed?
1713 1713 end
1714 1714 end
1715 1715
1716 1716 def test_destroying_a_stale_issue_should_not_raise_an_error
1717 1717 issue = Issue.find(1)
1718 1718 Issue.find(1).update_attribute :subject, "Updated"
1719 1719
1720 1720 assert_nothing_raised do
1721 1721 assert_difference 'Issue.count', -1 do
1722 1722 issue.destroy
1723 1723 end
1724 1724 assert issue.destroyed?
1725 1725 end
1726 1726 end
1727 1727
1728 1728 def test_blocked
1729 1729 blocked_issue = Issue.find(9)
1730 1730 blocking_issue = Issue.find(10)
1731 1731
1732 1732 assert blocked_issue.blocked?
1733 1733 assert !blocking_issue.blocked?
1734 1734 end
1735 1735
1736 1736 def test_blocked_issues_dont_allow_closed_statuses
1737 1737 blocked_issue = Issue.find(9)
1738 1738
1739 1739 allowed_statuses = blocked_issue.new_statuses_allowed_to(users(:users_002))
1740 1740 assert !allowed_statuses.empty?
1741 1741 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1742 1742 assert closed_statuses.empty?
1743 1743 end
1744 1744
1745 1745 def test_unblocked_issues_allow_closed_statuses
1746 1746 blocking_issue = Issue.find(10)
1747 1747
1748 1748 allowed_statuses = blocking_issue.new_statuses_allowed_to(users(:users_002))
1749 1749 assert !allowed_statuses.empty?
1750 1750 closed_statuses = allowed_statuses.select {|st| st.is_closed?}
1751 1751 assert !closed_statuses.empty?
1752 1752 end
1753 1753
1754 1754 def test_reschedule_an_issue_without_dates
1755 1755 with_settings :non_working_week_days => [] do
1756 1756 issue = Issue.new(:start_date => nil, :due_date => nil)
1757 1757 issue.reschedule_on '2012-10-09'.to_date
1758 1758 assert_equal '2012-10-09'.to_date, issue.start_date
1759 1759 assert_equal '2012-10-09'.to_date, issue.due_date
1760 1760 end
1761 1761
1762 1762 with_settings :non_working_week_days => %w(6 7) do
1763 1763 issue = Issue.new(:start_date => nil, :due_date => nil)
1764 1764 issue.reschedule_on '2012-10-09'.to_date
1765 1765 assert_equal '2012-10-09'.to_date, issue.start_date
1766 1766 assert_equal '2012-10-09'.to_date, issue.due_date
1767 1767
1768 1768 issue = Issue.new(:start_date => nil, :due_date => nil)
1769 1769 issue.reschedule_on '2012-10-13'.to_date
1770 1770 assert_equal '2012-10-15'.to_date, issue.start_date
1771 1771 assert_equal '2012-10-15'.to_date, issue.due_date
1772 1772 end
1773 1773 end
1774 1774
1775 1775 def test_reschedule_an_issue_with_start_date
1776 1776 with_settings :non_working_week_days => [] do
1777 1777 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1778 1778 issue.reschedule_on '2012-10-13'.to_date
1779 1779 assert_equal '2012-10-13'.to_date, issue.start_date
1780 1780 assert_equal '2012-10-13'.to_date, issue.due_date
1781 1781 end
1782 1782
1783 1783 with_settings :non_working_week_days => %w(6 7) do
1784 1784 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1785 1785 issue.reschedule_on '2012-10-11'.to_date
1786 1786 assert_equal '2012-10-11'.to_date, issue.start_date
1787 1787 assert_equal '2012-10-11'.to_date, issue.due_date
1788 1788
1789 1789 issue = Issue.new(:start_date => '2012-10-09', :due_date => nil)
1790 1790 issue.reschedule_on '2012-10-13'.to_date
1791 1791 assert_equal '2012-10-15'.to_date, issue.start_date
1792 1792 assert_equal '2012-10-15'.to_date, issue.due_date
1793 1793 end
1794 1794 end
1795 1795
1796 1796 def test_reschedule_an_issue_with_start_and_due_dates
1797 1797 with_settings :non_working_week_days => [] do
1798 1798 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-15')
1799 1799 issue.reschedule_on '2012-10-13'.to_date
1800 1800 assert_equal '2012-10-13'.to_date, issue.start_date
1801 1801 assert_equal '2012-10-19'.to_date, issue.due_date
1802 1802 end
1803 1803
1804 1804 with_settings :non_working_week_days => %w(6 7) do
1805 1805 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19') # 8 working days
1806 1806 issue.reschedule_on '2012-10-11'.to_date
1807 1807 assert_equal '2012-10-11'.to_date, issue.start_date
1808 1808 assert_equal '2012-10-23'.to_date, issue.due_date
1809 1809
1810 1810 issue = Issue.new(:start_date => '2012-10-09', :due_date => '2012-10-19')
1811 1811 issue.reschedule_on '2012-10-13'.to_date
1812 1812 assert_equal '2012-10-15'.to_date, issue.start_date
1813 1813 assert_equal '2012-10-25'.to_date, issue.due_date
1814 1814 end
1815 1815 end
1816 1816
1817 1817 def test_rescheduling_an_issue_to_a_later_due_date_should_reschedule_following_issue
1818 1818 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1819 1819 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1820 1820 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1821 1821 :relation_type => IssueRelation::TYPE_PRECEDES)
1822 1822 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1823 1823
1824 1824 issue1.reload
1825 1825 issue1.due_date = '2012-10-23'
1826 1826 issue1.save!
1827 1827 issue2.reload
1828 1828 assert_equal Date.parse('2012-10-24'), issue2.start_date
1829 1829 assert_equal Date.parse('2012-10-26'), issue2.due_date
1830 1830 end
1831 1831
1832 1832 def test_rescheduling_an_issue_to_an_earlier_due_date_should_reschedule_following_issue
1833 1833 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1834 1834 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1835 1835 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1836 1836 :relation_type => IssueRelation::TYPE_PRECEDES)
1837 1837 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1838 1838
1839 1839 issue1.reload
1840 1840 issue1.start_date = '2012-09-17'
1841 1841 issue1.due_date = '2012-09-18'
1842 1842 issue1.save!
1843 1843 issue2.reload
1844 1844 assert_equal Date.parse('2012-09-19'), issue2.start_date
1845 1845 assert_equal Date.parse('2012-09-21'), issue2.due_date
1846 1846 end
1847 1847
1848 1848 def test_rescheduling_reschedule_following_issue_earlier_should_consider_other_preceding_issues
1849 1849 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1850 1850 issue2 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1851 1851 issue3 = Issue.generate!(:start_date => '2012-10-01', :due_date => '2012-10-02')
1852 1852 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1853 1853 :relation_type => IssueRelation::TYPE_PRECEDES)
1854 1854 IssueRelation.create!(:issue_from => issue3, :issue_to => issue2,
1855 1855 :relation_type => IssueRelation::TYPE_PRECEDES)
1856 1856 assert_equal Date.parse('2012-10-18'), issue2.reload.start_date
1857 1857
1858 1858 issue1.reload
1859 1859 issue1.start_date = '2012-09-17'
1860 1860 issue1.due_date = '2012-09-18'
1861 1861 issue1.save!
1862 1862 issue2.reload
1863 1863 # Issue 2 must start after Issue 3
1864 1864 assert_equal Date.parse('2012-10-03'), issue2.start_date
1865 1865 assert_equal Date.parse('2012-10-05'), issue2.due_date
1866 1866 end
1867 1867
1868 1868 def test_rescheduling_a_stale_issue_should_not_raise_an_error
1869 1869 with_settings :non_working_week_days => [] do
1870 1870 stale = Issue.find(1)
1871 1871 issue = Issue.find(1)
1872 1872 issue.subject = "Updated"
1873 1873 issue.save!
1874 1874 date = 10.days.from_now.to_date
1875 1875 assert_nothing_raised do
1876 1876 stale.reschedule_on!(date)
1877 1877 end
1878 1878 assert_equal date, stale.reload.start_date
1879 1879 end
1880 1880 end
1881 1881
1882 1882 def test_child_issue_should_consider_parent_soonest_start_on_create
1883 1883 set_language_if_valid 'en'
1884 1884 issue1 = Issue.generate!(:start_date => '2012-10-15', :due_date => '2012-10-17')
1885 1885 issue2 = Issue.generate!(:start_date => '2012-10-18', :due_date => '2012-10-20')
1886 1886 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2,
1887 1887 :relation_type => IssueRelation::TYPE_PRECEDES)
1888 1888 issue1.reload
1889 1889 issue2.reload
1890 1890 assert_equal Date.parse('2012-10-18'), issue2.start_date
1891 1891
1892 1892 with_settings :date_format => '%m/%d/%Y' do
1893 1893 child = Issue.new(:parent_issue_id => issue2.id, :start_date => '2012-10-16',
1894 1894 :project_id => 1, :tracker_id => 1, :status_id => 1, :subject => 'Child', :author_id => 1)
1895 1895 assert !child.valid?
1896 1896 assert_include 'Start date cannot be earlier than 10/18/2012 because of preceding issues', child.errors.full_messages
1897 1897 assert_equal Date.parse('2012-10-18'), child.soonest_start
1898 1898 child.start_date = '2012-10-18'
1899 1899 assert child.save
1900 1900 end
1901 1901 end
1902 1902
1903 1903 def test_setting_parent_to_a_an_issue_that_precedes_should_not_validate
1904 1904 # tests that 3 cannot have 1 as parent:
1905 1905 #
1906 1906 # 1 -> 2 -> 3
1907 1907 #
1908 1908 set_language_if_valid 'en'
1909 1909 issue1 = Issue.generate!
1910 1910 issue2 = Issue.generate!
1911 1911 issue3 = Issue.generate!
1912 1912 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
1913 1913 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
1914 1914 issue3.reload
1915 1915 issue3.parent_issue_id = issue1.id
1916 1916 assert !issue3.valid?
1917 1917 assert_include 'Parent task is invalid', issue3.errors.full_messages
1918 1918 end
1919 1919
1920 1920 def test_setting_parent_to_a_an_issue_that_follows_should_not_validate
1921 1921 # tests that 1 cannot have 3 as parent:
1922 1922 #
1923 1923 # 1 -> 2 -> 3
1924 1924 #
1925 1925 set_language_if_valid 'en'
1926 1926 issue1 = Issue.generate!
1927 1927 issue2 = Issue.generate!
1928 1928 issue3 = Issue.generate!
1929 1929 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
1930 1930 IssueRelation.create!(:issue_from => issue2, :issue_to => issue3, :relation_type => IssueRelation::TYPE_PRECEDES)
1931 1931 issue1.reload
1932 1932 issue1.parent_issue_id = issue3.id
1933 1933 assert !issue1.valid?
1934 1934 assert_include 'Parent task is invalid', issue1.errors.full_messages
1935 1935 end
1936 1936
1937 1937 def test_setting_parent_to_a_an_issue_that_precedes_through_hierarchy_should_not_validate
1938 1938 # tests that 4 cannot have 1 as parent:
1939 1939 # changing the due date of 4 would update the end date of 1 which would reschedule 2
1940 1940 # which would change the end date of 3 which would reschedule 4 and so on...
1941 1941 #
1942 1942 # 3 -> 4
1943 1943 # ^
1944 1944 # 1 -> 2
1945 1945 #
1946 1946 set_language_if_valid 'en'
1947 1947 issue1 = Issue.generate!
1948 1948 issue2 = Issue.generate!
1949 1949 IssueRelation.create!(:issue_from => issue1, :issue_to => issue2, :relation_type => IssueRelation::TYPE_PRECEDES)
1950 1950 issue3 = Issue.generate!
1951 1951 issue2.reload
1952 1952 issue2.parent_issue_id = issue3.id
1953 1953 issue2.save!
1954 1954 issue4 = Issue.generate!
1955 1955 IssueRelation.create!(:issue_from => issue3, :issue_to => issue4, :relation_type => IssueRelation::TYPE_PRECEDES)
1956 1956 issue4.reload
1957 1957 issue4.parent_issue_id = issue1.id
1958 1958 assert !issue4.valid?
1959 1959 assert_include 'Parent task is invalid', issue4.errors.full_messages
1960 1960 end
1961 1961
1962 1962 def test_issue_and_following_issue_should_be_able_to_be_moved_to_the_same_parent
1963 1963 set_language_if_valid 'en'
1964 1964 issue1 = Issue.generate!
1965 1965 issue2 = Issue.generate!
1966 1966 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_FOLLOWS)
1967 1967 parent = Issue.generate!
1968 1968 issue1.reload.parent_issue_id = parent.id
1969 1969 assert_save issue1
1970 1970 parent.reload
1971 1971 issue2.reload.parent_issue_id = parent.id
1972 1972 assert_save issue2
1973 1973 assert IssueRelation.exists?(relation.id)
1974 1974 end
1975 1975
1976 1976 def test_issue_and_preceding_issue_should_be_able_to_be_moved_to_the_same_parent
1977 1977 set_language_if_valid 'en'
1978 1978 issue1 = Issue.generate!
1979 1979 issue2 = Issue.generate!
1980 1980 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_PRECEDES)
1981 1981 parent = Issue.generate!
1982 1982 issue1.reload.parent_issue_id = parent.id
1983 1983 assert_save issue1
1984 1984 parent.reload
1985 1985 issue2.reload.parent_issue_id = parent.id
1986 1986 assert_save issue2
1987 1987 assert IssueRelation.exists?(relation.id)
1988 1988 end
1989 1989
1990 1990 def test_issue_and_blocked_issue_should_be_able_to_be_moved_to_the_same_parent
1991 1991 set_language_if_valid 'en'
1992 1992 issue1 = Issue.generate!
1993 1993 issue2 = Issue.generate!
1994 1994 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKED)
1995 1995 parent = Issue.generate!
1996 1996 issue1.reload.parent_issue_id = parent.id
1997 1997 assert_save issue1
1998 1998 parent.reload
1999 1999 issue2.reload.parent_issue_id = parent.id
2000 2000 assert_save issue2
2001 2001 assert IssueRelation.exists?(relation.id)
2002 2002 end
2003 2003
2004 2004 def test_issue_and_blocking_issue_should_be_able_to_be_moved_to_the_same_parent
2005 2005 set_language_if_valid 'en'
2006 2006 issue1 = Issue.generate!
2007 2007 issue2 = Issue.generate!
2008 2008 relation = IssueRelation.create!(:issue_from => issue2, :issue_to => issue1, :relation_type => IssueRelation::TYPE_BLOCKS)
2009 2009 parent = Issue.generate!
2010 2010 issue1.reload.parent_issue_id = parent.id
2011 2011 assert_save issue1
2012 2012 parent.reload
2013 2013 issue2.reload.parent_issue_id = parent.id
2014 2014 assert_save issue2
2015 2015 assert IssueRelation.exists?(relation.id)
2016 2016 end
2017 2017
2018 2018 def test_issue_copy_should_be_able_to_be_moved_to_the_same_parent_as_copied_issue
2019 2019 issue = Issue.generate!
2020 2020 parent = Issue.generate!
2021 2021 issue.parent_issue_id = parent.id
2022 2022 issue.save!
2023 2023 issue.reload
2024 2024
2025 2025 copy = Issue.new.copy_from(issue, :link => true)
2026 2026 relation = new_record(IssueRelation) do
2027 2027 copy.save!
2028 2028 end
2029 2029
2030 2030 copy.parent_issue_id = parent.id
2031 2031 assert_save copy
2032 2032 assert IssueRelation.exists?(relation.id)
2033 2033 end
2034 2034
2035 2035 def test_overdue
2036 2036 assert Issue.new(:due_date => 1.day.ago.to_date).overdue?
2037 2037 assert !Issue.new(:due_date => Date.today).overdue?
2038 2038 assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
2039 2039 assert !Issue.new(:due_date => nil).overdue?
2040 2040 assert !Issue.new(:due_date => 1.day.ago.to_date,
2041 2041 :status => IssueStatus.where(:is_closed => true).first
2042 2042 ).overdue?
2043 2043 end
2044 2044
2045 2045 test "#behind_schedule? should be false if the issue has no start_date" do
2046 2046 assert !Issue.new(:start_date => nil,
2047 2047 :due_date => 1.day.from_now.to_date,
2048 2048 :done_ratio => 0).behind_schedule?
2049 2049 end
2050 2050
2051 2051 test "#behind_schedule? should be false if the issue has no end_date" do
2052 2052 assert !Issue.new(:start_date => 1.day.from_now.to_date,
2053 2053 :due_date => nil,
2054 2054 :done_ratio => 0).behind_schedule?
2055 2055 end
2056 2056
2057 2057 test "#behind_schedule? should be false if the issue has more done than it's calendar time" do
2058 2058 assert !Issue.new(:start_date => 50.days.ago.to_date,
2059 2059 :due_date => 50.days.from_now.to_date,
2060 2060 :done_ratio => 90).behind_schedule?
2061 2061 end
2062 2062
2063 2063 test "#behind_schedule? should be true if the issue hasn't been started at all" do
2064 2064 assert Issue.new(:start_date => 1.day.ago.to_date,
2065 2065 :due_date => 1.day.from_now.to_date,
2066 2066 :done_ratio => 0).behind_schedule?
2067 2067 end
2068 2068
2069 2069 test "#behind_schedule? should be true if the issue has used more calendar time than it's done ratio" do
2070 2070 assert Issue.new(:start_date => 100.days.ago.to_date,
2071 2071 :due_date => Date.today,
2072 2072 :done_ratio => 90).behind_schedule?
2073 2073 end
2074 2074
2075 2075 test "#assignable_users should be Users" do
2076 2076 assert_kind_of User, Issue.find(1).assignable_users.first
2077 2077 end
2078 2078
2079 2079 test "#assignable_users should include the issue author" do
2080 2080 non_project_member = User.generate!
2081 2081 issue = Issue.generate!(:author => non_project_member)
2082 2082
2083 2083 assert issue.assignable_users.include?(non_project_member)
2084 2084 end
2085 2085
2086 2086 def test_assignable_users_should_not_include_anonymous_user
2087 2087 issue = Issue.generate!(:author => User.anonymous)
2088 2088
2089 2089 assert !issue.assignable_users.include?(User.anonymous)
2090 2090 end
2091 2091
2092 2092 def test_assignable_users_should_not_include_locked_user
2093 2093 user = User.generate!
2094 2094 issue = Issue.generate!(:author => user)
2095 2095 user.lock!
2096 2096
2097 2097 assert !issue.assignable_users.include?(user)
2098 2098 end
2099 2099
2100 2100 test "#assignable_users should include the current assignee" do
2101 2101 user = User.generate!
2102 2102 issue = Issue.generate!(:assigned_to => user)
2103 2103 user.lock!
2104 2104
2105 2105 assert Issue.find(issue.id).assignable_users.include?(user)
2106 2106 end
2107 2107
2108 2108 test "#assignable_users should not show the issue author twice" do
2109 2109 assignable_user_ids = Issue.find(1).assignable_users.collect(&:id)
2110 2110 assert_equal 2, assignable_user_ids.length
2111 2111
2112 2112 assignable_user_ids.each do |user_id|
2113 2113 assert_equal 1, assignable_user_ids.select {|i| i == user_id}.length,
2114 2114 "User #{user_id} appears more or less than once"
2115 2115 end
2116 2116 end
2117 2117
2118 2118 test "#assignable_users with issue_group_assignment should include groups" do
2119 2119 issue = Issue.new(:project => Project.find(2))
2120 2120
2121 2121 with_settings :issue_group_assignment => '1' do
2122 2122 assert_equal %w(Group User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2123 2123 assert issue.assignable_users.include?(Group.find(11))
2124 2124 end
2125 2125 end
2126 2126
2127 2127 test "#assignable_users without issue_group_assignment should not include groups" do
2128 2128 issue = Issue.new(:project => Project.find(2))
2129 2129
2130 2130 with_settings :issue_group_assignment => '0' do
2131 2131 assert_equal %w(User), issue.assignable_users.map {|a| a.class.name}.uniq.sort
2132 2132 assert !issue.assignable_users.include?(Group.find(11))
2133 2133 end
2134 2134 end
2135 2135
2136 2136 def test_assignable_users_should_not_include_builtin_groups
2137 2137 Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [1])
2138 2138 Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [1])
2139 2139 issue = Issue.new(:project => Project.find(1))
2140 2140
2141 2141 with_settings :issue_group_assignment => '1' do
2142 2142 assert_nil issue.assignable_users.detect {|u| u.is_a?(GroupBuiltin)}
2143 2143 end
2144 2144 end
2145 2145
2146 2146 def test_create_should_send_email_notification
2147 2147 ActionMailer::Base.deliveries.clear
2148 2148 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2149 2149 :author_id => 3, :status_id => 1,
2150 2150 :priority => IssuePriority.all.first,
2151 2151 :subject => 'test_create', :estimated_hours => '1:30')
2152 2152 with_settings :notified_events => %w(issue_added) do
2153 2153 assert issue.save
2154 2154 assert_equal 1, ActionMailer::Base.deliveries.size
2155 2155 end
2156 2156 end
2157 2157
2158 2158 def test_create_should_send_one_email_notification_with_both_settings
2159 2159 ActionMailer::Base.deliveries.clear
2160 2160 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2161 2161 :author_id => 3, :status_id => 1,
2162 2162 :priority => IssuePriority.all.first,
2163 2163 :subject => 'test_create', :estimated_hours => '1:30')
2164 2164 with_settings :notified_events => %w(issue_added issue_updated) do
2165 2165 assert issue.save
2166 2166 assert_equal 1, ActionMailer::Base.deliveries.size
2167 2167 end
2168 2168 end
2169 2169
2170 2170 def test_create_should_not_send_email_notification_with_no_setting
2171 2171 ActionMailer::Base.deliveries.clear
2172 2172 issue = Issue.new(:project_id => 1, :tracker_id => 1,
2173 2173 :author_id => 3, :status_id => 1,
2174 2174 :priority => IssuePriority.all.first,
2175 2175 :subject => 'test_create', :estimated_hours => '1:30')
2176 2176 with_settings :notified_events => [] do
2177 2177 assert issue.save
2178 2178 assert_equal 0, ActionMailer::Base.deliveries.size
2179 2179 end
2180 2180 end
2181 2181
2182 2182 def test_update_should_notify_previous_assignee
2183 2183 ActionMailer::Base.deliveries.clear
2184 2184 user = User.find(3)
2185 2185 user.members.update_all ["mail_notification = ?", false]
2186 2186 user.update_attribute :mail_notification, 'only_assigned'
2187 2187
2188 2188 with_settings :notified_events => %w(issue_updated) do
2189 2189 issue = Issue.find(2)
2190 2190 issue.init_journal User.find(1)
2191 2191 issue.assigned_to = nil
2192 2192 issue.save!
2193 2193 assert_include user.mail, ActionMailer::Base.deliveries.last.bcc
2194 2194 end
2195 2195 end
2196 2196
2197 2197 def test_stale_issue_should_not_send_email_notification
2198 2198 ActionMailer::Base.deliveries.clear
2199 2199 issue = Issue.find(1)
2200 2200 stale = Issue.find(1)
2201 2201
2202 2202 issue.init_journal(User.find(1))
2203 2203 issue.subject = 'Subjet update'
2204 2204 with_settings :notified_events => %w(issue_updated) do
2205 2205 assert issue.save
2206 2206 assert_equal 1, ActionMailer::Base.deliveries.size
2207 2207 ActionMailer::Base.deliveries.clear
2208 2208
2209 2209 stale.init_journal(User.find(1))
2210 2210 stale.subject = 'Another subjet update'
2211 2211 assert_raise ActiveRecord::StaleObjectError do
2212 2212 stale.save
2213 2213 end
2214 2214 assert ActionMailer::Base.deliveries.empty?
2215 2215 end
2216 2216 end
2217 2217
2218 2218 def test_journalized_description
2219 2219 IssueCustomField.delete_all
2220 2220
2221 2221 i = Issue.first
2222 2222 old_description = i.description
2223 2223 new_description = "This is the new description"
2224 2224
2225 2225 i.init_journal(User.find(2))
2226 2226 i.description = new_description
2227 2227 assert_difference 'Journal.count', 1 do
2228 2228 assert_difference 'JournalDetail.count', 1 do
2229 2229 i.save!
2230 2230 end
2231 2231 end
2232 2232
2233 2233 detail = JournalDetail.order('id DESC').first
2234 2234 assert_equal i, detail.journal.journalized
2235 2235 assert_equal 'attr', detail.property
2236 2236 assert_equal 'description', detail.prop_key
2237 2237 assert_equal old_description, detail.old_value
2238 2238 assert_equal new_description, detail.value
2239 2239 end
2240 2240
2241 2241 def test_blank_descriptions_should_not_be_journalized
2242 2242 IssueCustomField.delete_all
2243 2243 Issue.where(:id => 1).update_all("description = NULL")
2244 2244
2245 2245 i = Issue.find(1)
2246 2246 i.init_journal(User.find(2))
2247 2247 i.subject = "blank description"
2248 2248 i.description = "\r\n"
2249 2249
2250 2250 assert_difference 'Journal.count', 1 do
2251 2251 assert_difference 'JournalDetail.count', 1 do
2252 2252 i.save!
2253 2253 end
2254 2254 end
2255 2255 end
2256 2256
2257 2257 def test_journalized_multi_custom_field
2258 2258 field = IssueCustomField.create!(:name => 'filter', :field_format => 'list',
2259 2259 :is_filter => true, :is_for_all => true,
2260 2260 :tracker_ids => [1],
2261 2261 :possible_values => ['value1', 'value2', 'value3'],
2262 2262 :multiple => true)
2263 2263
2264 2264 issue = Issue.create!(:project_id => 1, :tracker_id => 1,
2265 2265 :subject => 'Test', :author_id => 1)
2266 2266
2267 2267 assert_difference 'Journal.count' do
2268 2268 assert_difference 'JournalDetail.count' do
2269 2269 issue.init_journal(User.first)
2270 2270 issue.custom_field_values = {field.id => ['value1']}
2271 2271 issue.save!
2272 2272 end
2273 2273 assert_difference 'JournalDetail.count' do
2274 2274 issue.init_journal(User.first)
2275 2275 issue.custom_field_values = {field.id => ['value1', 'value2']}
2276 2276 issue.save!
2277 2277 end
2278 2278 assert_difference 'JournalDetail.count', 2 do
2279 2279 issue.init_journal(User.first)
2280 2280 issue.custom_field_values = {field.id => ['value3', 'value2']}
2281 2281 issue.save!
2282 2282 end
2283 2283 assert_difference 'JournalDetail.count', 2 do
2284 2284 issue.init_journal(User.first)
2285 2285 issue.custom_field_values = {field.id => nil}
2286 2286 issue.save!
2287 2287 end
2288 2288 end
2289 2289 end
2290 2290
2291 2291 def test_description_eol_should_be_normalized
2292 2292 i = Issue.new(:description => "CR \r LF \n CRLF \r\n")
2293 2293 assert_equal "CR \r\n LF \r\n CRLF \r\n", i.description
2294 2294 end
2295 2295
2296 2296 def test_saving_twice_should_not_duplicate_journal_details
2297 2297 i = Issue.first
2298 2298 i.init_journal(User.find(2), 'Some notes')
2299 2299 # initial changes
2300 2300 i.subject = 'New subject'
2301 2301 i.done_ratio = i.done_ratio + 10
2302 2302 assert_difference 'Journal.count' do
2303 2303 assert i.save
2304 2304 end
2305 2305 # 1 more change
2306 2306 i.priority = IssuePriority.where("id <> ?", i.priority_id).first
2307 2307 assert_no_difference 'Journal.count' do
2308 2308 assert_difference 'JournalDetail.count', 1 do
2309 2309 i.save
2310 2310 end
2311 2311 end
2312 2312 # no more change
2313 2313 assert_no_difference 'Journal.count' do
2314 2314 assert_no_difference 'JournalDetail.count' do
2315 2315 i.save
2316 2316 end
2317 2317 end
2318 2318 end
2319 2319
2320 2320 test "#done_ratio should use the issue_status according to Setting.issue_done_ratio" do
2321 2321 @issue = Issue.find(1)
2322 2322 @issue_status = IssueStatus.find(1)
2323 2323 @issue_status.update_attribute(:default_done_ratio, 50)
2324 2324 @issue2 = Issue.find(2)
2325 2325 @issue_status2 = IssueStatus.find(2)
2326 2326 @issue_status2.update_attribute(:default_done_ratio, 0)
2327 2327
2328 2328 with_settings :issue_done_ratio => 'issue_field' do
2329 2329 assert_equal 0, @issue.done_ratio
2330 2330 assert_equal 30, @issue2.done_ratio
2331 2331 end
2332 2332
2333 2333 with_settings :issue_done_ratio => 'issue_status' do
2334 2334 assert_equal 50, @issue.done_ratio
2335 2335 assert_equal 0, @issue2.done_ratio
2336 2336 end
2337 2337 end
2338 2338
2339 2339 test "#update_done_ratio_from_issue_status should update done_ratio according to Setting.issue_done_ratio" do
2340 2340 @issue = Issue.find(1)
2341 2341 @issue_status = IssueStatus.find(1)
2342 2342 @issue_status.update_attribute(:default_done_ratio, 50)
2343 2343 @issue2 = Issue.find(2)
2344 2344 @issue_status2 = IssueStatus.find(2)
2345 2345 @issue_status2.update_attribute(:default_done_ratio, 0)
2346 2346
2347 2347 with_settings :issue_done_ratio => 'issue_field' do
2348 2348 @issue.update_done_ratio_from_issue_status
2349 2349 @issue2.update_done_ratio_from_issue_status
2350 2350
2351 2351 assert_equal 0, @issue.read_attribute(:done_ratio)
2352 2352 assert_equal 30, @issue2.read_attribute(:done_ratio)
2353 2353 end
2354 2354
2355 2355 with_settings :issue_done_ratio => 'issue_status' do
2356 2356 @issue.update_done_ratio_from_issue_status
2357 2357 @issue2.update_done_ratio_from_issue_status
2358 2358
2359 2359 assert_equal 50, @issue.read_attribute(:done_ratio)
2360 2360 assert_equal 0, @issue2.read_attribute(:done_ratio)
2361 2361 end
2362 2362 end
2363 2363
2364 2364 test "#by_tracker" do
2365 2365 User.current = User.anonymous
2366 2366 groups = Issue.by_tracker(Project.find(1))
2367 2367 assert_equal 3, groups.count
2368 2368 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2369 2369 end
2370 2370
2371 2371 test "#by_version" do
2372 2372 User.current = User.anonymous
2373 2373 groups = Issue.by_version(Project.find(1))
2374 2374 assert_equal 3, groups.count
2375 2375 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2376 2376 end
2377 2377
2378 2378 test "#by_priority" do
2379 2379 User.current = User.anonymous
2380 2380 groups = Issue.by_priority(Project.find(1))
2381 2381 assert_equal 4, groups.count
2382 2382 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2383 2383 end
2384 2384
2385 2385 test "#by_category" do
2386 2386 User.current = User.anonymous
2387 2387 groups = Issue.by_category(Project.find(1))
2388 2388 assert_equal 2, groups.count
2389 2389 assert_equal 3, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2390 2390 end
2391 2391
2392 2392 test "#by_assigned_to" do
2393 2393 User.current = User.anonymous
2394 2394 groups = Issue.by_assigned_to(Project.find(1))
2395 2395 assert_equal 2, groups.count
2396 2396 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2397 2397 end
2398 2398
2399 2399 test "#by_author" do
2400 2400 User.current = User.anonymous
2401 2401 groups = Issue.by_author(Project.find(1))
2402 2402 assert_equal 4, groups.count
2403 2403 assert_equal 7, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2404 2404 end
2405 2405
2406 2406 test "#by_subproject" do
2407 2407 User.current = User.anonymous
2408 2408 groups = Issue.by_subproject(Project.find(1))
2409 2409 # Private descendant not visible
2410 2410 assert_equal 1, groups.count
2411 2411 assert_equal 2, groups.inject(0) {|sum, group| sum + group['total'].to_i}
2412 2412 end
2413 2413
2414 2414 def test_recently_updated_scope
2415 2415 #should return the last updated issue
2416 2416 assert_equal Issue.reorder("updated_on DESC").first, Issue.recently_updated.limit(1).first
2417 2417 end
2418 2418
2419 2419 def test_on_active_projects_scope
2420 2420 assert Project.find(2).archive
2421 2421
2422 2422 before = Issue.on_active_project.length
2423 2423 # test inclusion to results
2424 2424 issue = Issue.generate!(:tracker => Project.find(2).trackers.first)
2425 2425 assert_equal before + 1, Issue.on_active_project.length
2426 2426
2427 2427 # Move to an archived project
2428 2428 issue.project = Project.find(2)
2429 2429 assert issue.save
2430 2430 assert_equal before, Issue.on_active_project.length
2431 2431 end
2432 2432
2433 2433 test "Issue#recipients should include project recipients" do
2434 2434 issue = Issue.generate!
2435 2435 assert issue.project.recipients.present?
2436 2436 issue.project.recipients.each do |project_recipient|
2437 2437 assert issue.recipients.include?(project_recipient)
2438 2438 end
2439 2439 end
2440 2440
2441 2441 test "Issue#recipients should include the author if the author is active" do
2442 2442 issue = Issue.generate!(:author => User.generate!)
2443 2443 assert issue.author, "No author set for Issue"
2444 2444 assert issue.recipients.include?(issue.author.mail)
2445 2445 end
2446 2446
2447 2447 test "Issue#recipients should include the assigned to user if the assigned to user is active" do
2448 2448 issue = Issue.generate!(:assigned_to => User.generate!)
2449 2449 assert issue.assigned_to, "No assigned_to set for Issue"
2450 2450 assert issue.recipients.include?(issue.assigned_to.mail)
2451 2451 end
2452 2452
2453 2453 test "Issue#recipients should not include users who opt out of all email" do
2454 2454 issue = Issue.generate!(:author => User.generate!)
2455 2455 issue.author.update_attribute(:mail_notification, :none)
2456 2456 assert !issue.recipients.include?(issue.author.mail)
2457 2457 end
2458 2458
2459 2459 test "Issue#recipients should not include the issue author if they are only notified of assigned issues" do
2460 2460 issue = Issue.generate!(:author => User.generate!)
2461 2461 issue.author.update_attribute(:mail_notification, :only_assigned)
2462 2462 assert !issue.recipients.include?(issue.author.mail)
2463 2463 end
2464 2464
2465 2465 test "Issue#recipients should not include the assigned user if they are only notified of owned issues" do
2466 2466 issue = Issue.generate!(:assigned_to => User.generate!)
2467 2467 issue.assigned_to.update_attribute(:mail_notification, :only_owner)
2468 2468 assert !issue.recipients.include?(issue.assigned_to.mail)
2469 2469 end
2470 2470
2471 2471 def test_last_journal_id_with_journals_should_return_the_journal_id
2472 2472 assert_equal 2, Issue.find(1).last_journal_id
2473 2473 end
2474 2474
2475 2475 def test_last_journal_id_without_journals_should_return_nil
2476 2476 assert_nil Issue.find(3).last_journal_id
2477 2477 end
2478 2478
2479 2479 def test_journals_after_should_return_journals_with_greater_id
2480 2480 assert_equal [Journal.find(2)], Issue.find(1).journals_after('1')
2481 2481 assert_equal [], Issue.find(1).journals_after('2')
2482 2482 end
2483 2483
2484 2484 def test_journals_after_with_blank_arg_should_return_all_journals
2485 2485 assert_equal [Journal.find(1), Journal.find(2)], Issue.find(1).journals_after('')
2486 2486 end
2487 2487
2488 2488 def test_css_classes_should_include_tracker
2489 2489 issue = Issue.new(:tracker => Tracker.find(2))
2490 2490 classes = issue.css_classes.split(' ')
2491 2491 assert_include 'tracker-2', classes
2492 2492 end
2493 2493
2494 2494 def test_css_classes_should_include_priority
2495 2495 issue = Issue.new(:priority => IssuePriority.find(8))
2496 2496 classes = issue.css_classes.split(' ')
2497 2497 assert_include 'priority-8', classes
2498 2498 assert_include 'priority-highest', classes
2499 2499 end
2500 2500
2501 2501 def test_css_classes_should_include_user_and_group_assignment
2502 2502 project = Project.first
2503 2503 user = User.generate!
2504 2504 group = Group.generate!
2505 2505 Member.create!(:principal => group, :project => project, :role_ids => [1, 2])
2506 2506 group.users << user
2507 2507 assert user.member_of?(project)
2508 2508 issue1 = Issue.generate(:assigned_to_id => group.id)
2509 2509 assert_include 'assigned-to-my-group', issue1.css_classes(user)
2510 2510 assert_not_include 'assigned-to-me', issue1.css_classes(user)
2511 2511 issue2 = Issue.generate(:assigned_to_id => user.id)
2512 2512 assert_not_include 'assigned-to-my-group', issue2.css_classes(user)
2513 2513 assert_include 'assigned-to-me', issue2.css_classes(user)
2514 2514 end
2515 2515
2516 2516 def test_save_attachments_with_hash_should_save_attachments_in_keys_order
2517 2517 set_tmp_attachments_directory
2518 2518 issue = Issue.generate!
2519 2519 issue.save_attachments({
2520 2520 'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')},
2521 2521 '3' => {'file' => mock_file_with_options(:original_filename => 'bar')},
2522 2522 '1' => {'file' => mock_file_with_options(:original_filename => 'foo')}
2523 2523 })
2524 2524 issue.attach_saved_attachments
2525 2525
2526 2526 assert_equal 3, issue.reload.attachments.count
2527 2527 assert_equal %w(upload foo bar), issue.attachments.map(&:filename)
2528 2528 end
2529 2529
2530 2530 def test_closed_on_should_be_nil_when_creating_an_open_issue
2531 2531 issue = Issue.generate!(:status_id => 1).reload
2532 2532 assert !issue.closed?
2533 2533 assert_nil issue.closed_on
2534 2534 end
2535 2535
2536 2536 def test_closed_on_should_be_set_when_creating_a_closed_issue
2537 2537 issue = Issue.generate!(:status_id => 5).reload
2538 2538 assert issue.closed?
2539 2539 assert_not_nil issue.closed_on
2540 2540 assert_equal issue.updated_on, issue.closed_on
2541 2541 assert_equal issue.created_on, issue.closed_on
2542 2542 end
2543 2543
2544 2544 def test_closed_on_should_be_nil_when_updating_an_open_issue
2545 2545 issue = Issue.find(1)
2546 2546 issue.subject = 'Not closed yet'
2547 2547 issue.save!
2548 2548 issue.reload
2549 2549 assert_nil issue.closed_on
2550 2550 end
2551 2551
2552 2552 def test_closed_on_should_be_set_when_closing_an_open_issue
2553 2553 issue = Issue.find(1)
2554 2554 issue.subject = 'Now closed'
2555 2555 issue.status_id = 5
2556 2556 issue.save!
2557 2557 issue.reload
2558 2558 assert_not_nil issue.closed_on
2559 2559 assert_equal issue.updated_on, issue.closed_on
2560 2560 end
2561 2561
2562 2562 def test_closed_on_should_not_be_updated_when_updating_a_closed_issue
2563 2563 issue = Issue.open(false).first
2564 2564 was_closed_on = issue.closed_on
2565 2565 assert_not_nil was_closed_on
2566 2566 issue.subject = 'Updating a closed issue'
2567 2567 issue.save!
2568 2568 issue.reload
2569 2569 assert_equal was_closed_on, issue.closed_on
2570 2570 end
2571 2571
2572 2572 def test_closed_on_should_be_preserved_when_reopening_a_closed_issue
2573 2573 issue = Issue.open(false).first
2574 2574 was_closed_on = issue.closed_on
2575 2575 assert_not_nil was_closed_on
2576 2576 issue.subject = 'Reopening a closed issue'
2577 2577 issue.status_id = 1
2578 2578 issue.save!
2579 2579 issue.reload
2580 2580 assert !issue.closed?
2581 2581 assert_equal was_closed_on, issue.closed_on
2582 2582 end
2583 2583
2584 2584 def test_status_was_should_return_nil_for_new_issue
2585 2585 issue = Issue.new
2586 2586 assert_nil issue.status_was
2587 2587 end
2588 2588
2589 2589 def test_status_was_should_return_status_before_change
2590 2590 issue = Issue.find(1)
2591 2591 issue.status = IssueStatus.find(2)
2592 2592 assert_equal IssueStatus.find(1), issue.status_was
2593 2593 end
2594 2594
2595 2595 def test_status_was_should_return_status_before_change_with_status_id
2596 2596 issue = Issue.find(1)
2597 2597 assert_equal IssueStatus.find(1), issue.status
2598 2598 issue.status_id = 2
2599 2599 assert_equal IssueStatus.find(1), issue.status_was
2600 2600 end
2601 2601
2602 2602 def test_status_was_should_be_reset_on_save
2603 2603 issue = Issue.find(1)
2604 2604 issue.status = IssueStatus.find(2)
2605 2605 assert_equal IssueStatus.find(1), issue.status_was
2606 2606 assert issue.save!
2607 2607 assert_equal IssueStatus.find(2), issue.status_was
2608 2608 end
2609 2609
2610 2610 def test_closing_should_return_true_when_closing_an_issue
2611 2611 issue = Issue.find(1)
2612 2612 issue.status = IssueStatus.find(2)
2613 2613 assert_equal false, issue.closing?
2614 2614 issue.status = IssueStatus.find(5)
2615 2615 assert_equal true, issue.closing?
2616 2616 end
2617 2617
2618 2618 def test_closing_should_return_true_when_closing_an_issue_with_status_id
2619 2619 issue = Issue.find(1)
2620 2620 issue.status_id = 2
2621 2621 assert_equal false, issue.closing?
2622 2622 issue.status_id = 5
2623 2623 assert_equal true, issue.closing?
2624 2624 end
2625 2625
2626 2626 def test_closing_should_return_true_for_new_closed_issue
2627 2627 issue = Issue.new
2628 2628 assert_equal false, issue.closing?
2629 2629 issue.status = IssueStatus.find(5)
2630 2630 assert_equal true, issue.closing?
2631 2631 end
2632 2632
2633 2633 def test_closing_should_return_true_for_new_closed_issue_with_status_id
2634 2634 issue = Issue.new
2635 2635 assert_equal false, issue.closing?
2636 2636 issue.status_id = 5
2637 2637 assert_equal true, issue.closing?
2638 2638 end
2639 2639
2640 2640 def test_closing_should_be_reset_after_save
2641 2641 issue = Issue.find(1)
2642 2642 issue.status_id = 5
2643 2643 assert_equal true, issue.closing?
2644 2644 issue.save!
2645 2645 assert_equal false, issue.closing?
2646 2646 end
2647 2647
2648 2648 def test_reopening_should_return_true_when_reopening_an_issue
2649 2649 issue = Issue.find(8)
2650 2650 issue.status = IssueStatus.find(6)
2651 2651 assert_equal false, issue.reopening?
2652 2652 issue.status = IssueStatus.find(2)
2653 2653 assert_equal true, issue.reopening?
2654 2654 end
2655 2655
2656 2656 def test_reopening_should_return_true_when_reopening_an_issue_with_status_id
2657 2657 issue = Issue.find(8)
2658 2658 issue.status_id = 6
2659 2659 assert_equal false, issue.reopening?
2660 2660 issue.status_id = 2
2661 2661 assert_equal true, issue.reopening?
2662 2662 end
2663 2663
2664 2664 def test_reopening_should_return_false_for_new_open_issue
2665 2665 issue = Issue.new
2666 2666 issue.status = IssueStatus.find(1)
2667 2667 assert_equal false, issue.reopening?
2668 2668 end
2669 2669
2670 2670 def test_reopening_should_be_reset_after_save
2671 2671 issue = Issue.find(8)
2672 2672 issue.status_id = 2
2673 2673 assert_equal true, issue.reopening?
2674 2674 issue.save!
2675 2675 assert_equal false, issue.reopening?
2676 2676 end
2677 2677
2678 2678 def test_default_status_without_tracker_should_be_nil
2679 2679 issue = Issue.new
2680 2680 assert_nil issue.tracker
2681 2681 assert_nil issue.default_status
2682 2682 end
2683 2683
2684 2684 def test_default_status_should_be_tracker_default_status
2685 2685 issue = Issue.new(:tracker_id => 1)
2686 2686 assert_not_nil issue.status
2687 2687 assert_equal issue.tracker.default_status, issue.default_status
2688 2688 end
2689 2689
2690 2690 def test_initializing_with_tracker_should_set_default_status
2691 2691 issue = Issue.new(:tracker => Tracker.find(1))
2692 2692 assert_not_nil issue.status
2693 2693 assert_equal issue.default_status, issue.status
2694 2694 end
2695 2695
2696 2696 def test_initializing_with_tracker_id_should_set_default_status
2697 2697 issue = Issue.new(:tracker_id => 1)
2698 2698 assert_not_nil issue.status
2699 2699 assert_equal issue.default_status, issue.status
2700 2700 end
2701 2701
2702 2702 def test_setting_tracker_should_set_default_status
2703 2703 issue = Issue.new
2704 2704 issue.tracker = Tracker.find(1)
2705 2705 assert_not_nil issue.status
2706 2706 assert_equal issue.default_status, issue.status
2707 2707 end
2708 2708
2709 2709 def test_changing_tracker_should_set_default_status_if_status_was_default
2710 2710 WorkflowTransition.delete_all
2711 2711 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1
2712 2712 Tracker.find(2).update! :default_status_id => 2
2713 2713
2714 2714 issue = Issue.new(:tracker_id => 1, :status_id => 1)
2715 2715 assert_equal IssueStatus.find(1), issue.status
2716 2716 issue.tracker = Tracker.find(2)
2717 2717 assert_equal IssueStatus.find(2), issue.status
2718 2718 end
2719 2719
2720 2720 def test_changing_tracker_should_set_default_status_if_status_is_not_used_by_tracker
2721 2721 WorkflowTransition.delete_all
2722 2722 Tracker.find(2).update! :default_status_id => 2
2723 2723
2724 2724 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2725 2725 assert_equal IssueStatus.find(3), issue.status
2726 2726 issue.tracker = Tracker.find(2)
2727 2727 assert_equal IssueStatus.find(2), issue.status
2728 2728 end
2729 2729
2730 2730 def test_changing_tracker_should_keep_status_if_status_was_not_default_and_is_used_by_tracker
2731 2731 WorkflowTransition.delete_all
2732 2732 WorkflowTransition.create! :role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3
2733 2733 Tracker.find(2).update! :default_status_id => 2
2734 2734
2735 2735 issue = Issue.new(:tracker_id => 1, :status_id => 3)
2736 2736 assert_equal IssueStatus.find(3), issue.status
2737 2737 issue.tracker = Tracker.find(2)
2738 2738 assert_equal IssueStatus.find(3), issue.status
2739 2739 end
2740 2740
2741 2741 def test_assigned_to_was_with_a_group
2742 2742 group = Group.find(10)
2743 2743
2744 2744 issue = Issue.generate!(:assigned_to => group)
2745 2745 issue.reload.assigned_to = nil
2746 2746 assert_equal group, issue.assigned_to_was
2747 2747 end
2748 2748 end
@@ -1,1084 +1,1084
1 1 # encoding: utf-8
2 2 #
3 3 # Redmine - project management software
4 4 # Copyright (C) 2006-2016 Jean-Philippe Lang
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; either version 2
9 9 # of the License, or (at your option) any later version.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 19
20 20 require File.expand_path('../../test_helper', __FILE__)
21 21
22 22 class MailHandlerTest < ActiveSupport::TestCase
23 23 fixtures :users, :projects, :enabled_modules, :roles,
24 24 :members, :member_roles, :users,
25 :email_addresses,
25 :email_addresses, :user_preferences,
26 26 :issues, :issue_statuses,
27 27 :workflows, :trackers, :projects_trackers,
28 28 :versions, :enumerations, :issue_categories,
29 29 :custom_fields, :custom_fields_trackers, :custom_fields_projects,
30 30 :boards, :messages
31 31
32 32 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
33 33
34 34 def setup
35 35 ActionMailer::Base.deliveries.clear
36 36 Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
37 37 end
38 38
39 39 def teardown
40 40 Setting.clear_cache
41 41 end
42 42
43 43 def test_add_issue_with_specific_overrides
44 44 issue = submit_email('ticket_on_given_project.eml',
45 45 :allow_override => ['status', 'start_date', 'due_date', 'assigned_to', 'fixed_version', 'estimated_hours', 'done_ratio']
46 46 )
47 47 assert issue.is_a?(Issue)
48 48 assert !issue.new_record?
49 49 issue.reload
50 50 assert_equal Project.find(2), issue.project
51 51 assert_equal issue.project.trackers.first, issue.tracker
52 52 assert_equal 'New ticket on a given project', issue.subject
53 53 assert_equal User.find_by_login('jsmith'), issue.author
54 54 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
55 55 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
56 56 assert_equal '2010-01-01', issue.start_date.to_s
57 57 assert_equal '2010-12-31', issue.due_date.to_s
58 58 assert_equal User.find_by_login('jsmith'), issue.assigned_to
59 59 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
60 60 assert_equal 2.5, issue.estimated_hours
61 61 assert_equal 30, issue.done_ratio
62 62 # keywords should be removed from the email body
63 63 assert !issue.description.match(/^Project:/i)
64 64 assert !issue.description.match(/^Status:/i)
65 65 assert !issue.description.match(/^Start Date:/i)
66 66 end
67 67
68 68 def test_add_issue_with_all_overrides
69 69 issue = submit_email('ticket_on_given_project.eml', :allow_override => 'all')
70 70 assert issue.is_a?(Issue)
71 71 assert !issue.new_record?
72 72 issue.reload
73 73 assert_equal Project.find(2), issue.project
74 74 assert_equal issue.project.trackers.first, issue.tracker
75 75 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
76 76 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
77 77 assert_equal '2010-01-01', issue.start_date.to_s
78 78 assert_equal '2010-12-31', issue.due_date.to_s
79 79 assert_equal User.find_by_login('jsmith'), issue.assigned_to
80 80 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
81 81 assert_equal 2.5, issue.estimated_hours
82 82 assert_equal 30, issue.done_ratio
83 83 end
84 84
85 85 def test_add_issue_without_overrides_should_ignore_attributes
86 86 WorkflowRule.delete_all
87 87 issue = submit_email('ticket_on_given_project.eml')
88 88 assert issue.is_a?(Issue)
89 89 assert !issue.new_record?
90 90 issue.reload
91 91 assert_equal Project.find(2), issue.project
92 92 assert_equal 'New ticket on a given project', issue.subject
93 93 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
94 94 assert_equal User.find_by_login('jsmith'), issue.author
95 95
96 96 assert_equal issue.project.trackers.first, issue.tracker
97 97 assert_equal 'New', issue.status.name
98 98 assert_not_equal '2010-01-01', issue.start_date.to_s
99 99 assert_nil issue.due_date
100 100 assert_nil issue.assigned_to
101 101 assert_nil issue.fixed_version
102 102 assert_nil issue.estimated_hours
103 103 assert_equal 0, issue.done_ratio
104 104 end
105 105
106 106 def test_add_issue_to_project_specified_by_subaddress
107 107 # This email has redmine+onlinestore@somenet.foo as 'To' header
108 108 issue = submit_email(
109 109 'ticket_on_project_given_by_to_header.eml',
110 110 :issue => {:tracker => 'Support request'},
111 111 :project_from_subaddress => 'redmine@somenet.foo'
112 112 )
113 113 assert issue.is_a?(Issue)
114 114 assert !issue.new_record?
115 115 issue.reload
116 116 assert_equal 'onlinestore', issue.project.identifier
117 117 assert_equal 'Support request', issue.tracker.name
118 118 end
119 119
120 120 def test_add_issue_with_default_tracker
121 121 # This email contains: 'Project: onlinestore'
122 122 issue = submit_email(
123 123 'ticket_on_given_project.eml',
124 124 :issue => {:tracker => 'Support request'}
125 125 )
126 126 assert issue.is_a?(Issue)
127 127 assert !issue.new_record?
128 128 issue.reload
129 129 assert_equal 'Support request', issue.tracker.name
130 130 end
131 131
132 132 def test_add_issue_with_default_version
133 133 # This email contains: 'Project: onlinestore'
134 134 issue = submit_email(
135 135 'ticket_on_given_project.eml',
136 136 :issue => {:fixed_version => 'Alpha'}
137 137 )
138 138 assert issue.is_a?(Issue)
139 139 assert !issue.new_record?
140 140 assert_equal 'Alpha', issue.reload.fixed_version.name
141 141 end
142 142
143 143 def test_add_issue_with_status_override
144 144 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
145 145 issue = submit_email('ticket_on_given_project.eml', :allow_override => ['status'])
146 146 assert issue.is_a?(Issue)
147 147 assert !issue.new_record?
148 148 issue.reload
149 149 assert_equal Project.find(2), issue.project
150 150 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
151 151 end
152 152
153 153 def test_add_issue_should_accept_is_private_attribute
154 154 issue = submit_email('ticket_on_given_project.eml', :issue => {:is_private => '1'})
155 155 assert issue.is_a?(Issue)
156 156 assert !issue.new_record?
157 157 assert_equal true, issue.reload.is_private
158 158 end
159 159
160 160 def test_add_issue_with_group_assignment
161 161 with_settings :issue_group_assignment => '1' do
162 162 issue = submit_email('ticket_on_given_project.eml', :allow_override => ['assigned_to']) do |email|
163 163 email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
164 164 end
165 165 assert issue.is_a?(Issue)
166 166 assert !issue.new_record?
167 167 issue.reload
168 168 assert_equal Group.find(11), issue.assigned_to
169 169 end
170 170 end
171 171
172 172 def test_add_issue_with_partial_attributes_override
173 173 issue = submit_email(
174 174 'ticket_with_attributes.eml',
175 175 :issue => {:priority => 'High'},
176 176 :allow_override => ['tracker']
177 177 )
178 178 assert issue.is_a?(Issue)
179 179 assert !issue.new_record?
180 180 issue.reload
181 181 assert_equal 'New ticket on a given project', issue.subject
182 182 assert_equal User.find_by_login('jsmith'), issue.author
183 183 assert_equal Project.find(2), issue.project
184 184 assert_equal 'Feature request', issue.tracker.to_s
185 185 assert_nil issue.category
186 186 assert_equal 'High', issue.priority.to_s
187 187 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
188 188 end
189 189
190 190 def test_add_issue_with_spaces_between_attribute_and_separator
191 191 issue = submit_email(
192 192 'ticket_with_spaces_between_attribute_and_separator.eml',
193 193 :allow_override => 'tracker,category,priority'
194 194 )
195 195 assert issue.is_a?(Issue)
196 196 assert !issue.new_record?
197 197 issue.reload
198 198 assert_equal 'New ticket on a given project', issue.subject
199 199 assert_equal User.find_by_login('jsmith'), issue.author
200 200 assert_equal Project.find(2), issue.project
201 201 assert_equal 'Feature request', issue.tracker.to_s
202 202 assert_equal 'Stock management', issue.category.to_s
203 203 assert_equal 'Urgent', issue.priority.to_s
204 204 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
205 205 end
206 206
207 207 def test_add_issue_with_attachment_to_specific_project
208 208 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
209 209 assert issue.is_a?(Issue)
210 210 assert !issue.new_record?
211 211 issue.reload
212 212 assert_equal 'Ticket created by email with attachment', issue.subject
213 213 assert_equal User.find_by_login('jsmith'), issue.author
214 214 assert_equal Project.find(2), issue.project
215 215 assert_equal 'This is a new ticket with attachments', issue.description
216 216 # Attachment properties
217 217 assert_equal 1, issue.attachments.size
218 218 assert_equal 'Paella.jpg', issue.attachments.first.filename
219 219 assert_equal 'image/jpeg', issue.attachments.first.content_type
220 220 assert_equal 10790, issue.attachments.first.filesize
221 221 end
222 222
223 223 def test_add_issue_with_custom_fields
224 224 issue = submit_email('ticket_with_custom_fields.eml',
225 225 :issue => {:project => 'onlinestore'}, :allow_override => ['database', 'Searchable_field']
226 226 )
227 227 assert issue.is_a?(Issue)
228 228 assert !issue.new_record?
229 229 issue.reload
230 230 assert_equal 'New ticket with custom field values', issue.subject
231 231 assert_equal 'PostgreSQL', issue.custom_field_value(1)
232 232 assert_equal 'Value for a custom field', issue.custom_field_value(2)
233 233 assert !issue.description.match(/^searchable field:/i)
234 234 end
235 235
236 236 def test_add_issue_with_version_custom_fields
237 237 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
238 238
239 239 issue = submit_email('ticket_with_custom_fields.eml',
240 240 :issue => {:project => 'ecookbook'}, :allow_override => ['affected version']
241 241 ) do |email|
242 242 email << "Affected version: 1.0\n"
243 243 end
244 244 assert issue.is_a?(Issue)
245 245 assert !issue.new_record?
246 246 issue.reload
247 247 assert_equal '2', issue.custom_field_value(field)
248 248 end
249 249
250 250 def test_add_issue_should_match_assignee_on_display_name
251 251 user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz')
252 252 User.add_to_project(user, Project.find(2))
253 253 issue = submit_email('ticket_on_given_project.eml', :allow_override => ['assigned_to']) do |email|
254 254 email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz')
255 255 end
256 256 assert issue.is_a?(Issue)
257 257 assert_equal user, issue.assigned_to
258 258 end
259 259
260 260 def test_add_issue_should_set_default_start_date
261 261 with_settings :default_issue_start_date_to_creation_date => '1' do
262 262 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
263 263 assert issue.is_a?(Issue)
264 264 assert_equal Date.today, issue.start_date
265 265 end
266 266 end
267 267
268 268 def test_add_issue_should_add_cc_as_watchers
269 269 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
270 270 assert issue.is_a?(Issue)
271 271 assert !issue.new_record?
272 272 issue.reload
273 273 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
274 274 assert_equal 1, issue.watcher_user_ids.size
275 275 end
276 276
277 277 def test_add_issue_from_additional_email_address
278 278 user = User.find(2)
279 279 user.mail = 'mainaddress@somenet.foo'
280 280 user.save!
281 281 EmailAddress.create!(:user => user, :address => 'jsmith@somenet.foo')
282 282
283 283 issue = submit_email('ticket_on_given_project.eml')
284 284 assert issue
285 285 assert_equal user, issue.author
286 286 end
287 287
288 288 def test_add_issue_by_unknown_user
289 289 assert_no_difference 'User.count' do
290 290 assert_equal false,
291 291 submit_email(
292 292 'ticket_by_unknown_user.eml',
293 293 :issue => {:project => 'ecookbook'}
294 294 )
295 295 end
296 296 end
297 297
298 298 def test_add_issue_by_anonymous_user
299 299 Role.anonymous.add_permission!(:add_issues)
300 300 assert_no_difference 'User.count' do
301 301 issue = submit_email(
302 302 'ticket_by_unknown_user.eml',
303 303 :issue => {:project => 'ecookbook'},
304 304 :unknown_user => 'accept'
305 305 )
306 306 assert issue.is_a?(Issue)
307 307 assert issue.author.anonymous?
308 308 end
309 309 end
310 310
311 311 def test_add_issue_by_anonymous_user_with_no_from_address
312 312 Role.anonymous.add_permission!(:add_issues)
313 313 assert_no_difference 'User.count' do
314 314 issue = submit_email(
315 315 'ticket_by_empty_user.eml',
316 316 :issue => {:project => 'ecookbook'},
317 317 :unknown_user => 'accept'
318 318 )
319 319 assert issue.is_a?(Issue)
320 320 assert issue.author.anonymous?
321 321 end
322 322 end
323 323
324 324 def test_add_issue_by_anonymous_user_on_private_project
325 325 Role.anonymous.add_permission!(:add_issues)
326 326 assert_no_difference 'User.count' do
327 327 assert_no_difference 'Issue.count' do
328 328 assert_equal false,
329 329 submit_email(
330 330 'ticket_by_unknown_user.eml',
331 331 :issue => {:project => 'onlinestore'},
332 332 :unknown_user => 'accept'
333 333 )
334 334 end
335 335 end
336 336 end
337 337
338 338 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
339 339 assert_no_difference 'User.count' do
340 340 assert_difference 'Issue.count' do
341 341 issue = submit_email(
342 342 'ticket_by_unknown_user.eml',
343 343 :issue => {:project => 'onlinestore'},
344 344 :no_permission_check => '1',
345 345 :unknown_user => 'accept'
346 346 )
347 347 assert issue.is_a?(Issue)
348 348 assert issue.author.anonymous?
349 349 assert !issue.project.is_public?
350 350 end
351 351 end
352 352 end
353 353
354 354 def test_add_issue_by_created_user
355 355 Setting.default_language = 'en'
356 356 assert_difference 'User.count' do
357 357 issue = submit_email(
358 358 'ticket_by_unknown_user.eml',
359 359 :issue => {:project => 'ecookbook'},
360 360 :unknown_user => 'create'
361 361 )
362 362 assert issue.is_a?(Issue)
363 363 assert issue.author.active?
364 364 assert_equal 'john.doe@somenet.foo', issue.author.mail
365 365 assert_equal 'John', issue.author.firstname
366 366 assert_equal 'Doe', issue.author.lastname
367 367
368 368 # account information
369 369 email = ActionMailer::Base.deliveries.first
370 370 assert_not_nil email
371 371 assert email.subject.include?('account activation')
372 372 login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
373 373 password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
374 374 assert_equal issue.author, User.try_to_login(login, password)
375 375 end
376 376 end
377 377
378 378 def test_add_issue_should_send_notification
379 379 issue = submit_email('ticket_on_given_project.eml', :allow_override => 'all')
380 380 assert issue.is_a?(Issue)
381 381 assert !issue.new_record?
382 382
383 383 mail = ActionMailer::Base.deliveries.last
384 384 assert_not_nil mail
385 385 assert mail.subject.include?("##{issue.id}")
386 386 assert mail.subject.include?('New ticket on a given project')
387 387 end
388 388
389 389 def test_created_user_should_be_added_to_groups
390 390 group1 = Group.generate!
391 391 group2 = Group.generate!
392 392
393 393 assert_difference 'User.count' do
394 394 submit_email(
395 395 'ticket_by_unknown_user.eml',
396 396 :issue => {:project => 'ecookbook'},
397 397 :unknown_user => 'create',
398 398 :default_group => "#{group1.name},#{group2.name}"
399 399 )
400 400 end
401 401 user = User.order('id DESC').first
402 402 assert_equal [group1, group2].sort, user.groups.sort
403 403 end
404 404
405 405 def test_created_user_should_not_receive_account_information_with_no_account_info_option
406 406 assert_difference 'User.count' do
407 407 submit_email(
408 408 'ticket_by_unknown_user.eml',
409 409 :issue => {:project => 'ecookbook'},
410 410 :unknown_user => 'create',
411 411 :no_account_notice => '1'
412 412 )
413 413 end
414 414
415 415 # only 1 email for the new issue notification
416 416 assert_equal 1, ActionMailer::Base.deliveries.size
417 417 email = ActionMailer::Base.deliveries.first
418 418 assert_include 'Ticket by unknown user', email.subject
419 419 end
420 420
421 421 def test_created_user_should_have_mail_notification_to_none_with_no_notification_option
422 422 assert_difference 'User.count' do
423 423 submit_email(
424 424 'ticket_by_unknown_user.eml',
425 425 :issue => {:project => 'ecookbook'},
426 426 :unknown_user => 'create',
427 427 :no_notification => '1'
428 428 )
429 429 end
430 430 user = User.order('id DESC').first
431 431 assert_equal 'none', user.mail_notification
432 432 end
433 433
434 434 def test_add_issue_without_from_header
435 435 Role.anonymous.add_permission!(:add_issues)
436 436 assert_equal false, submit_email('ticket_without_from_header.eml')
437 437 end
438 438
439 439 def test_add_issue_with_invalid_attributes
440 440 with_settings :default_issue_start_date_to_creation_date => '0' do
441 441 issue = submit_email(
442 442 'ticket_with_invalid_attributes.eml',
443 443 :allow_override => 'tracker,category,priority'
444 444 )
445 445 assert issue.is_a?(Issue)
446 446 assert !issue.new_record?
447 447 issue.reload
448 448 assert_nil issue.assigned_to
449 449 assert_nil issue.start_date
450 450 assert_nil issue.due_date
451 451 assert_equal 0, issue.done_ratio
452 452 assert_equal 'Normal', issue.priority.to_s
453 453 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
454 454 end
455 455 end
456 456
457 457 def test_add_issue_with_invalid_project_should_be_assigned_to_default_project
458 458 issue = submit_email('ticket_on_given_project.eml', :issue => {:project => 'ecookbook'}, :allow_override => 'project') do |email|
459 459 email.gsub!(/^Project:.+$/, 'Project: invalid')
460 460 end
461 461 assert issue.is_a?(Issue)
462 462 assert !issue.new_record?
463 463 assert_equal 'ecookbook', issue.project.identifier
464 464 end
465 465
466 466 def test_add_issue_with_localized_attributes
467 467 User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
468 468 issue = submit_email(
469 469 'ticket_with_localized_attributes.eml',
470 470 :allow_override => 'tracker,category,priority'
471 471 )
472 472 assert issue.is_a?(Issue)
473 473 assert !issue.new_record?
474 474 issue.reload
475 475 assert_equal 'New ticket on a given project', issue.subject
476 476 assert_equal User.find_by_login('jsmith'), issue.author
477 477 assert_equal Project.find(2), issue.project
478 478 assert_equal 'Feature request', issue.tracker.to_s
479 479 assert_equal 'Stock management', issue.category.to_s
480 480 assert_equal 'Urgent', issue.priority.to_s
481 481 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
482 482 end
483 483
484 484 def test_add_issue_with_japanese_keywords
485 485 ja_dev = "\xe9\x96\x8b\xe7\x99\xba".force_encoding('UTF-8')
486 486 tracker = Tracker.generate!(:name => ja_dev)
487 487 Project.find(1).trackers << tracker
488 488 issue = submit_email(
489 489 'japanese_keywords_iso_2022_jp.eml',
490 490 :issue => {:project => 'ecookbook'},
491 491 :allow_override => 'tracker'
492 492 )
493 493 assert_kind_of Issue, issue
494 494 assert_equal tracker, issue.tracker
495 495 end
496 496
497 497 def test_add_issue_from_apple_mail
498 498 issue = submit_email(
499 499 'apple_mail_with_attachment.eml',
500 500 :issue => {:project => 'ecookbook'}
501 501 )
502 502 assert_kind_of Issue, issue
503 503 assert_equal 1, issue.attachments.size
504 504
505 505 attachment = issue.attachments.first
506 506 assert_equal 'paella.jpg', attachment.filename
507 507 assert_equal 10790, attachment.filesize
508 508 assert File.exist?(attachment.diskfile)
509 509 assert_equal 10790, File.size(attachment.diskfile)
510 510 assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
511 511 end
512 512
513 513 def test_thunderbird_with_attachment_ja
514 514 issue = submit_email(
515 515 'thunderbird_with_attachment_ja.eml',
516 516 :issue => {:project => 'ecookbook'}
517 517 )
518 518 assert_kind_of Issue, issue
519 519 assert_equal 1, issue.attachments.size
520 520 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
521 521 attachment = issue.attachments.first
522 522 assert_equal ja, attachment.filename
523 523 assert_equal 5, attachment.filesize
524 524 assert File.exist?(attachment.diskfile)
525 525 assert_equal 5, File.size(attachment.diskfile)
526 526 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
527 527 end
528 528
529 529 def test_gmail_with_attachment_ja
530 530 issue = submit_email(
531 531 'gmail_with_attachment_ja.eml',
532 532 :issue => {:project => 'ecookbook'}
533 533 )
534 534 assert_kind_of Issue, issue
535 535 assert_equal 1, issue.attachments.size
536 536 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt".force_encoding('UTF-8')
537 537 attachment = issue.attachments.first
538 538 assert_equal ja, attachment.filename
539 539 assert_equal 5, attachment.filesize
540 540 assert File.exist?(attachment.diskfile)
541 541 assert_equal 5, File.size(attachment.diskfile)
542 542 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
543 543 end
544 544
545 545 def test_thunderbird_with_attachment_latin1
546 546 issue = submit_email(
547 547 'thunderbird_with_attachment_iso-8859-1.eml',
548 548 :issue => {:project => 'ecookbook'}
549 549 )
550 550 assert_kind_of Issue, issue
551 551 assert_equal 1, issue.attachments.size
552 552 u = "".force_encoding('UTF-8')
553 553 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
554 554 11.times { u << u1 }
555 555 attachment = issue.attachments.first
556 556 assert_equal "#{u}.png", attachment.filename
557 557 assert_equal 130, attachment.filesize
558 558 assert File.exist?(attachment.diskfile)
559 559 assert_equal 130, File.size(attachment.diskfile)
560 560 assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest
561 561 end
562 562
563 563 def test_gmail_with_attachment_latin1
564 564 issue = submit_email(
565 565 'gmail_with_attachment_iso-8859-1.eml',
566 566 :issue => {:project => 'ecookbook'}
567 567 )
568 568 assert_kind_of Issue, issue
569 569 assert_equal 1, issue.attachments.size
570 570 u = "".force_encoding('UTF-8')
571 571 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc".force_encoding('UTF-8')
572 572 11.times { u << u1 }
573 573 attachment = issue.attachments.first
574 574 assert_equal "#{u}.txt", attachment.filename
575 575 assert_equal 5, attachment.filesize
576 576 assert File.exist?(attachment.diskfile)
577 577 assert_equal 5, File.size(attachment.diskfile)
578 578 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
579 579 end
580 580
581 581 def test_mail_with_attachment_latin2
582 582 issue = submit_email(
583 583 'ticket_with_text_attachment_iso-8859-2.eml',
584 584 :issue => {:project => 'ecookbook'}
585 585 )
586 586 assert_kind_of Issue, issue
587 587 assert_equal 1, issue.attachments.size
588 588 attachment = issue.attachments.first
589 589 assert_equal 'latin2.txt', attachment.filename
590 590 assert_equal 19, attachment.filesize
591 591 assert File.exist?(attachment.diskfile)
592 592 assert_equal 19, File.size(attachment.diskfile)
593 593 content = "p\xF8\xEDli\xB9 \xBEluou\xE8k\xFD k\xF9n".force_encoding('CP852')
594 594 assert_equal content, File.read(attachment.diskfile).force_encoding('CP852')
595 595 end
596 596
597 597 def test_multiple_inline_text_parts_should_be_appended_to_issue_description
598 598 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
599 599 assert_include 'first', issue.description
600 600 assert_include 'second', issue.description
601 601 assert_include 'third', issue.description
602 602 end
603 603
604 604 def test_attachment_text_part_should_be_added_as_issue_attachment
605 605 issue = submit_email('multiple_text_parts.eml', :issue => {:project => 'ecookbook'})
606 606 assert_not_include 'Plain text attachment', issue.description
607 607 attachment = issue.attachments.detect {|a| a.filename == 'textfile.txt'}
608 608 assert_not_nil attachment
609 609 assert_include 'Plain text attachment', File.read(attachment.diskfile)
610 610 end
611 611
612 612 def test_add_issue_with_iso_8859_1_subject
613 613 issue = submit_email(
614 614 'subject_as_iso-8859-1.eml',
615 615 :issue => {:project => 'ecookbook'}
616 616 )
617 617 str = "Testmail from Webmail: \xc3\xa4 \xc3\xb6 \xc3\xbc...".force_encoding('UTF-8')
618 618 assert_kind_of Issue, issue
619 619 assert_equal str, issue.subject
620 620 end
621 621
622 622 def test_quoted_printable_utf8
623 623 issue = submit_email(
624 624 'quoted_printable_utf8.eml',
625 625 :issue => {:project => 'ecookbook'}
626 626 )
627 627 assert_kind_of Issue, issue
628 628 str = "Freundliche Gr\xc3\xbcsse".force_encoding('UTF-8')
629 629 assert_equal str, issue.description
630 630 end
631 631
632 632 def test_gmail_iso8859_2
633 633 issue = submit_email(
634 634 'gmail-iso8859-2.eml',
635 635 :issue => {:project => 'ecookbook'}
636 636 )
637 637 assert_kind_of Issue, issue
638 638 str = "Na \xc5\xa1triku se su\xc5\xa1i \xc5\xa1osi\xc4\x87.".force_encoding('UTF-8')
639 639 assert issue.description.include?(str)
640 640 end
641 641
642 642 def test_add_issue_with_japanese_subject
643 643 issue = submit_email(
644 644 'subject_japanese_1.eml',
645 645 :issue => {:project => 'ecookbook'}
646 646 )
647 647 assert_kind_of Issue, issue
648 648 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
649 649 assert_equal ja, issue.subject
650 650 end
651 651
652 652 def test_add_issue_with_korean_body
653 653 # Make sure mail bodies with a charset unknown to Ruby
654 654 # but known to the Mail gem 2.5.4 are handled correctly
655 655 kr = "\xEA\xB3\xA0\xEB\xA7\x99\xEC\x8A\xB5\xEB\x8B\x88\xEB\x8B\xA4.".force_encoding('UTF-8')
656 656 issue = submit_email(
657 657 'body_ks_c_5601-1987.eml',
658 658 :issue => {:project => 'ecookbook'}
659 659 )
660 660 assert_kind_of Issue, issue
661 661 assert_equal kr, issue.description
662 662 end
663 663
664 664 def test_add_issue_with_no_subject_header
665 665 issue = submit_email(
666 666 'no_subject_header.eml',
667 667 :issue => {:project => 'ecookbook'}
668 668 )
669 669 assert_kind_of Issue, issue
670 670 assert_equal '(no subject)', issue.subject
671 671 end
672 672
673 673 def test_add_issue_with_mixed_japanese_subject
674 674 issue = submit_email(
675 675 'subject_japanese_2.eml',
676 676 :issue => {:project => 'ecookbook'}
677 677 )
678 678 assert_kind_of Issue, issue
679 679 ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88".force_encoding('UTF-8')
680 680 assert_equal ja, issue.subject
681 681 end
682 682
683 683 def test_should_ignore_emails_from_locked_users
684 684 User.find(2).lock!
685 685
686 686 MailHandler.any_instance.expects(:dispatch).never
687 687 assert_no_difference 'Issue.count' do
688 688 assert_equal false, submit_email('ticket_on_given_project.eml')
689 689 end
690 690 end
691 691
692 692 def test_should_ignore_emails_from_emission_address
693 693 Role.anonymous.add_permission!(:add_issues)
694 694 assert_no_difference 'User.count' do
695 695 assert_equal false,
696 696 submit_email(
697 697 'ticket_from_emission_address.eml',
698 698 :issue => {:project => 'ecookbook'},
699 699 :unknown_user => 'create'
700 700 )
701 701 end
702 702 end
703 703
704 704 def test_should_ignore_auto_replied_emails
705 705 MailHandler.any_instance.expects(:dispatch).never
706 706 [
707 707 "Auto-Submitted: auto-replied",
708 708 "Auto-Submitted: Auto-Replied",
709 709 "Auto-Submitted: auto-generated",
710 710 'X-Autoreply: yes'
711 711 ].each do |header|
712 712 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
713 713 raw = header + "\n" + raw
714 714
715 715 assert_no_difference 'Issue.count' do
716 716 assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
717 717 end
718 718 end
719 719 end
720 720
721 721 test "should not ignore Auto-Submitted headers not defined in RFC3834" do
722 722 [
723 723 "Auto-Submitted: auto-forwarded"
724 724 ].each do |header|
725 725 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
726 726 raw = header + "\n" + raw
727 727
728 728 assert_difference 'Issue.count', 1 do
729 729 assert_not_nil MailHandler.receive(raw), "email with #{header} header was ignored"
730 730 end
731 731 end
732 732 end
733 733
734 734 def test_add_issue_should_send_email_notification
735 735 Setting.notified_events = ['issue_added']
736 736 # This email contains: 'Project: onlinestore'
737 737 issue = submit_email('ticket_on_given_project.eml')
738 738 assert issue.is_a?(Issue)
739 739 assert_equal 1, ActionMailer::Base.deliveries.size
740 740 end
741 741
742 742 def test_update_issue
743 743 journal = submit_email('ticket_reply.eml')
744 744 assert journal.is_a?(Journal)
745 745 assert_equal User.find_by_login('jsmith'), journal.user
746 746 assert_equal Issue.find(2), journal.journalized
747 747 assert_match /This is reply/, journal.notes
748 748 assert_equal false, journal.private_notes
749 749 assert_equal 'Feature request', journal.issue.tracker.name
750 750 end
751 751
752 752 def test_update_issue_should_accept_issue_id_after_space_inside_brackets
753 753 journal = submit_email('ticket_reply_with_status.eml') do |email|
754 754 assert email.sub!(/^Subject:.*$/, "Subject: Re: [Feature request #2] Add ingredients categories")
755 755 end
756 756 assert journal.is_a?(Journal)
757 757 assert_equal Issue.find(2), journal.journalized
758 758 end
759 759
760 760 def test_update_issue_should_accept_issue_id_inside_brackets
761 761 journal = submit_email('ticket_reply_with_status.eml') do |email|
762 762 assert email.sub!(/^Subject:.*$/, "Subject: Re: [#2] Add ingredients categories")
763 763 end
764 764 assert journal.is_a?(Journal)
765 765 assert_equal Issue.find(2), journal.journalized
766 766 end
767 767
768 768 def test_update_issue_should_ignore_bogus_issue_ids_in_subject
769 769 journal = submit_email('ticket_reply_with_status.eml') do |email|
770 770 assert email.sub!(/^Subject:.*$/, "Subject: Re: [12345#1][bogus#1][Feature request #2] Add ingredients categories")
771 771 end
772 772 assert journal.is_a?(Journal)
773 773 assert_equal Issue.find(2), journal.journalized
774 774 end
775 775
776 776 def test_update_issue_with_attribute_changes
777 777 journal = submit_email('ticket_reply_with_status.eml', :allow_override => ['status','assigned_to','start_date','due_date', 'float field'])
778 778 assert journal.is_a?(Journal)
779 779 issue = Issue.find(journal.issue.id)
780 780 assert_equal User.find_by_login('jsmith'), journal.user
781 781 assert_equal Issue.find(2), journal.journalized
782 782 assert_match /This is reply/, journal.notes
783 783 assert_equal 'Feature request', journal.issue.tracker.name
784 784 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
785 785 assert_equal '2010-01-01', issue.start_date.to_s
786 786 assert_equal '2010-12-31', issue.due_date.to_s
787 787 assert_equal User.find_by_login('jsmith'), issue.assigned_to
788 788 assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
789 789 # keywords should be removed from the email body
790 790 assert !journal.notes.match(/^Status:/i)
791 791 assert !journal.notes.match(/^Start Date:/i)
792 792 end
793 793
794 794 def test_update_issue_with_attachment
795 795 assert_difference 'Journal.count' do
796 796 assert_difference 'JournalDetail.count' do
797 797 assert_difference 'Attachment.count' do
798 798 assert_no_difference 'Issue.count' do
799 799 journal = submit_email('ticket_with_attachment.eml') do |raw|
800 800 raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
801 801 end
802 802 end
803 803 end
804 804 end
805 805 end
806 806 journal = Journal.order('id DESC').first
807 807 assert_equal Issue.find(2), journal.journalized
808 808 assert_equal 1, journal.details.size
809 809
810 810 detail = journal.details.first
811 811 assert_equal 'attachment', detail.property
812 812 assert_equal 'Paella.jpg', detail.value
813 813 end
814 814
815 815 def test_update_issue_should_send_email_notification
816 816 journal = submit_email('ticket_reply.eml')
817 817 assert journal.is_a?(Journal)
818 818 assert_equal 1, ActionMailer::Base.deliveries.size
819 819 end
820 820
821 821 def test_update_issue_should_not_set_defaults
822 822 journal = submit_email(
823 823 'ticket_reply.eml',
824 824 :issue => {:tracker => 'Support request', :priority => 'High'}
825 825 )
826 826 assert journal.is_a?(Journal)
827 827 assert_match /This is reply/, journal.notes
828 828 assert_equal 'Feature request', journal.issue.tracker.name
829 829 assert_equal 'Normal', journal.issue.priority.name
830 830 end
831 831
832 832 def test_update_issue_should_add_cc_as_watchers
833 833 Watcher.delete_all
834 834 issue = Issue.find(2)
835 835
836 836 assert_difference 'Watcher.count' do
837 837 assert submit_email('issue_update_with_cc.eml')
838 838 end
839 839 issue.reload
840 840 assert_equal 1, issue.watcher_user_ids.size
841 841 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
842 842 end
843 843
844 844 def test_update_issue_should_not_add_cc_as_watchers_if_already_watching
845 845 Watcher.delete_all
846 846 issue = Issue.find(2)
847 847 Watcher.create!(:watchable => issue, :user => User.find_by_mail('dlopper@somenet.foo'))
848 848
849 849 assert_no_difference 'Watcher.count' do
850 850 assert submit_email('issue_update_with_cc.eml')
851 851 end
852 852 end
853 853
854 854 def test_replying_to_a_private_note_should_add_reply_as_private
855 855 private_journal = Journal.create!(:notes => 'Private notes', :journalized => Issue.find(1), :private_notes => true, :user_id => 2)
856 856
857 857 assert_difference 'Journal.count' do
858 858 journal = submit_email('ticket_reply.eml') do |email|
859 859 email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
860 860 end
861 861
862 862 assert_kind_of Journal, journal
863 863 assert_match /This is reply/, journal.notes
864 864 assert_equal true, journal.private_notes
865 865 end
866 866 end
867 867
868 868 def test_reply_to_a_message
869 869 m = submit_email('message_reply.eml')
870 870 assert m.is_a?(Message)
871 871 assert !m.new_record?
872 872 m.reload
873 873 assert_equal 'Reply via email', m.subject
874 874 # The email replies to message #2 which is part of the thread of message #1
875 875 assert_equal Message.find(1), m.parent
876 876 end
877 877
878 878 def test_reply_to_a_message_by_subject
879 879 m = submit_email('message_reply_by_subject.eml')
880 880 assert m.is_a?(Message)
881 881 assert !m.new_record?
882 882 m.reload
883 883 assert_equal 'Reply to the first post', m.subject
884 884 assert_equal Message.find(1), m.parent
885 885 end
886 886
887 887 def test_should_convert_tags_of_html_only_emails
888 888 with_settings :text_formatting => 'textile' do
889 889 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
890 890 assert issue.is_a?(Issue)
891 891 assert !issue.new_record?
892 892 issue.reload
893 893 assert_equal 'HTML email', issue.subject
894 894 assert_equal "This is a *html-only* email.\r\n\r\nh1. With a title\r\n\r\nand a paragraph.", issue.description
895 895 end
896 896 end
897 897
898 898 def test_should_handle_outlook_web_access_2010_html_only
899 899 issue = submit_email('outlook_web_access_2010_html_only.eml', :issue => {:project => 'ecookbook'})
900 900 assert issue.is_a?(Issue)
901 901 issue.reload
902 902 assert_equal 'Upgrade Redmine to 3.0.x', issue.subject
903 903 assert_equal "A mess.\r\n\r\n--Geoff Maciolek\r\nMYCOMPANYNAME, LLC", issue.description
904 904 end
905 905
906 906 def test_should_handle_outlook_2010_html_only
907 907 issue = submit_email('outlook_2010_html_only.eml', :issue => {:project => 'ecookbook'})
908 908 assert issue.is_a?(Issue)
909 909 issue.reload
910 910 assert_equal 'Test email', issue.subject
911 911 assert_equal "Simple, unadorned test email generated by Outlook 2010. It is in HTML format, but" +
912 912 " no special formatting has been chosen. I’m going to save this as a draft and then manually" +
913 913 " drop it into the Inbox for scraping by Redmine 3.0.2.", issue.description
914 914 end
915 915
916 916 test "truncate emails with no setting should add the entire email into the issue" do
917 917 with_settings :mail_handler_body_delimiters => '' do
918 918 issue = submit_email('ticket_on_given_project.eml')
919 919 assert_issue_created(issue)
920 920 assert issue.description.include?('---')
921 921 assert issue.description.include?('This paragraph is after the delimiter')
922 922 end
923 923 end
924 924
925 925 test "truncate emails with a single string should truncate the email at the delimiter for the issue" do
926 926 with_settings :mail_handler_body_delimiters => '---' do
927 927 issue = submit_email('ticket_on_given_project.eml')
928 928 assert_issue_created(issue)
929 929 assert issue.description.include?('This paragraph is before delimiters')
930 930 assert issue.description.include?('--- This line starts with a delimiter')
931 931 assert !issue.description.match(/^---$/)
932 932 assert !issue.description.include?('This paragraph is after the delimiter')
933 933 end
934 934 end
935 935
936 936 test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do
937 937 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
938 938 journal = submit_email('issue_update_with_quoted_reply_above.eml')
939 939 assert journal.is_a?(Journal)
940 940 assert journal.notes.include?('An update to the issue by the sender.')
941 941 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
942 942 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
943 943 end
944 944 end
945 945
946 946 test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do
947 947 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
948 948 journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
949 949 assert journal.is_a?(Journal)
950 950 assert journal.notes.include?('An update to the issue by the sender.')
951 951 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
952 952 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
953 953 end
954 954 end
955 955
956 956 test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do
957 957 with_settings :mail_handler_body_delimiters => "---\nBREAK" do
958 958 issue = submit_email('ticket_on_given_project.eml')
959 959 assert_issue_created(issue)
960 960 assert issue.description.include?('This paragraph is before delimiters')
961 961 assert !issue.description.include?('BREAK')
962 962 assert !issue.description.include?('This paragraph is between delimiters')
963 963 assert !issue.description.match(/^---$/)
964 964 assert !issue.description.include?('This paragraph is after the delimiter')
965 965 end
966 966 end
967 967
968 968 def test_attachments_that_match_mail_handler_excluded_filenames_should_be_ignored
969 969 with_settings :mail_handler_excluded_filenames => '*.vcf, *.jpg' do
970 970 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
971 971 assert issue.is_a?(Issue)
972 972 assert !issue.new_record?
973 973 assert_equal 0, issue.reload.attachments.size
974 974 end
975 975 end
976 976
977 977 def test_attachments_that_do_not_match_mail_handler_excluded_filenames_should_be_attached
978 978 with_settings :mail_handler_excluded_filenames => '*.vcf, *.gif' do
979 979 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
980 980 assert issue.is_a?(Issue)
981 981 assert !issue.new_record?
982 982 assert_equal 1, issue.reload.attachments.size
983 983 end
984 984 end
985 985
986 986 def test_email_with_long_subject_line
987 987 issue = submit_email('ticket_with_long_subject.eml')
988 988 assert issue.is_a?(Issue)
989 989 assert_equal issue.subject, 'New ticket on a given project with a very long subject line which exceeds 255 chars and should not be ignored but chopped off. And if the subject line is still not long enough, we just add more text. And more text. Wow, this is really annoying. Especially, if you have nothing to say...'[0,255]
990 990 end
991 991
992 992 def test_first_keyword_should_be_matched
993 993 issue = submit_email('ticket_with_duplicate_keyword.eml', :allow_override => 'priority')
994 994 assert issue.is_a?(Issue)
995 995 assert_equal 'High', issue.priority.name
996 996 end
997 997
998 998 def test_keyword_after_delimiter_should_be_ignored
999 999 with_settings :mail_handler_body_delimiters => "== DELIMITER ==" do
1000 1000 issue = submit_email('ticket_with_keyword_after_delimiter.eml', :allow_override => 'priority')
1001 1001 assert issue.is_a?(Issue)
1002 1002 assert_equal 'Normal', issue.priority.name
1003 1003 end
1004 1004 end
1005 1005
1006 1006 def test_new_user_from_attributes_should_return_valid_user
1007 1007 to_test = {
1008 1008 # [address, name] => [login, firstname, lastname]
1009 1009 ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
1010 1010 ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
1011 1011 ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
1012 1012 ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
1013 1013 ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
1014 1014 ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
1015 1015 }
1016 1016
1017 1017 to_test.each do |attrs, expected|
1018 1018 user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
1019 1019
1020 1020 assert user.valid?, user.errors.full_messages.to_s
1021 1021 assert_equal attrs.first, user.mail
1022 1022 assert_equal expected[0], user.login
1023 1023 assert_equal expected[1], user.firstname
1024 1024 assert_equal expected[2], user.lastname
1025 1025 assert_equal 'only_my_events', user.mail_notification
1026 1026 end
1027 1027 end
1028 1028
1029 1029 def test_new_user_from_attributes_should_use_default_login_if_invalid
1030 1030 user = MailHandler.new_user_from_attributes('foo+bar@example.net')
1031 1031 assert user.valid?
1032 1032 assert user.login =~ /^user[a-f0-9]+$/
1033 1033 assert_equal 'foo+bar@example.net', user.mail
1034 1034 end
1035 1035
1036 1036 def test_new_user_with_utf8_encoded_fullname_should_be_decoded
1037 1037 assert_difference 'User.count' do
1038 1038 issue = submit_email(
1039 1039 'fullname_of_sender_as_utf8_encoded.eml',
1040 1040 :issue => {:project => 'ecookbook'},
1041 1041 :unknown_user => 'create'
1042 1042 )
1043 1043 end
1044 1044 user = User.order('id DESC').first
1045 1045 assert_equal "foo@example.org", user.mail
1046 1046 str1 = "\xc3\x84\xc3\xa4".force_encoding('UTF-8')
1047 1047 str2 = "\xc3\x96\xc3\xb6".force_encoding('UTF-8')
1048 1048 assert_equal str1, user.firstname
1049 1049 assert_equal str2, user.lastname
1050 1050 end
1051 1051
1052 1052 def test_extract_options_from_env_should_return_options
1053 1053 options = MailHandler.extract_options_from_env({
1054 1054 'tracker' => 'defect',
1055 1055 'project' => 'foo',
1056 1056 'unknown_user' => 'create'
1057 1057 })
1058 1058
1059 1059 assert_equal({
1060 1060 :issue => {:tracker => 'defect', :project => 'foo'},
1061 1061 :unknown_user => 'create'
1062 1062 }, options)
1063 1063 end
1064 1064
1065 1065 def test_safe_receive_should_rescue_exceptions_and_return_false
1066 1066 MailHandler.stubs(:receive).raises(Exception.new "Something went wrong")
1067 1067
1068 1068 assert_equal false, MailHandler.safe_receive
1069 1069 end
1070 1070
1071 1071 private
1072 1072
1073 1073 def submit_email(filename, options={})
1074 1074 raw = IO.read(File.join(FIXTURES_PATH, filename))
1075 1075 yield raw if block_given?
1076 1076 MailHandler.receive(raw, options)
1077 1077 end
1078 1078
1079 1079 def assert_issue_created(issue)
1080 1080 assert issue.is_a?(Issue)
1081 1081 assert !issue.new_record?
1082 1082 issue.reload
1083 1083 end
1084 1084 end
@@ -1,89 +1,89
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class UserPreferenceTest < ActiveSupport::TestCase
21 21 fixtures :users, :user_preferences
22 22
23 23 def test_hide_mail_should_default_to_true
24 24 preference = UserPreference.new
25 25 assert_equal true, preference.hide_mail
26 26 end
27 27
28 28 def test_hide_mail_should_default_to_false_with_setting
29 29 with_settings :default_users_hide_mail => '0' do
30 30 preference = UserPreference.new
31 31 assert_equal false, preference.hide_mail
32 32 end
33 33 end
34 34
35 def test_no_self_notified_should_default_to_true
36 preference = UserPreference.new
37 assert_equal true, preference.no_self_notified
38 end
39
35 40 def test_create
36 41 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
37 42 user.login = "newuser"
38 43 user.password, user.password_confirmation = "password", "password"
39 44 assert user.save
40 45
41 46 assert_kind_of UserPreference, user.pref
42 47 assert_kind_of Hash, user.pref.others
43 48 assert user.pref.save
44 49 end
45 50
46 51 def test_update
47 52 user = User.find(1)
48 53 assert_equal true, user.pref.hide_mail
49 54 user.pref['preftest'] = 'value'
50 55 assert user.pref.save
51 56
52 57 user.reload
53 58 assert_equal 'value', user.pref['preftest']
54 59 end
55 60
56 61 def test_others_hash
57 62 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
58 63 user.login = "newuser"
59 64 user.password, user.password_confirmation = "password", "password"
60 65 assert user.save
61 66 assert_nil user.preference
62 67 up = UserPreference.new(:user => user)
63 68 assert_kind_of Hash, up.others
64 69 up.others = nil
65 70 assert_nil up.others
66 71 assert up.save
67 72 assert_kind_of Hash, up.others
68 73 end
69 74
70 def test_others_should_be_blank_after_initialization
71 pref = User.new.pref
72 assert_equal({}, pref.others)
73 end
74
75 75 def test_reading_value_from_nil_others_hash
76 76 up = UserPreference.new(:user => User.new)
77 77 up.others = nil
78 78 assert_nil up.others
79 79 assert_nil up[:foo]
80 80 end
81 81
82 82 def test_writing_value_to_nil_others_hash
83 83 up = UserPreference.new(:user => User.new)
84 84 up.others = nil
85 85 assert_nil up.others
86 86 up[:foo] = 'bar'
87 87 assert_equal 'bar', up[:foo]
88 88 end
89 89 end
General Comments 0
You need to be logged in to leave comments. Login now