##// END OF EJS Templates
Fixed: no email sent with 'Notifiy for any event on the selected projects only' (#7421)....
Jean-Philippe Lang -
r4641:73a2d926f6e4
parent child
Show More
@@ -1,537 +1,542
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require "digest/sha1"
19 19
20 20 class User < Principal
21 21 include Redmine::SafeAttributes
22 22
23 23 # Account statuses
24 24 STATUS_ANONYMOUS = 0
25 25 STATUS_ACTIVE = 1
26 26 STATUS_REGISTERED = 2
27 27 STATUS_LOCKED = 3
28 28
29 29 USER_FORMATS = {
30 30 :firstname_lastname => '#{firstname} #{lastname}',
31 31 :firstname => '#{firstname}',
32 32 :lastname_firstname => '#{lastname} #{firstname}',
33 33 :lastname_coma_firstname => '#{lastname}, #{firstname}',
34 34 :username => '#{login}'
35 35 }
36 36
37 37 MAIL_NOTIFICATION_OPTIONS = [
38 38 ['all', :label_user_mail_option_all],
39 39 ['selected', :label_user_mail_option_selected],
40 40 ['only_my_events', :label_user_mail_option_only_my_events],
41 41 ['only_assigned', :label_user_mail_option_only_assigned],
42 42 ['only_owner', :label_user_mail_option_only_owner],
43 43 ['none', :label_user_mail_option_none]
44 44 ]
45 45
46 46 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
47 47 :after_remove => Proc.new {|user, group| group.user_removed(user)}
48 48 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
49 49 has_many :changesets, :dependent => :nullify
50 50 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
51 51 has_one :rss_token, :class_name => 'Token', :conditions => "action='feeds'"
52 52 has_one :api_token, :class_name => 'Token', :conditions => "action='api'"
53 53 belongs_to :auth_source
54 54
55 55 # Active non-anonymous users scope
56 56 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
57 57
58 58 acts_as_customizable
59 59
60 60 attr_accessor :password, :password_confirmation
61 61 attr_accessor :last_before_login_on
62 62 # Prevents unauthorized assignments
63 63 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password
64 64
65 65 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
66 66 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
67 67 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
68 68 # Login must contain lettres, numbers, underscores only
69 69 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
70 70 validates_length_of :login, :maximum => 30
71 71 validates_length_of :firstname, :lastname, :maximum => 30
72 72 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
73 73 validates_length_of :mail, :maximum => 60, :allow_nil => true
74 74 validates_confirmation_of :password, :allow_nil => true
75 75 validates_inclusion_of :mail_notification, :in => MAIL_NOTIFICATION_OPTIONS.collect(&:first), :allow_blank => true
76 76
77 77 before_destroy :remove_references_before_destroy
78 78
79 79 def before_create
80 80 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
81 81 true
82 82 end
83 83
84 84 def before_save
85 85 # update hashed_password if password was set
86 86 self.hashed_password = User.hash_password(self.password) if self.password && self.auth_source_id.blank?
87 87 end
88 88
89 89 def reload(*args)
90 90 @name = nil
91 91 super
92 92 end
93 93
94 94 def mail=(arg)
95 95 write_attribute(:mail, arg.to_s.strip)
96 96 end
97 97
98 98 def identity_url=(url)
99 99 if url.blank?
100 100 write_attribute(:identity_url, '')
101 101 else
102 102 begin
103 103 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
104 104 rescue OpenIdAuthentication::InvalidOpenId
105 105 # Invlaid url, don't save
106 106 end
107 107 end
108 108 self.read_attribute(:identity_url)
109 109 end
110 110
111 111 # Returns the user that matches provided login and password, or nil
112 112 def self.try_to_login(login, password)
113 113 # Make sure no one can sign in with an empty password
114 114 return nil if password.to_s.empty?
115 115 user = find_by_login(login)
116 116 if user
117 117 # user is already in local database
118 118 return nil if !user.active?
119 119 if user.auth_source
120 120 # user has an external authentication method
121 121 return nil unless user.auth_source.authenticate(login, password)
122 122 else
123 123 # authentication with local password
124 124 return nil unless User.hash_password(password) == user.hashed_password
125 125 end
126 126 else
127 127 # user is not yet registered, try to authenticate with available sources
128 128 attrs = AuthSource.authenticate(login, password)
129 129 if attrs
130 130 user = new(attrs)
131 131 user.login = login
132 132 user.language = Setting.default_language
133 133 if user.save
134 134 user.reload
135 135 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
136 136 end
137 137 end
138 138 end
139 139 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
140 140 user
141 141 rescue => text
142 142 raise text
143 143 end
144 144
145 145 # Returns the user who matches the given autologin +key+ or nil
146 146 def self.try_to_autologin(key)
147 147 tokens = Token.find_all_by_action_and_value('autologin', key)
148 148 # Make sure there's only 1 token that matches the key
149 149 if tokens.size == 1
150 150 token = tokens.first
151 151 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
152 152 token.user.update_attribute(:last_login_on, Time.now)
153 153 token.user
154 154 end
155 155 end
156 156 end
157 157
158 158 # Return user's full name for display
159 159 def name(formatter = nil)
160 160 if formatter
161 161 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
162 162 else
163 163 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
164 164 end
165 165 end
166 166
167 167 def active?
168 168 self.status == STATUS_ACTIVE
169 169 end
170 170
171 171 def registered?
172 172 self.status == STATUS_REGISTERED
173 173 end
174 174
175 175 def locked?
176 176 self.status == STATUS_LOCKED
177 177 end
178 178
179 179 def activate
180 180 self.status = STATUS_ACTIVE
181 181 end
182 182
183 183 def register
184 184 self.status = STATUS_REGISTERED
185 185 end
186 186
187 187 def lock
188 188 self.status = STATUS_LOCKED
189 189 end
190 190
191 191 def activate!
192 192 update_attribute(:status, STATUS_ACTIVE)
193 193 end
194 194
195 195 def register!
196 196 update_attribute(:status, STATUS_REGISTERED)
197 197 end
198 198
199 199 def lock!
200 200 update_attribute(:status, STATUS_LOCKED)
201 201 end
202 202
203 203 def check_password?(clear_password)
204 204 if auth_source_id.present?
205 205 auth_source.authenticate(self.login, clear_password)
206 206 else
207 207 User.hash_password(clear_password) == self.hashed_password
208 208 end
209 209 end
210 210
211 211 # Does the backend storage allow this user to change their password?
212 212 def change_password_allowed?
213 213 return true if auth_source_id.blank?
214 214 return auth_source.allow_password_changes?
215 215 end
216 216
217 217 # Generate and set a random password. Useful for automated user creation
218 218 # Based on Token#generate_token_value
219 219 #
220 220 def random_password
221 221 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
222 222 password = ''
223 223 40.times { |i| password << chars[rand(chars.size-1)] }
224 224 self.password = password
225 225 self.password_confirmation = password
226 226 self
227 227 end
228 228
229 229 def pref
230 230 self.preference ||= UserPreference.new(:user => self)
231 231 end
232 232
233 233 def time_zone
234 234 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
235 235 end
236 236
237 237 def wants_comments_in_reverse_order?
238 238 self.pref[:comments_sorting] == 'desc'
239 239 end
240 240
241 241 # Return user's RSS key (a 40 chars long string), used to access feeds
242 242 def rss_key
243 243 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
244 244 token.value
245 245 end
246 246
247 247 # Return user's API key (a 40 chars long string), used to access the API
248 248 def api_key
249 249 token = self.api_token || self.create_api_token(:action => 'api')
250 250 token.value
251 251 end
252 252
253 253 # Return an array of project ids for which the user has explicitly turned mail notifications on
254 254 def notified_projects_ids
255 255 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
256 256 end
257 257
258 258 def notified_project_ids=(ids)
259 259 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
260 260 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
261 261 @notified_projects_ids = nil
262 262 notified_projects_ids
263 263 end
264 264
265 265 def valid_notification_options
266 266 self.class.valid_notification_options(self)
267 267 end
268 268
269 269 # Only users that belong to more than 1 project can select projects for which they are notified
270 270 def self.valid_notification_options(user=nil)
271 271 # Note that @user.membership.size would fail since AR ignores
272 272 # :include association option when doing a count
273 273 if user.nil? || user.memberships.length < 1
274 274 MAIL_NOTIFICATION_OPTIONS.reject {|option| option.first == 'selected'}
275 275 else
276 276 MAIL_NOTIFICATION_OPTIONS
277 277 end
278 278 end
279 279
280 280 # Find a user account by matching the exact login and then a case-insensitive
281 281 # version. Exact matches will be given priority.
282 282 def self.find_by_login(login)
283 283 # force string comparison to be case sensitive on MySQL
284 284 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
285 285
286 286 # First look for an exact match
287 287 user = first(:conditions => ["#{type_cast} login = ?", login])
288 288 # Fail over to case-insensitive if none was found
289 289 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
290 290 end
291 291
292 292 def self.find_by_rss_key(key)
293 293 token = Token.find_by_value(key)
294 294 token && token.user.active? ? token.user : nil
295 295 end
296 296
297 297 def self.find_by_api_key(key)
298 298 token = Token.find_by_action_and_value('api', key)
299 299 token && token.user.active? ? token.user : nil
300 300 end
301 301
302 302 # Makes find_by_mail case-insensitive
303 303 def self.find_by_mail(mail)
304 304 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
305 305 end
306 306
307 307 def to_s
308 308 name
309 309 end
310 310
311 311 # Returns the current day according to user's time zone
312 312 def today
313 313 if time_zone.nil?
314 314 Date.today
315 315 else
316 316 Time.now.in_time_zone(time_zone).to_date
317 317 end
318 318 end
319 319
320 320 def logged?
321 321 true
322 322 end
323 323
324 324 def anonymous?
325 325 !logged?
326 326 end
327 327
328 328 # Return user's roles for project
329 329 def roles_for_project(project)
330 330 roles = []
331 331 # No role on archived projects
332 332 return roles unless project && project.active?
333 333 if logged?
334 334 # Find project membership
335 335 membership = memberships.detect {|m| m.project_id == project.id}
336 336 if membership
337 337 roles = membership.roles
338 338 else
339 339 @role_non_member ||= Role.non_member
340 340 roles << @role_non_member
341 341 end
342 342 else
343 343 @role_anonymous ||= Role.anonymous
344 344 roles << @role_anonymous
345 345 end
346 346 roles
347 347 end
348 348
349 349 # Return true if the user is a member of project
350 350 def member_of?(project)
351 351 !roles_for_project(project).detect {|role| role.member?}.nil?
352 352 end
353 353
354 354 # Return true if the user is allowed to do the specified action on a specific context
355 355 # Action can be:
356 356 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
357 357 # * a permission Symbol (eg. :edit_project)
358 358 # Context can be:
359 359 # * a project : returns true if user is allowed to do the specified action on this project
360 360 # * a group of projects : returns true if user is allowed on every project
361 361 # * nil with options[:global] set : check if user has at least one role allowed for this action,
362 362 # or falls back to Non Member / Anonymous permissions depending if the user is logged
363 363 def allowed_to?(action, context, options={})
364 364 if context && context.is_a?(Project)
365 365 # No action allowed on archived projects
366 366 return false unless context.active?
367 367 # No action allowed on disabled modules
368 368 return false unless context.allows_to?(action)
369 369 # Admin users are authorized for anything else
370 370 return true if admin?
371 371
372 372 roles = roles_for_project(context)
373 373 return false unless roles
374 374 roles.detect {|role| (context.is_public? || role.member?) && role.allowed_to?(action)}
375 375
376 376 elsif context && context.is_a?(Array)
377 377 # Authorize if user is authorized on every element of the array
378 378 context.map do |project|
379 379 allowed_to?(action,project,options)
380 380 end.inject do |memo,allowed|
381 381 memo && allowed
382 382 end
383 383 elsif options[:global]
384 384 # Admin users are always authorized
385 385 return true if admin?
386 386
387 387 # authorize if user has at least one role that has this permission
388 388 roles = memberships.collect {|m| m.roles}.flatten.uniq
389 389 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
390 390 else
391 391 false
392 392 end
393 393 end
394 394
395 395 # Is the user allowed to do the specified action on any project?
396 396 # See allowed_to? for the actions and valid options.
397 397 def allowed_to_globally?(action, options)
398 398 allowed_to?(action, nil, options.reverse_merge(:global => true))
399 399 end
400 400
401 401 safe_attributes 'login',
402 402 'firstname',
403 403 'lastname',
404 404 'mail',
405 405 'mail_notification',
406 406 'language',
407 407 'custom_field_values',
408 408 'custom_fields',
409 409 'identity_url'
410 410
411 411 safe_attributes 'status',
412 412 'auth_source_id',
413 413 :if => lambda {|user, current_user| current_user.admin?}
414 414
415 415 safe_attributes 'group_ids',
416 416 :if => lambda {|user, current_user| current_user.admin? && !user.new_record?}
417 417
418 418 # Utility method to help check if a user should be notified about an
419 419 # event.
420 420 #
421 421 # TODO: only supports Issue events currently
422 422 def notify_about?(object)
423 423 case mail_notification
424 424 when 'all'
425 425 true
426 426 when 'selected'
427 # Handled by the Project
427 # user receives notifications for created/assigned issues on unselected projects
428 if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
429 true
430 else
431 false
432 end
428 433 when 'none'
429 434 false
430 435 when 'only_my_events'
431 436 if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
432 437 true
433 438 else
434 439 false
435 440 end
436 441 when 'only_assigned'
437 442 if object.is_a?(Issue) && object.assigned_to == self
438 443 true
439 444 else
440 445 false
441 446 end
442 447 when 'only_owner'
443 448 if object.is_a?(Issue) && object.author == self
444 449 true
445 450 else
446 451 false
447 452 end
448 453 else
449 454 false
450 455 end
451 456 end
452 457
453 458 def self.current=(user)
454 459 @current_user = user
455 460 end
456 461
457 462 def self.current
458 463 @current_user ||= User.anonymous
459 464 end
460 465
461 466 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
462 467 # one anonymous user per database.
463 468 def self.anonymous
464 469 anonymous_user = AnonymousUser.find(:first)
465 470 if anonymous_user.nil?
466 471 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
467 472 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
468 473 end
469 474 anonymous_user
470 475 end
471 476
472 477 protected
473 478
474 479 def validate
475 480 # Password length validation based on setting
476 481 if !password.nil? && password.size < Setting.password_min_length.to_i
477 482 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
478 483 end
479 484 end
480 485
481 486 private
482 487
483 488 # Removes references that are not handled by associations
484 489 # Things that are not deleted are reassociated with the anonymous user
485 490 def remove_references_before_destroy
486 491 return if self.id.nil?
487 492
488 493 substitute = User.anonymous
489 494 Attachment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
490 495 Comment.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
491 496 Issue.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
492 497 Issue.update_all 'assigned_to_id = NULL', ['assigned_to_id = ?', id]
493 498 Journal.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
494 499 JournalDetail.update_all ['old_value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND old_value = ?", id.to_s]
495 500 JournalDetail.update_all ['value = ?', substitute.id.to_s], ["property = 'attr' AND prop_key = 'assigned_to_id' AND value = ?", id.to_s]
496 501 Message.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
497 502 News.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
498 503 # Remove private queries and keep public ones
499 504 Query.delete_all ['user_id = ? AND is_public = ?', id, false]
500 505 Query.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
501 506 TimeEntry.update_all ['user_id = ?', substitute.id], ['user_id = ?', id]
502 507 Token.delete_all ['user_id = ?', id]
503 508 Watcher.delete_all ['user_id = ?', id]
504 509 WikiContent.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
505 510 WikiContent::Version.update_all ['author_id = ?', substitute.id], ['author_id = ?', id]
506 511 end
507 512
508 513 # Return password digest
509 514 def self.hash_password(clear_password)
510 515 Digest::SHA1.hexdigest(clear_password || "")
511 516 end
512 517 end
513 518
514 519 class AnonymousUser < User
515 520
516 521 def validate_on_create
517 522 # There should be only one AnonymousUser in the database
518 523 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
519 524 end
520 525
521 526 def available_custom_fields
522 527 []
523 528 end
524 529
525 530 # Overrides a few properties
526 531 def logged?; false end
527 532 def admin; false end
528 533 def name(*args); I18n.t(:label_user_anonymous) end
529 534 def mail; nil end
530 535 def time_zone; nil end
531 536 def rss_key; nil end
532 537
533 538 # Anonymous user can not be destroyed
534 539 def destroy
535 540 false
536 541 end
537 542 end
@@ -1,749 +1,766
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class UserTest < ActiveSupport::TestCase
21 21 fixtures :users, :members, :projects, :roles, :member_roles, :auth_sources
22 22
23 23 def setup
24 24 @admin = User.find(1)
25 25 @jsmith = User.find(2)
26 26 @dlopper = User.find(3)
27 27 end
28 28
29 29 test 'object_daddy creation' do
30 30 User.generate_with_protected!(:firstname => 'Testing connection')
31 31 User.generate_with_protected!(:firstname => 'Testing connection')
32 32 assert_equal 2, User.count(:all, :conditions => {:firstname => 'Testing connection'})
33 33 end
34 34
35 35 def test_truth
36 36 assert_kind_of User, @jsmith
37 37 end
38 38
39 39 def test_mail_should_be_stripped
40 40 u = User.new
41 41 u.mail = " foo@bar.com "
42 42 assert_equal "foo@bar.com", u.mail
43 43 end
44 44
45 45 def test_create
46 46 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
47 47
48 48 user.login = "jsmith"
49 49 user.password, user.password_confirmation = "password", "password"
50 50 # login uniqueness
51 51 assert !user.save
52 52 assert_equal 1, user.errors.count
53 53
54 54 user.login = "newuser"
55 55 user.password, user.password_confirmation = "passwd", "password"
56 56 # password confirmation
57 57 assert !user.save
58 58 assert_equal 1, user.errors.count
59 59
60 60 user.password, user.password_confirmation = "password", "password"
61 61 assert user.save
62 62 end
63 63
64 64 context "User#before_create" do
65 65 should "set the mail_notification to the default Setting" do
66 66 @user1 = User.generate_with_protected!
67 67 assert_equal 'only_my_events', @user1.mail_notification
68 68
69 69 with_settings :default_notification_option => 'all' do
70 70 @user2 = User.generate_with_protected!
71 71 assert_equal 'all', @user2.mail_notification
72 72 end
73 73 end
74 74 end
75 75
76 76 context "User.login" do
77 77 should "be case-insensitive." do
78 78 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
79 79 u.login = 'newuser'
80 80 u.password, u.password_confirmation = "password", "password"
81 81 assert u.save
82 82
83 83 u = User.new(:firstname => "Similar", :lastname => "User", :mail => "similaruser@somenet.foo")
84 84 u.login = 'NewUser'
85 85 u.password, u.password_confirmation = "password", "password"
86 86 assert !u.save
87 87 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:login)
88 88 end
89 89 end
90 90
91 91 def test_mail_uniqueness_should_not_be_case_sensitive
92 92 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
93 93 u.login = 'newuser1'
94 94 u.password, u.password_confirmation = "password", "password"
95 95 assert u.save
96 96
97 97 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
98 98 u.login = 'newuser2'
99 99 u.password, u.password_confirmation = "password", "password"
100 100 assert !u.save
101 101 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:mail)
102 102 end
103 103
104 104 def test_update
105 105 assert_equal "admin", @admin.login
106 106 @admin.login = "john"
107 107 assert @admin.save, @admin.errors.full_messages.join("; ")
108 108 @admin.reload
109 109 assert_equal "john", @admin.login
110 110 end
111 111
112 112 def test_destroy_should_delete_members_and_roles
113 113 members = Member.find_all_by_user_id(2)
114 114 ms = members.size
115 115 rs = members.collect(&:roles).flatten.size
116 116
117 117 assert_difference 'Member.count', - ms do
118 118 assert_difference 'MemberRole.count', - rs do
119 119 User.find(2).destroy
120 120 end
121 121 end
122 122
123 123 assert_nil User.find_by_id(2)
124 124 assert Member.find_all_by_user_id(2).empty?
125 125 end
126 126
127 127 def test_destroy_should_update_attachments
128 128 attachment = Attachment.create!(:container => Project.find(1),
129 129 :file => uploaded_test_file("testfile.txt", "text/plain"),
130 130 :author_id => 2)
131 131
132 132 User.find(2).destroy
133 133 assert_nil User.find_by_id(2)
134 134 assert_equal User.anonymous, attachment.reload.author
135 135 end
136 136
137 137 def test_destroy_should_update_comments
138 138 comment = Comment.create!(
139 139 :commented => News.create!(:project_id => 1, :author_id => 1, :title => 'foo', :description => 'foo'),
140 140 :author => User.find(2),
141 141 :comments => 'foo'
142 142 )
143 143
144 144 User.find(2).destroy
145 145 assert_nil User.find_by_id(2)
146 146 assert_equal User.anonymous, comment.reload.author
147 147 end
148 148
149 149 def test_destroy_should_update_issues
150 150 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
151 151
152 152 User.find(2).destroy
153 153 assert_nil User.find_by_id(2)
154 154 assert_equal User.anonymous, issue.reload.author
155 155 end
156 156
157 157 def test_destroy_should_unassign_issues
158 158 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
159 159
160 160 User.find(2).destroy
161 161 assert_nil User.find_by_id(2)
162 162 assert_nil issue.reload.assigned_to
163 163 end
164 164
165 165 def test_destroy_should_update_journals
166 166 issue = Issue.create!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'foo')
167 167 issue.init_journal(User.find(2), "update")
168 168 issue.save!
169 169
170 170 User.find(2).destroy
171 171 assert_nil User.find_by_id(2)
172 172 assert_equal User.anonymous, issue.journals.first.reload.user
173 173 end
174 174
175 175 def test_destroy_should_update_journal_details_old_value
176 176 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo', :assigned_to_id => 2)
177 177 issue.init_journal(User.find(1), "update")
178 178 issue.assigned_to_id = nil
179 179 assert_difference 'JournalDetail.count' do
180 180 issue.save!
181 181 end
182 182 journal_detail = JournalDetail.first(:order => 'id DESC')
183 183 assert_equal '2', journal_detail.old_value
184 184
185 185 User.find(2).destroy
186 186 assert_nil User.find_by_id(2)
187 187 assert_equal User.anonymous.id.to_s, journal_detail.reload.old_value
188 188 end
189 189
190 190 def test_destroy_should_update_journal_details_value
191 191 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
192 192 issue.init_journal(User.find(1), "update")
193 193 issue.assigned_to_id = 2
194 194 assert_difference 'JournalDetail.count' do
195 195 issue.save!
196 196 end
197 197 journal_detail = JournalDetail.first(:order => 'id DESC')
198 198 assert_equal '2', journal_detail.value
199 199
200 200 User.find(2).destroy
201 201 assert_nil User.find_by_id(2)
202 202 assert_equal User.anonymous.id.to_s, journal_detail.reload.value
203 203 end
204 204
205 205 def test_destroy_should_update_messages
206 206 board = Board.create!(:project_id => 1, :name => 'Board', :description => 'Board')
207 207 message = Message.create!(:board_id => board.id, :author_id => 2, :subject => 'foo', :content => 'foo')
208 208
209 209 User.find(2).destroy
210 210 assert_nil User.find_by_id(2)
211 211 assert_equal User.anonymous, message.reload.author
212 212 end
213 213
214 214 def test_destroy_should_update_news
215 215 news = News.create!(:project_id => 1, :author_id => 2, :title => 'foo', :description => 'foo')
216 216
217 217 User.find(2).destroy
218 218 assert_nil User.find_by_id(2)
219 219 assert_equal User.anonymous, news.reload.author
220 220 end
221 221
222 222 def test_destroy_should_delete_private_queries
223 223 query = Query.new(:name => 'foo', :is_public => false)
224 224 query.project_id = 1
225 225 query.user_id = 2
226 226 query.save!
227 227
228 228 User.find(2).destroy
229 229 assert_nil User.find_by_id(2)
230 230 assert_nil Query.find_by_id(query.id)
231 231 end
232 232
233 233 def test_destroy_should_update_public_queries
234 234 query = Query.new(:name => 'foo', :is_public => true)
235 235 query.project_id = 1
236 236 query.user_id = 2
237 237 query.save!
238 238
239 239 User.find(2).destroy
240 240 assert_nil User.find_by_id(2)
241 241 assert_equal User.anonymous, query.reload.user
242 242 end
243 243
244 244 def test_destroy_should_update_time_entries
245 245 entry = TimeEntry.new(:hours => '2', :spent_on => Date.today, :activity => TimeEntryActivity.create!(:name => 'foo'))
246 246 entry.project_id = 1
247 247 entry.user_id = 2
248 248 entry.save!
249 249
250 250 User.find(2).destroy
251 251 assert_nil User.find_by_id(2)
252 252 assert_equal User.anonymous, entry.reload.user
253 253 end
254 254
255 255 def test_destroy_should_delete_tokens
256 256 token = Token.create!(:user_id => 2, :value => 'foo')
257 257
258 258 User.find(2).destroy
259 259 assert_nil User.find_by_id(2)
260 260 assert_nil Token.find_by_id(token.id)
261 261 end
262 262
263 263 def test_destroy_should_delete_watchers
264 264 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'foo')
265 265 watcher = Watcher.create!(:user_id => 2, :watchable => issue)
266 266
267 267 User.find(2).destroy
268 268 assert_nil User.find_by_id(2)
269 269 assert_nil Watcher.find_by_id(watcher.id)
270 270 end
271 271
272 272 def test_destroy_should_update_wiki_contents
273 273 wiki_content = WikiContent.create!(
274 274 :text => 'foo',
275 275 :author_id => 2,
276 276 :page => WikiPage.create!(:title => 'Foo', :wiki => Wiki.create!(:project_id => 1, :start_page => 'Start'))
277 277 )
278 278 wiki_content.text = 'bar'
279 279 assert_difference 'WikiContent::Version.count' do
280 280 wiki_content.save!
281 281 end
282 282
283 283 User.find(2).destroy
284 284 assert_nil User.find_by_id(2)
285 285 assert_equal User.anonymous, wiki_content.reload.author
286 286 wiki_content.versions.each do |version|
287 287 assert_equal User.anonymous, version.reload.author
288 288 end
289 289 end
290 290
291 291 def test_destroy_should_nullify_issue_categories
292 292 category = IssueCategory.create!(:project_id => 1, :assigned_to_id => 2, :name => 'foo')
293 293
294 294 User.find(2).destroy
295 295 assert_nil User.find_by_id(2)
296 296 assert_nil category.reload.assigned_to_id
297 297 end
298 298
299 299 def test_destroy_should_nullify_changesets
300 300 changeset = Changeset.create!(
301 301 :repository => Repository::Subversion.create!(
302 302 :project_id => 1,
303 303 :url => 'file:///var/svn'
304 304 ),
305 305 :revision => '12',
306 306 :committed_on => Time.now,
307 307 :committer => 'jsmith'
308 308 )
309 309 assert_equal 2, changeset.user_id
310 310
311 311 User.find(2).destroy
312 312 assert_nil User.find_by_id(2)
313 313 assert_nil changeset.reload.user_id
314 314 end
315 315
316 316 def test_anonymous_user_should_not_be_destroyable
317 317 assert_no_difference 'User.count' do
318 318 assert_equal false, User.anonymous.destroy
319 319 end
320 320 end
321 321
322 322 def test_validate_login_presence
323 323 @admin.login = ""
324 324 assert !@admin.save
325 325 assert_equal 1, @admin.errors.count
326 326 end
327 327
328 328 def test_validate_mail_notification_inclusion
329 329 u = User.new
330 330 u.mail_notification = 'foo'
331 331 u.save
332 332 assert_not_nil u.errors.on(:mail_notification)
333 333 end
334 334
335 335 context "User#try_to_login" do
336 336 should "fall-back to case-insensitive if user login is not found as-typed." do
337 337 user = User.try_to_login("AdMin", "admin")
338 338 assert_kind_of User, user
339 339 assert_equal "admin", user.login
340 340 end
341 341
342 342 should "select the exact matching user first" do
343 343 case_sensitive_user = User.generate_with_protected!(:login => 'changed', :password => 'admin', :password_confirmation => 'admin')
344 344 # bypass validations to make it appear like existing data
345 345 case_sensitive_user.update_attribute(:login, 'ADMIN')
346 346
347 347 user = User.try_to_login("ADMIN", "admin")
348 348 assert_kind_of User, user
349 349 assert_equal "ADMIN", user.login
350 350
351 351 end
352 352 end
353 353
354 354 def test_password
355 355 user = User.try_to_login("admin", "admin")
356 356 assert_kind_of User, user
357 357 assert_equal "admin", user.login
358 358 user.password = "hello"
359 359 assert user.save
360 360
361 361 user = User.try_to_login("admin", "hello")
362 362 assert_kind_of User, user
363 363 assert_equal "admin", user.login
364 364 assert_equal User.hash_password("hello"), user.hashed_password
365 365 end
366 366
367 367 def test_name_format
368 368 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
369 369 Setting.user_format = :firstname_lastname
370 370 assert_equal 'John Smith', @jsmith.reload.name
371 371 Setting.user_format = :username
372 372 assert_equal 'jsmith', @jsmith.reload.name
373 373 end
374 374
375 375 def test_lock
376 376 user = User.try_to_login("jsmith", "jsmith")
377 377 assert_equal @jsmith, user
378 378
379 379 @jsmith.status = User::STATUS_LOCKED
380 380 assert @jsmith.save
381 381
382 382 user = User.try_to_login("jsmith", "jsmith")
383 383 assert_equal nil, user
384 384 end
385 385
386 386 if ldap_configured?
387 387 context "#try_to_login using LDAP" do
388 388 context "with failed connection to the LDAP server" do
389 389 should "return nil" do
390 390 @auth_source = AuthSourceLdap.find(1)
391 391 AuthSource.any_instance.stubs(:initialize_ldap_con).raises(Net::LDAP::LdapError, 'Cannot connect')
392 392
393 393 assert_equal nil, User.try_to_login('edavis', 'wrong')
394 394 end
395 395 end
396 396
397 397 context "with an unsuccessful authentication" do
398 398 should "return nil" do
399 399 assert_equal nil, User.try_to_login('edavis', 'wrong')
400 400 end
401 401 end
402 402
403 403 context "on the fly registration" do
404 404 setup do
405 405 @auth_source = AuthSourceLdap.find(1)
406 406 end
407 407
408 408 context "with a successful authentication" do
409 409 should "create a new user account if it doesn't exist" do
410 410 assert_difference('User.count') do
411 411 user = User.try_to_login('edavis', '123456')
412 412 assert !user.admin?
413 413 end
414 414 end
415 415
416 416 should "retrieve existing user" do
417 417 user = User.try_to_login('edavis', '123456')
418 418 user.admin = true
419 419 user.save!
420 420
421 421 assert_no_difference('User.count') do
422 422 user = User.try_to_login('edavis', '123456')
423 423 assert user.admin?
424 424 end
425 425 end
426 426 end
427 427 end
428 428 end
429 429
430 430 else
431 431 puts "Skipping LDAP tests."
432 432 end
433 433
434 434 def test_create_anonymous
435 435 AnonymousUser.delete_all
436 436 anon = User.anonymous
437 437 assert !anon.new_record?
438 438 assert_kind_of AnonymousUser, anon
439 439 end
440 440
441 441 should_have_one :rss_token
442 442
443 443 def test_rss_key
444 444 assert_nil @jsmith.rss_token
445 445 key = @jsmith.rss_key
446 446 assert_equal 40, key.length
447 447
448 448 @jsmith.reload
449 449 assert_equal key, @jsmith.rss_key
450 450 end
451 451
452 452
453 453 should_have_one :api_token
454 454
455 455 context "User#api_key" do
456 456 should "generate a new one if the user doesn't have one" do
457 457 user = User.generate_with_protected!(:api_token => nil)
458 458 assert_nil user.api_token
459 459
460 460 key = user.api_key
461 461 assert_equal 40, key.length
462 462 user.reload
463 463 assert_equal key, user.api_key
464 464 end
465 465
466 466 should "return the existing api token value" do
467 467 user = User.generate_with_protected!
468 468 token = Token.generate!(:action => 'api')
469 469 user.api_token = token
470 470 assert user.save
471 471
472 472 assert_equal token.value, user.api_key
473 473 end
474 474 end
475 475
476 476 context "User#find_by_api_key" do
477 477 should "return nil if no matching key is found" do
478 478 assert_nil User.find_by_api_key('zzzzzzzzz')
479 479 end
480 480
481 481 should "return nil if the key is found for an inactive user" do
482 482 user = User.generate_with_protected!(:status => User::STATUS_LOCKED)
483 483 token = Token.generate!(:action => 'api')
484 484 user.api_token = token
485 485 user.save
486 486
487 487 assert_nil User.find_by_api_key(token.value)
488 488 end
489 489
490 490 should "return the user if the key is found for an active user" do
491 491 user = User.generate_with_protected!(:status => User::STATUS_ACTIVE)
492 492 token = Token.generate!(:action => 'api')
493 493 user.api_token = token
494 494 user.save
495 495
496 496 assert_equal user, User.find_by_api_key(token.value)
497 497 end
498 498 end
499 499
500 500 def test_roles_for_project
501 501 # user with a role
502 502 roles = @jsmith.roles_for_project(Project.find(1))
503 503 assert_kind_of Role, roles.first
504 504 assert_equal "Manager", roles.first.name
505 505
506 506 # user with no role
507 507 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
508 508 end
509 509
510 510 def test_valid_notification_options
511 511 # without memberships
512 512 assert_equal 5, User.find(7).valid_notification_options.size
513 513 # with memberships
514 514 assert_equal 6, User.find(2).valid_notification_options.size
515 515 end
516 516
517 517 def test_valid_notification_options_class_method
518 518 assert_equal 5, User.valid_notification_options.size
519 519 assert_equal 5, User.valid_notification_options(User.find(7)).size
520 520 assert_equal 6, User.valid_notification_options(User.find(2)).size
521 521 end
522 522
523 523 def test_mail_notification_all
524 524 @jsmith.mail_notification = 'all'
525 525 @jsmith.notified_project_ids = []
526 526 @jsmith.save
527 527 @jsmith.reload
528 528 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
529 529 end
530 530
531 531 def test_mail_notification_selected
532 532 @jsmith.mail_notification = 'selected'
533 533 @jsmith.notified_project_ids = [1]
534 534 @jsmith.save
535 535 @jsmith.reload
536 536 assert Project.find(1).recipients.include?(@jsmith.mail)
537 537 end
538 538
539 539 def test_mail_notification_only_my_events
540 540 @jsmith.mail_notification = 'only_my_events'
541 541 @jsmith.notified_project_ids = []
542 542 @jsmith.save
543 543 @jsmith.reload
544 544 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
545 545 end
546 546
547 547 def test_comments_sorting_preference
548 548 assert !@jsmith.wants_comments_in_reverse_order?
549 549 @jsmith.pref.comments_sorting = 'asc'
550 550 assert !@jsmith.wants_comments_in_reverse_order?
551 551 @jsmith.pref.comments_sorting = 'desc'
552 552 assert @jsmith.wants_comments_in_reverse_order?
553 553 end
554 554
555 555 def test_find_by_mail_should_be_case_insensitive
556 556 u = User.find_by_mail('JSmith@somenet.foo')
557 557 assert_not_nil u
558 558 assert_equal 'jsmith@somenet.foo', u.mail
559 559 end
560 560
561 561 def test_random_password
562 562 u = User.new
563 563 u.random_password
564 564 assert !u.password.blank?
565 565 assert !u.password_confirmation.blank?
566 566 end
567 567
568 568 context "#change_password_allowed?" do
569 569 should "be allowed if no auth source is set" do
570 570 user = User.generate_with_protected!
571 571 assert user.change_password_allowed?
572 572 end
573 573
574 574 should "delegate to the auth source" do
575 575 user = User.generate_with_protected!
576 576
577 577 allowed_auth_source = AuthSource.generate!
578 578 def allowed_auth_source.allow_password_changes?; true; end
579 579
580 580 denied_auth_source = AuthSource.generate!
581 581 def denied_auth_source.allow_password_changes?; false; end
582 582
583 583 assert user.change_password_allowed?
584 584
585 585 user.auth_source = allowed_auth_source
586 586 assert user.change_password_allowed?, "User not allowed to change password, though auth source does"
587 587
588 588 user.auth_source = denied_auth_source
589 589 assert !user.change_password_allowed?, "User allowed to change password, though auth source does not"
590 590 end
591 591
592 592 end
593 593
594 594 context "#allowed_to?" do
595 595 context "with a unique project" do
596 596 should "return false if project is archived" do
597 597 project = Project.find(1)
598 598 Project.any_instance.stubs(:status).returns(Project::STATUS_ARCHIVED)
599 599 assert ! @admin.allowed_to?(:view_issues, Project.find(1))
600 600 end
601 601
602 602 should "return false if related module is disabled" do
603 603 project = Project.find(1)
604 604 project.enabled_module_names = ["issue_tracking"]
605 605 assert @admin.allowed_to?(:add_issues, project)
606 606 assert ! @admin.allowed_to?(:view_wiki_pages, project)
607 607 end
608 608
609 609 should "authorize nearly everything for admin users" do
610 610 project = Project.find(1)
611 611 assert ! @admin.member_of?(project)
612 612 %w(edit_issues delete_issues manage_news manage_documents manage_wiki).each do |p|
613 613 assert @admin.allowed_to?(p.to_sym, project)
614 614 end
615 615 end
616 616
617 617 should "authorize normal users depending on their roles" do
618 618 project = Project.find(1)
619 619 assert @jsmith.allowed_to?(:delete_messages, project) #Manager
620 620 assert ! @dlopper.allowed_to?(:delete_messages, project) #Developper
621 621 end
622 622 end
623 623
624 624 context "with multiple projects" do
625 625 should "return false if array is empty" do
626 626 assert ! @admin.allowed_to?(:view_project, [])
627 627 end
628 628
629 629 should "return true only if user has permission on all these projects" do
630 630 assert @admin.allowed_to?(:view_project, Project.all)
631 631 assert ! @dlopper.allowed_to?(:view_project, Project.all) #cannot see Project(2)
632 632 assert @jsmith.allowed_to?(:edit_issues, @jsmith.projects) #Manager or Developer everywhere
633 633 assert ! @jsmith.allowed_to?(:delete_issue_watchers, @jsmith.projects) #Dev cannot delete_issue_watchers
634 634 end
635 635
636 636 should "behave correctly with arrays of 1 project" do
637 637 assert ! User.anonymous.allowed_to?(:delete_issues, [Project.first])
638 638 end
639 639 end
640 640
641 641 context "with options[:global]" do
642 642 should "authorize if user has at least one role that has this permission" do
643 643 @dlopper2 = User.find(5) #only Developper on a project, not Manager anywhere
644 644 @anonymous = User.find(6)
645 645 assert @jsmith.allowed_to?(:delete_issue_watchers, nil, :global => true)
646 646 assert ! @dlopper2.allowed_to?(:delete_issue_watchers, nil, :global => true)
647 647 assert @dlopper2.allowed_to?(:add_issues, nil, :global => true)
648 648 assert ! @anonymous.allowed_to?(:add_issues, nil, :global => true)
649 649 assert @anonymous.allowed_to?(:view_issues, nil, :global => true)
650 650 end
651 651 end
652 652 end
653 653
654 654 context "User#notify_about?" do
655 655 context "Issues" do
656 656 setup do
657 657 @project = Project.find(1)
658 658 @author = User.generate_with_protected!
659 659 @assignee = User.generate_with_protected!
660 660 @issue = Issue.generate_for_project!(@project, :assigned_to => @assignee, :author => @author)
661 661 end
662 662
663 663 should "be true for a user with :all" do
664 664 @author.update_attribute(:mail_notification, 'all')
665 665 assert @author.notify_about?(@issue)
666 666 end
667 667
668 668 should "be false for a user with :none" do
669 669 @author.update_attribute(:mail_notification, 'none')
670 670 assert ! @author.notify_about?(@issue)
671 671 end
672 672
673 673 should "be false for a user with :only_my_events and isn't an author, creator, or assignee" do
674 674 @user = User.generate_with_protected!(:mail_notification => 'only_my_events')
675 Member.create!(:user => @user, :project => @project, :role_ids => [1])
675 676 assert ! @user.notify_about?(@issue)
676 677 end
677 678
678 679 should "be true for a user with :only_my_events and is the author" do
679 680 @author.update_attribute(:mail_notification, 'only_my_events')
680 681 assert @author.notify_about?(@issue)
681 682 end
682 683
683 684 should "be true for a user with :only_my_events and is the assignee" do
684 685 @assignee.update_attribute(:mail_notification, 'only_my_events')
685 686 assert @assignee.notify_about?(@issue)
686 687 end
687 688
688 689 should "be true for a user with :only_assigned and is the assignee" do
689 690 @assignee.update_attribute(:mail_notification, 'only_assigned')
690 691 assert @assignee.notify_about?(@issue)
691 692 end
692 693
693 694 should "be false for a user with :only_assigned and is not the assignee" do
694 695 @author.update_attribute(:mail_notification, 'only_assigned')
695 696 assert ! @author.notify_about?(@issue)
696 697 end
697 698
698 699 should "be true for a user with :only_owner and is the author" do
699 700 @author.update_attribute(:mail_notification, 'only_owner')
700 701 assert @author.notify_about?(@issue)
701 702 end
702 703
703 704 should "be false for a user with :only_owner and is not the author" do
704 705 @assignee.update_attribute(:mail_notification, 'only_owner')
705 706 assert ! @assignee.notify_about?(@issue)
706 707 end
708
709 should "be true for a user with :selected and is the author" do
710 @author.update_attribute(:mail_notification, 'selected')
711 assert @author.notify_about?(@issue)
712 end
713
714 should "be true for a user with :selected and is the assignee" do
715 @assignee.update_attribute(:mail_notification, 'selected')
716 assert @assignee.notify_about?(@issue)
717 end
718
719 should "be false for a user with :selected and is not the author or assignee" do
720 @user = User.generate_with_protected!(:mail_notification => 'selected')
721 Member.create!(:user => @user, :project => @project, :role_ids => [1])
722 assert ! @user.notify_about?(@issue)
723 end
707 724 end
708 725
709 726 context "other events" do
710 727 should 'be added and tested'
711 728 end
712 729 end
713 730
714 731 if Object.const_defined?(:OpenID)
715 732
716 733 def test_setting_identity_url
717 734 normalized_open_id_url = 'http://example.com/'
718 735 u = User.new( :identity_url => 'http://example.com/' )
719 736 assert_equal normalized_open_id_url, u.identity_url
720 737 end
721 738
722 739 def test_setting_identity_url_without_trailing_slash
723 740 normalized_open_id_url = 'http://example.com/'
724 741 u = User.new( :identity_url => 'http://example.com' )
725 742 assert_equal normalized_open_id_url, u.identity_url
726 743 end
727 744
728 745 def test_setting_identity_url_without_protocol
729 746 normalized_open_id_url = 'http://example.com/'
730 747 u = User.new( :identity_url => 'example.com' )
731 748 assert_equal normalized_open_id_url, u.identity_url
732 749 end
733 750
734 751 def test_setting_blank_identity_url
735 752 u = User.new( :identity_url => 'example.com' )
736 753 u.identity_url = ''
737 754 assert u.identity_url.blank?
738 755 end
739 756
740 757 def test_setting_invalid_identity_url
741 758 u = User.new( :identity_url => 'this is not an openid url' )
742 759 assert u.identity_url.blank?
743 760 end
744 761
745 762 else
746 763 puts "Skipping openid tests."
747 764 end
748 765
749 766 end
General Comments 0
You need to be logged in to leave comments. Login now