@@ -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