##// END OF EJS Templates
Use User#generate_password in MailHandler....
Jean-Philippe Lang -
r11227:e14caf8e33fb
parent child
Show More
@@ -1,498 +1,496
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class MailHandler < ActionMailer::Base
18 class MailHandler < ActionMailer::Base
19 include ActionView::Helpers::SanitizeHelper
19 include ActionView::Helpers::SanitizeHelper
20 include Redmine::I18n
20 include Redmine::I18n
21
21
22 class UnauthorizedAction < StandardError; end
22 class UnauthorizedAction < StandardError; end
23 class MissingInformation < StandardError; end
23 class MissingInformation < StandardError; end
24
24
25 attr_reader :email, :user
25 attr_reader :email, :user
26
26
27 def self.receive(email, options={})
27 def self.receive(email, options={})
28 @@handler_options = options.dup
28 @@handler_options = options.dup
29
29
30 @@handler_options[:issue] ||= {}
30 @@handler_options[:issue] ||= {}
31
31
32 if @@handler_options[:allow_override].is_a?(String)
32 if @@handler_options[:allow_override].is_a?(String)
33 @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip)
33 @@handler_options[:allow_override] = @@handler_options[:allow_override].split(',').collect(&:strip)
34 end
34 end
35 @@handler_options[:allow_override] ||= []
35 @@handler_options[:allow_override] ||= []
36 # Project needs to be overridable if not specified
36 # Project needs to be overridable if not specified
37 @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project)
37 @@handler_options[:allow_override] << 'project' unless @@handler_options[:issue].has_key?(:project)
38 # Status overridable by default
38 # Status overridable by default
39 @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status)
39 @@handler_options[:allow_override] << 'status' unless @@handler_options[:issue].has_key?(:status)
40
40
41 @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1' ? true : false)
41 @@handler_options[:no_permission_check] = (@@handler_options[:no_permission_check].to_s == '1' ? true : false)
42
42
43 email.force_encoding('ASCII-8BIT') if email.respond_to?(:force_encoding)
43 email.force_encoding('ASCII-8BIT') if email.respond_to?(:force_encoding)
44 super(email)
44 super(email)
45 end
45 end
46
46
47 def logger
47 def logger
48 Rails.logger
48 Rails.logger
49 end
49 end
50
50
51 cattr_accessor :ignored_emails_headers
51 cattr_accessor :ignored_emails_headers
52 @@ignored_emails_headers = {
52 @@ignored_emails_headers = {
53 'X-Auto-Response-Suppress' => 'oof',
53 'X-Auto-Response-Suppress' => 'oof',
54 'Auto-Submitted' => /^auto-/
54 'Auto-Submitted' => /^auto-/
55 }
55 }
56
56
57 # Processes incoming emails
57 # Processes incoming emails
58 # Returns the created object (eg. an issue, a message) or false
58 # Returns the created object (eg. an issue, a message) or false
59 def receive(email)
59 def receive(email)
60 @email = email
60 @email = email
61 sender_email = email.from.to_a.first.to_s.strip
61 sender_email = email.from.to_a.first.to_s.strip
62 # Ignore emails received from the application emission address to avoid hell cycles
62 # Ignore emails received from the application emission address to avoid hell cycles
63 if sender_email.downcase == Setting.mail_from.to_s.strip.downcase
63 if sender_email.downcase == Setting.mail_from.to_s.strip.downcase
64 if logger && logger.info
64 if logger && logger.info
65 logger.info "MailHandler: ignoring email from Redmine emission address [#{sender_email}]"
65 logger.info "MailHandler: ignoring email from Redmine emission address [#{sender_email}]"
66 end
66 end
67 return false
67 return false
68 end
68 end
69 # Ignore auto generated emails
69 # Ignore auto generated emails
70 self.class.ignored_emails_headers.each do |key, ignored_value|
70 self.class.ignored_emails_headers.each do |key, ignored_value|
71 value = email.header[key]
71 value = email.header[key]
72 if value
72 if value
73 value = value.to_s.downcase
73 value = value.to_s.downcase
74 if (ignored_value.is_a?(Regexp) && value.match(ignored_value)) || value == ignored_value
74 if (ignored_value.is_a?(Regexp) && value.match(ignored_value)) || value == ignored_value
75 if logger && logger.info
75 if logger && logger.info
76 logger.info "MailHandler: ignoring email with #{key}:#{value} header"
76 logger.info "MailHandler: ignoring email with #{key}:#{value} header"
77 end
77 end
78 return false
78 return false
79 end
79 end
80 end
80 end
81 end
81 end
82 @user = User.find_by_mail(sender_email) if sender_email.present?
82 @user = User.find_by_mail(sender_email) if sender_email.present?
83 if @user && !@user.active?
83 if @user && !@user.active?
84 if logger && logger.info
84 if logger && logger.info
85 logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]"
85 logger.info "MailHandler: ignoring email from non-active user [#{@user.login}]"
86 end
86 end
87 return false
87 return false
88 end
88 end
89 if @user.nil?
89 if @user.nil?
90 # Email was submitted by an unknown user
90 # Email was submitted by an unknown user
91 case @@handler_options[:unknown_user]
91 case @@handler_options[:unknown_user]
92 when 'accept'
92 when 'accept'
93 @user = User.anonymous
93 @user = User.anonymous
94 when 'create'
94 when 'create'
95 @user = create_user_from_email
95 @user = create_user_from_email
96 if @user
96 if @user
97 if logger && logger.info
97 if logger && logger.info
98 logger.info "MailHandler: [#{@user.login}] account created"
98 logger.info "MailHandler: [#{@user.login}] account created"
99 end
99 end
100 Mailer.account_information(@user, @user.password).deliver
100 Mailer.account_information(@user, @user.password).deliver
101 else
101 else
102 if logger && logger.error
102 if logger && logger.error
103 logger.error "MailHandler: could not create account for [#{sender_email}]"
103 logger.error "MailHandler: could not create account for [#{sender_email}]"
104 end
104 end
105 return false
105 return false
106 end
106 end
107 else
107 else
108 # Default behaviour, emails from unknown users are ignored
108 # Default behaviour, emails from unknown users are ignored
109 if logger && logger.info
109 if logger && logger.info
110 logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]"
110 logger.info "MailHandler: ignoring email from unknown user [#{sender_email}]"
111 end
111 end
112 return false
112 return false
113 end
113 end
114 end
114 end
115 User.current = @user
115 User.current = @user
116 dispatch
116 dispatch
117 end
117 end
118
118
119 private
119 private
120
120
121 MESSAGE_ID_RE = %r{^<?redmine\.([a-z0-9_]+)\-(\d+)\.\d+@}
121 MESSAGE_ID_RE = %r{^<?redmine\.([a-z0-9_]+)\-(\d+)\.\d+@}
122 ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]*#(\d+)\]}
122 ISSUE_REPLY_SUBJECT_RE = %r{\[[^\]]*#(\d+)\]}
123 MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]}
123 MESSAGE_REPLY_SUBJECT_RE = %r{\[[^\]]*msg(\d+)\]}
124
124
125 def dispatch
125 def dispatch
126 headers = [email.in_reply_to, email.references].flatten.compact
126 headers = [email.in_reply_to, email.references].flatten.compact
127 subject = email.subject.to_s
127 subject = email.subject.to_s
128 if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE}
128 if headers.detect {|h| h.to_s =~ MESSAGE_ID_RE}
129 klass, object_id = $1, $2.to_i
129 klass, object_id = $1, $2.to_i
130 method_name = "receive_#{klass}_reply"
130 method_name = "receive_#{klass}_reply"
131 if self.class.private_instance_methods.collect(&:to_s).include?(method_name)
131 if self.class.private_instance_methods.collect(&:to_s).include?(method_name)
132 send method_name, object_id
132 send method_name, object_id
133 else
133 else
134 # ignoring it
134 # ignoring it
135 end
135 end
136 elsif m = subject.match(ISSUE_REPLY_SUBJECT_RE)
136 elsif m = subject.match(ISSUE_REPLY_SUBJECT_RE)
137 receive_issue_reply(m[1].to_i)
137 receive_issue_reply(m[1].to_i)
138 elsif m = subject.match(MESSAGE_REPLY_SUBJECT_RE)
138 elsif m = subject.match(MESSAGE_REPLY_SUBJECT_RE)
139 receive_message_reply(m[1].to_i)
139 receive_message_reply(m[1].to_i)
140 else
140 else
141 dispatch_to_default
141 dispatch_to_default
142 end
142 end
143 rescue ActiveRecord::RecordInvalid => e
143 rescue ActiveRecord::RecordInvalid => e
144 # TODO: send a email to the user
144 # TODO: send a email to the user
145 logger.error e.message if logger
145 logger.error e.message if logger
146 false
146 false
147 rescue MissingInformation => e
147 rescue MissingInformation => e
148 logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger
148 logger.error "MailHandler: missing information from #{user}: #{e.message}" if logger
149 false
149 false
150 rescue UnauthorizedAction => e
150 rescue UnauthorizedAction => e
151 logger.error "MailHandler: unauthorized attempt from #{user}" if logger
151 logger.error "MailHandler: unauthorized attempt from #{user}" if logger
152 false
152 false
153 end
153 end
154
154
155 def dispatch_to_default
155 def dispatch_to_default
156 receive_issue
156 receive_issue
157 end
157 end
158
158
159 # Creates a new issue
159 # Creates a new issue
160 def receive_issue
160 def receive_issue
161 project = target_project
161 project = target_project
162 # check permission
162 # check permission
163 unless @@handler_options[:no_permission_check]
163 unless @@handler_options[:no_permission_check]
164 raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
164 raise UnauthorizedAction unless user.allowed_to?(:add_issues, project)
165 end
165 end
166
166
167 issue = Issue.new(:author => user, :project => project)
167 issue = Issue.new(:author => user, :project => project)
168 issue.safe_attributes = issue_attributes_from_keywords(issue)
168 issue.safe_attributes = issue_attributes_from_keywords(issue)
169 issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
169 issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
170 issue.subject = cleaned_up_subject
170 issue.subject = cleaned_up_subject
171 if issue.subject.blank?
171 if issue.subject.blank?
172 issue.subject = '(no subject)'
172 issue.subject = '(no subject)'
173 end
173 end
174 issue.description = cleaned_up_text_body
174 issue.description = cleaned_up_text_body
175
175
176 # add To and Cc as watchers before saving so the watchers can reply to Redmine
176 # add To and Cc as watchers before saving so the watchers can reply to Redmine
177 add_watchers(issue)
177 add_watchers(issue)
178 issue.save!
178 issue.save!
179 add_attachments(issue)
179 add_attachments(issue)
180 logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
180 logger.info "MailHandler: issue ##{issue.id} created by #{user}" if logger && logger.info
181 issue
181 issue
182 end
182 end
183
183
184 # Adds a note to an existing issue
184 # Adds a note to an existing issue
185 def receive_issue_reply(issue_id, from_journal=nil)
185 def receive_issue_reply(issue_id, from_journal=nil)
186 issue = Issue.find_by_id(issue_id)
186 issue = Issue.find_by_id(issue_id)
187 return unless issue
187 return unless issue
188 # check permission
188 # check permission
189 unless @@handler_options[:no_permission_check]
189 unless @@handler_options[:no_permission_check]
190 unless user.allowed_to?(:add_issue_notes, issue.project) ||
190 unless user.allowed_to?(:add_issue_notes, issue.project) ||
191 user.allowed_to?(:edit_issues, issue.project)
191 user.allowed_to?(:edit_issues, issue.project)
192 raise UnauthorizedAction
192 raise UnauthorizedAction
193 end
193 end
194 end
194 end
195
195
196 # ignore CLI-supplied defaults for new issues
196 # ignore CLI-supplied defaults for new issues
197 @@handler_options[:issue].clear
197 @@handler_options[:issue].clear
198
198
199 journal = issue.init_journal(user)
199 journal = issue.init_journal(user)
200 if from_journal && from_journal.private_notes?
200 if from_journal && from_journal.private_notes?
201 # If the received email was a reply to a private note, make the added note private
201 # If the received email was a reply to a private note, make the added note private
202 issue.private_notes = true
202 issue.private_notes = true
203 end
203 end
204 issue.safe_attributes = issue_attributes_from_keywords(issue)
204 issue.safe_attributes = issue_attributes_from_keywords(issue)
205 issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
205 issue.safe_attributes = {'custom_field_values' => custom_field_values_from_keywords(issue)}
206 journal.notes = cleaned_up_text_body
206 journal.notes = cleaned_up_text_body
207 add_attachments(issue)
207 add_attachments(issue)
208 issue.save!
208 issue.save!
209 if logger && logger.info
209 if logger && logger.info
210 logger.info "MailHandler: issue ##{issue.id} updated by #{user}"
210 logger.info "MailHandler: issue ##{issue.id} updated by #{user}"
211 end
211 end
212 journal
212 journal
213 end
213 end
214
214
215 # Reply will be added to the issue
215 # Reply will be added to the issue
216 def receive_journal_reply(journal_id)
216 def receive_journal_reply(journal_id)
217 journal = Journal.find_by_id(journal_id)
217 journal = Journal.find_by_id(journal_id)
218 if journal && journal.journalized_type == 'Issue'
218 if journal && journal.journalized_type == 'Issue'
219 receive_issue_reply(journal.journalized_id, journal)
219 receive_issue_reply(journal.journalized_id, journal)
220 end
220 end
221 end
221 end
222
222
223 # Receives a reply to a forum message
223 # Receives a reply to a forum message
224 def receive_message_reply(message_id)
224 def receive_message_reply(message_id)
225 message = Message.find_by_id(message_id)
225 message = Message.find_by_id(message_id)
226 if message
226 if message
227 message = message.root
227 message = message.root
228
228
229 unless @@handler_options[:no_permission_check]
229 unless @@handler_options[:no_permission_check]
230 raise UnauthorizedAction unless user.allowed_to?(:add_messages, message.project)
230 raise UnauthorizedAction unless user.allowed_to?(:add_messages, message.project)
231 end
231 end
232
232
233 if !message.locked?
233 if !message.locked?
234 reply = Message.new(:subject => cleaned_up_subject.gsub(%r{^.*msg\d+\]}, '').strip,
234 reply = Message.new(:subject => cleaned_up_subject.gsub(%r{^.*msg\d+\]}, '').strip,
235 :content => cleaned_up_text_body)
235 :content => cleaned_up_text_body)
236 reply.author = user
236 reply.author = user
237 reply.board = message.board
237 reply.board = message.board
238 message.children << reply
238 message.children << reply
239 add_attachments(reply)
239 add_attachments(reply)
240 reply
240 reply
241 else
241 else
242 if logger && logger.info
242 if logger && logger.info
243 logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic"
243 logger.info "MailHandler: ignoring reply from [#{sender_email}] to a locked topic"
244 end
244 end
245 end
245 end
246 end
246 end
247 end
247 end
248
248
249 def add_attachments(obj)
249 def add_attachments(obj)
250 if email.attachments && email.attachments.any?
250 if email.attachments && email.attachments.any?
251 email.attachments.each do |attachment|
251 email.attachments.each do |attachment|
252 filename = attachment.filename
252 filename = attachment.filename
253 unless filename.respond_to?(:encoding)
253 unless filename.respond_to?(:encoding)
254 # try to reencode to utf8 manually with ruby1.8
254 # try to reencode to utf8 manually with ruby1.8
255 h = attachment.header['Content-Disposition']
255 h = attachment.header['Content-Disposition']
256 unless h.nil?
256 unless h.nil?
257 begin
257 begin
258 if m = h.value.match(/filename\*[0-9\*]*=([^=']+)'/)
258 if m = h.value.match(/filename\*[0-9\*]*=([^=']+)'/)
259 filename = Redmine::CodesetUtil.to_utf8(filename, m[1])
259 filename = Redmine::CodesetUtil.to_utf8(filename, m[1])
260 elsif m = h.value.match(/filename=.*=\?([^\?]+)\?[BbQq]\?/)
260 elsif m = h.value.match(/filename=.*=\?([^\?]+)\?[BbQq]\?/)
261 # http://tools.ietf.org/html/rfc2047#section-4
261 # http://tools.ietf.org/html/rfc2047#section-4
262 filename = Redmine::CodesetUtil.to_utf8(filename, m[1])
262 filename = Redmine::CodesetUtil.to_utf8(filename, m[1])
263 end
263 end
264 rescue
264 rescue
265 # nop
265 # nop
266 end
266 end
267 end
267 end
268 end
268 end
269 obj.attachments << Attachment.create(:container => obj,
269 obj.attachments << Attachment.create(:container => obj,
270 :file => attachment.decoded,
270 :file => attachment.decoded,
271 :filename => filename,
271 :filename => filename,
272 :author => user,
272 :author => user,
273 :content_type => attachment.mime_type)
273 :content_type => attachment.mime_type)
274 end
274 end
275 end
275 end
276 end
276 end
277
277
278 # Adds To and Cc as watchers of the given object if the sender has the
278 # Adds To and Cc as watchers of the given object if the sender has the
279 # appropriate permission
279 # appropriate permission
280 def add_watchers(obj)
280 def add_watchers(obj)
281 if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project)
281 if user.allowed_to?("add_#{obj.class.name.underscore}_watchers".to_sym, obj.project)
282 addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase}
282 addresses = [email.to, email.cc].flatten.compact.uniq.collect {|a| a.strip.downcase}
283 unless addresses.empty?
283 unless addresses.empty?
284 watchers = User.active.where('LOWER(mail) IN (?)', addresses).all
284 watchers = User.active.where('LOWER(mail) IN (?)', addresses).all
285 watchers.each {|w| obj.add_watcher(w)}
285 watchers.each {|w| obj.add_watcher(w)}
286 end
286 end
287 end
287 end
288 end
288 end
289
289
290 def get_keyword(attr, options={})
290 def get_keyword(attr, options={})
291 @keywords ||= {}
291 @keywords ||= {}
292 if @keywords.has_key?(attr)
292 if @keywords.has_key?(attr)
293 @keywords[attr]
293 @keywords[attr]
294 else
294 else
295 @keywords[attr] = begin
295 @keywords[attr] = begin
296 if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) &&
296 if (options[:override] || @@handler_options[:allow_override].include?(attr.to_s)) &&
297 (v = extract_keyword!(plain_text_body, attr, options[:format]))
297 (v = extract_keyword!(plain_text_body, attr, options[:format]))
298 v
298 v
299 elsif !@@handler_options[:issue][attr].blank?
299 elsif !@@handler_options[:issue][attr].blank?
300 @@handler_options[:issue][attr]
300 @@handler_options[:issue][attr]
301 end
301 end
302 end
302 end
303 end
303 end
304 end
304 end
305
305
306 # Destructively extracts the value for +attr+ in +text+
306 # Destructively extracts the value for +attr+ in +text+
307 # Returns nil if no matching keyword found
307 # Returns nil if no matching keyword found
308 def extract_keyword!(text, attr, format=nil)
308 def extract_keyword!(text, attr, format=nil)
309 keys = [attr.to_s.humanize]
309 keys = [attr.to_s.humanize]
310 if attr.is_a?(Symbol)
310 if attr.is_a?(Symbol)
311 if user && user.language.present?
311 if user && user.language.present?
312 keys << l("field_#{attr}", :default => '', :locale => user.language)
312 keys << l("field_#{attr}", :default => '', :locale => user.language)
313 end
313 end
314 if Setting.default_language.present?
314 if Setting.default_language.present?
315 keys << l("field_#{attr}", :default => '', :locale => Setting.default_language)
315 keys << l("field_#{attr}", :default => '', :locale => Setting.default_language)
316 end
316 end
317 end
317 end
318 keys.reject! {|k| k.blank?}
318 keys.reject! {|k| k.blank?}
319 keys.collect! {|k| Regexp.escape(k)}
319 keys.collect! {|k| Regexp.escape(k)}
320 format ||= '.+'
320 format ||= '.+'
321 keyword = nil
321 keyword = nil
322 regexp = /^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i
322 regexp = /^(#{keys.join('|')})[ \t]*:[ \t]*(#{format})\s*$/i
323 if m = text.match(regexp)
323 if m = text.match(regexp)
324 keyword = m[2].strip
324 keyword = m[2].strip
325 text.gsub!(regexp, '')
325 text.gsub!(regexp, '')
326 end
326 end
327 keyword
327 keyword
328 end
328 end
329
329
330 def target_project
330 def target_project
331 # TODO: other ways to specify project:
331 # TODO: other ways to specify project:
332 # * parse the email To field
332 # * parse the email To field
333 # * specific project (eg. Setting.mail_handler_target_project)
333 # * specific project (eg. Setting.mail_handler_target_project)
334 target = Project.find_by_identifier(get_keyword(:project))
334 target = Project.find_by_identifier(get_keyword(:project))
335 raise MissingInformation.new('Unable to determine target project') if target.nil?
335 raise MissingInformation.new('Unable to determine target project') if target.nil?
336 target
336 target
337 end
337 end
338
338
339 # Returns a Hash of issue attributes extracted from keywords in the email body
339 # Returns a Hash of issue attributes extracted from keywords in the email body
340 def issue_attributes_from_keywords(issue)
340 def issue_attributes_from_keywords(issue)
341 assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_assignee_from_keyword(k, issue)
341 assigned_to = (k = get_keyword(:assigned_to, :override => true)) && find_assignee_from_keyword(k, issue)
342
342
343 attrs = {
343 attrs = {
344 'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.named(k).first.try(:id),
344 'tracker_id' => (k = get_keyword(:tracker)) && issue.project.trackers.named(k).first.try(:id),
345 'status_id' => (k = get_keyword(:status)) && IssueStatus.named(k).first.try(:id),
345 'status_id' => (k = get_keyword(:status)) && IssueStatus.named(k).first.try(:id),
346 'priority_id' => (k = get_keyword(:priority)) && IssuePriority.named(k).first.try(:id),
346 'priority_id' => (k = get_keyword(:priority)) && IssuePriority.named(k).first.try(:id),
347 'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.named(k).first.try(:id),
347 'category_id' => (k = get_keyword(:category)) && issue.project.issue_categories.named(k).first.try(:id),
348 'assigned_to_id' => assigned_to.try(:id),
348 'assigned_to_id' => assigned_to.try(:id),
349 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) &&
349 'fixed_version_id' => (k = get_keyword(:fixed_version, :override => true)) &&
350 issue.project.shared_versions.named(k).first.try(:id),
350 issue.project.shared_versions.named(k).first.try(:id),
351 'start_date' => get_keyword(:start_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
351 'start_date' => get_keyword(:start_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
352 'due_date' => get_keyword(:due_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
352 'due_date' => get_keyword(:due_date, :override => true, :format => '\d{4}-\d{2}-\d{2}'),
353 'estimated_hours' => get_keyword(:estimated_hours, :override => true),
353 'estimated_hours' => get_keyword(:estimated_hours, :override => true),
354 'done_ratio' => get_keyword(:done_ratio, :override => true, :format => '(\d|10)?0')
354 'done_ratio' => get_keyword(:done_ratio, :override => true, :format => '(\d|10)?0')
355 }.delete_if {|k, v| v.blank? }
355 }.delete_if {|k, v| v.blank? }
356
356
357 if issue.new_record? && attrs['tracker_id'].nil?
357 if issue.new_record? && attrs['tracker_id'].nil?
358 attrs['tracker_id'] = issue.project.trackers.first.try(:id)
358 attrs['tracker_id'] = issue.project.trackers.first.try(:id)
359 end
359 end
360
360
361 attrs
361 attrs
362 end
362 end
363
363
364 # Returns a Hash of issue custom field values extracted from keywords in the email body
364 # Returns a Hash of issue custom field values extracted from keywords in the email body
365 def custom_field_values_from_keywords(customized)
365 def custom_field_values_from_keywords(customized)
366 customized.custom_field_values.inject({}) do |h, v|
366 customized.custom_field_values.inject({}) do |h, v|
367 if keyword = get_keyword(v.custom_field.name, :override => true)
367 if keyword = get_keyword(v.custom_field.name, :override => true)
368 h[v.custom_field.id.to_s] = v.custom_field.value_from_keyword(keyword, customized)
368 h[v.custom_field.id.to_s] = v.custom_field.value_from_keyword(keyword, customized)
369 end
369 end
370 h
370 h
371 end
371 end
372 end
372 end
373
373
374 # Returns the text/plain part of the email
374 # Returns the text/plain part of the email
375 # If not found (eg. HTML-only email), returns the body with tags removed
375 # If not found (eg. HTML-only email), returns the body with tags removed
376 def plain_text_body
376 def plain_text_body
377 return @plain_text_body unless @plain_text_body.nil?
377 return @plain_text_body unless @plain_text_body.nil?
378
378
379 part = email.text_part || email.html_part || email
379 part = email.text_part || email.html_part || email
380 @plain_text_body = Redmine::CodesetUtil.to_utf8(part.body.decoded, part.charset)
380 @plain_text_body = Redmine::CodesetUtil.to_utf8(part.body.decoded, part.charset)
381
381
382 # strip html tags and remove doctype directive
382 # strip html tags and remove doctype directive
383 @plain_text_body = strip_tags(@plain_text_body.strip)
383 @plain_text_body = strip_tags(@plain_text_body.strip)
384 @plain_text_body.sub! %r{^<!DOCTYPE .*$}, ''
384 @plain_text_body.sub! %r{^<!DOCTYPE .*$}, ''
385 @plain_text_body
385 @plain_text_body
386 end
386 end
387
387
388 def cleaned_up_text_body
388 def cleaned_up_text_body
389 cleanup_body(plain_text_body)
389 cleanup_body(plain_text_body)
390 end
390 end
391
391
392 def cleaned_up_subject
392 def cleaned_up_subject
393 subject = email.subject.to_s
393 subject = email.subject.to_s
394 unless subject.respond_to?(:encoding)
394 unless subject.respond_to?(:encoding)
395 # try to reencode to utf8 manually with ruby1.8
395 # try to reencode to utf8 manually with ruby1.8
396 begin
396 begin
397 if h = email.header[:subject]
397 if h = email.header[:subject]
398 # http://tools.ietf.org/html/rfc2047#section-4
398 # http://tools.ietf.org/html/rfc2047#section-4
399 if m = h.value.match(/=\?([^\?]+)\?[BbQq]\?/)
399 if m = h.value.match(/=\?([^\?]+)\?[BbQq]\?/)
400 subject = Redmine::CodesetUtil.to_utf8(subject, m[1])
400 subject = Redmine::CodesetUtil.to_utf8(subject, m[1])
401 end
401 end
402 end
402 end
403 rescue
403 rescue
404 # nop
404 # nop
405 end
405 end
406 end
406 end
407 subject.strip[0,255]
407 subject.strip[0,255]
408 end
408 end
409
409
410 def self.full_sanitizer
410 def self.full_sanitizer
411 @full_sanitizer ||= HTML::FullSanitizer.new
411 @full_sanitizer ||= HTML::FullSanitizer.new
412 end
412 end
413
413
414 def self.assign_string_attribute_with_limit(object, attribute, value, limit=nil)
414 def self.assign_string_attribute_with_limit(object, attribute, value, limit=nil)
415 limit ||= object.class.columns_hash[attribute.to_s].limit || 255
415 limit ||= object.class.columns_hash[attribute.to_s].limit || 255
416 value = value.to_s.slice(0, limit)
416 value = value.to_s.slice(0, limit)
417 object.send("#{attribute}=", value)
417 object.send("#{attribute}=", value)
418 end
418 end
419
419
420 # Returns a User from an email address and a full name
420 # Returns a User from an email address and a full name
421 def self.new_user_from_attributes(email_address, fullname=nil)
421 def self.new_user_from_attributes(email_address, fullname=nil)
422 user = User.new
422 user = User.new
423
423
424 # Truncating the email address would result in an invalid format
424 # Truncating the email address would result in an invalid format
425 user.mail = email_address
425 user.mail = email_address
426 assign_string_attribute_with_limit(user, 'login', email_address, User::LOGIN_LENGTH_LIMIT)
426 assign_string_attribute_with_limit(user, 'login', email_address, User::LOGIN_LENGTH_LIMIT)
427
427
428 names = fullname.blank? ? email_address.gsub(/@.*$/, '').split('.') : fullname.split
428 names = fullname.blank? ? email_address.gsub(/@.*$/, '').split('.') : fullname.split
429 assign_string_attribute_with_limit(user, 'firstname', names.shift, 30)
429 assign_string_attribute_with_limit(user, 'firstname', names.shift, 30)
430 assign_string_attribute_with_limit(user, 'lastname', names.join(' '), 30)
430 assign_string_attribute_with_limit(user, 'lastname', names.join(' '), 30)
431 user.lastname = '-' if user.lastname.blank?
431 user.lastname = '-' if user.lastname.blank?
432
433 password_length = [Setting.password_min_length.to_i, 10].max
434 user.password = Redmine::Utils.random_hex(password_length / 2 + 1)
435 user.language = Setting.default_language
432 user.language = Setting.default_language
433 user.generate_password = true
436
434
437 unless user.valid?
435 unless user.valid?
438 user.login = "user#{Redmine::Utils.random_hex(6)}" unless user.errors[:login].blank?
436 user.login = "user#{Redmine::Utils.random_hex(6)}" unless user.errors[:login].blank?
439 user.firstname = "-" unless user.errors[:firstname].blank?
437 user.firstname = "-" unless user.errors[:firstname].blank?
440 (puts user.errors[:lastname];user.lastname = "-") unless user.errors[:lastname].blank?
438 (puts user.errors[:lastname];user.lastname = "-") unless user.errors[:lastname].blank?
441 end
439 end
442
440
443 user
441 user
444 end
442 end
445
443
446 # Creates a User for the +email+ sender
444 # Creates a User for the +email+ sender
447 # Returns the user or nil if it could not be created
445 # Returns the user or nil if it could not be created
448 def create_user_from_email
446 def create_user_from_email
449 from = email.header['from'].to_s
447 from = email.header['from'].to_s
450 addr, name = from, nil
448 addr, name = from, nil
451 if m = from.match(/^"?(.+?)"?\s+<(.+@.+)>$/)
449 if m = from.match(/^"?(.+?)"?\s+<(.+@.+)>$/)
452 addr, name = m[2], m[1]
450 addr, name = m[2], m[1]
453 end
451 end
454 if addr.present?
452 if addr.present?
455 user = self.class.new_user_from_attributes(addr, name)
453 user = self.class.new_user_from_attributes(addr, name)
456 if user.save
454 if user.save
457 user
455 user
458 else
456 else
459 logger.error "MailHandler: failed to create User: #{user.errors.full_messages}" if logger
457 logger.error "MailHandler: failed to create User: #{user.errors.full_messages}" if logger
460 nil
458 nil
461 end
459 end
462 else
460 else
463 logger.error "MailHandler: failed to create User: no FROM address found" if logger
461 logger.error "MailHandler: failed to create User: no FROM address found" if logger
464 nil
462 nil
465 end
463 end
466 end
464 end
467
465
468 # Removes the email body of text after the truncation configurations.
466 # Removes the email body of text after the truncation configurations.
469 def cleanup_body(body)
467 def cleanup_body(body)
470 delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)}
468 delimiters = Setting.mail_handler_body_delimiters.to_s.split(/[\r\n]+/).reject(&:blank?).map {|s| Regexp.escape(s)}
471 unless delimiters.empty?
469 unless delimiters.empty?
472 regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
470 regex = Regexp.new("^[> ]*(#{ delimiters.join('|') })\s*[\r\n].*", Regexp::MULTILINE)
473 body = body.gsub(regex, '')
471 body = body.gsub(regex, '')
474 end
472 end
475 body.strip
473 body.strip
476 end
474 end
477
475
478 def find_assignee_from_keyword(keyword, issue)
476 def find_assignee_from_keyword(keyword, issue)
479 keyword = keyword.to_s.downcase
477 keyword = keyword.to_s.downcase
480 assignable = issue.assignable_users
478 assignable = issue.assignable_users
481 assignee = nil
479 assignee = nil
482 assignee ||= assignable.detect {|a|
480 assignee ||= assignable.detect {|a|
483 a.mail.to_s.downcase == keyword ||
481 a.mail.to_s.downcase == keyword ||
484 a.login.to_s.downcase == keyword
482 a.login.to_s.downcase == keyword
485 }
483 }
486 if assignee.nil? && keyword.match(/ /)
484 if assignee.nil? && keyword.match(/ /)
487 firstname, lastname = *(keyword.split) # "First Last Throwaway"
485 firstname, lastname = *(keyword.split) # "First Last Throwaway"
488 assignee ||= assignable.detect {|a|
486 assignee ||= assignable.detect {|a|
489 a.is_a?(User) && a.firstname.to_s.downcase == firstname &&
487 a.is_a?(User) && a.firstname.to_s.downcase == firstname &&
490 a.lastname.to_s.downcase == lastname
488 a.lastname.to_s.downcase == lastname
491 }
489 }
492 end
490 end
493 if assignee.nil?
491 if assignee.nil?
494 assignee ||= assignable.detect {|a| a.name.downcase == keyword}
492 assignee ||= assignable.detect {|a| a.name.downcase == keyword}
495 end
493 end
496 assignee
494 assignee
497 end
495 end
498 end
496 end
@@ -1,776 +1,768
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2013 Jean-Philippe Lang
4 # Copyright (C) 2006-2013 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require File.expand_path('../../test_helper', __FILE__)
20 require File.expand_path('../../test_helper', __FILE__)
21
21
22 class MailHandlerTest < ActiveSupport::TestCase
22 class MailHandlerTest < ActiveSupport::TestCase
23 fixtures :users, :projects, :enabled_modules, :roles,
23 fixtures :users, :projects, :enabled_modules, :roles,
24 :members, :member_roles, :users,
24 :members, :member_roles, :users,
25 :issues, :issue_statuses,
25 :issues, :issue_statuses,
26 :workflows, :trackers, :projects_trackers,
26 :workflows, :trackers, :projects_trackers,
27 :versions, :enumerations, :issue_categories,
27 :versions, :enumerations, :issue_categories,
28 :custom_fields, :custom_fields_trackers, :custom_fields_projects,
28 :custom_fields, :custom_fields_trackers, :custom_fields_projects,
29 :boards, :messages
29 :boards, :messages
30
30
31 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
31 FIXTURES_PATH = File.dirname(__FILE__) + '/../fixtures/mail_handler'
32
32
33 def setup
33 def setup
34 ActionMailer::Base.deliveries.clear
34 ActionMailer::Base.deliveries.clear
35 Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
35 Setting.notified_events = Redmine::Notifiable.all.collect(&:name)
36 end
36 end
37
37
38 def teardown
38 def teardown
39 Setting.clear_cache
39 Setting.clear_cache
40 end
40 end
41
41
42 def test_add_issue
42 def test_add_issue
43 ActionMailer::Base.deliveries.clear
43 ActionMailer::Base.deliveries.clear
44 # This email contains: 'Project: onlinestore'
44 # This email contains: 'Project: onlinestore'
45 issue = submit_email('ticket_on_given_project.eml')
45 issue = submit_email('ticket_on_given_project.eml')
46 assert issue.is_a?(Issue)
46 assert issue.is_a?(Issue)
47 assert !issue.new_record?
47 assert !issue.new_record?
48 issue.reload
48 issue.reload
49 assert_equal Project.find(2), issue.project
49 assert_equal Project.find(2), issue.project
50 assert_equal issue.project.trackers.first, issue.tracker
50 assert_equal issue.project.trackers.first, issue.tracker
51 assert_equal 'New ticket on a given project', issue.subject
51 assert_equal 'New ticket on a given project', issue.subject
52 assert_equal User.find_by_login('jsmith'), issue.author
52 assert_equal User.find_by_login('jsmith'), issue.author
53 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
53 assert_equal IssueStatus.find_by_name('Resolved'), issue.status
54 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
54 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
55 assert_equal '2010-01-01', issue.start_date.to_s
55 assert_equal '2010-01-01', issue.start_date.to_s
56 assert_equal '2010-12-31', issue.due_date.to_s
56 assert_equal '2010-12-31', issue.due_date.to_s
57 assert_equal User.find_by_login('jsmith'), issue.assigned_to
57 assert_equal User.find_by_login('jsmith'), issue.assigned_to
58 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
58 assert_equal Version.find_by_name('Alpha'), issue.fixed_version
59 assert_equal 2.5, issue.estimated_hours
59 assert_equal 2.5, issue.estimated_hours
60 assert_equal 30, issue.done_ratio
60 assert_equal 30, issue.done_ratio
61 assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt]
61 assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt]
62 # keywords should be removed from the email body
62 # keywords should be removed from the email body
63 assert !issue.description.match(/^Project:/i)
63 assert !issue.description.match(/^Project:/i)
64 assert !issue.description.match(/^Status:/i)
64 assert !issue.description.match(/^Status:/i)
65 assert !issue.description.match(/^Start Date:/i)
65 assert !issue.description.match(/^Start Date:/i)
66 # Email notification should be sent
66 # Email notification should be sent
67 mail = ActionMailer::Base.deliveries.last
67 mail = ActionMailer::Base.deliveries.last
68 assert_not_nil mail
68 assert_not_nil mail
69 assert mail.subject.include?('New ticket on a given project')
69 assert mail.subject.include?('New ticket on a given project')
70 end
70 end
71
71
72 def test_add_issue_with_default_tracker
72 def test_add_issue_with_default_tracker
73 # This email contains: 'Project: onlinestore'
73 # This email contains: 'Project: onlinestore'
74 issue = submit_email(
74 issue = submit_email(
75 'ticket_on_given_project.eml',
75 'ticket_on_given_project.eml',
76 :issue => {:tracker => 'Support request'}
76 :issue => {:tracker => 'Support request'}
77 )
77 )
78 assert issue.is_a?(Issue)
78 assert issue.is_a?(Issue)
79 assert !issue.new_record?
79 assert !issue.new_record?
80 issue.reload
80 issue.reload
81 assert_equal 'Support request', issue.tracker.name
81 assert_equal 'Support request', issue.tracker.name
82 end
82 end
83
83
84 def test_add_issue_with_status
84 def test_add_issue_with_status
85 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
85 # This email contains: 'Project: onlinestore' and 'Status: Resolved'
86 issue = submit_email('ticket_on_given_project.eml')
86 issue = submit_email('ticket_on_given_project.eml')
87 assert issue.is_a?(Issue)
87 assert issue.is_a?(Issue)
88 assert !issue.new_record?
88 assert !issue.new_record?
89 issue.reload
89 issue.reload
90 assert_equal Project.find(2), issue.project
90 assert_equal Project.find(2), issue.project
91 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
91 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
92 end
92 end
93
93
94 def test_add_issue_with_attributes_override
94 def test_add_issue_with_attributes_override
95 issue = submit_email(
95 issue = submit_email(
96 'ticket_with_attributes.eml',
96 'ticket_with_attributes.eml',
97 :allow_override => 'tracker,category,priority'
97 :allow_override => 'tracker,category,priority'
98 )
98 )
99 assert issue.is_a?(Issue)
99 assert issue.is_a?(Issue)
100 assert !issue.new_record?
100 assert !issue.new_record?
101 issue.reload
101 issue.reload
102 assert_equal 'New ticket on a given project', issue.subject
102 assert_equal 'New ticket on a given project', issue.subject
103 assert_equal User.find_by_login('jsmith'), issue.author
103 assert_equal User.find_by_login('jsmith'), issue.author
104 assert_equal Project.find(2), issue.project
104 assert_equal Project.find(2), issue.project
105 assert_equal 'Feature request', issue.tracker.to_s
105 assert_equal 'Feature request', issue.tracker.to_s
106 assert_equal 'Stock management', issue.category.to_s
106 assert_equal 'Stock management', issue.category.to_s
107 assert_equal 'Urgent', issue.priority.to_s
107 assert_equal 'Urgent', issue.priority.to_s
108 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
108 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
109 end
109 end
110
110
111 def test_add_issue_with_group_assignment
111 def test_add_issue_with_group_assignment
112 with_settings :issue_group_assignment => '1' do
112 with_settings :issue_group_assignment => '1' do
113 issue = submit_email('ticket_on_given_project.eml') do |email|
113 issue = submit_email('ticket_on_given_project.eml') do |email|
114 email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
114 email.gsub!('Assigned to: John Smith', 'Assigned to: B Team')
115 end
115 end
116 assert issue.is_a?(Issue)
116 assert issue.is_a?(Issue)
117 assert !issue.new_record?
117 assert !issue.new_record?
118 issue.reload
118 issue.reload
119 assert_equal Group.find(11), issue.assigned_to
119 assert_equal Group.find(11), issue.assigned_to
120 end
120 end
121 end
121 end
122
122
123 def test_add_issue_with_partial_attributes_override
123 def test_add_issue_with_partial_attributes_override
124 issue = submit_email(
124 issue = submit_email(
125 'ticket_with_attributes.eml',
125 'ticket_with_attributes.eml',
126 :issue => {:priority => 'High'},
126 :issue => {:priority => 'High'},
127 :allow_override => ['tracker']
127 :allow_override => ['tracker']
128 )
128 )
129 assert issue.is_a?(Issue)
129 assert issue.is_a?(Issue)
130 assert !issue.new_record?
130 assert !issue.new_record?
131 issue.reload
131 issue.reload
132 assert_equal 'New ticket on a given project', issue.subject
132 assert_equal 'New ticket on a given project', issue.subject
133 assert_equal User.find_by_login('jsmith'), issue.author
133 assert_equal User.find_by_login('jsmith'), issue.author
134 assert_equal Project.find(2), issue.project
134 assert_equal Project.find(2), issue.project
135 assert_equal 'Feature request', issue.tracker.to_s
135 assert_equal 'Feature request', issue.tracker.to_s
136 assert_nil issue.category
136 assert_nil issue.category
137 assert_equal 'High', issue.priority.to_s
137 assert_equal 'High', issue.priority.to_s
138 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
138 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
139 end
139 end
140
140
141 def test_add_issue_with_spaces_between_attribute_and_separator
141 def test_add_issue_with_spaces_between_attribute_and_separator
142 issue = submit_email(
142 issue = submit_email(
143 'ticket_with_spaces_between_attribute_and_separator.eml',
143 'ticket_with_spaces_between_attribute_and_separator.eml',
144 :allow_override => 'tracker,category,priority'
144 :allow_override => 'tracker,category,priority'
145 )
145 )
146 assert issue.is_a?(Issue)
146 assert issue.is_a?(Issue)
147 assert !issue.new_record?
147 assert !issue.new_record?
148 issue.reload
148 issue.reload
149 assert_equal 'New ticket on a given project', issue.subject
149 assert_equal 'New ticket on a given project', issue.subject
150 assert_equal User.find_by_login('jsmith'), issue.author
150 assert_equal User.find_by_login('jsmith'), issue.author
151 assert_equal Project.find(2), issue.project
151 assert_equal Project.find(2), issue.project
152 assert_equal 'Feature request', issue.tracker.to_s
152 assert_equal 'Feature request', issue.tracker.to_s
153 assert_equal 'Stock management', issue.category.to_s
153 assert_equal 'Stock management', issue.category.to_s
154 assert_equal 'Urgent', issue.priority.to_s
154 assert_equal 'Urgent', issue.priority.to_s
155 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
155 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
156 end
156 end
157
157
158 def test_add_issue_with_attachment_to_specific_project
158 def test_add_issue_with_attachment_to_specific_project
159 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
159 issue = submit_email('ticket_with_attachment.eml', :issue => {:project => 'onlinestore'})
160 assert issue.is_a?(Issue)
160 assert issue.is_a?(Issue)
161 assert !issue.new_record?
161 assert !issue.new_record?
162 issue.reload
162 issue.reload
163 assert_equal 'Ticket created by email with attachment', issue.subject
163 assert_equal 'Ticket created by email with attachment', issue.subject
164 assert_equal User.find_by_login('jsmith'), issue.author
164 assert_equal User.find_by_login('jsmith'), issue.author
165 assert_equal Project.find(2), issue.project
165 assert_equal Project.find(2), issue.project
166 assert_equal 'This is a new ticket with attachments', issue.description
166 assert_equal 'This is a new ticket with attachments', issue.description
167 # Attachment properties
167 # Attachment properties
168 assert_equal 1, issue.attachments.size
168 assert_equal 1, issue.attachments.size
169 assert_equal 'Paella.jpg', issue.attachments.first.filename
169 assert_equal 'Paella.jpg', issue.attachments.first.filename
170 assert_equal 'image/jpeg', issue.attachments.first.content_type
170 assert_equal 'image/jpeg', issue.attachments.first.content_type
171 assert_equal 10790, issue.attachments.first.filesize
171 assert_equal 10790, issue.attachments.first.filesize
172 end
172 end
173
173
174 def test_add_issue_with_custom_fields
174 def test_add_issue_with_custom_fields
175 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'})
175 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'onlinestore'})
176 assert issue.is_a?(Issue)
176 assert issue.is_a?(Issue)
177 assert !issue.new_record?
177 assert !issue.new_record?
178 issue.reload
178 issue.reload
179 assert_equal 'New ticket with custom field values', issue.subject
179 assert_equal 'New ticket with custom field values', issue.subject
180 assert_equal 'PostgreSQL', issue.custom_field_value(1)
180 assert_equal 'PostgreSQL', issue.custom_field_value(1)
181 assert_equal 'Value for a custom field', issue.custom_field_value(2)
181 assert_equal 'Value for a custom field', issue.custom_field_value(2)
182 assert !issue.description.match(/^searchable field:/i)
182 assert !issue.description.match(/^searchable field:/i)
183 end
183 end
184
184
185 def test_add_issue_with_version_custom_fields
185 def test_add_issue_with_version_custom_fields
186 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
186 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1,2,3])
187
187
188 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'ecookbook'}) do |email|
188 issue = submit_email('ticket_with_custom_fields.eml', :issue => {:project => 'ecookbook'}) do |email|
189 email << "Affected version: 1.0\n"
189 email << "Affected version: 1.0\n"
190 end
190 end
191 assert issue.is_a?(Issue)
191 assert issue.is_a?(Issue)
192 assert !issue.new_record?
192 assert !issue.new_record?
193 issue.reload
193 issue.reload
194 assert_equal '2', issue.custom_field_value(field)
194 assert_equal '2', issue.custom_field_value(field)
195 end
195 end
196
196
197 def test_add_issue_should_match_assignee_on_display_name
197 def test_add_issue_should_match_assignee_on_display_name
198 user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz')
198 user = User.generate!(:firstname => 'Foo Bar', :lastname => 'Foo Baz')
199 User.add_to_project(user, Project.find(2))
199 User.add_to_project(user, Project.find(2))
200 issue = submit_email('ticket_on_given_project.eml') do |email|
200 issue = submit_email('ticket_on_given_project.eml') do |email|
201 email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz')
201 email.sub!(/^Assigned to.*$/, 'Assigned to: Foo Bar Foo baz')
202 end
202 end
203 assert issue.is_a?(Issue)
203 assert issue.is_a?(Issue)
204 assert_equal user, issue.assigned_to
204 assert_equal user, issue.assigned_to
205 end
205 end
206
206
207 def test_add_issue_with_cc
207 def test_add_issue_with_cc
208 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
208 issue = submit_email('ticket_with_cc.eml', :issue => {:project => 'ecookbook'})
209 assert issue.is_a?(Issue)
209 assert issue.is_a?(Issue)
210 assert !issue.new_record?
210 assert !issue.new_record?
211 issue.reload
211 issue.reload
212 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
212 assert issue.watched_by?(User.find_by_mail('dlopper@somenet.foo'))
213 assert_equal 1, issue.watcher_user_ids.size
213 assert_equal 1, issue.watcher_user_ids.size
214 end
214 end
215
215
216 def test_add_issue_by_unknown_user
216 def test_add_issue_by_unknown_user
217 assert_no_difference 'User.count' do
217 assert_no_difference 'User.count' do
218 assert_equal false,
218 assert_equal false,
219 submit_email(
219 submit_email(
220 'ticket_by_unknown_user.eml',
220 'ticket_by_unknown_user.eml',
221 :issue => {:project => 'ecookbook'}
221 :issue => {:project => 'ecookbook'}
222 )
222 )
223 end
223 end
224 end
224 end
225
225
226 def test_add_issue_by_anonymous_user
226 def test_add_issue_by_anonymous_user
227 Role.anonymous.add_permission!(:add_issues)
227 Role.anonymous.add_permission!(:add_issues)
228 assert_no_difference 'User.count' do
228 assert_no_difference 'User.count' do
229 issue = submit_email(
229 issue = submit_email(
230 'ticket_by_unknown_user.eml',
230 'ticket_by_unknown_user.eml',
231 :issue => {:project => 'ecookbook'},
231 :issue => {:project => 'ecookbook'},
232 :unknown_user => 'accept'
232 :unknown_user => 'accept'
233 )
233 )
234 assert issue.is_a?(Issue)
234 assert issue.is_a?(Issue)
235 assert issue.author.anonymous?
235 assert issue.author.anonymous?
236 end
236 end
237 end
237 end
238
238
239 def test_add_issue_by_anonymous_user_with_no_from_address
239 def test_add_issue_by_anonymous_user_with_no_from_address
240 Role.anonymous.add_permission!(:add_issues)
240 Role.anonymous.add_permission!(:add_issues)
241 assert_no_difference 'User.count' do
241 assert_no_difference 'User.count' do
242 issue = submit_email(
242 issue = submit_email(
243 'ticket_by_empty_user.eml',
243 'ticket_by_empty_user.eml',
244 :issue => {:project => 'ecookbook'},
244 :issue => {:project => 'ecookbook'},
245 :unknown_user => 'accept'
245 :unknown_user => 'accept'
246 )
246 )
247 assert issue.is_a?(Issue)
247 assert issue.is_a?(Issue)
248 assert issue.author.anonymous?
248 assert issue.author.anonymous?
249 end
249 end
250 end
250 end
251
251
252 def test_add_issue_by_anonymous_user_on_private_project
252 def test_add_issue_by_anonymous_user_on_private_project
253 Role.anonymous.add_permission!(:add_issues)
253 Role.anonymous.add_permission!(:add_issues)
254 assert_no_difference 'User.count' do
254 assert_no_difference 'User.count' do
255 assert_no_difference 'Issue.count' do
255 assert_no_difference 'Issue.count' do
256 assert_equal false,
256 assert_equal false,
257 submit_email(
257 submit_email(
258 'ticket_by_unknown_user.eml',
258 'ticket_by_unknown_user.eml',
259 :issue => {:project => 'onlinestore'},
259 :issue => {:project => 'onlinestore'},
260 :unknown_user => 'accept'
260 :unknown_user => 'accept'
261 )
261 )
262 end
262 end
263 end
263 end
264 end
264 end
265
265
266 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
266 def test_add_issue_by_anonymous_user_on_private_project_without_permission_check
267 assert_no_difference 'User.count' do
267 assert_no_difference 'User.count' do
268 assert_difference 'Issue.count' do
268 assert_difference 'Issue.count' do
269 issue = submit_email(
269 issue = submit_email(
270 'ticket_by_unknown_user.eml',
270 'ticket_by_unknown_user.eml',
271 :issue => {:project => 'onlinestore'},
271 :issue => {:project => 'onlinestore'},
272 :no_permission_check => '1',
272 :no_permission_check => '1',
273 :unknown_user => 'accept'
273 :unknown_user => 'accept'
274 )
274 )
275 assert issue.is_a?(Issue)
275 assert issue.is_a?(Issue)
276 assert issue.author.anonymous?
276 assert issue.author.anonymous?
277 assert !issue.project.is_public?
277 assert !issue.project.is_public?
278 assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt]
278 assert_equal [issue.id, 1, 2], [issue.root_id, issue.lft, issue.rgt]
279 end
279 end
280 end
280 end
281 end
281 end
282
282
283 def test_add_issue_by_created_user
283 def test_add_issue_by_created_user
284 Setting.default_language = 'en'
284 Setting.default_language = 'en'
285 assert_difference 'User.count' do
285 assert_difference 'User.count' do
286 issue = submit_email(
286 issue = submit_email(
287 'ticket_by_unknown_user.eml',
287 'ticket_by_unknown_user.eml',
288 :issue => {:project => 'ecookbook'},
288 :issue => {:project => 'ecookbook'},
289 :unknown_user => 'create'
289 :unknown_user => 'create'
290 )
290 )
291 assert issue.is_a?(Issue)
291 assert issue.is_a?(Issue)
292 assert issue.author.active?
292 assert issue.author.active?
293 assert_equal 'john.doe@somenet.foo', issue.author.mail
293 assert_equal 'john.doe@somenet.foo', issue.author.mail
294 assert_equal 'John', issue.author.firstname
294 assert_equal 'John', issue.author.firstname
295 assert_equal 'Doe', issue.author.lastname
295 assert_equal 'Doe', issue.author.lastname
296
296
297 # account information
297 # account information
298 email = ActionMailer::Base.deliveries.first
298 email = ActionMailer::Base.deliveries.first
299 assert_not_nil email
299 assert_not_nil email
300 assert email.subject.include?('account activation')
300 assert email.subject.include?('account activation')
301 login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
301 login = mail_body(email).match(/\* Login: (.*)$/)[1].strip
302 password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
302 password = mail_body(email).match(/\* Password: (.*)$/)[1].strip
303 assert_equal issue.author, User.try_to_login(login, password)
303 assert_equal issue.author, User.try_to_login(login, password)
304 end
304 end
305 end
305 end
306
306
307 def test_add_issue_without_from_header
307 def test_add_issue_without_from_header
308 Role.anonymous.add_permission!(:add_issues)
308 Role.anonymous.add_permission!(:add_issues)
309 assert_equal false, submit_email('ticket_without_from_header.eml')
309 assert_equal false, submit_email('ticket_without_from_header.eml')
310 end
310 end
311
311
312 def test_add_issue_with_invalid_attributes
312 def test_add_issue_with_invalid_attributes
313 issue = submit_email(
313 issue = submit_email(
314 'ticket_with_invalid_attributes.eml',
314 'ticket_with_invalid_attributes.eml',
315 :allow_override => 'tracker,category,priority'
315 :allow_override => 'tracker,category,priority'
316 )
316 )
317 assert issue.is_a?(Issue)
317 assert issue.is_a?(Issue)
318 assert !issue.new_record?
318 assert !issue.new_record?
319 issue.reload
319 issue.reload
320 assert_nil issue.assigned_to
320 assert_nil issue.assigned_to
321 assert_nil issue.start_date
321 assert_nil issue.start_date
322 assert_nil issue.due_date
322 assert_nil issue.due_date
323 assert_equal 0, issue.done_ratio
323 assert_equal 0, issue.done_ratio
324 assert_equal 'Normal', issue.priority.to_s
324 assert_equal 'Normal', issue.priority.to_s
325 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
325 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
326 end
326 end
327
327
328 def test_add_issue_with_localized_attributes
328 def test_add_issue_with_localized_attributes
329 User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
329 User.find_by_mail('jsmith@somenet.foo').update_attribute 'language', 'fr'
330 issue = submit_email(
330 issue = submit_email(
331 'ticket_with_localized_attributes.eml',
331 'ticket_with_localized_attributes.eml',
332 :allow_override => 'tracker,category,priority'
332 :allow_override => 'tracker,category,priority'
333 )
333 )
334 assert issue.is_a?(Issue)
334 assert issue.is_a?(Issue)
335 assert !issue.new_record?
335 assert !issue.new_record?
336 issue.reload
336 issue.reload
337 assert_equal 'New ticket on a given project', issue.subject
337 assert_equal 'New ticket on a given project', issue.subject
338 assert_equal User.find_by_login('jsmith'), issue.author
338 assert_equal User.find_by_login('jsmith'), issue.author
339 assert_equal Project.find(2), issue.project
339 assert_equal Project.find(2), issue.project
340 assert_equal 'Feature request', issue.tracker.to_s
340 assert_equal 'Feature request', issue.tracker.to_s
341 assert_equal 'Stock management', issue.category.to_s
341 assert_equal 'Stock management', issue.category.to_s
342 assert_equal 'Urgent', issue.priority.to_s
342 assert_equal 'Urgent', issue.priority.to_s
343 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
343 assert issue.description.include?('Lorem ipsum dolor sit amet, consectetuer adipiscing elit.')
344 end
344 end
345
345
346 def test_add_issue_with_japanese_keywords
346 def test_add_issue_with_japanese_keywords
347 ja_dev = "\xe9\x96\x8b\xe7\x99\xba"
347 ja_dev = "\xe9\x96\x8b\xe7\x99\xba"
348 ja_dev.force_encoding('UTF-8') if ja_dev.respond_to?(:force_encoding)
348 ja_dev.force_encoding('UTF-8') if ja_dev.respond_to?(:force_encoding)
349 tracker = Tracker.create!(:name => ja_dev)
349 tracker = Tracker.create!(:name => ja_dev)
350 Project.find(1).trackers << tracker
350 Project.find(1).trackers << tracker
351 issue = submit_email(
351 issue = submit_email(
352 'japanese_keywords_iso_2022_jp.eml',
352 'japanese_keywords_iso_2022_jp.eml',
353 :issue => {:project => 'ecookbook'},
353 :issue => {:project => 'ecookbook'},
354 :allow_override => 'tracker'
354 :allow_override => 'tracker'
355 )
355 )
356 assert_kind_of Issue, issue
356 assert_kind_of Issue, issue
357 assert_equal tracker, issue.tracker
357 assert_equal tracker, issue.tracker
358 end
358 end
359
359
360 def test_add_issue_from_apple_mail
360 def test_add_issue_from_apple_mail
361 issue = submit_email(
361 issue = submit_email(
362 'apple_mail_with_attachment.eml',
362 'apple_mail_with_attachment.eml',
363 :issue => {:project => 'ecookbook'}
363 :issue => {:project => 'ecookbook'}
364 )
364 )
365 assert_kind_of Issue, issue
365 assert_kind_of Issue, issue
366 assert_equal 1, issue.attachments.size
366 assert_equal 1, issue.attachments.size
367
367
368 attachment = issue.attachments.first
368 attachment = issue.attachments.first
369 assert_equal 'paella.jpg', attachment.filename
369 assert_equal 'paella.jpg', attachment.filename
370 assert_equal 10790, attachment.filesize
370 assert_equal 10790, attachment.filesize
371 assert File.exist?(attachment.diskfile)
371 assert File.exist?(attachment.diskfile)
372 assert_equal 10790, File.size(attachment.diskfile)
372 assert_equal 10790, File.size(attachment.diskfile)
373 assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
373 assert_equal 'caaf384198bcbc9563ab5c058acd73cd', attachment.digest
374 end
374 end
375
375
376 def test_thunderbird_with_attachment_ja
376 def test_thunderbird_with_attachment_ja
377 issue = submit_email(
377 issue = submit_email(
378 'thunderbird_with_attachment_ja.eml',
378 'thunderbird_with_attachment_ja.eml',
379 :issue => {:project => 'ecookbook'}
379 :issue => {:project => 'ecookbook'}
380 )
380 )
381 assert_kind_of Issue, issue
381 assert_kind_of Issue, issue
382 assert_equal 1, issue.attachments.size
382 assert_equal 1, issue.attachments.size
383 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt"
383 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt"
384 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
384 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
385 attachment = issue.attachments.first
385 attachment = issue.attachments.first
386 assert_equal ja, attachment.filename
386 assert_equal ja, attachment.filename
387 assert_equal 5, attachment.filesize
387 assert_equal 5, attachment.filesize
388 assert File.exist?(attachment.diskfile)
388 assert File.exist?(attachment.diskfile)
389 assert_equal 5, File.size(attachment.diskfile)
389 assert_equal 5, File.size(attachment.diskfile)
390 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
390 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
391 end
391 end
392
392
393 def test_gmail_with_attachment_ja
393 def test_gmail_with_attachment_ja
394 issue = submit_email(
394 issue = submit_email(
395 'gmail_with_attachment_ja.eml',
395 'gmail_with_attachment_ja.eml',
396 :issue => {:project => 'ecookbook'}
396 :issue => {:project => 'ecookbook'}
397 )
397 )
398 assert_kind_of Issue, issue
398 assert_kind_of Issue, issue
399 assert_equal 1, issue.attachments.size
399 assert_equal 1, issue.attachments.size
400 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt"
400 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88.txt"
401 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
401 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
402 attachment = issue.attachments.first
402 attachment = issue.attachments.first
403 assert_equal ja, attachment.filename
403 assert_equal ja, attachment.filename
404 assert_equal 5, attachment.filesize
404 assert_equal 5, attachment.filesize
405 assert File.exist?(attachment.diskfile)
405 assert File.exist?(attachment.diskfile)
406 assert_equal 5, File.size(attachment.diskfile)
406 assert_equal 5, File.size(attachment.diskfile)
407 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
407 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
408 end
408 end
409
409
410 def test_thunderbird_with_attachment_latin1
410 def test_thunderbird_with_attachment_latin1
411 issue = submit_email(
411 issue = submit_email(
412 'thunderbird_with_attachment_iso-8859-1.eml',
412 'thunderbird_with_attachment_iso-8859-1.eml',
413 :issue => {:project => 'ecookbook'}
413 :issue => {:project => 'ecookbook'}
414 )
414 )
415 assert_kind_of Issue, issue
415 assert_kind_of Issue, issue
416 assert_equal 1, issue.attachments.size
416 assert_equal 1, issue.attachments.size
417 u = ""
417 u = ""
418 u.force_encoding('UTF-8') if u.respond_to?(:force_encoding)
418 u.force_encoding('UTF-8') if u.respond_to?(:force_encoding)
419 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc"
419 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc"
420 u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding)
420 u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding)
421 11.times { u << u1 }
421 11.times { u << u1 }
422 attachment = issue.attachments.first
422 attachment = issue.attachments.first
423 assert_equal "#{u}.png", attachment.filename
423 assert_equal "#{u}.png", attachment.filename
424 assert_equal 130, attachment.filesize
424 assert_equal 130, attachment.filesize
425 assert File.exist?(attachment.diskfile)
425 assert File.exist?(attachment.diskfile)
426 assert_equal 130, File.size(attachment.diskfile)
426 assert_equal 130, File.size(attachment.diskfile)
427 assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest
427 assert_equal '4d80e667ac37dddfe05502530f152abb', attachment.digest
428 end
428 end
429
429
430 def test_gmail_with_attachment_latin1
430 def test_gmail_with_attachment_latin1
431 issue = submit_email(
431 issue = submit_email(
432 'gmail_with_attachment_iso-8859-1.eml',
432 'gmail_with_attachment_iso-8859-1.eml',
433 :issue => {:project => 'ecookbook'}
433 :issue => {:project => 'ecookbook'}
434 )
434 )
435 assert_kind_of Issue, issue
435 assert_kind_of Issue, issue
436 assert_equal 1, issue.attachments.size
436 assert_equal 1, issue.attachments.size
437 u = ""
437 u = ""
438 u.force_encoding('UTF-8') if u.respond_to?(:force_encoding)
438 u.force_encoding('UTF-8') if u.respond_to?(:force_encoding)
439 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc"
439 u1 = "\xc3\x84\xc3\xa4\xc3\x96\xc3\xb6\xc3\x9c\xc3\xbc"
440 u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding)
440 u1.force_encoding('UTF-8') if u1.respond_to?(:force_encoding)
441 11.times { u << u1 }
441 11.times { u << u1 }
442 attachment = issue.attachments.first
442 attachment = issue.attachments.first
443 assert_equal "#{u}.txt", attachment.filename
443 assert_equal "#{u}.txt", attachment.filename
444 assert_equal 5, attachment.filesize
444 assert_equal 5, attachment.filesize
445 assert File.exist?(attachment.diskfile)
445 assert File.exist?(attachment.diskfile)
446 assert_equal 5, File.size(attachment.diskfile)
446 assert_equal 5, File.size(attachment.diskfile)
447 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
447 assert_equal 'd8e8fca2dc0f896fd7cb4cb0031ba249', attachment.digest
448 end
448 end
449
449
450 def test_add_issue_with_iso_8859_1_subject
450 def test_add_issue_with_iso_8859_1_subject
451 issue = submit_email(
451 issue = submit_email(
452 'subject_as_iso-8859-1.eml',
452 'subject_as_iso-8859-1.eml',
453 :issue => {:project => 'ecookbook'}
453 :issue => {:project => 'ecookbook'}
454 )
454 )
455 str = "Testmail from Webmail: \xc3\xa4 \xc3\xb6 \xc3\xbc..."
455 str = "Testmail from Webmail: \xc3\xa4 \xc3\xb6 \xc3\xbc..."
456 str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
456 str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
457 assert_kind_of Issue, issue
457 assert_kind_of Issue, issue
458 assert_equal str, issue.subject
458 assert_equal str, issue.subject
459 end
459 end
460
460
461 def test_add_issue_with_japanese_subject
461 def test_add_issue_with_japanese_subject
462 issue = submit_email(
462 issue = submit_email(
463 'subject_japanese_1.eml',
463 'subject_japanese_1.eml',
464 :issue => {:project => 'ecookbook'}
464 :issue => {:project => 'ecookbook'}
465 )
465 )
466 assert_kind_of Issue, issue
466 assert_kind_of Issue, issue
467 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88"
467 ja = "\xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88"
468 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
468 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
469 assert_equal ja, issue.subject
469 assert_equal ja, issue.subject
470 end
470 end
471
471
472 def test_add_issue_with_no_subject_header
472 def test_add_issue_with_no_subject_header
473 issue = submit_email(
473 issue = submit_email(
474 'no_subject_header.eml',
474 'no_subject_header.eml',
475 :issue => {:project => 'ecookbook'}
475 :issue => {:project => 'ecookbook'}
476 )
476 )
477 assert_kind_of Issue, issue
477 assert_kind_of Issue, issue
478 assert_equal '(no subject)', issue.subject
478 assert_equal '(no subject)', issue.subject
479 end
479 end
480
480
481 def test_add_issue_with_mixed_japanese_subject
481 def test_add_issue_with_mixed_japanese_subject
482 issue = submit_email(
482 issue = submit_email(
483 'subject_japanese_2.eml',
483 'subject_japanese_2.eml',
484 :issue => {:project => 'ecookbook'}
484 :issue => {:project => 'ecookbook'}
485 )
485 )
486 assert_kind_of Issue, issue
486 assert_kind_of Issue, issue
487 ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88"
487 ja = "Re: \xe3\x83\x86\xe3\x82\xb9\xe3\x83\x88"
488 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
488 ja.force_encoding('UTF-8') if ja.respond_to?(:force_encoding)
489 assert_equal ja, issue.subject
489 assert_equal ja, issue.subject
490 end
490 end
491
491
492 def test_should_ignore_emails_from_locked_users
492 def test_should_ignore_emails_from_locked_users
493 User.find(2).lock!
493 User.find(2).lock!
494
494
495 MailHandler.any_instance.expects(:dispatch).never
495 MailHandler.any_instance.expects(:dispatch).never
496 assert_no_difference 'Issue.count' do
496 assert_no_difference 'Issue.count' do
497 assert_equal false, submit_email('ticket_on_given_project.eml')
497 assert_equal false, submit_email('ticket_on_given_project.eml')
498 end
498 end
499 end
499 end
500
500
501 def test_should_ignore_emails_from_emission_address
501 def test_should_ignore_emails_from_emission_address
502 Role.anonymous.add_permission!(:add_issues)
502 Role.anonymous.add_permission!(:add_issues)
503 assert_no_difference 'User.count' do
503 assert_no_difference 'User.count' do
504 assert_equal false,
504 assert_equal false,
505 submit_email(
505 submit_email(
506 'ticket_from_emission_address.eml',
506 'ticket_from_emission_address.eml',
507 :issue => {:project => 'ecookbook'},
507 :issue => {:project => 'ecookbook'},
508 :unknown_user => 'create'
508 :unknown_user => 'create'
509 )
509 )
510 end
510 end
511 end
511 end
512
512
513 def test_should_ignore_auto_replied_emails
513 def test_should_ignore_auto_replied_emails
514 MailHandler.any_instance.expects(:dispatch).never
514 MailHandler.any_instance.expects(:dispatch).never
515 [
515 [
516 "X-Auto-Response-Suppress: OOF",
516 "X-Auto-Response-Suppress: OOF",
517 "Auto-Submitted: auto-replied",
517 "Auto-Submitted: auto-replied",
518 "Auto-Submitted: Auto-Replied",
518 "Auto-Submitted: Auto-Replied",
519 "Auto-Submitted: auto-generated"
519 "Auto-Submitted: auto-generated"
520 ].each do |header|
520 ].each do |header|
521 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
521 raw = IO.read(File.join(FIXTURES_PATH, 'ticket_on_given_project.eml'))
522 raw = header + "\n" + raw
522 raw = header + "\n" + raw
523
523
524 assert_no_difference 'Issue.count' do
524 assert_no_difference 'Issue.count' do
525 assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
525 assert_equal false, MailHandler.receive(raw), "email with #{header} header was not ignored"
526 end
526 end
527 end
527 end
528 end
528 end
529
529
530 def test_add_issue_should_send_email_notification
530 def test_add_issue_should_send_email_notification
531 Setting.notified_events = ['issue_added']
531 Setting.notified_events = ['issue_added']
532 ActionMailer::Base.deliveries.clear
532 ActionMailer::Base.deliveries.clear
533 # This email contains: 'Project: onlinestore'
533 # This email contains: 'Project: onlinestore'
534 issue = submit_email('ticket_on_given_project.eml')
534 issue = submit_email('ticket_on_given_project.eml')
535 assert issue.is_a?(Issue)
535 assert issue.is_a?(Issue)
536 assert_equal 1, ActionMailer::Base.deliveries.size
536 assert_equal 1, ActionMailer::Base.deliveries.size
537 end
537 end
538
538
539 def test_update_issue
539 def test_update_issue
540 journal = submit_email('ticket_reply.eml')
540 journal = submit_email('ticket_reply.eml')
541 assert journal.is_a?(Journal)
541 assert journal.is_a?(Journal)
542 assert_equal User.find_by_login('jsmith'), journal.user
542 assert_equal User.find_by_login('jsmith'), journal.user
543 assert_equal Issue.find(2), journal.journalized
543 assert_equal Issue.find(2), journal.journalized
544 assert_match /This is reply/, journal.notes
544 assert_match /This is reply/, journal.notes
545 assert_equal false, journal.private_notes
545 assert_equal false, journal.private_notes
546 assert_equal 'Feature request', journal.issue.tracker.name
546 assert_equal 'Feature request', journal.issue.tracker.name
547 end
547 end
548
548
549 def test_update_issue_with_attribute_changes
549 def test_update_issue_with_attribute_changes
550 # This email contains: 'Status: Resolved'
550 # This email contains: 'Status: Resolved'
551 journal = submit_email('ticket_reply_with_status.eml')
551 journal = submit_email('ticket_reply_with_status.eml')
552 assert journal.is_a?(Journal)
552 assert journal.is_a?(Journal)
553 issue = Issue.find(journal.issue.id)
553 issue = Issue.find(journal.issue.id)
554 assert_equal User.find_by_login('jsmith'), journal.user
554 assert_equal User.find_by_login('jsmith'), journal.user
555 assert_equal Issue.find(2), journal.journalized
555 assert_equal Issue.find(2), journal.journalized
556 assert_match /This is reply/, journal.notes
556 assert_match /This is reply/, journal.notes
557 assert_equal 'Feature request', journal.issue.tracker.name
557 assert_equal 'Feature request', journal.issue.tracker.name
558 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
558 assert_equal IssueStatus.find_by_name("Resolved"), issue.status
559 assert_equal '2010-01-01', issue.start_date.to_s
559 assert_equal '2010-01-01', issue.start_date.to_s
560 assert_equal '2010-12-31', issue.due_date.to_s
560 assert_equal '2010-12-31', issue.due_date.to_s
561 assert_equal User.find_by_login('jsmith'), issue.assigned_to
561 assert_equal User.find_by_login('jsmith'), issue.assigned_to
562 assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
562 assert_equal "52.6", issue.custom_value_for(CustomField.find_by_name('Float field')).value
563 # keywords should be removed from the email body
563 # keywords should be removed from the email body
564 assert !journal.notes.match(/^Status:/i)
564 assert !journal.notes.match(/^Status:/i)
565 assert !journal.notes.match(/^Start Date:/i)
565 assert !journal.notes.match(/^Start Date:/i)
566 end
566 end
567
567
568 def test_update_issue_with_attachment
568 def test_update_issue_with_attachment
569 assert_difference 'Journal.count' do
569 assert_difference 'Journal.count' do
570 assert_difference 'JournalDetail.count' do
570 assert_difference 'JournalDetail.count' do
571 assert_difference 'Attachment.count' do
571 assert_difference 'Attachment.count' do
572 assert_no_difference 'Issue.count' do
572 assert_no_difference 'Issue.count' do
573 journal = submit_email('ticket_with_attachment.eml') do |raw|
573 journal = submit_email('ticket_with_attachment.eml') do |raw|
574 raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
574 raw.gsub! /^Subject: .*$/, 'Subject: Re: [Cookbook - Feature #2] (New) Add ingredients categories'
575 end
575 end
576 end
576 end
577 end
577 end
578 end
578 end
579 end
579 end
580 journal = Journal.first(:order => 'id DESC')
580 journal = Journal.first(:order => 'id DESC')
581 assert_equal Issue.find(2), journal.journalized
581 assert_equal Issue.find(2), journal.journalized
582 assert_equal 1, journal.details.size
582 assert_equal 1, journal.details.size
583
583
584 detail = journal.details.first
584 detail = journal.details.first
585 assert_equal 'attachment', detail.property
585 assert_equal 'attachment', detail.property
586 assert_equal 'Paella.jpg', detail.value
586 assert_equal 'Paella.jpg', detail.value
587 end
587 end
588
588
589 def test_update_issue_should_send_email_notification
589 def test_update_issue_should_send_email_notification
590 ActionMailer::Base.deliveries.clear
590 ActionMailer::Base.deliveries.clear
591 journal = submit_email('ticket_reply.eml')
591 journal = submit_email('ticket_reply.eml')
592 assert journal.is_a?(Journal)
592 assert journal.is_a?(Journal)
593 assert_equal 1, ActionMailer::Base.deliveries.size
593 assert_equal 1, ActionMailer::Base.deliveries.size
594 end
594 end
595
595
596 def test_update_issue_should_not_set_defaults
596 def test_update_issue_should_not_set_defaults
597 journal = submit_email(
597 journal = submit_email(
598 'ticket_reply.eml',
598 'ticket_reply.eml',
599 :issue => {:tracker => 'Support request', :priority => 'High'}
599 :issue => {:tracker => 'Support request', :priority => 'High'}
600 )
600 )
601 assert journal.is_a?(Journal)
601 assert journal.is_a?(Journal)
602 assert_match /This is reply/, journal.notes
602 assert_match /This is reply/, journal.notes
603 assert_equal 'Feature request', journal.issue.tracker.name
603 assert_equal 'Feature request', journal.issue.tracker.name
604 assert_equal 'Normal', journal.issue.priority.name
604 assert_equal 'Normal', journal.issue.priority.name
605 end
605 end
606
606
607 def test_replying_to_a_private_note_should_add_reply_as_private
607 def test_replying_to_a_private_note_should_add_reply_as_private
608 private_journal = Journal.create!(:notes => 'Private notes', :journalized => Issue.find(1), :private_notes => true, :user_id => 2)
608 private_journal = Journal.create!(:notes => 'Private notes', :journalized => Issue.find(1), :private_notes => true, :user_id => 2)
609
609
610 assert_difference 'Journal.count' do
610 assert_difference 'Journal.count' do
611 journal = submit_email('ticket_reply.eml') do |email|
611 journal = submit_email('ticket_reply.eml') do |email|
612 email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
612 email.sub! %r{^In-Reply-To:.*$}, "In-Reply-To: <redmine.journal-#{private_journal.id}.20060719210421@osiris>"
613 end
613 end
614
614
615 assert_kind_of Journal, journal
615 assert_kind_of Journal, journal
616 assert_match /This is reply/, journal.notes
616 assert_match /This is reply/, journal.notes
617 assert_equal true, journal.private_notes
617 assert_equal true, journal.private_notes
618 end
618 end
619 end
619 end
620
620
621 def test_reply_to_a_message
621 def test_reply_to_a_message
622 m = submit_email('message_reply.eml')
622 m = submit_email('message_reply.eml')
623 assert m.is_a?(Message)
623 assert m.is_a?(Message)
624 assert !m.new_record?
624 assert !m.new_record?
625 m.reload
625 m.reload
626 assert_equal 'Reply via email', m.subject
626 assert_equal 'Reply via email', m.subject
627 # The email replies to message #2 which is part of the thread of message #1
627 # The email replies to message #2 which is part of the thread of message #1
628 assert_equal Message.find(1), m.parent
628 assert_equal Message.find(1), m.parent
629 end
629 end
630
630
631 def test_reply_to_a_message_by_subject
631 def test_reply_to_a_message_by_subject
632 m = submit_email('message_reply_by_subject.eml')
632 m = submit_email('message_reply_by_subject.eml')
633 assert m.is_a?(Message)
633 assert m.is_a?(Message)
634 assert !m.new_record?
634 assert !m.new_record?
635 m.reload
635 m.reload
636 assert_equal 'Reply to the first post', m.subject
636 assert_equal 'Reply to the first post', m.subject
637 assert_equal Message.find(1), m.parent
637 assert_equal Message.find(1), m.parent
638 end
638 end
639
639
640 def test_should_strip_tags_of_html_only_emails
640 def test_should_strip_tags_of_html_only_emails
641 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
641 issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
642 assert issue.is_a?(Issue)
642 assert issue.is_a?(Issue)
643 assert !issue.new_record?
643 assert !issue.new_record?
644 issue.reload
644 issue.reload
645 assert_equal 'HTML email', issue.subject
645 assert_equal 'HTML email', issue.subject
646 assert_equal 'This is a html-only email.', issue.description
646 assert_equal 'This is a html-only email.', issue.description
647 end
647 end
648
648
649 test "truncate emails with no setting should add the entire email into the issue" do
649 test "truncate emails with no setting should add the entire email into the issue" do
650 with_settings :mail_handler_body_delimiters => '' do
650 with_settings :mail_handler_body_delimiters => '' do
651 issue = submit_email('ticket_on_given_project.eml')
651 issue = submit_email('ticket_on_given_project.eml')
652 assert_issue_created(issue)
652 assert_issue_created(issue)
653 assert issue.description.include?('---')
653 assert issue.description.include?('---')
654 assert issue.description.include?('This paragraph is after the delimiter')
654 assert issue.description.include?('This paragraph is after the delimiter')
655 end
655 end
656 end
656 end
657
657
658 test "truncate emails with a single string should truncate the email at the delimiter for the issue" do
658 test "truncate emails with a single string should truncate the email at the delimiter for the issue" do
659 with_settings :mail_handler_body_delimiters => '---' do
659 with_settings :mail_handler_body_delimiters => '---' do
660 issue = submit_email('ticket_on_given_project.eml')
660 issue = submit_email('ticket_on_given_project.eml')
661 assert_issue_created(issue)
661 assert_issue_created(issue)
662 assert issue.description.include?('This paragraph is before delimiters')
662 assert issue.description.include?('This paragraph is before delimiters')
663 assert issue.description.include?('--- This line starts with a delimiter')
663 assert issue.description.include?('--- This line starts with a delimiter')
664 assert !issue.description.match(/^---$/)
664 assert !issue.description.match(/^---$/)
665 assert !issue.description.include?('This paragraph is after the delimiter')
665 assert !issue.description.include?('This paragraph is after the delimiter')
666 end
666 end
667 end
667 end
668
668
669 test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do
669 test "truncate emails with a single quoted reply should truncate the email at the delimiter with the quoted reply symbols (>)" do
670 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
670 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
671 journal = submit_email('issue_update_with_quoted_reply_above.eml')
671 journal = submit_email('issue_update_with_quoted_reply_above.eml')
672 assert journal.is_a?(Journal)
672 assert journal.is_a?(Journal)
673 assert journal.notes.include?('An update to the issue by the sender.')
673 assert journal.notes.include?('An update to the issue by the sender.')
674 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
674 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
675 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
675 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
676 end
676 end
677 end
677 end
678
678
679 test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do
679 test "truncate emails with multiple quoted replies should truncate the email at the delimiter with the quoted reply symbols (>)" do
680 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
680 with_settings :mail_handler_body_delimiters => '--- Reply above. Do not remove this line. ---' do
681 journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
681 journal = submit_email('issue_update_with_multiple_quoted_reply_above.eml')
682 assert journal.is_a?(Journal)
682 assert journal.is_a?(Journal)
683 assert journal.notes.include?('An update to the issue by the sender.')
683 assert journal.notes.include?('An update to the issue by the sender.')
684 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
684 assert !journal.notes.match(Regexp.escape("--- Reply above. Do not remove this line. ---"))
685 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
685 assert !journal.notes.include?('Looks like the JSON api for projects was missed.')
686 end
686 end
687 end
687 end
688
688
689 test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do
689 test "truncate emails with multiple strings should truncate the email at the first delimiter found (BREAK)" do
690 with_settings :mail_handler_body_delimiters => "---\nBREAK" do
690 with_settings :mail_handler_body_delimiters => "---\nBREAK" do
691 issue = submit_email('ticket_on_given_project.eml')
691 issue = submit_email('ticket_on_given_project.eml')
692 assert_issue_created(issue)
692 assert_issue_created(issue)
693 assert issue.description.include?('This paragraph is before delimiters')
693 assert issue.description.include?('This paragraph is before delimiters')
694 assert !issue.description.include?('BREAK')
694 assert !issue.description.include?('BREAK')
695 assert !issue.description.include?('This paragraph is between delimiters')
695 assert !issue.description.include?('This paragraph is between delimiters')
696 assert !issue.description.match(/^---$/)
696 assert !issue.description.match(/^---$/)
697 assert !issue.description.include?('This paragraph is after the delimiter')
697 assert !issue.description.include?('This paragraph is after the delimiter')
698 end
698 end
699 end
699 end
700
700
701 def test_email_with_long_subject_line
701 def test_email_with_long_subject_line
702 issue = submit_email('ticket_with_long_subject.eml')
702 issue = submit_email('ticket_with_long_subject.eml')
703 assert issue.is_a?(Issue)
703 assert issue.is_a?(Issue)
704 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]
704 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]
705 end
705 end
706
706
707 def test_new_user_from_attributes_should_return_valid_user
707 def test_new_user_from_attributes_should_return_valid_user
708 to_test = {
708 to_test = {
709 # [address, name] => [login, firstname, lastname]
709 # [address, name] => [login, firstname, lastname]
710 ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
710 ['jsmith@example.net', nil] => ['jsmith@example.net', 'jsmith', '-'],
711 ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
711 ['jsmith@example.net', 'John'] => ['jsmith@example.net', 'John', '-'],
712 ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
712 ['jsmith@example.net', 'John Smith'] => ['jsmith@example.net', 'John', 'Smith'],
713 ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
713 ['jsmith@example.net', 'John Paul Smith'] => ['jsmith@example.net', 'John', 'Paul Smith'],
714 ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
714 ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsTheMaximumLength Smith'] => ['jsmith@example.net', 'AVeryLongFirstnameThatExceedsT', 'Smith'],
715 ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
715 ['jsmith@example.net', 'John AVeryLongLastnameThatExceedsTheMaximumLength'] => ['jsmith@example.net', 'John', 'AVeryLongLastnameThatExceedsTh']
716 }
716 }
717
717
718 to_test.each do |attrs, expected|
718 to_test.each do |attrs, expected|
719 user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
719 user = MailHandler.new_user_from_attributes(attrs.first, attrs.last)
720
720
721 assert user.valid?, user.errors.full_messages.to_s
721 assert user.valid?, user.errors.full_messages.to_s
722 assert_equal attrs.first, user.mail
722 assert_equal attrs.first, user.mail
723 assert_equal expected[0], user.login
723 assert_equal expected[0], user.login
724 assert_equal expected[1], user.firstname
724 assert_equal expected[1], user.firstname
725 assert_equal expected[2], user.lastname
725 assert_equal expected[2], user.lastname
726 end
726 end
727 end
727 end
728
728
729 def test_new_user_from_attributes_should_respect_minimum_password_length
730 with_settings :password_min_length => 15 do
731 user = MailHandler.new_user_from_attributes('jsmith@example.net')
732 assert user.valid?
733 assert user.password.length >= 15
734 end
735 end
736
737 def test_new_user_from_attributes_should_use_default_login_if_invalid
729 def test_new_user_from_attributes_should_use_default_login_if_invalid
738 user = MailHandler.new_user_from_attributes('foo+bar@example.net')
730 user = MailHandler.new_user_from_attributes('foo+bar@example.net')
739 assert user.valid?
731 assert user.valid?
740 assert user.login =~ /^user[a-f0-9]+$/
732 assert user.login =~ /^user[a-f0-9]+$/
741 assert_equal 'foo+bar@example.net', user.mail
733 assert_equal 'foo+bar@example.net', user.mail
742 end
734 end
743
735
744 def test_new_user_with_utf8_encoded_fullname_should_be_decoded
736 def test_new_user_with_utf8_encoded_fullname_should_be_decoded
745 assert_difference 'User.count' do
737 assert_difference 'User.count' do
746 issue = submit_email(
738 issue = submit_email(
747 'fullname_of_sender_as_utf8_encoded.eml',
739 'fullname_of_sender_as_utf8_encoded.eml',
748 :issue => {:project => 'ecookbook'},
740 :issue => {:project => 'ecookbook'},
749 :unknown_user => 'create'
741 :unknown_user => 'create'
750 )
742 )
751 end
743 end
752
744
753 user = User.first(:order => 'id DESC')
745 user = User.first(:order => 'id DESC')
754 assert_equal "foo@example.org", user.mail
746 assert_equal "foo@example.org", user.mail
755 str1 = "\xc3\x84\xc3\xa4"
747 str1 = "\xc3\x84\xc3\xa4"
756 str2 = "\xc3\x96\xc3\xb6"
748 str2 = "\xc3\x96\xc3\xb6"
757 str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
749 str1.force_encoding('UTF-8') if str1.respond_to?(:force_encoding)
758 str2.force_encoding('UTF-8') if str2.respond_to?(:force_encoding)
750 str2.force_encoding('UTF-8') if str2.respond_to?(:force_encoding)
759 assert_equal str1, user.firstname
751 assert_equal str1, user.firstname
760 assert_equal str2, user.lastname
752 assert_equal str2, user.lastname
761 end
753 end
762
754
763 private
755 private
764
756
765 def submit_email(filename, options={})
757 def submit_email(filename, options={})
766 raw = IO.read(File.join(FIXTURES_PATH, filename))
758 raw = IO.read(File.join(FIXTURES_PATH, filename))
767 yield raw if block_given?
759 yield raw if block_given?
768 MailHandler.receive(raw, options)
760 MailHandler.receive(raw, options)
769 end
761 end
770
762
771 def assert_issue_created(issue)
763 def assert_issue_created(issue)
772 assert issue.is_a?(Issue)
764 assert issue.is_a?(Issue)
773 assert !issue.new_record?
765 assert !issue.new_record?
774 issue.reload
766 issue.reload
775 end
767 end
776 end
768 end
@@ -1,1105 +1,1119
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
2 # Copyright (C) 2006-2013 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class UserTest < ActiveSupport::TestCase
20 class UserTest < ActiveSupport::TestCase
21 fixtures :users, :members, :projects, :roles, :member_roles, :auth_sources,
21 fixtures :users, :members, :projects, :roles, :member_roles, :auth_sources,
22 :trackers, :issue_statuses,
22 :trackers, :issue_statuses,
23 :projects_trackers,
23 :projects_trackers,
24 :watchers,
24 :watchers,
25 :issue_categories, :enumerations, :issues,
25 :issue_categories, :enumerations, :issues,
26 :journals, :journal_details,
26 :journals, :journal_details,
27 :groups_users,
27 :groups_users,
28 :enabled_modules
28 :enabled_modules
29
29
30 def setup
30 def setup
31 @admin = User.find(1)
31 @admin = User.find(1)
32 @jsmith = User.find(2)
32 @jsmith = User.find(2)
33 @dlopper = User.find(3)
33 @dlopper = User.find(3)
34 end
34 end
35
35
36 def test_sorted_scope_should_sort_user_by_display_name
36 def test_sorted_scope_should_sort_user_by_display_name
37 assert_equal User.all.map(&:name).map(&:downcase).sort, User.sorted.all.map(&:name).map(&:downcase)
37 assert_equal User.all.map(&:name).map(&:downcase).sort, User.sorted.all.map(&:name).map(&:downcase)
38 end
38 end
39
39
40 def test_generate
40 def test_generate
41 User.generate!(:firstname => 'Testing connection')
41 User.generate!(:firstname => 'Testing connection')
42 User.generate!(:firstname => 'Testing connection')
42 User.generate!(:firstname => 'Testing connection')
43 assert_equal 2, User.count(:all, :conditions => {:firstname => 'Testing connection'})
43 assert_equal 2, User.count(:all, :conditions => {:firstname => 'Testing connection'})
44 end
44 end
45
45
46 def test_truth
46 def test_truth
47 assert_kind_of User, @jsmith
47 assert_kind_of User, @jsmith
48 end
48 end
49
49
50 def test_mail_should_be_stripped
50 def test_mail_should_be_stripped
51 u = User.new
51 u = User.new
52 u.mail = " foo@bar.com "
52 u.mail = " foo@bar.com "
53 assert_equal "foo@bar.com", u.mail
53 assert_equal "foo@bar.com", u.mail
54 end
54 end
55
55
56 def test_mail_validation
56 def test_mail_validation
57 u = User.new
57 u = User.new
58 u.mail = ''
58 u.mail = ''
59 assert !u.valid?
59 assert !u.valid?
60 assert_include I18n.translate('activerecord.errors.messages.blank'), u.errors[:mail]
60 assert_include I18n.translate('activerecord.errors.messages.blank'), u.errors[:mail]
61 end
61 end
62
62
63 def test_login_length_validation
63 def test_login_length_validation
64 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
64 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
65 user.login = "x" * (User::LOGIN_LENGTH_LIMIT+1)
65 user.login = "x" * (User::LOGIN_LENGTH_LIMIT+1)
66 assert !user.valid?
66 assert !user.valid?
67
67
68 user.login = "x" * (User::LOGIN_LENGTH_LIMIT)
68 user.login = "x" * (User::LOGIN_LENGTH_LIMIT)
69 assert user.valid?
69 assert user.valid?
70 assert user.save
70 assert user.save
71 end
71 end
72
72
73 def test_generate_password_should_respect_minimum_password_length
74 with_settings :password_min_length => 15 do
75 user = User.generate!(:generate_password => true)
76 assert user.password.length >= 15
77 end
78 end
79
80 def test_generate_password_should_not_generate_password_with_less_than_10_characters
81 with_settings :password_min_length => 4 do
82 user = User.generate!(:generate_password => true)
83 assert user.password.length >= 10
84 end
85 end
86
73 def test_generate_password_on_create_should_set_password
87 def test_generate_password_on_create_should_set_password
74 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
88 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
75 user.login = "newuser"
89 user.login = "newuser"
76 user.generate_password = true
90 user.generate_password = true
77 assert user.save
91 assert user.save
78
92
79 password = user.password
93 password = user.password
80 assert user.check_password?(password)
94 assert user.check_password?(password)
81 end
95 end
82
96
83 def test_generate_password_on_update_should_update_password
97 def test_generate_password_on_update_should_update_password
84 user = User.find(2)
98 user = User.find(2)
85 hash = user.hashed_password
99 hash = user.hashed_password
86 user.generate_password = true
100 user.generate_password = true
87 assert user.save
101 assert user.save
88
102
89 password = user.password
103 password = user.password
90 assert user.check_password?(password)
104 assert user.check_password?(password)
91 assert_not_equal hash, user.reload.hashed_password
105 assert_not_equal hash, user.reload.hashed_password
92 end
106 end
93
107
94 def test_create
108 def test_create
95 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
109 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
96
110
97 user.login = "jsmith"
111 user.login = "jsmith"
98 user.password, user.password_confirmation = "password", "password"
112 user.password, user.password_confirmation = "password", "password"
99 # login uniqueness
113 # login uniqueness
100 assert !user.save
114 assert !user.save
101 assert_equal 1, user.errors.count
115 assert_equal 1, user.errors.count
102
116
103 user.login = "newuser"
117 user.login = "newuser"
104 user.password, user.password_confirmation = "password", "pass"
118 user.password, user.password_confirmation = "password", "pass"
105 # password confirmation
119 # password confirmation
106 assert !user.save
120 assert !user.save
107 assert_equal 1, user.errors.count
121 assert_equal 1, user.errors.count
108
122
109 user.password, user.password_confirmation = "password", "password"
123 user.password, user.password_confirmation = "password", "password"
110 assert user.save
124 assert user.save
111 end
125 end
112
126
113 def test_user_before_create_should_set_the_mail_notification_to_the_default_setting
127 def test_user_before_create_should_set_the_mail_notification_to_the_default_setting
114 @user1 = User.generate!
128 @user1 = User.generate!
115 assert_equal 'only_my_events', @user1.mail_notification
129 assert_equal 'only_my_events', @user1.mail_notification
116 with_settings :default_notification_option => 'all' do
130 with_settings :default_notification_option => 'all' do
117 @user2 = User.generate!
131 @user2 = User.generate!
118 assert_equal 'all', @user2.mail_notification
132 assert_equal 'all', @user2.mail_notification
119 end
133 end
120 end
134 end
121
135
122 def test_user_login_should_be_case_insensitive
136 def test_user_login_should_be_case_insensitive
123 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
137 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
124 u.login = 'newuser'
138 u.login = 'newuser'
125 u.password, u.password_confirmation = "password", "password"
139 u.password, u.password_confirmation = "password", "password"
126 assert u.save
140 assert u.save
127 u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo")
141 u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo")
128 u.login = 'NewUser'
142 u.login = 'NewUser'
129 u.password, u.password_confirmation = "password", "password"
143 u.password, u.password_confirmation = "password", "password"
130 assert !u.save
144 assert !u.save
131 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:login]
145 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:login]
132 end
146 end
133
147
134 def test_mail_uniqueness_should_not_be_case_sensitive
148 def test_mail_uniqueness_should_not_be_case_sensitive
135 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
149 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
136 u.login = 'newuser1'
150 u.login = 'newuser1'
137 u.password, u.password_confirmation = "password", "password"
151 u.password, u.password_confirmation = "password", "password"
138 assert u.save
152 assert u.save
139
153
140 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
154 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
141 u.login = 'newuser2'
155 u.login = 'newuser2'
142 u.password, u.password_confirmation = "password", "password"
156 u.password, u.password_confirmation = "password", "password"
143 assert !u.save
157 assert !u.save
144 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:mail]
158 assert_include I18n.translate('activerecord.errors.messages.taken'), u.errors[:mail]
145 end
159 end
146
160
147 def test_update
161 def test_update
148 assert_equal "admin", @admin.login
162 assert_equal "admin", @admin.login
149 @admin.login = "john"
163 @admin.login = "john"
150 assert @admin.save, @admin.errors.full_messages.join("; ")
164 assert @admin.save, @admin.errors.full_messages.join("; ")
151 @admin.reload
165 @admin.reload
152 assert_equal "john", @admin.login
166 assert_equal "john", @admin.login
153 end
167 end
154
168
155 def test_update_should_not_fail_for_legacy_user_with_different_case_logins
169 def test_update_should_not_fail_for_legacy_user_with_different_case_logins
156 u1 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser1@somenet.foo")
170 u1 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser1@somenet.foo")
157 u1.login = 'newuser1'
171 u1.login = 'newuser1'
158 assert u1.save
172 assert u1.save
159
173
160 u2 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser2@somenet.foo")
174 u2 = User.new(:firstname => "new", :lastname => "user", :mail => "newuser2@somenet.foo")
161 u2.login = 'newuser1'
175 u2.login = 'newuser1'
162 assert u2.save(:validate => false)
176 assert u2.save(:validate => false)
163
177
164 user = User.find(u2.id)
178 user = User.find(u2.id)
165 user.firstname = "firstname"
179 user.firstname = "firstname"
166 assert user.save, "Save failed"
180 assert user.save, "Save failed"
167 end
181 end
168
182
169 def test_destroy_should_delete_members_and_roles
183 def test_destroy_should_delete_members_and_roles
170 members = Member.find_all_by_user_id(2)
184 members = Member.find_all_by_user_id(2)
171 ms = members.size
185 ms = members.size
172 rs = members.collect(&:roles).flatten.size
186 rs = members.collect(&:roles).flatten.size
173
187
174 assert_difference 'Member.count', - ms do
188 assert_difference 'Member.count', - ms do
175 assert_difference 'MemberRole.count', - rs do
189 assert_difference 'MemberRole.count', - rs do
176 User.find(2).destroy
190 User.find(2).destroy
177 end
191 end
178 end
192 end
179
193
180 assert_nil User.find_by_id(2)
194 assert_nil User.find_by_id(2)
181 assert Member.find_all_by_user_id(2).empty?
195 assert Member.find_all_by_user_id(2).empty?
182 end
196 end
183
197
184 def test_destroy_should_update_attachments
198 def test_destroy_should_update_attachments
185 attachment = Attachment.create!(:container => Project.find(1),
199 attachment = Attachment.create!(:container => Project.find(1),
186 :file => uploaded_test_file("testfile.txt", "text/plain"),
200 :file => uploaded_test_file("testfile.txt", "text/plain"),
187 :author_id => 2)
201 :author_id => 2)
188
202
189 User.find(2).destroy
203 User.find(2).destroy
190 assert_nil User.find_by_id(2)
204 assert_nil User.find_by_id(2)
191 assert_equal User.anonymous, attachment.reload.author
205 assert_equal User.anonymous, attachment.reload.author
192 end
206 end
193
207
194 def test_destroy_should_update_comments
208 def test_destroy_should_update_comments
195 comment = Comment.create!(
209 comment = Comment.create!(
196 :commented => News.create!(:project_id => 1, :author_id => 1, :title => 'foo', :description => 'foo'),
210 :commented => News.create!(:project_id => 1, :author_id => 1, :title => 'foo', :description => 'foo'),
197 :author => User.find(2),
211 :author => User.find(2),
198 :comments => 'foo'
212 :comments => 'foo'
199 )
213 )
200
214
201 User.find(2).destroy
215 User.find(2).destroy
202 assert_nil User.find_by_id(2)
216 assert_nil User.find_by_id(2)
203 assert_equal User.anonymous, comment.reload.author
217 assert_equal User.anonymous, comment.reload.author
204 end
218 end
205
219
206 def test_destroy_should_update_issues
220 def test_destroy_should_update_issues
207 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
221 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
208
222
209 User.find(2).destroy
223 User.find(2).destroy
210 assert_nil User.find_by_id(2)
224 assert_nil User.find_by_id(2)
211 assert_equal User.anonymous, issue.reload.author
225 assert_equal User.anonymous, issue.reload.author
212 end
226 end
213
227
214 def test_destroy_should_unassign_issues
228 def test_destroy_should_unassign_issues
215 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
229 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
216
230
217 User.find(2).destroy
231 User.find(2).destroy
218 assert_nil User.find_by_id(2)
232 assert_nil User.find_by_id(2)
219 assert_nil issue.reload.assigned_to
233 assert_nil issue.reload.assigned_to
220 end
234 end
221
235
222 def test_destroy_should_update_journals
236 def test_destroy_should_update_journals
223 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
237 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
224 issue.init_journal(User.find(2), "update")
238 issue.init_journal(User.find(2), "update")
225 issue.save!
239 issue.save!
226
240
227 User.find(2).destroy
241 User.find(2).destroy
228 assert_nil User.find_by_id(2)
242 assert_nil User.find_by_id(2)
229 assert_equal User.anonymous, issue.journals.first.reload.user
243 assert_equal User.anonymous, issue.journals.first.reload.user
230 end
244 end
231
245
232 def test_destroy_should_update_journal_details_old_value
246 def test_destroy_should_update_journal_details_old_value
233 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
247 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
234 issue.init_journal(User.find(1), "update")
248 issue.init_journal(User.find(1), "update")
235 issue.assigned_to_id = nil
249 issue.assigned_to_id = nil
236 assert_difference 'JournalDetail.count' do
250 assert_difference 'JournalDetail.count' do
237 issue.save!
251 issue.save!
238 end
252 end
239 journal_detail = JournalDetail.first(:order => 'id DESC')
253 journal_detail = JournalDetail.first(:order => 'id DESC')
240 assert_equal '2', journal_detail.old_value
254 assert_equal '2', journal_detail.old_value
241
255
242 User.find(2).destroy
256 User.find(2).destroy
243 assert_nil User.find_by_id(2)
257 assert_nil User.find_by_id(2)
244 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
258 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
245 end
259 end
246
260
247 def test_destroy_should_update_journal_details_value
261 def test_destroy_should_update_journal_details_value
248 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
262 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
249 issue.init_journal(User.find(1), "update")
263 issue.init_journal(User.find(1), "update")
250 issue.assigned_to_id = 2
264 issue.assigned_to_id = 2
251 assert_difference 'JournalDetail.count' do
265 assert_difference 'JournalDetail.count' do
252 issue.save!
266 issue.save!
253 end
267 end
254 journal_detail = JournalDetail.first(:order => 'id DESC')
268 journal_detail = JournalDetail.first(:order => 'id DESC')
255 assert_equal '2', journal_detail.value
269 assert_equal '2', journal_detail.value
256
270
257 User.find(2).destroy
271 User.find(2).destroy
258 assert_nil User.find_by_id(2)
272 assert_nil User.find_by_id(2)
259 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
273 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
260 end
274 end
261
275
262 def test_destroy_should_update_messages
276 def test_destroy_should_update_messages
263 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
277 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
264 message = Message.create!(:board_id => board.id, :author_id => 2, :subject => 'foo', :content => 'foo')
278 message = Message.create!(:board_id => board.id, :author_id => 2, :subject => 'foo', :content => 'foo')
265
279
266 User.find(2).destroy
280 User.find(2).destroy
267 assert_nil User.find_by_id(2)
281 assert_nil User.find_by_id(2)
268 assert_equal User.anonymous, message.reload.author
282 assert_equal User.anonymous, message.reload.author
269 end
283 end
270
284
271 def test_destroy_should_update_news
285 def test_destroy_should_update_news
272 news = News.create!(:project_id => 1, :author_id => 2, :title => 'foo', :description => 'foo')
286 news = News.create!(:project_id => 1, :author_id => 2, :title => 'foo', :description => 'foo')
273
287
274 User.find(2).destroy
288 User.find(2).destroy
275 assert_nil User.find_by_id(2)
289 assert_nil User.find_by_id(2)
276 assert_equal User.anonymous, news.reload.author
290 assert_equal User.anonymous, news.reload.author
277 end
291 end
278
292
279 def test_destroy_should_delete_private_queries
293 def test_destroy_should_delete_private_queries
280 query = Query.new(:name => 'foo', :is_public => false)
294 query = Query.new(:name => 'foo', :is_public => false)
281 query.project_id = 1
295 query.project_id = 1
282 query.user_id = 2
296 query.user_id = 2
283 query.save!
297 query.save!
284
298
285 User.find(2).destroy
299 User.find(2).destroy
286 assert_nil User.find_by_id(2)
300 assert_nil User.find_by_id(2)
287 assert_nil Query.find_by_id(query.id)
301 assert_nil Query.find_by_id(query.id)
288 end
302 end
289
303
290 def test_destroy_should_update_public_queries
304 def test_destroy_should_update_public_queries
291 query = Query.new(:name => 'foo', :is_public => true)
305 query = Query.new(:name => 'foo', :is_public => true)
292 query.project_id = 1
306 query.project_id = 1
293 query.user_id = 2
307 query.user_id = 2
294 query.save!
308 query.save!
295
309
296 User.find(2).destroy
310 User.find(2).destroy
297 assert_nil User.find_by_id(2)
311 assert_nil User.find_by_id(2)
298 assert_equal User.anonymous, query.reload.user
312 assert_equal User.anonymous, query.reload.user
299 end
313 end
300
314
301 def test_destroy_should_update_time_entries
315 def test_destroy_should_update_time_entries
302 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today, :activity => TimeEntryActivity.create!(:name => 'foo'))
316 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today, :activity => TimeEntryActivity.create!(:name => 'foo'))
303 entry.project_id = 1
317 entry.project_id = 1
304 entry.user_id = 2
318 entry.user_id = 2
305 entry.save!
319 entry.save!
306
320
307 User.find(2).destroy
321 User.find(2).destroy
308 assert_nil User.find_by_id(2)
322 assert_nil User.find_by_id(2)
309 assert_equal User.anonymous, entry.reload.user
323 assert_equal User.anonymous, entry.reload.user
310 end
324 end
311
325
312 def test_destroy_should_delete_tokens
326 def test_destroy_should_delete_tokens
313 token = Token.create!(:user_id => 2, :value => 'foo')
327 token = Token.create!(:user_id => 2, :value => 'foo')
314
328
315 User.find(2).destroy
329 User.find(2).destroy
316 assert_nil User.find_by_id(2)
330 assert_nil User.find_by_id(2)
317 assert_nil Token.find_by_id(token.id)
331 assert_nil Token.find_by_id(token.id)
318 end
332 end
319
333
320 def test_destroy_should_delete_watchers
334 def test_destroy_should_delete_watchers
321 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
335 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
322 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
336 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
323
337
324 User.find(2).destroy
338 User.find(2).destroy
325 assert_nil User.find_by_id(2)
339 assert_nil User.find_by_id(2)
326 assert_nil Watcher.find_by_id(watcher.id)
340 assert_nil Watcher.find_by_id(watcher.id)
327 end
341 end
328
342
329 def test_destroy_should_update_wiki_contents
343 def test_destroy_should_update_wiki_contents
330 wiki_content = WikiContent.create!(
344 wiki_content = WikiContent.create!(
331 :text => 'foo',
345 :text => 'foo',
332 :author_id => 2,
346 :author_id => 2,
333 :page => WikiPage.create!(:title => 'Foo', :wiki => Wiki.create!(:project_id => 1, :start_page => 'Start'))
347 :page => WikiPage.create!(:title => 'Foo', :wiki => Wiki.create!(:project_id => 1, :start_page => 'Start'))
334 )
348 )
335 wiki_content.text = 'bar'
349 wiki_content.text = 'bar'
336 assert_difference 'WikiContent::Version.count' do
350 assert_difference 'WikiContent::Version.count' do
337 wiki_content.save!
351 wiki_content.save!
338 end
352 end
339
353
340 User.find(2).destroy
354 User.find(2).destroy
341 assert_nil User.find_by_id(2)
355 assert_nil User.find_by_id(2)
342 assert_equal User.anonymous, wiki_content.reload.author
356 assert_equal User.anonymous, wiki_content.reload.author
343 wiki_content.versions.each do |version|
357 wiki_content.versions.each do |version|
344 assert_equal User.anonymous, version.reload.author
358 assert_equal User.anonymous, version.reload.author
345 end
359 end
346 end
360 end
347
361
348 def test_destroy_should_nullify_issue_categories
362 def test_destroy_should_nullify_issue_categories
349 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
363 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
350
364
351 User.find(2).destroy
365 User.find(2).destroy
352 assert_nil User.find_by_id(2)
366 assert_nil User.find_by_id(2)
353 assert_nil category.reload.assigned_to_id
367 assert_nil category.reload.assigned_to_id
354 end
368 end
355
369
356 def test_destroy_should_nullify_changesets
370 def test_destroy_should_nullify_changesets
357 changeset = Changeset.create!(
371 changeset = Changeset.create!(
358 :repository => Repository::Subversion.create!(
372 :repository => Repository::Subversion.create!(
359 :project_id => 1,
373 :project_id => 1,
360 :url => 'file:///tmp',
374 :url => 'file:///tmp',
361 :identifier => 'tmp'
375 :identifier => 'tmp'
362 ),
376 ),
363 :revision => '12',
377 :revision => '12',
364 :committed_on => Time.now,
378 :committed_on => Time.now,
365 :committer => 'jsmith'
379 :committer => 'jsmith'
366 )
380 )
367 assert_equal 2, changeset.user_id
381 assert_equal 2, changeset.user_id
368
382
369 User.find(2).destroy
383 User.find(2).destroy
370 assert_nil User.find_by_id(2)
384 assert_nil User.find_by_id(2)
371 assert_nil changeset.reload.user_id
385 assert_nil changeset.reload.user_id
372 end
386 end
373
387
374 def test_anonymous_user_should_not_be_destroyable
388 def test_anonymous_user_should_not_be_destroyable
375 assert_no_difference 'User.count' do
389 assert_no_difference 'User.count' do
376 assert_equal false, User.anonymous.destroy
390 assert_equal false, User.anonymous.destroy
377 end
391 end
378 end
392 end
379
393
380 def test_validate_login_presence
394 def test_validate_login_presence
381 @admin.login = ""
395 @admin.login = ""
382 assert !@admin.save
396 assert !@admin.save
383 assert_equal 1, @admin.errors.count
397 assert_equal 1, @admin.errors.count
384 end
398 end
385
399
386 def test_validate_mail_notification_inclusion
400 def test_validate_mail_notification_inclusion
387 u = User.new
401 u = User.new
388 u.mail_notification = 'foo'
402 u.mail_notification = 'foo'
389 u.save
403 u.save
390 assert_not_nil u.errors[:mail_notification]
404 assert_not_nil u.errors[:mail_notification]
391 end
405 end
392
406
393 context "User#try_to_login" do
407 context "User#try_to_login" do
394 should "fall-back to case-insensitive if user login is not found as-typed." do
408 should "fall-back to case-insensitive if user login is not found as-typed." do
395 user = User.try_to_login("AdMin", "admin")
409 user = User.try_to_login("AdMin", "admin")
396 assert_kind_of User, user
410 assert_kind_of User, user
397 assert_equal "admin", user.login
411 assert_equal "admin", user.login
398 end
412 end
399
413
400 should "select the exact matching user first" do
414 should "select the exact matching user first" do
401 case_sensitive_user = User.generate! do |user|
415 case_sensitive_user = User.generate! do |user|
402 user.password = "admin123"
416 user.password = "admin123"
403 end
417 end
404 # bypass validations to make it appear like existing data
418 # bypass validations to make it appear like existing data
405 case_sensitive_user.update_attribute(:login, 'ADMIN')
419 case_sensitive_user.update_attribute(:login, 'ADMIN')
406
420
407 user = User.try_to_login("ADMIN", "admin123")
421 user = User.try_to_login("ADMIN", "admin123")
408 assert_kind_of User, user
422 assert_kind_of User, user
409 assert_equal "ADMIN", user.login
423 assert_equal "ADMIN", user.login
410
424
411 end
425 end
412 end
426 end
413
427
414 def test_password
428 def test_password
415 user = User.try_to_login("admin", "admin")
429 user = User.try_to_login("admin", "admin")
416 assert_kind_of User, user
430 assert_kind_of User, user
417 assert_equal "admin", user.login
431 assert_equal "admin", user.login
418 user.password = "hello123"
432 user.password = "hello123"
419 assert user.save
433 assert user.save
420
434
421 user = User.try_to_login("admin", "hello123")
435 user = User.try_to_login("admin", "hello123")
422 assert_kind_of User, user
436 assert_kind_of User, user
423 assert_equal "admin", user.login
437 assert_equal "admin", user.login
424 end
438 end
425
439
426 def test_validate_password_length
440 def test_validate_password_length
427 with_settings :password_min_length => '100' do
441 with_settings :password_min_length => '100' do
428 user = User.new(:firstname => "new100", :lastname => "user100", :mail => "newuser100@somenet.foo")
442 user = User.new(:firstname => "new100", :lastname => "user100", :mail => "newuser100@somenet.foo")
429 user.login = "newuser100"
443 user.login = "newuser100"
430 user.password, user.password_confirmation = "password100", "password100"
444 user.password, user.password_confirmation = "password100", "password100"
431 assert !user.save
445 assert !user.save
432 assert_equal 1, user.errors.count
446 assert_equal 1, user.errors.count
433 end
447 end
434 end
448 end
435
449
436 def test_name_format
450 def test_name_format
437 assert_equal 'John S.', @jsmith.name(:firstname_lastinitial)
451 assert_equal 'John S.', @jsmith.name(:firstname_lastinitial)
438 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
452 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
439 with_settings :user_format => :firstname_lastname do
453 with_settings :user_format => :firstname_lastname do
440 assert_equal 'John Smith', @jsmith.reload.name
454 assert_equal 'John Smith', @jsmith.reload.name
441 end
455 end
442 with_settings :user_format => :username do
456 with_settings :user_format => :username do
443 assert_equal 'jsmith', @jsmith.reload.name
457 assert_equal 'jsmith', @jsmith.reload.name
444 end
458 end
445 with_settings :user_format => :lastname do
459 with_settings :user_format => :lastname do
446 assert_equal 'Smith', @jsmith.reload.name
460 assert_equal 'Smith', @jsmith.reload.name
447 end
461 end
448 end
462 end
449
463
450 def test_today_should_return_the_day_according_to_user_time_zone
464 def test_today_should_return_the_day_according_to_user_time_zone
451 preference = User.find(1).pref
465 preference = User.find(1).pref
452 date = Date.new(2012, 05, 15)
466 date = Date.new(2012, 05, 15)
453 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
467 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
454 Date.stubs(:today).returns(date)
468 Date.stubs(:today).returns(date)
455 Time.stubs(:now).returns(time)
469 Time.stubs(:now).returns(time)
456
470
457 preference.update_attribute :time_zone, 'Baku' # UTC+4
471 preference.update_attribute :time_zone, 'Baku' # UTC+4
458 assert_equal '2012-05-16', User.find(1).today.to_s
472 assert_equal '2012-05-16', User.find(1).today.to_s
459
473
460 preference.update_attribute :time_zone, 'La Paz' # UTC-4
474 preference.update_attribute :time_zone, 'La Paz' # UTC-4
461 assert_equal '2012-05-15', User.find(1).today.to_s
475 assert_equal '2012-05-15', User.find(1).today.to_s
462
476
463 preference.update_attribute :time_zone, ''
477 preference.update_attribute :time_zone, ''
464 assert_equal '2012-05-15', User.find(1).today.to_s
478 assert_equal '2012-05-15', User.find(1).today.to_s
465 end
479 end
466
480
467 def test_time_to_date_should_return_the_date_according_to_user_time_zone
481 def test_time_to_date_should_return_the_date_according_to_user_time_zone
468 preference = User.find(1).pref
482 preference = User.find(1).pref
469 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
483 time = Time.gm(2012, 05, 15, 23, 30).utc # 2012-05-15 23:30 UTC
470
484
471 preference.update_attribute :time_zone, 'Baku' # UTC+4
485 preference.update_attribute :time_zone, 'Baku' # UTC+4
472 assert_equal '2012-05-16', User.find(1).time_to_date(time).to_s
486 assert_equal '2012-05-16', User.find(1).time_to_date(time).to_s
473
487
474 preference.update_attribute :time_zone, 'La Paz' # UTC-4
488 preference.update_attribute :time_zone, 'La Paz' # UTC-4
475 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
489 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
476
490
477 preference.update_attribute :time_zone, ''
491 preference.update_attribute :time_zone, ''
478 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
492 assert_equal '2012-05-15', User.find(1).time_to_date(time).to_s
479 end
493 end
480
494
481 def test_fields_for_order_statement_should_return_fields_according_user_format_setting
495 def test_fields_for_order_statement_should_return_fields_according_user_format_setting
482 with_settings :user_format => 'lastname_coma_firstname' do
496 with_settings :user_format => 'lastname_coma_firstname' do
483 assert_equal ['users.lastname', 'users.firstname', 'users.id'], User.fields_for_order_statement
497 assert_equal ['users.lastname', 'users.firstname', 'users.id'], User.fields_for_order_statement
484 end
498 end
485 end
499 end
486
500
487 def test_fields_for_order_statement_width_table_name_should_prepend_table_name
501 def test_fields_for_order_statement_width_table_name_should_prepend_table_name
488 with_settings :user_format => 'lastname_firstname' do
502 with_settings :user_format => 'lastname_firstname' do
489 assert_equal ['authors.lastname', 'authors.firstname', 'authors.id'], User.fields_for_order_statement('authors')
503 assert_equal ['authors.lastname', 'authors.firstname', 'authors.id'], User.fields_for_order_statement('authors')
490 end
504 end
491 end
505 end
492
506
493 def test_fields_for_order_statement_with_blank_format_should_return_default
507 def test_fields_for_order_statement_with_blank_format_should_return_default
494 with_settings :user_format => '' do
508 with_settings :user_format => '' do
495 assert_equal ['users.firstname', 'users.lastname', 'users.id'], User.fields_for_order_statement
509 assert_equal ['users.firstname', 'users.lastname', 'users.id'], User.fields_for_order_statement
496 end
510 end
497 end
511 end
498
512
499 def test_fields_for_order_statement_with_invalid_format_should_return_default
513 def test_fields_for_order_statement_with_invalid_format_should_return_default
500 with_settings :user_format => 'foo' do
514 with_settings :user_format => 'foo' do
501 assert_equal ['users.firstname', 'users.lastname', 'users.id'], User.fields_for_order_statement
515 assert_equal ['users.firstname', 'users.lastname', 'users.id'], User.fields_for_order_statement
502 end
516 end
503 end
517 end
504
518
505 def test_lock
519 def test_lock
506 user = User.try_to_login("jsmith", "jsmith")
520 user = User.try_to_login("jsmith", "jsmith")
507 assert_equal @jsmith, user
521 assert_equal @jsmith, user
508
522
509 @jsmith.status = User::STATUS_LOCKED
523 @jsmith.status = User::STATUS_LOCKED
510 assert @jsmith.save
524 assert @jsmith.save
511
525
512 user = User.try_to_login("jsmith", "jsmith")
526 user = User.try_to_login("jsmith", "jsmith")
513 assert_equal nil, user
527 assert_equal nil, user
514 end
528 end
515
529
516 context ".try_to_login" do
530 context ".try_to_login" do
517 context "with good credentials" do
531 context "with good credentials" do
518 should "return the user" do
532 should "return the user" do
519 user = User.try_to_login("admin", "admin")
533 user = User.try_to_login("admin", "admin")
520 assert_kind_of User, user
534 assert_kind_of User, user
521 assert_equal "admin", user.login
535 assert_equal "admin", user.login
522 end
536 end
523 end
537 end
524
538
525 context "with wrong credentials" do
539 context "with wrong credentials" do
526 should "return nil" do
540 should "return nil" do
527 assert_nil User.try_to_login("admin", "foo")
541 assert_nil User.try_to_login("admin", "foo")
528 end
542 end
529 end
543 end
530 end
544 end
531
545
532 if ldap_configured?
546 if ldap_configured?
533 context "#try_to_login using LDAP" do
547 context "#try_to_login using LDAP" do
534 context "with failed connection to the LDAP server" do
548 context "with failed connection to the LDAP server" do
535 should "return nil" do
549 should "return nil" do
536 @auth_source = AuthSourceLdap.find(1)
550 @auth_source = AuthSourceLdap.find(1)
537 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
551 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
538
552
539 assert_equal nil, User.try_to_login('edavis', 'wrong')
553 assert_equal nil, User.try_to_login('edavis', 'wrong')
540 end
554 end
541 end
555 end
542
556
543 context "with an unsuccessful authentication" do
557 context "with an unsuccessful authentication" do
544 should "return nil" do
558 should "return nil" do
545 assert_equal nil, User.try_to_login('edavis', 'wrong')
559 assert_equal nil, User.try_to_login('edavis', 'wrong')
546 end
560 end
547 end
561 end
548
562
549 context "binding with user's account" do
563 context "binding with user's account" do
550 setup do
564 setup do
551 @auth_source = AuthSourceLdap.find(1)
565 @auth_source = AuthSourceLdap.find(1)
552 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
566 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
553 @auth_source.account_password = ''
567 @auth_source.account_password = ''
554 @auth_source.save!
568 @auth_source.save!
555
569
556 @ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
570 @ldap_user = User.new(:mail => 'example1@redmine.org', :firstname => 'LDAP', :lastname => 'user', :auth_source_id => 1)
557 @ldap_user.login = 'example1'
571 @ldap_user.login = 'example1'
558 @ldap_user.save!
572 @ldap_user.save!
559 end
573 end
560
574
561 context "with a successful authentication" do
575 context "with a successful authentication" do
562 should "return the user" do
576 should "return the user" do
563 assert_equal @ldap_user, User.try_to_login('example1', '123456')
577 assert_equal @ldap_user, User.try_to_login('example1', '123456')
564 end
578 end
565 end
579 end
566
580
567 context "with an unsuccessful authentication" do
581 context "with an unsuccessful authentication" do
568 should "return nil" do
582 should "return nil" do
569 assert_nil User.try_to_login('example1', '11111')
583 assert_nil User.try_to_login('example1', '11111')
570 end
584 end
571 end
585 end
572 end
586 end
573
587
574 context "on the fly registration" do
588 context "on the fly registration" do
575 setup do
589 setup do
576 @auth_source = AuthSourceLdap.find(1)
590 @auth_source = AuthSourceLdap.find(1)
577 @auth_source.update_attribute :onthefly_register, true
591 @auth_source.update_attribute :onthefly_register, true
578 end
592 end
579
593
580 context "with a successful authentication" do
594 context "with a successful authentication" do
581 should "create a new user account if it doesn't exist" do
595 should "create a new user account if it doesn't exist" do
582 assert_difference('User.count') do
596 assert_difference('User.count') do
583 user = User.try_to_login('edavis', '123456')
597 user = User.try_to_login('edavis', '123456')
584 assert !user.admin?
598 assert !user.admin?
585 end
599 end
586 end
600 end
587
601
588 should "retrieve existing user" do
602 should "retrieve existing user" do
589 user = User.try_to_login('edavis', '123456')
603 user = User.try_to_login('edavis', '123456')
590 user.admin = true
604 user.admin = true
591 user.save!
605 user.save!
592
606
593 assert_no_difference('User.count') do
607 assert_no_difference('User.count') do
594 user = User.try_to_login('edavis', '123456')
608 user = User.try_to_login('edavis', '123456')
595 assert user.admin?
609 assert user.admin?
596 end
610 end
597 end
611 end
598 end
612 end
599
613
600 context "binding with user's account" do
614 context "binding with user's account" do
601 setup do
615 setup do
602 @auth_source = AuthSourceLdap.find(1)
616 @auth_source = AuthSourceLdap.find(1)
603 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
617 @auth_source.account = "uid=$login,ou=Person,dc=redmine,dc=org"
604 @auth_source.account_password = ''
618 @auth_source.account_password = ''
605 @auth_source.save!
619 @auth_source.save!
606 end
620 end
607
621
608 context "with a successful authentication" do
622 context "with a successful authentication" do
609 should "create a new user account if it doesn't exist" do
623 should "create a new user account if it doesn't exist" do
610 assert_difference('User.count') do
624 assert_difference('User.count') do
611 user = User.try_to_login('example1', '123456')
625 user = User.try_to_login('example1', '123456')
612 assert_kind_of User, user
626 assert_kind_of User, user
613 end
627 end
614 end
628 end
615 end
629 end
616
630
617 context "with an unsuccessful authentication" do
631 context "with an unsuccessful authentication" do
618 should "return nil" do
632 should "return nil" do
619 assert_nil User.try_to_login('example1', '11111')
633 assert_nil User.try_to_login('example1', '11111')
620 end
634 end
621 end
635 end
622 end
636 end
623 end
637 end
624 end
638 end
625
639
626 else
640 else
627 puts "Skipping LDAP tests."
641 puts "Skipping LDAP tests."
628 end
642 end
629
643
630 def test_create_anonymous
644 def test_create_anonymous
631 AnonymousUser.delete_all
645 AnonymousUser.delete_all
632 anon = User.anonymous
646 anon = User.anonymous
633 assert !anon.new_record?
647 assert !anon.new_record?
634 assert_kind_of AnonymousUser, anon
648 assert_kind_of AnonymousUser, anon
635 end
649 end
636
650
637 def test_ensure_single_anonymous_user
651 def test_ensure_single_anonymous_user
638 AnonymousUser.delete_all
652 AnonymousUser.delete_all
639 anon1 = User.anonymous
653 anon1 = User.anonymous
640 assert !anon1.new_record?
654 assert !anon1.new_record?
641 assert_kind_of AnonymousUser, anon1
655 assert_kind_of AnonymousUser, anon1
642 anon2 = AnonymousUser.create(
656 anon2 = AnonymousUser.create(
643 :lastname => 'Anonymous', :firstname => '',
657 :lastname => 'Anonymous', :firstname => '',
644 :mail => '', :login => '', :status => 0)
658 :mail => '', :login => '', :status => 0)
645 assert_equal 1, anon2.errors.count
659 assert_equal 1, anon2.errors.count
646 end
660 end
647
661
648 def test_rss_key
662 def test_rss_key
649 assert_nil @jsmith.rss_token
663 assert_nil @jsmith.rss_token
650 key = @jsmith.rss_key
664 key = @jsmith.rss_key
651 assert_equal 40, key.length
665 assert_equal 40, key.length
652
666
653 @jsmith.reload
667 @jsmith.reload
654 assert_equal key, @jsmith.rss_key
668 assert_equal key, @jsmith.rss_key
655 end
669 end
656
670
657 def test_rss_key_should_not_be_generated_twice
671 def test_rss_key_should_not_be_generated_twice
658 assert_difference 'Token.count', 1 do
672 assert_difference 'Token.count', 1 do
659 key1 = @jsmith.rss_key
673 key1 = @jsmith.rss_key
660 key2 = @jsmith.rss_key
674 key2 = @jsmith.rss_key
661 assert_equal key1, key2
675 assert_equal key1, key2
662 end
676 end
663 end
677 end
664
678
665 def test_api_key_should_not_be_generated_twice
679 def test_api_key_should_not_be_generated_twice
666 assert_difference 'Token.count', 1 do
680 assert_difference 'Token.count', 1 do
667 key1 = @jsmith.api_key
681 key1 = @jsmith.api_key
668 key2 = @jsmith.api_key
682 key2 = @jsmith.api_key
669 assert_equal key1, key2
683 assert_equal key1, key2
670 end
684 end
671 end
685 end
672
686
673 context "User#api_key" do
687 context "User#api_key" do
674 should "generate a new one if the user doesn't have one" do
688 should "generate a new one if the user doesn't have one" do
675 user = User.generate!(:api_token => nil)
689 user = User.generate!(:api_token => nil)
676 assert_nil user.api_token
690 assert_nil user.api_token
677
691
678 key = user.api_key
692 key = user.api_key
679 assert_equal 40, key.length
693 assert_equal 40, key.length
680 user.reload
694 user.reload
681 assert_equal key, user.api_key
695 assert_equal key, user.api_key
682 end
696 end
683
697
684 should "return the existing api token value" do
698 should "return the existing api token value" do
685 user = User.generate!
699 user = User.generate!
686 token = Token.create!(:action => 'api')
700 token = Token.create!(:action => 'api')
687 user.api_token = token
701 user.api_token = token
688 assert user.save
702 assert user.save
689
703
690 assert_equal token.value, user.api_key
704 assert_equal token.value, user.api_key
691 end
705 end
692 end
706 end
693
707
694 context "User#find_by_api_key" do
708 context "User#find_by_api_key" do
695 should "return nil if no matching key is found" do
709 should "return nil if no matching key is found" do
696 assert_nil User.find_by_api_key('zzzzzzzzz')
710 assert_nil User.find_by_api_key('zzzzzzzzz')
697 end
711 end
698
712
699 should "return nil if the key is found for an inactive user" do
713 should "return nil if the key is found for an inactive user" do
700 user = User.generate!
714 user = User.generate!
701 user.status = User::STATUS_LOCKED
715 user.status = User::STATUS_LOCKED
702 token = Token.create!(:action => 'api')
716 token = Token.create!(:action => 'api')
703 user.api_token = token
717 user.api_token = token
704 user.save
718 user.save
705
719
706 assert_nil User.find_by_api_key(token.value)
720 assert_nil User.find_by_api_key(token.value)
707 end
721 end
708
722
709 should "return the user if the key is found for an active user" do
723 should "return the user if the key is found for an active user" do
710 user = User.generate!
724 user = User.generate!
711 token = Token.create!(:action => 'api')
725 token = Token.create!(:action => 'api')
712 user.api_token = token
726 user.api_token = token
713 user.save
727 user.save
714
728
715 assert_equal user, User.find_by_api_key(token.value)
729 assert_equal user, User.find_by_api_key(token.value)
716 end
730 end
717 end
731 end
718
732
719 def test_default_admin_account_changed_should_return_false_if_account_was_not_changed
733 def test_default_admin_account_changed_should_return_false_if_account_was_not_changed
720 user = User.find_by_login("admin")
734 user = User.find_by_login("admin")
721 user.password = "admin"
735 user.password = "admin"
722 assert user.save(:validate => false)
736 assert user.save(:validate => false)
723
737
724 assert_equal false, User.default_admin_account_changed?
738 assert_equal false, User.default_admin_account_changed?
725 end
739 end
726
740
727 def test_default_admin_account_changed_should_return_true_if_password_was_changed
741 def test_default_admin_account_changed_should_return_true_if_password_was_changed
728 user = User.find_by_login("admin")
742 user = User.find_by_login("admin")
729 user.password = "newpassword"
743 user.password = "newpassword"
730 user.save!
744 user.save!
731
745
732 assert_equal true, User.default_admin_account_changed?
746 assert_equal true, User.default_admin_account_changed?
733 end
747 end
734
748
735 def test_default_admin_account_changed_should_return_true_if_account_is_disabled
749 def test_default_admin_account_changed_should_return_true_if_account_is_disabled
736 user = User.find_by_login("admin")
750 user = User.find_by_login("admin")
737 user.password = "admin"
751 user.password = "admin"
738 user.status = User::STATUS_LOCKED
752 user.status = User::STATUS_LOCKED
739 assert user.save(:validate => false)
753 assert user.save(:validate => false)
740
754
741 assert_equal true, User.default_admin_account_changed?
755 assert_equal true, User.default_admin_account_changed?
742 end
756 end
743
757
744 def test_default_admin_account_changed_should_return_true_if_account_does_not_exist
758 def test_default_admin_account_changed_should_return_true_if_account_does_not_exist
745 user = User.find_by_login("admin")
759 user = User.find_by_login("admin")
746 user.destroy
760 user.destroy
747
761
748 assert_equal true, User.default_admin_account_changed?
762 assert_equal true, User.default_admin_account_changed?
749 end
763 end
750
764
751 def test_roles_for_project
765 def test_roles_for_project
752 # user with a role
766 # user with a role
753 roles = @jsmith.roles_for_project(Project.find(1))
767 roles = @jsmith.roles_for_project(Project.find(1))
754 assert_kind_of Role, roles.first
768 assert_kind_of Role, roles.first
755 assert_equal "Manager", roles.first.name
769 assert_equal "Manager", roles.first.name
756
770
757 # user with no role
771 # user with no role
758 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
772 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
759 end
773 end
760
774
761 def test_projects_by_role_for_user_with_role
775 def test_projects_by_role_for_user_with_role
762 user = User.find(2)
776 user = User.find(2)
763 assert_kind_of Hash, user.projects_by_role
777 assert_kind_of Hash, user.projects_by_role
764 assert_equal 2, user.projects_by_role.size
778 assert_equal 2, user.projects_by_role.size
765 assert_equal [1,5], user.projects_by_role[Role.find(1)].collect(&:id).sort
779 assert_equal [1,5], user.projects_by_role[Role.find(1)].collect(&:id).sort
766 assert_equal [2], user.projects_by_role[Role.find(2)].collect(&:id).sort
780 assert_equal [2], user.projects_by_role[Role.find(2)].collect(&:id).sort
767 end
781 end
768
782
769 def test_accessing_projects_by_role_with_no_projects_should_return_an_empty_array
783 def test_accessing_projects_by_role_with_no_projects_should_return_an_empty_array
770 user = User.find(2)
784 user = User.find(2)
771 assert_equal [], user.projects_by_role[Role.find(3)]
785 assert_equal [], user.projects_by_role[Role.find(3)]
772 # should not update the hash
786 # should not update the hash
773 assert_nil user.projects_by_role.values.detect(&:blank?)
787 assert_nil user.projects_by_role.values.detect(&:blank?)
774 end
788 end
775
789
776 def test_projects_by_role_for_user_with_no_role
790 def test_projects_by_role_for_user_with_no_role
777 user = User.generate!
791 user = User.generate!
778 assert_equal({}, user.projects_by_role)
792 assert_equal({}, user.projects_by_role)
779 end
793 end
780
794
781 def test_projects_by_role_for_anonymous
795 def test_projects_by_role_for_anonymous
782 assert_equal({}, User.anonymous.projects_by_role)
796 assert_equal({}, User.anonymous.projects_by_role)
783 end
797 end
784
798
785 def test_valid_notification_options
799 def test_valid_notification_options
786 # without memberships
800 # without memberships
787 assert_equal 5, User.find(7).valid_notification_options.size
801 assert_equal 5, User.find(7).valid_notification_options.size
788 # with memberships
802 # with memberships
789 assert_equal 6, User.find(2).valid_notification_options.size
803 assert_equal 6, User.find(2).valid_notification_options.size
790 end
804 end
791
805
792 def test_valid_notification_options_class_method
806 def test_valid_notification_options_class_method
793 assert_equal 5, User.valid_notification_options.size
807 assert_equal 5, User.valid_notification_options.size
794 assert_equal 5, User.valid_notification_options(User.find(7)).size
808 assert_equal 5, User.valid_notification_options(User.find(7)).size
795 assert_equal 6, User.valid_notification_options(User.find(2)).size
809 assert_equal 6, User.valid_notification_options(User.find(2)).size
796 end
810 end
797
811
798 def test_mail_notification_all
812 def test_mail_notification_all
799 @jsmith.mail_notification = 'all'
813 @jsmith.mail_notification = 'all'
800 @jsmith.notified_project_ids = []
814 @jsmith.notified_project_ids = []
801 @jsmith.save
815 @jsmith.save
802 @jsmith.reload
816 @jsmith.reload
803 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
817 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
804 end
818 end
805
819
806 def test_mail_notification_selected
820 def test_mail_notification_selected
807 @jsmith.mail_notification = 'selected'
821 @jsmith.mail_notification = 'selected'
808 @jsmith.notified_project_ids = [1]
822 @jsmith.notified_project_ids = [1]
809 @jsmith.save
823 @jsmith.save
810 @jsmith.reload
824 @jsmith.reload
811 assert Project.find(1).recipients.include?(@jsmith.mail)
825 assert Project.find(1).recipients.include?(@jsmith.mail)
812 end
826 end
813
827
814 def test_mail_notification_only_my_events
828 def test_mail_notification_only_my_events
815 @jsmith.mail_notification = 'only_my_events'
829 @jsmith.mail_notification = 'only_my_events'
816 @jsmith.notified_project_ids = []
830 @jsmith.notified_project_ids = []
817 @jsmith.save
831 @jsmith.save
818 @jsmith.reload
832 @jsmith.reload
819 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
833 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
820 end
834 end
821
835
822 def test_comments_sorting_preference
836 def test_comments_sorting_preference
823 assert !@jsmith.wants_comments_in_reverse_order?
837 assert !@jsmith.wants_comments_in_reverse_order?
824 @jsmith.pref.comments_sorting = 'asc'
838 @jsmith.pref.comments_sorting = 'asc'
825 assert !@jsmith.wants_comments_in_reverse_order?
839 assert !@jsmith.wants_comments_in_reverse_order?
826 @jsmith.pref.comments_sorting = 'desc'
840 @jsmith.pref.comments_sorting = 'desc'
827 assert @jsmith.wants_comments_in_reverse_order?
841 assert @jsmith.wants_comments_in_reverse_order?
828 end
842 end
829
843
830 def test_find_by_mail_should_be_case_insensitive
844 def test_find_by_mail_should_be_case_insensitive
831 u = User.find_by_mail('JSmith@somenet.foo')
845 u = User.find_by_mail('JSmith@somenet.foo')
832 assert_not_nil u
846 assert_not_nil u
833 assert_equal 'jsmith@somenet.foo', u.mail
847 assert_equal 'jsmith@somenet.foo', u.mail
834 end
848 end
835
849
836 def test_random_password
850 def test_random_password
837 u = User.new
851 u = User.new
838 u.random_password
852 u.random_password
839 assert !u.password.blank?
853 assert !u.password.blank?
840 assert !u.password_confirmation.blank?
854 assert !u.password_confirmation.blank?
841 end
855 end
842
856
843 context "#change_password_allowed?" do
857 context "#change_password_allowed?" do
844 should "be allowed if no auth source is set" do
858 should "be allowed if no auth source is set" do
845 user = User.generate!
859 user = User.generate!
846 assert user.change_password_allowed?
860 assert user.change_password_allowed?
847 end
861 end
848
862
849 should "delegate to the auth source" do
863 should "delegate to the auth source" do
850 user = User.generate!
864 user = User.generate!
851
865
852 allowed_auth_source = AuthSource.generate!
866 allowed_auth_source = AuthSource.generate!
853 def allowed_auth_source.allow_password_changes?; true; end
867 def allowed_auth_source.allow_password_changes?; true; end
854
868
855 denied_auth_source = AuthSource.generate!
869 denied_auth_source = AuthSource.generate!
856 def denied_auth_source.allow_password_changes?; false; end
870 def denied_auth_source.allow_password_changes?; false; end
857
871
858 assert user.change_password_allowed?
872 assert user.change_password_allowed?
859
873
860 user.auth_source = allowed_auth_source
874 user.auth_source = allowed_auth_source
861 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
875 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
862
876
863 user.auth_source = denied_auth_source
877 user.auth_source = denied_auth_source
864 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
878 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
865 end
879 end
866 end
880 end
867
881
868 def test_own_account_deletable_should_be_true_with_unsubscrive_enabled
882 def test_own_account_deletable_should_be_true_with_unsubscrive_enabled
869 with_settings :unsubscribe => '1' do
883 with_settings :unsubscribe => '1' do
870 assert_equal true, User.find(2).own_account_deletable?
884 assert_equal true, User.find(2).own_account_deletable?
871 end
885 end
872 end
886 end
873
887
874 def test_own_account_deletable_should_be_false_with_unsubscrive_disabled
888 def test_own_account_deletable_should_be_false_with_unsubscrive_disabled
875 with_settings :unsubscribe => '0' do
889 with_settings :unsubscribe => '0' do
876 assert_equal false, User.find(2).own_account_deletable?
890 assert_equal false, User.find(2).own_account_deletable?
877 end
891 end
878 end
892 end
879
893
880 def test_own_account_deletable_should_be_false_for_a_single_admin
894 def test_own_account_deletable_should_be_false_for_a_single_admin
881 User.delete_all(["admin = ? AND id <> ?", true, 1])
895 User.delete_all(["admin = ? AND id <> ?", true, 1])
882
896
883 with_settings :unsubscribe => '1' do
897 with_settings :unsubscribe => '1' do
884 assert_equal false, User.find(1).own_account_deletable?
898 assert_equal false, User.find(1).own_account_deletable?
885 end
899 end
886 end
900 end
887
901
888 def test_own_account_deletable_should_be_true_for_an_admin_if_other_admin_exists
902 def test_own_account_deletable_should_be_true_for_an_admin_if_other_admin_exists
889 User.generate! do |user|
903 User.generate! do |user|
890 user.admin = true
904 user.admin = true
891 end
905 end
892
906
893 with_settings :unsubscribe => '1' do
907 with_settings :unsubscribe => '1' do
894 assert_equal true, User.find(1).own_account_deletable?
908 assert_equal true, User.find(1).own_account_deletable?
895 end
909 end
896 end
910 end
897
911
898 context "#allowed_to?" do
912 context "#allowed_to?" do
899 context "with a unique project" do
913 context "with a unique project" do
900 should "return false if project is archived" do
914 should "return false if project is archived" do
901 project = Project.find(1)
915 project = Project.find(1)
902 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
916 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
903 assert_equal false, @admin.allowed_to?(:view_issues, Project.find(1))
917 assert_equal false, @admin.allowed_to?(:view_issues, Project.find(1))
904 end
918 end
905
919
906 should "return false for write action if project is closed" do
920 should "return false for write action if project is closed" do
907 project = Project.find(1)
921 project = Project.find(1)
908 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
922 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
909 assert_equal false, @admin.allowed_to?(:edit_project, Project.find(1))
923 assert_equal false, @admin.allowed_to?(:edit_project, Project.find(1))
910 end
924 end
911
925
912 should "return true for read action if project is closed" do
926 should "return true for read action if project is closed" do
913 project = Project.find(1)
927 project = Project.find(1)
914 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
928 Project.any_instance.stubs(:status).returns(Project::STATUS_CLOSED)
915 assert_equal true, @admin.allowed_to?(:view_project, Project.find(1))
929 assert_equal true, @admin.allowed_to?(:view_project, Project.find(1))
916 end
930 end
917
931
918 should "return false if related module is disabled" do
932 should "return false if related module is disabled" do
919 project = Project.find(1)
933 project = Project.find(1)
920 project.enabled_module_names = ["issue_tracking"]
934 project.enabled_module_names = ["issue_tracking"]
921 assert_equal true, @admin.allowed_to?(:add_issues, project)
935 assert_equal true, @admin.allowed_to?(:add_issues, project)
922 assert_equal false, @admin.allowed_to?(:view_wiki_pages, project)
936 assert_equal false, @admin.allowed_to?(:view_wiki_pages, project)
923 end
937 end
924
938
925 should "authorize nearly everything for admin users" do
939 should "authorize nearly everything for admin users" do
926 project = Project.find(1)
940 project = Project.find(1)
927 assert ! @admin.member_of?(project)
941 assert ! @admin.member_of?(project)
928 %w(edit_issues delete_issues manage_news add_documents manage_wiki).each do |p|
942 %w(edit_issues delete_issues manage_news add_documents manage_wiki).each do |p|
929 assert_equal true, @admin.allowed_to?(p.to_sym, project)
943 assert_equal true, @admin.allowed_to?(p.to_sym, project)
930 end
944 end
931 end
945 end
932
946
933 should "authorize normal users depending on their roles" do
947 should "authorize normal users depending on their roles" do
934 project = Project.find(1)
948 project = Project.find(1)
935 assert_equal true, @jsmith.allowed_to?(:delete_messages, project) #Manager
949 assert_equal true, @jsmith.allowed_to?(:delete_messages, project) #Manager
936 assert_equal false, @dlopper.allowed_to?(:delete_messages, project) #Developper
950 assert_equal false, @dlopper.allowed_to?(:delete_messages, project) #Developper
937 end
951 end
938 end
952 end
939
953
940 context "with multiple projects" do
954 context "with multiple projects" do
941 should "return false if array is empty" do
955 should "return false if array is empty" do
942 assert_equal false, @admin.allowed_to?(:view_project, [])
956 assert_equal false, @admin.allowed_to?(:view_project, [])
943 end
957 end
944
958
945 should "return true only if user has permission on all these projects" do
959 should "return true only if user has permission on all these projects" do
946 assert_equal true, @admin.allowed_to?(:view_project, Project.all)
960 assert_equal true, @admin.allowed_to?(:view_project, Project.all)
947 assert_equal false, @dlopper.allowed_to?(:view_project, Project.all) #cannot see Project(2)
961 assert_equal false, @dlopper.allowed_to?(:view_project, Project.all) #cannot see Project(2)
948 assert_equal true, @jsmith.allowed_to?(:edit_issues, @jsmith.projects) #Manager or Developer everywhere
962 assert_equal true, @jsmith.allowed_to?(:edit_issues, @jsmith.projects) #Manager or Developer everywhere
949 assert_equal false, @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects) #Dev cannot delete_issue_watchers
963 assert_equal false, @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects) #Dev cannot delete_issue_watchers
950 end
964 end
951
965
952 should "behave correctly with arrays of 1 project" do
966 should "behave correctly with arrays of 1 project" do
953 assert_equal false, User.anonymous.allowed_to?(:delete_issues, [Project.first])
967 assert_equal false, User.anonymous.allowed_to?(:delete_issues, [Project.first])
954 end
968 end
955 end
969 end
956
970
957 context "with options[:global]" do
971 context "with options[:global]" do
958 should "authorize if user has at least one role that has this permission" do
972 should "authorize if user has at least one role that has this permission" do
959 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
973 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
960 @anonymous = User.find(6)
974 @anonymous = User.find(6)
961 assert_equal true, @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
975 assert_equal true, @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
962 assert_equal false, @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
976 assert_equal false, @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
963 assert_equal true, @dlopper2.allowed_to?(:add_issues, nil, :global => true)
977 assert_equal true, @dlopper2.allowed_to?(:add_issues, nil, :global => true)
964 assert_equal false, @anonymous.allowed_to?(:add_issues, nil, :global => true)
978 assert_equal false, @anonymous.allowed_to?(:add_issues, nil, :global => true)
965 assert_equal true, @anonymous.allowed_to?(:view_issues, nil, :global => true)
979 assert_equal true, @anonymous.allowed_to?(:view_issues, nil, :global => true)
966 end
980 end
967 end
981 end
968 end
982 end
969
983
970 context "User#notify_about?" do
984 context "User#notify_about?" do
971 context "Issues" do
985 context "Issues" do
972 setup do
986 setup do
973 @project = Project.find(1)
987 @project = Project.find(1)
974 @author = User.generate!
988 @author = User.generate!
975 @assignee = User.generate!
989 @assignee = User.generate!
976 @issue = Issue.generate!(:project => @project, :assigned_to => @assignee, :author => @author)
990 @issue = Issue.generate!(:project => @project, :assigned_to => @assignee, :author => @author)
977 end
991 end
978
992
979 should "be true for a user with :all" do
993 should "be true for a user with :all" do
980 @author.update_attribute(:mail_notification, 'all')
994 @author.update_attribute(:mail_notification, 'all')
981 assert @author.notify_about?(@issue)
995 assert @author.notify_about?(@issue)
982 end
996 end
983
997
984 should "be false for a user with :none" do
998 should "be false for a user with :none" do
985 @author.update_attribute(:mail_notification, 'none')
999 @author.update_attribute(:mail_notification, 'none')
986 assert ! @author.notify_about?(@issue)
1000 assert ! @author.notify_about?(@issue)
987 end
1001 end
988
1002
989 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
1003 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
990 @user = User.generate!(:mail_notification => 'only_my_events')
1004 @user = User.generate!(:mail_notification => 'only_my_events')
991 Member.create!(:user => @user, :project => @project, :role_ids => [1])
1005 Member.create!(:user => @user, :project => @project, :role_ids => [1])
992 assert ! @user.notify_about?(@issue)
1006 assert ! @user.notify_about?(@issue)
993 end
1007 end
994
1008
995 should "be true for a user with :only_my_events and is the author" do
1009 should "be true for a user with :only_my_events and is the author" do
996 @author.update_attribute(:mail_notification, 'only_my_events')
1010 @author.update_attribute(:mail_notification, 'only_my_events')
997 assert @author.notify_about?(@issue)
1011 assert @author.notify_about?(@issue)
998 end
1012 end
999
1013
1000 should "be true for a user with :only_my_events and is the assignee" do
1014 should "be true for a user with :only_my_events and is the assignee" do
1001 @assignee.update_attribute(:mail_notification, 'only_my_events')
1015 @assignee.update_attribute(:mail_notification, 'only_my_events')
1002 assert @assignee.notify_about?(@issue)
1016 assert @assignee.notify_about?(@issue)
1003 end
1017 end
1004
1018
1005 should "be true for a user with :only_assigned and is the assignee" do
1019 should "be true for a user with :only_assigned and is the assignee" do
1006 @assignee.update_attribute(:mail_notification, 'only_assigned')
1020 @assignee.update_attribute(:mail_notification, 'only_assigned')
1007 assert @assignee.notify_about?(@issue)
1021 assert @assignee.notify_about?(@issue)
1008 end
1022 end
1009
1023
1010 should "be false for a user with :only_assigned and is not the assignee" do
1024 should "be false for a user with :only_assigned and is not the assignee" do
1011 @author.update_attribute(:mail_notification, 'only_assigned')
1025 @author.update_attribute(:mail_notification, 'only_assigned')
1012 assert ! @author.notify_about?(@issue)
1026 assert ! @author.notify_about?(@issue)
1013 end
1027 end
1014
1028
1015 should "be true for a user with :only_owner and is the author" do
1029 should "be true for a user with :only_owner and is the author" do
1016 @author.update_attribute(:mail_notification, 'only_owner')
1030 @author.update_attribute(:mail_notification, 'only_owner')
1017 assert @author.notify_about?(@issue)
1031 assert @author.notify_about?(@issue)
1018 end
1032 end
1019
1033
1020 should "be false for a user with :only_owner and is not the author" do
1034 should "be false for a user with :only_owner and is not the author" do
1021 @assignee.update_attribute(:mail_notification, 'only_owner')
1035 @assignee.update_attribute(:mail_notification, 'only_owner')
1022 assert ! @assignee.notify_about?(@issue)
1036 assert ! @assignee.notify_about?(@issue)
1023 end
1037 end
1024
1038
1025 should "be true for a user with :selected and is the author" do
1039 should "be true for a user with :selected and is the author" do
1026 @author.update_attribute(:mail_notification, 'selected')
1040 @author.update_attribute(:mail_notification, 'selected')
1027 assert @author.notify_about?(@issue)
1041 assert @author.notify_about?(@issue)
1028 end
1042 end
1029
1043
1030 should "be true for a user with :selected and is the assignee" do
1044 should "be true for a user with :selected and is the assignee" do
1031 @assignee.update_attribute(:mail_notification, 'selected')
1045 @assignee.update_attribute(:mail_notification, 'selected')
1032 assert @assignee.notify_about?(@issue)
1046 assert @assignee.notify_about?(@issue)
1033 end
1047 end
1034
1048
1035 should "be false for a user with :selected and is not the author or assignee" do
1049 should "be false for a user with :selected and is not the author or assignee" do
1036 @user = User.generate!(:mail_notification => 'selected')
1050 @user = User.generate!(:mail_notification => 'selected')
1037 Member.create!(:user => @user, :project => @project, :role_ids => [1])
1051 Member.create!(:user => @user, :project => @project, :role_ids => [1])
1038 assert ! @user.notify_about?(@issue)
1052 assert ! @user.notify_about?(@issue)
1039 end
1053 end
1040 end
1054 end
1041 end
1055 end
1042
1056
1043 def test_notify_about_news
1057 def test_notify_about_news
1044 user = User.generate!
1058 user = User.generate!
1045 news = News.new
1059 news = News.new
1046
1060
1047 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1061 User::MAIL_NOTIFICATION_OPTIONS.map(&:first).each do |option|
1048 user.mail_notification = option
1062 user.mail_notification = option
1049 assert_equal (option != 'none'), user.notify_about?(news)
1063 assert_equal (option != 'none'), user.notify_about?(news)
1050 end
1064 end
1051 end
1065 end
1052
1066
1053 def test_salt_unsalted_passwords
1067 def test_salt_unsalted_passwords
1054 # Restore a user with an unsalted password
1068 # Restore a user with an unsalted password
1055 user = User.find(1)
1069 user = User.find(1)
1056 user.salt = nil
1070 user.salt = nil
1057 user.hashed_password = User.hash_password("unsalted")
1071 user.hashed_password = User.hash_password("unsalted")
1058 user.save!
1072 user.save!
1059
1073
1060 User.salt_unsalted_passwords!
1074 User.salt_unsalted_passwords!
1061
1075
1062 user.reload
1076 user.reload
1063 # Salt added
1077 # Salt added
1064 assert !user.salt.blank?
1078 assert !user.salt.blank?
1065 # Password still valid
1079 # Password still valid
1066 assert user.check_password?("unsalted")
1080 assert user.check_password?("unsalted")
1067 assert_equal user, User.try_to_login(user.login, "unsalted")
1081 assert_equal user, User.try_to_login(user.login, "unsalted")
1068 end
1082 end
1069
1083
1070 if Object.const_defined?(:OpenID)
1084 if Object.const_defined?(:OpenID)
1071
1085
1072 def test_setting_identity_url
1086 def test_setting_identity_url
1073 normalized_open_id_url = 'http://example.com/'
1087 normalized_open_id_url = 'http://example.com/'
1074 u = User.new( :identity_url => 'http://example.com/' )
1088 u = User.new( :identity_url => 'http://example.com/' )
1075 assert_equal normalized_open_id_url, u.identity_url
1089 assert_equal normalized_open_id_url, u.identity_url
1076 end
1090 end
1077
1091
1078 def test_setting_identity_url_without_trailing_slash
1092 def test_setting_identity_url_without_trailing_slash
1079 normalized_open_id_url = 'http://example.com/'
1093 normalized_open_id_url = 'http://example.com/'
1080 u = User.new( :identity_url => 'http://example.com' )
1094 u = User.new( :identity_url => 'http://example.com' )
1081 assert_equal normalized_open_id_url, u.identity_url
1095 assert_equal normalized_open_id_url, u.identity_url
1082 end
1096 end
1083
1097
1084 def test_setting_identity_url_without_protocol
1098 def test_setting_identity_url_without_protocol
1085 normalized_open_id_url = 'http://example.com/'
1099 normalized_open_id_url = 'http://example.com/'
1086 u = User.new( :identity_url => 'example.com' )
1100 u = User.new( :identity_url => 'example.com' )
1087 assert_equal normalized_open_id_url, u.identity_url
1101 assert_equal normalized_open_id_url, u.identity_url
1088 end
1102 end
1089
1103
1090 def test_setting_blank_identity_url
1104 def test_setting_blank_identity_url
1091 u = User.new( :identity_url => 'example.com' )
1105 u = User.new( :identity_url => 'example.com' )
1092 u.identity_url = ''
1106 u.identity_url = ''
1093 assert u.identity_url.blank?
1107 assert u.identity_url.blank?
1094 end
1108 end
1095
1109
1096 def test_setting_invalid_identity_url
1110 def test_setting_invalid_identity_url
1097 u = User.new( :identity_url => 'this is not an openid url' )
1111 u = User.new( :identity_url => 'this is not an openid url' )
1098 assert u.identity_url.blank?
1112 assert u.identity_url.blank?
1099 end
1113 end
1100
1114
1101 else
1115 else
1102 puts "Skipping openid tests."
1116 puts "Skipping openid tests."
1103 end
1117 end
1104
1118
1105 end
1119 end
General Comments 0
You need to be logged in to leave comments. Login now