1<?php 2 3/** 4 * @file 5 * Tests for user.module. 6 */ 7 8class UserRegistrationTestCase extends DrupalWebTestCase { 9 public static function getInfo() { 10 return array( 11 'name' => 'User registration', 12 'description' => 'Test registration of user under different configurations.', 13 'group' => 'User' 14 ); 15 } 16 17 function setUp() { 18 parent::setUp('field_test'); 19 } 20 21 function testRegistrationWithEmailVerification() { 22 // Require e-mail verification. 23 variable_set('user_email_verification', TRUE); 24 25 // Set registration to administrator only. 26 variable_set('user_register', USER_REGISTER_ADMINISTRATORS_ONLY); 27 $this->drupalGet('user/register'); 28 $this->assertResponse(403, 'Registration page is inaccessible when only administrators can create accounts.'); 29 30 // Allow registration by site visitors without administrator approval. 31 variable_set('user_register', USER_REGISTER_VISITORS); 32 $edit = array(); 33 $edit['name'] = $name = $this->randomName(); 34 $edit['mail'] = $mail = $edit['name'] . '@example.com'; 35 $this->drupalPost('user/register', $edit, t('Create new account')); 36 $this->assertText(t('A welcome message with further instructions has been sent to your e-mail address.'), 'User registered successfully.'); 37 $accounts = user_load_multiple(array(), array('name' => $name, 'mail' => $mail)); 38 $new_user = reset($accounts); 39 $this->assertTrue($new_user->status, 'New account is active after registration.'); 40 41 // Allow registration by site visitors, but require administrator approval. 42 variable_set('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL); 43 $edit = array(); 44 $edit['name'] = $name = $this->randomName(); 45 $edit['mail'] = $mail = $edit['name'] . '@example.com'; 46 $this->drupalPost('user/register', $edit, t('Create new account')); 47 $accounts = user_load_multiple(array(), array('name' => $name, 'mail' => $mail)); 48 $new_user = reset($accounts); 49 $this->assertFalse($new_user->status, 'New account is blocked until approved by an administrator.'); 50 } 51 52 function testRegistrationWithoutEmailVerification() { 53 // Don't require e-mail verification. 54 variable_set('user_email_verification', FALSE); 55 56 // Allow registration by site visitors without administrator approval. 57 variable_set('user_register', USER_REGISTER_VISITORS); 58 $edit = array(); 59 $edit['name'] = $name = $this->randomName(); 60 $edit['mail'] = $mail = $edit['name'] . '@example.com'; 61 62 // Try entering a mismatching password. 63 $edit['pass[pass1]'] = '99999.0'; 64 $edit['pass[pass2]'] = '99999'; 65 $this->drupalPost('user/register', $edit, t('Create new account')); 66 $this->assertText(t('The specified passwords do not match.'), 'Typing mismatched passwords displays an error message.'); 67 68 // Enter a correct password. 69 $edit['pass[pass1]'] = $new_pass = $this->randomName(); 70 $edit['pass[pass2]'] = $new_pass; 71 $this->drupalPost('user/register', $edit, t('Create new account')); 72 $accounts = user_load_multiple(array(), array('name' => $name, 'mail' => $mail)); 73 $new_user = reset($accounts); 74 $this->assertText(t('Registration successful. You are now logged in.'), 'Users are logged in after registering.'); 75 $this->drupalLogout(); 76 77 // Allow registration by site visitors, but require administrator approval. 78 variable_set('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL); 79 $edit = array(); 80 $edit['name'] = $name = $this->randomName(); 81 $edit['mail'] = $mail = $edit['name'] . '@example.com'; 82 $edit['pass[pass1]'] = $pass = $this->randomName(); 83 $edit['pass[pass2]'] = $pass; 84 $this->drupalPost('user/register', $edit, t('Create new account')); 85 $this->assertText(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.'), 'Users are notified of pending approval'); 86 87 // Try to login before administrator approval. 88 $auth = array( 89 'name' => $name, 90 'pass' => $pass, 91 ); 92 $this->drupalPost('user/login', $auth, t('Log in')); 93 $this->assertText(t('The username @name has not been activated or is blocked.', array('@name' => $name)), 'User cannot login yet.'); 94 95 // Activate the new account. 96 $accounts = user_load_multiple(array(), array('name' => $name, 'mail' => $mail)); 97 $new_user = reset($accounts); 98 $admin_user = $this->drupalCreateUser(array('administer users')); 99 $this->drupalLogin($admin_user); 100 $edit = array( 101 'status' => 1, 102 ); 103 $this->drupalPost('user/' . $new_user->uid . '/edit', $edit, t('Save')); 104 $this->drupalLogout(); 105 106 // Login after administrator approval. 107 $this->drupalPost('user/login', $auth, t('Log in')); 108 $this->assertText(t('Member for'), 'User can log in after administrator approval.'); 109 } 110 111 function testRegistrationEmailDuplicates() { 112 // Don't require e-mail verification. 113 variable_set('user_email_verification', FALSE); 114 115 // Allow registration by site visitors without administrator approval. 116 variable_set('user_register', USER_REGISTER_VISITORS); 117 118 // Set up a user to check for duplicates. 119 $duplicate_user = $this->drupalCreateUser(); 120 121 $edit = array(); 122 $edit['name'] = $this->randomName(); 123 $edit['mail'] = $duplicate_user->mail; 124 125 // Attempt to create a new account using an existing e-mail address. 126 $this->drupalPost('user/register', $edit, t('Create new account')); 127 $this->assertText(t('The e-mail address @email is already registered.', array('@email' => $duplicate_user->mail)), 'Supplying an exact duplicate email address displays an error message'); 128 129 // Attempt to bypass duplicate email registration validation by adding spaces. 130 $edit['mail'] = ' ' . $duplicate_user->mail . ' '; 131 132 $this->drupalPost('user/register', $edit, t('Create new account')); 133 $this->assertText(t('The e-mail address @email is already registered.', array('@email' => $duplicate_user->mail)), 'Supplying a duplicate email address with added whitespace displays an error message'); 134 } 135 136 function testRegistrationDefaultValues() { 137 // Allow registration by site visitors without administrator approval. 138 variable_set('user_register', USER_REGISTER_VISITORS); 139 140 // Don't require e-mail verification. 141 variable_set('user_email_verification', FALSE); 142 143 // Set the default timezone to Brussels. 144 variable_set('configurable_timezones', 1); 145 variable_set('date_default_timezone', 'Europe/Brussels'); 146 147 // Check that the account information fieldset's options are not displayed 148 // is a fieldset if there is not more than one fieldset in the form. 149 $this->drupalGet('user/register'); 150 $this->assertNoRaw('<fieldset id="edit-account"><legend>Account information</legend>', 'Account settings fieldset was hidden.'); 151 152 $edit = array(); 153 $edit['name'] = $name = $this->randomName(); 154 $edit['mail'] = $mail = $edit['name'] . '@example.com'; 155 $edit['pass[pass1]'] = $new_pass = $this->randomName(); 156 $edit['pass[pass2]'] = $new_pass; 157 $this->drupalPost(NULL, $edit, t('Create new account')); 158 159 // Check user fields. 160 $accounts = user_load_multiple(array(), array('name' => $name, 'mail' => $mail)); 161 $new_user = reset($accounts); 162 $this->assertEqual($new_user->name, $name, 'Username matches.'); 163 $this->assertEqual($new_user->mail, $mail, 'E-mail address matches.'); 164 $this->assertEqual($new_user->theme, '', 'Correct theme field.'); 165 $this->assertEqual($new_user->signature, '', 'Correct signature field.'); 166 $this->assertTrue(($new_user->created > REQUEST_TIME - 20 ), 'Correct creation time.'); 167 $this->assertEqual($new_user->status, variable_get('user_register', USER_REGISTER_VISITORS_ADMINISTRATIVE_APPROVAL) == USER_REGISTER_VISITORS ? 1 : 0, 'Correct status field.'); 168 $this->assertEqual($new_user->timezone, variable_get('date_default_timezone'), 'Correct time zone field.'); 169 $this->assertEqual($new_user->language, '', 'Correct language field.'); 170 $this->assertEqual($new_user->picture, '', 'Correct picture field.'); 171 $this->assertEqual($new_user->init, $mail, 'Correct init field.'); 172 } 173 174 /** 175 * Tests Field API fields on user registration forms. 176 */ 177 function testRegistrationWithUserFields() { 178 // Create a field, and an instance on 'user' entity type. 179 $field = array( 180 'type' => 'test_field', 181 'field_name' => 'test_user_field', 182 'cardinality' => 1, 183 ); 184 field_create_field($field); 185 $instance = array( 186 'field_name' => 'test_user_field', 187 'entity_type' => 'user', 188 'label' => 'Some user field', 189 'bundle' => 'user', 190 'required' => TRUE, 191 'settings' => array('user_register_form' => FALSE), 192 ); 193 field_create_instance($instance); 194 195 // Check that the field does not appear on the registration form. 196 $this->drupalGet('user/register'); 197 $this->assertNoText($instance['label'], 'The field does not appear on user registration form'); 198 199 // Have the field appear on the registration form. 200 $instance['settings']['user_register_form'] = TRUE; 201 field_update_instance($instance); 202 $this->drupalGet('user/register'); 203 $this->assertText($instance['label'], 'The field appears on user registration form'); 204 205 // Check that validation errors are correctly reported. 206 $edit = array(); 207 $edit['name'] = $name = $this->randomName(); 208 $edit['mail'] = $mail = $edit['name'] . '@example.com'; 209 // Missing input in required field. 210 $edit['test_user_field[und][0][value]'] = ''; 211 $this->drupalPost(NULL, $edit, t('Create new account')); 212 $this->assertRaw(t('@name field is required.', array('@name' => $instance['label'])), 'Field validation error was correctly reported.'); 213 // Invalid input. 214 $edit['test_user_field[und][0][value]'] = '-1'; 215 $this->drupalPost(NULL, $edit, t('Create new account')); 216 $this->assertRaw(t('%name does not accept the value -1.', array('%name' => $instance['label'])), 'Field validation error was correctly reported.'); 217 218 // Submit with valid data. 219 $value = rand(1, 255); 220 $edit['test_user_field[und][0][value]'] = $value; 221 $this->drupalPost(NULL, $edit, t('Create new account')); 222 // Check user fields. 223 $accounts = user_load_multiple(array(), array('name' => $name, 'mail' => $mail)); 224 $new_user = reset($accounts); 225 $this->assertEqual($new_user->test_user_field[LANGUAGE_NONE][0]['value'], $value, 'The field value was correctly saved.'); 226 227 // Check that the 'add more' button works. 228 $field['cardinality'] = FIELD_CARDINALITY_UNLIMITED; 229 field_update_field($field); 230 foreach (array('js', 'nojs') as $js) { 231 $this->drupalGet('user/register'); 232 // Add two inputs. 233 $value = rand(1, 255); 234 $edit = array(); 235 $edit['test_user_field[und][0][value]'] = $value; 236 if ($js == 'js') { 237 $this->drupalPostAJAX(NULL, $edit, 'test_user_field_add_more'); 238 $this->drupalPostAJAX(NULL, $edit, 'test_user_field_add_more'); 239 } 240 else { 241 $this->drupalPost(NULL, $edit, t('Add another item')); 242 $this->drupalPost(NULL, $edit, t('Add another item')); 243 } 244 // Submit with three values. 245 $edit['test_user_field[und][1][value]'] = $value + 1; 246 $edit['test_user_field[und][2][value]'] = $value + 2; 247 $edit['name'] = $name = $this->randomName(); 248 $edit['mail'] = $mail = $edit['name'] . '@example.com'; 249 $this->drupalPost(NULL, $edit, t('Create new account')); 250 // Check user fields. 251 $accounts = user_load_multiple(array(), array('name' => $name, 'mail' => $mail)); 252 $new_user = reset($accounts); 253 $this->assertEqual($new_user->test_user_field[LANGUAGE_NONE][0]['value'], $value, format_string('@js : The field value was correclty saved.', array('@js' => $js))); 254 $this->assertEqual($new_user->test_user_field[LANGUAGE_NONE][1]['value'], $value + 1, format_string('@js : The field value was correclty saved.', array('@js' => $js))); 255 $this->assertEqual($new_user->test_user_field[LANGUAGE_NONE][2]['value'], $value + 2, format_string('@js : The field value was correclty saved.', array('@js' => $js))); 256 } 257 } 258} 259 260class UserValidationTestCase extends DrupalWebTestCase { 261 public static function getInfo() { 262 return array( 263 'name' => 'Username/e-mail validation', 264 'description' => 'Verify that username/email validity checks behave as designed.', 265 'group' => 'User' 266 ); 267 } 268 269 // Username validation. 270 function testUsernames() { 271 $test_cases = array( // '<username>' => array('<description>', 'assert<testName>'), 272 'foo' => array('Valid username', 'assertNull'), 273 'FOO' => array('Valid username', 'assertNull'), 274 'Foo O\'Bar' => array('Valid username', 'assertNull'), 275 'foo@bar' => array('Valid username', 'assertNull'), 276 'foo@example.com' => array('Valid username', 'assertNull'), 277 'foo@-example.com' => array('Valid username', 'assertNull'), // invalid domains are allowed in usernames 278 'þòøÇߪř€' => array('Valid username', 'assertNull'), 279 'foo+bar' => array('Valid username', 'assertNull'), // '+' symbol is allowed 280 'ᚠᛇᚻ᛫ᛒᛦᚦ' => array('Valid UTF8 username', 'assertNull'), // runes 281 ' foo' => array('Invalid username that starts with a space', 'assertNotNull'), 282 'foo ' => array('Invalid username that ends with a space', 'assertNotNull'), 283 'foo bar' => array('Invalid username that contains 2 spaces \' \'', 'assertNotNull'), 284 '' => array('Invalid empty username', 'assertNotNull'), 285 'foo/' => array('Invalid username containing invalid chars', 'assertNotNull'), 286 'foo' . chr(0) . 'bar' => array('Invalid username containing chr(0)', 'assertNotNull'), // NULL 287 'foo' . chr(13) . 'bar' => array('Invalid username containing chr(13)', 'assertNotNull'), // CR 288 str_repeat('x', USERNAME_MAX_LENGTH + 1) => array('Invalid excessively long username', 'assertNotNull'), 289 ); 290 foreach ($test_cases as $name => $test_case) { 291 list($description, $test) = $test_case; 292 $result = user_validate_name($name); 293 $this->$test($result, $description . ' (' . $name . ')'); 294 } 295 } 296 297 // Mail validation. More extensive tests can be found at common.test 298 function testMailAddresses() { 299 $test_cases = array( // '<username>' => array('<description>', 'assert<testName>'), 300 '' => array('Empty mail address', 'assertNotNull'), 301 'foo' => array('Invalid mail address', 'assertNotNull'), 302 'foo@example.com' => array('Valid mail address', 'assertNull'), 303 ); 304 foreach ($test_cases as $name => $test_case) { 305 list($description, $test) = $test_case; 306 $result = user_validate_mail($name); 307 $this->$test($result, $description . ' (' . $name . ')'); 308 } 309 } 310} 311 312/** 313 * Functional tests for user logins, including rate limiting of login attempts. 314 */ 315class UserLoginTestCase extends DrupalWebTestCase { 316 public static function getInfo() { 317 return array( 318 'name' => 'User login', 319 'description' => 'Ensure that login works as expected.', 320 'group' => 'User', 321 ); 322 } 323 324 function setUp() { 325 parent::setUp('user_session_test', 'user_flood_test'); 326 } 327 328 /** 329 * Test the global login flood control. 330 */ 331 function testGlobalLoginFloodControl() { 332 // Set the global login limit. 333 variable_set('user_failed_login_ip_limit', 10); 334 // Set a high per-user limit out so that it is not relevant in the test. 335 variable_set('user_failed_login_user_limit', 4000); 336 337 $user1 = $this->drupalCreateUser(array()); 338 $incorrect_user1 = clone $user1; 339 $incorrect_user1->pass_raw .= 'incorrect'; 340 341 // Try 2 failed logins. 342 for ($i = 0; $i < 2; $i++) { 343 $this->assertFailedLogin($incorrect_user1); 344 } 345 346 // A successful login will not reset the IP-based flood control count. 347 $this->drupalLogin($user1); 348 $this->drupalLogout(); 349 350 // Try 8 more failed logins, they should not trigger the flood control 351 // mechanism. 352 for ($i = 0; $i < 8; $i++) { 353 $this->assertFailedLogin($incorrect_user1); 354 } 355 356 // The next login trial should result in an IP-based flood error message. 357 $this->assertFailedLogin($incorrect_user1, 'ip'); 358 359 // A login with the correct password should also result in a flood error 360 // message. 361 $this->assertFailedLogin($user1, 'ip'); 362 } 363 364 /** 365 * Test the per-user login flood control. 366 */ 367 function testPerUserLoginFloodControl() { 368 // Set a high global limit out so that it is not relevant in the test. 369 variable_set('user_failed_login_ip_limit', 4000); 370 // Set the per-user login limit. 371 variable_set('user_failed_login_user_limit', 3); 372 373 $user1 = $this->drupalCreateUser(array()); 374 $incorrect_user1 = clone $user1; 375 $incorrect_user1->pass_raw .= 'incorrect'; 376 377 $user2 = $this->drupalCreateUser(array()); 378 379 // Try 2 failed logins. 380 for ($i = 0; $i < 2; $i++) { 381 $this->assertFailedLogin($incorrect_user1); 382 } 383 384 // A successful login will reset the per-user flood control count. 385 $this->drupalLogin($user1); 386 $this->drupalLogout(); 387 388 // Try 3 failed logins for user 1, they will not trigger flood control. 389 for ($i = 0; $i < 3; $i++) { 390 $this->assertFailedLogin($incorrect_user1); 391 } 392 393 // Try one successful attempt for user 2, it should not trigger any 394 // flood control. 395 $this->drupalLogin($user2); 396 $this->drupalLogout(); 397 398 // Try one more attempt for user 1, it should be rejected, even if the 399 // correct password has been used. 400 $this->assertFailedLogin($user1, 'user'); 401 } 402 403 /** 404 * Test that user password is re-hashed upon login after changing $count_log2. 405 */ 406 function testPasswordRehashOnLogin() { 407 // Load password hashing API. 408 require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); 409 // Set initial $count_log2 to the default, DRUPAL_HASH_COUNT. 410 variable_set('password_count_log2', DRUPAL_HASH_COUNT); 411 // Create a new user and authenticate. 412 $account = $this->drupalCreateUser(array()); 413 $password = $account->pass_raw; 414 $this->drupalLogin($account); 415 $this->drupalLogout(); 416 // Load the stored user. The password hash should reflect $count_log2. 417 $account = user_load($account->uid); 418 $this->assertIdentical(_password_get_count_log2($account->pass), DRUPAL_HASH_COUNT); 419 // Change $count_log2 and log in again. 420 variable_set('password_count_log2', DRUPAL_HASH_COUNT + 1); 421 $account->pass_raw = $password; 422 $this->drupalLogin($account); 423 // Load the stored user, which should have a different password hash now. 424 $account = user_load($account->uid, TRUE); 425 $this->assertIdentical(_password_get_count_log2($account->pass), DRUPAL_HASH_COUNT + 1); 426 } 427 428 /** 429 * Test logging in when an anon session already exists. 430 */ 431 function testLoginWithAnonSession() { 432 // Visit the callback to generate a session for this anon user. 433 $this->drupalGet('user_session_test_anon_session'); 434 // Now login. 435 $account = $this->drupalCreateUser(array()); 436 $this->drupalLogin($account); 437 } 438 439 /** 440 * Make an unsuccessful login attempt. 441 * 442 * @param $account 443 * A user object with name and pass_raw attributes for the login attempt. 444 * @param $flood_trigger 445 * Whether or not to expect that the flood control mechanism will be 446 * triggered. 447 */ 448 function assertFailedLogin($account, $flood_trigger = NULL) { 449 $edit = array( 450 'name' => $account->name, 451 'pass' => $account->pass_raw, 452 ); 453 $this->drupalPost('user', $edit, t('Log in')); 454 $this->assertNoFieldByXPath("//input[@name='pass' and @value!='']", NULL, 'Password value attribute is blank.'); 455 if (isset($flood_trigger)) { 456 $this->assertResponse(403); 457 $user_log = db_query_range('SELECT message FROM {watchdog} WHERE type = :type ORDER BY wid DESC', 0, 1, array(':type' => 'user'))->fetchField(); 458 $user_flood_test_log = db_query_range('SELECT message FROM {watchdog} WHERE type = :type ORDER BY wid DESC', 0, 1, array(':type' => 'user_flood_test'))->fetchField(); 459 if ($flood_trigger == 'user') { 460 $this->assertRaw(t('Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => url('user/password'), '@count' => variable_get('user_failed_login_user_limit', 5)))); 461 $this->assertEqual('Flood control blocked login attempt for %user from %ip.', $user_log, 'A watchdog message was logged for the login attempt blocked by flood control per user'); 462 $this->assertEqual('hook_user_flood_control was passed username %username and IP %ip.', $user_flood_test_log, 'hook_user_flood_control was invoked by flood control per user'); 463 } 464 else { 465 // No uid, so the limit is IP-based. 466 $this->assertRaw(t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => url('user/password')))); 467 $this->assertEqual('Flood control blocked login attempt from %ip.', $user_log, 'A watchdog message was logged for the login attempt blocked by flood control per IP'); 468 $this->assertEqual('hook_user_flood_control was passed IP %ip.', $user_flood_test_log, 'hook_user_flood_control was invoked by flood control per IP'); 469 } 470 } 471 else { 472 $this->assertText(t('Sorry, unrecognized username or password. Have you forgotten your password?')); 473 } 474 } 475} 476 477/** 478 * Tests resetting a user password. 479 */ 480class UserPasswordResetTestCase extends DrupalWebTestCase { 481 protected $profile = 'standard'; 482 483 function setUp() { 484 parent::setUp('user_form_test'); 485 } 486 487 public static function getInfo() { 488 return array( 489 'name' => 'Reset password', 490 'description' => 'Ensure that password reset methods work as expected.', 491 'group' => 'User', 492 ); 493 } 494 495 /** 496 * Retrieves password reset email and extracts the login link. 497 */ 498 public function getResetURL($bypass_form = FALSE) { 499 // Assume the most recent email. 500 $_emails = $this->drupalGetMails(); 501 $email = end($_emails); 502 $urls = array(); 503 preg_match('#.+user/reset/.+#', $email['body'], $urls); 504 505 return $urls[0] . ($bypass_form ? '/login' : ''); 506 } 507 508 /** 509 * Generates login link. 510 */ 511 public function generateResetURL($account, $bypass_form = FALSE) { 512 return user_pass_reset_url($account) . ($bypass_form ? '/login' : ''); 513 } 514 515 /** 516 * Turns a password reset URL into a 'confirm' URL. 517 */ 518 public function getConfirmURL($reset_url) { 519 // Last part is always the hash; replace with "confirm". 520 $parts = explode('/', $reset_url); 521 array_pop($parts); 522 array_push($parts, 'confirm'); 523 return implode('/', $parts); 524 } 525 526 /** 527 * Tests password reset functionality. 528 */ 529 function testUserPasswordReset($use_direct_login_link = FALSE) { 530 // Create a user. 531 $account = $this->drupalCreateUser(); 532 $this->drupalLogin($account); 533 $this->drupalLogout(); 534 // Attempt to reset password. 535 $edit = array('name' => $account->name); 536 $this->drupalPost('user/password', $edit, t('E-mail new password')); 537 // Confirm the password reset. 538 $this->assertText(t('Further instructions have been sent to your e-mail address.'), 'Password reset instructions mailed message displayed.'); 539 // Ensure that flood control was not triggered. 540 $this->assertNoText(t('is temporarily blocked. Try again later'), 'Flood control was not triggered by single password reset.'); 541 542 // Create an image field to enable an Ajax request on the user profile page. 543 $field = array( 544 'field_name' => 'field_avatar', 545 'type' => 'image', 546 'settings' => array(), 547 'cardinality' => 1, 548 ); 549 field_create_field($field); 550 551 $instance = array( 552 'field_name' => $field['field_name'], 553 'entity_type' => 'user', 554 'label' => 'Avatar', 555 'bundle' => 'user', 556 'required' => FALSE, 557 'settings' => array(), 558 'widget' => array( 559 'type' => 'image_image', 560 'settings' => array(), 561 ), 562 ); 563 field_create_instance($instance); 564 565 variable_del("user_test_pass_reset_form_submit_{$account->uid}"); 566 $resetURL = $this->getResetURL($use_direct_login_link); 567 $this->drupalGet($resetURL); 568 569 // Check successful login. 570 if (!$use_direct_login_link) { 571 $this->assertUrl($this->getConfirmURL($resetURL), array(), 'The user is redirected to the reset password confirm form.'); 572 $this->drupalPost(NULL, NULL, t('Log in')); 573 // The form was fully processed before redirecting. 574 $form_submit_handled = variable_get("user_test_pass_reset_form_submit_{$account->uid}", FALSE); 575 $this->assertTrue($form_submit_handled, 'A custom submit handler executed.'); 576 } 577 $this->assertText('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'); 578 579 // Make sure the Ajax request from uploading a file does not invalidate the 580 // reset token. 581 $image = current($this->drupalGetTestFiles('image')); 582 $edit = array( 583 'files[field_avatar_und_0]' => drupal_realpath($image->uri), 584 ); 585 $this->drupalPostAJAX(NULL, $edit, 'field_avatar_und_0_upload_button'); 586 587 // Change the forgotten password. 588 $password = user_password(); 589 $edit = array('pass[pass1]' => $password, 'pass[pass2]' => $password); 590 $this->drupalPost(NULL, $edit, t('Save')); 591 $this->assertText(t('The changes have been saved.'), 'Forgotten password changed.'); 592 593 // Ensure blocked and deleted accounts can't access the direct login link. 594 $this->drupalLogout(); 595 $reset_url = $this->generateResetURL($account, $use_direct_login_link); 596 user_save($account, array('status' => 0)); 597 $this->drupalGet($reset_url); 598 $this->assertResponse(403); 599 user_delete($account->uid); 600 $this->drupalGet($reset_url); 601 $this->assertResponse(403); 602 } 603 604 /** 605 * Test user-based flood control on password reset. 606 */ 607 function testPasswordResetFloodControlPerUser() { 608 // Set a very low limit for testing. 609 variable_set('user_pass_reset_user_limit', 2); 610 611 // Create a user. 612 $account = $this->drupalCreateUser(); 613 $this->drupalLogin($account); 614 $this->drupalLogout(); 615 616 $edit = array('name' => $account->name); 617 618 // Try 2 requests that should not trigger flood control. 619 for ($i = 0; $i < 2; $i++) { 620 $this->drupalPost('user/password', $edit, t('E-mail new password')); 621 // Confirm the password reset. 622 $this->assertText(t('Further instructions have been sent to your e-mail address.'), 'Password reset instructions mailed message displayed.'); 623 // Ensure that flood control was not triggered. 624 $this->assertNoText(t('is temporarily blocked. Try again later'), 'Flood control was not triggered by password reset.'); 625 } 626 627 // A successful password reset should clear flood events. 628 $resetURL = $this->getResetURL(); 629 $this->drupalGet($resetURL); 630 631 // Check successful login. 632 $this->drupalPost(NULL, NULL, t('Log in')); 633 $this->drupalLogout(); 634 635 // Try 2 requests that should not trigger flood control. 636 for ($i = 0; $i < 2; $i++) { 637 $this->drupalPost('user/password', $edit, t('E-mail new password')); 638 // Confirm the password reset. 639 $this->assertText(t('Further instructions have been sent to your e-mail address.'), 'Password reset instructions mailed message displayed.'); 640 // Ensure that flood control was not triggered. 641 $this->assertNoText(t('is temporarily blocked. Try again later'), 'Flood control was not triggered by password reset.'); 642 } 643 644 // The next request should trigger flood control 645 $this->drupalPost('user/password', $edit, t('E-mail new password')); 646 // Confirm the password reset was blocked. 647 $this->assertNoText(t('Further instructions have been sent to your e-mail address.'), 'Password reset instructions mailed message not displayed for excessive password resets.'); 648 // Ensure that flood control was triggered. 649 $this->assertText(t('Sorry, there have been more than 2 password reset attempts for this account. It is temporarily blocked.'), 'Flood control was triggered by excessive password resets for one user.'); 650 } 651 652 /** 653 * Test IP-based flood control on password reset. 654 */ 655 function testPasswordResetFloodControlPerIp() { 656 // Set a very low limit for testing. 657 variable_set('user_pass_reset_ip_limit', 2); 658 659 // Try 2 requests that should not trigger flood control. 660 for ($i = 0; $i < 2; $i++) { 661 $name = $this->randomName(); 662 $edit = array('name' => $name); 663 $this->drupalPost('user/password', $edit, t('E-mail new password')); 664 // Confirm the password reset was not blocked. Note that @name is used 665 // instead of %name as assertText() works with plain text not HTML. 666 $this->assertText(t('Sorry, @name is not recognized as a user name or an e-mail address.', array('@name' => $name)), 'User name not recognized message displayed.'); 667 // Ensure that flood control was not triggered. 668 $this->assertNoText(t('is temporarily blocked. Try again later'), 'Flood control was not triggered by password reset.'); 669 } 670 671 // The next request should trigger flood control 672 $name = $this->randomName(); 673 $edit = array('name' => $name); 674 $this->drupalPost('user/password', $edit, t('E-mail new password')); 675 // Confirm the password reset was blocked early. Note that @name is used 676 // instead of %name as assertText() works with plain text not HTML. 677 $this->assertNoText(t('Sorry, @name is not recognized as a user name or an e-mail address.', array('@name' => $name)), 'User name not recognized message not displayed.'); 678 // Ensure that flood control was triggered. 679 $this->assertText(t('Sorry, too many password reset attempts from your IP address. This IP address is temporarily blocked.'), 'Flood control was triggered by excessive password resets from one IP.'); 680 } 681 682 /** 683 * Test user password reset while logged in. 684 */ 685 function testUserPasswordResetLoggedIn($use_direct_login_link = FALSE) { 686 $another_account = $this->drupalCreateUser(); 687 $account = $this->drupalCreateUser(); 688 $this->drupalLogin($account); 689 // Make sure the test account has a valid password. 690 user_save($account, array('pass' => user_password())); 691 692 // Try to use the login link while logged in as a different user. 693 // Generate one time login link. 694 $reset_url = $this->generateResetURL($another_account, $use_direct_login_link); 695 $this->drupalGet($reset_url); 696 $this->assertRaw(t( 697 'Another user (%other_user) is already logged into the site on this computer, but you tried to use a one-time link for user %resetting_user. Please <a href="!logout">logout</a> and try using the link again.', 698 array('%other_user' => $account->name, '%resetting_user' => $another_account->name, '!logout' => url('user/logout')) 699 )); 700 701 // Test the link for a deleted user while logged in. 702 user_delete($another_account->uid); 703 $this->drupalGet($reset_url); 704 $this->assertText('The one-time login link you clicked is invalid.'); 705 706 // Generate a one time login link for the logged-in user. 707 $fapi_action = $use_direct_login_link ? 'build' : 'submit'; 708 variable_del("user_test_pass_reset_form_{$fapi_action}_{$account->uid}"); 709 $reset_url = $this->generateResetURL($account, $use_direct_login_link); 710 $this->drupalGet($reset_url); 711 if ($use_direct_login_link) { 712 // The form is never fully built; user is logged out (session destroyed) 713 // and redirected to the same URL, then logged in again and redirected 714 // during form build. 715 $form_built = variable_get("user_test_pass_reset_form_build_{$account->uid}", FALSE); 716 $this->assertTrue(!$form_built, 'The password reset form was never fully built.'); 717 } 718 else { 719 $this->assertUrl($this->getConfirmURL($reset_url), array(), 'The user is redirected to the reset password confirm form.'); 720 $this->assertText('Reset password'); 721 $this->drupalPost(NULL, NULL, t('Log in')); 722 // The form was fully processed before redirecting. 723 $form_submit_handled = variable_get("user_test_pass_reset_form_submit_{$account->uid}", FALSE); 724 $this->assertTrue($form_submit_handled, 'A custom submit handler executed.'); 725 } 726 $this->assertText('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'); 727 728 // The user can change the forgotten password on the page they are 729 // redirected to. 730 $pass = user_password(); 731 $edit = array( 732 'pass[pass1]' => $pass, 733 'pass[pass2]' => $pass, 734 ); 735 $this->drupalPost(NULL, $edit, t('Save')); 736 737 $this->assertText('The changes have been saved.'); 738 } 739 740 /** 741 * Test direct login link that bypasses the password reset form. 742 */ 743 function testUserDirectLogin() { 744 $this->testUserPasswordReset(TRUE); 745 $this->testUserPasswordResetLoggedIn(TRUE); 746 } 747 748 /** 749 * Attempts login using an expired password reset link. 750 */ 751 function testUserPasswordResetExpired() { 752 // Set password reset timeout variable to 43200 seconds = 12 hours. 753 $timeout = 43200; 754 variable_set('user_password_reset_timeout', $timeout); 755 756 // Create a user. 757 $account = $this->drupalCreateUser(); 758 $this->drupalLogin($account); 759 // Load real user object. 760 $account = user_load($account->uid, TRUE); 761 $this->drupalLogout(); 762 763 // To attempt an expired password reset, create a password reset link as if 764 // its request time was 60 seconds older than the allowed limit of timeout. 765 $bogus_timestamp = REQUEST_TIME - variable_get('user_password_reset_timeout', 86400) - 60; 766 $this->drupalGet("user/reset/$account->uid/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login, $account->uid)); 767 $this->assertText(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'Expired password reset request rejected.'); 768 } 769 770 /** 771 * Prefill the text box on incorrect login via link to password reset page. 772 */ 773 function testUserPasswordTextboxFilled() { 774 $this->drupalGet('user/login'); 775 $edit = array( 776 'name' => $this->randomName(), 777 'pass' => $this->randomName(), 778 ); 779 $this->drupalPost('user', $edit, t('Log in')); 780 $this->assertRaw(t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', 781 array('@password' => url('user/password', array('query' => array('name' => $edit['name'])))))); 782 unset($edit['pass']); 783 $this->drupalGet('user/password', array('query' => array('name' => $edit['name']))); 784 $this->assertFieldByName('name', $edit['name'], 'User name found.'); 785 } 786 787 /** 788 * Make sure that users cannot forge password reset URLs of other users. 789 */ 790 function testResetImpersonation() { 791 // Make sure user 1 has a valid password, so it does not interfere with the 792 // test user accounts that are created below. 793 $account = user_load(1); 794 user_save($account, array('pass' => user_password())); 795 796 // Create two identical user accounts except for the user name. They must 797 // have the same empty password, so we can't use $this->drupalCreateUser(). 798 $edit = array(); 799 $edit['name'] = $this->randomName(); 800 $edit['mail'] = $edit['name'] . '@example.com'; 801 $edit['status'] = 1; 802 803 $user1 = user_save(drupal_anonymous_user(), $edit); 804 805 $edit['name'] = $this->randomName(); 806 $user2 = user_save(drupal_anonymous_user(), $edit); 807 808 // The password reset URL must not be valid for the second user when only 809 // the user ID is changed in the URL. 810 $reset_url = user_pass_reset_url($user1); 811 $attack_reset_url = str_replace("user/reset/$user1->uid", "user/reset/$user2->uid", $reset_url); 812 $this->drupalGet($attack_reset_url); 813 $this->assertNoText($user2->name, 'The invalid password reset page does not show the user name.'); 814 $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.'); 815 $this->assertText('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'); 816 817 // When legacy code calls user_pass_rehash() without providing the $uid 818 // parameter, neither password reset URL should be valid since it is 819 // impossible for the system to determine which user account the token was 820 // intended for. 821 $timestamp = REQUEST_TIME; 822 // Pass an explicit NULL for the $uid parameter of user_pass_rehash() 823 // rather than not passing it at all, to avoid triggering PHP warnings in 824 // the test. 825 $reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL); 826 $reset_url = url("user/reset/$user1->uid/$timestamp/$reset_url_token", array('absolute' => TRUE)); 827 $this->drupalGet($reset_url); 828 $this->assertNoText($user1->name, 'The invalid password reset page does not show the user name.'); 829 $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.'); 830 $this->assertText('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'); 831 $attack_reset_url = str_replace("user/reset/$user1->uid", "user/reset/$user2->uid", $reset_url); 832 $this->drupalGet($attack_reset_url); 833 $this->assertNoText($user2->name, 'The invalid password reset page does not show the user name.'); 834 $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.'); 835 $this->assertText('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'); 836 837 // To verify that user_pass_rehash() never returns a valid result in the 838 // above situation (even if legacy code also called it to attempt to 839 // validate the token, rather than just to generate the URL), check that a 840 // second call with the same parameters produces a different result. 841 $new_reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL); 842 $this->assertNotEqual($reset_url_token, $new_reset_url_token); 843 844 // However, when the duplicate account is removed, the password reset URL 845 // should be valid. 846 user_delete($user2->uid); 847 $reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL); 848 $reset_url = url("user/reset/$user1->uid/$timestamp/$reset_url_token", array('absolute' => TRUE)); 849 $this->drupalGet($reset_url); 850 $this->assertText($user1->name, 'The valid password reset page shows the user name.'); 851 $this->assertUrl($this->getConfirmURL($reset_url), array(), 'The user is redirected to the reset password confirm form.'); 852 $this->assertNoText('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'); 853 } 854 855} 856 857/** 858 * Test cancelling a user. 859 */ 860class UserCancelTestCase extends DrupalWebTestCase { 861 public static function getInfo() { 862 return array( 863 'name' => 'Cancel account', 864 'description' => 'Ensure that account cancellation methods work as expected.', 865 'group' => 'User', 866 ); 867 } 868 869 function setUp() { 870 parent::setUp('comment'); 871 } 872 873 /** 874 * Attempt to cancel account without permission. 875 */ 876 function testUserCancelWithoutPermission() { 877 variable_set('user_cancel_method', 'user_cancel_reassign'); 878 879 // Create a user. 880 $account = $this->drupalCreateUser(array()); 881 $this->drupalLogin($account); 882 // Load real user object. 883 $account = user_load($account->uid, TRUE); 884 885 // Create a node. 886 $node = $this->drupalCreateNode(array('uid' => $account->uid)); 887 888 // Attempt to cancel account. 889 $this->drupalGet('user/' . $account->uid . '/edit'); 890 $this->assertNoRaw(t('Cancel account'), 'No cancel account button displayed.'); 891 892 // Attempt bogus account cancellation request confirmation. 893 $timestamp = $account->login; 894 $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid)); 895 $this->assertResponse(403, 'Bogus cancelling request rejected.'); 896 $account = user_load($account->uid); 897 $this->assertTrue($account->status == 1, 'User account was not canceled.'); 898 899 // Confirm user's content has not been altered. 900 $test_node = node_load($node->nid, NULL, TRUE); 901 $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), 'Node of the user has not been altered.'); 902 } 903 904 /** 905 * Tests that user account for uid 1 cannot be cancelled. 906 * 907 * This should never be possible, or the site owner would become unable to 908 * administer the site. 909 */ 910 function testUserCancelUid1() { 911 // Update uid 1's name and password to we know it. 912 $password = user_password(); 913 require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc'); 914 $account = array( 915 'name' => 'user1', 916 'pass' => user_hash_password(trim($password)), 917 ); 918 // We cannot use user_save() here or the password would be hashed again. 919 db_update('users') 920 ->fields($account) 921 ->condition('uid', 1) 922 ->execute(); 923 924 // Reload and log in uid 1. 925 $user1 = user_load(1, TRUE); 926 $user1->pass_raw = $password; 927 928 // Try to cancel uid 1's account with a different user. 929 $this->admin_user = $this->drupalCreateUser(array('administer users')); 930 $this->drupalLogin($this->admin_user); 931 $edit = array( 932 'operation' => 'cancel', 933 'accounts[1]' => TRUE, 934 ); 935 $this->drupalPost('admin/people', $edit, t('Update')); 936 937 // Verify that uid 1's account was not cancelled. 938 $user1 = user_load(1, TRUE); 939 $this->assertEqual($user1->status, 1, 'User #1 still exists and is not blocked.'); 940 } 941 942 /** 943 * Attempt invalid account cancellations. 944 */ 945 function testUserCancelInvalid() { 946 variable_set('user_cancel_method', 'user_cancel_reassign'); 947 948 // Create a user. 949 $account = $this->drupalCreateUser(array('cancel account')); 950 $this->drupalLogin($account); 951 // Load real user object. 952 $account = user_load($account->uid, TRUE); 953 954 // Create a node. 955 $node = $this->drupalCreateNode(array('uid' => $account->uid)); 956 957 // Attempt to cancel account. 958 $this->drupalPost('user/' . $account->uid . '/edit', NULL, t('Cancel account')); 959 960 // Confirm account cancellation. 961 $timestamp = time(); 962 $this->drupalPost(NULL, NULL, t('Cancel account')); 963 $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.'); 964 965 // Attempt bogus account cancellation request confirmation. 966 $bogus_timestamp = $timestamp + 60; 967 $this->drupalGet("user/$account->uid/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login, $account->uid)); 968 $this->assertText(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), 'Bogus cancelling request rejected.'); 969 $account = user_load($account->uid); 970 $this->assertTrue($account->status == 1, 'User account was not canceled.'); 971 972 // Attempt expired account cancellation request confirmation. 973 $bogus_timestamp = $timestamp - 86400 - 60; 974 $this->drupalGet("user/$account->uid/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login, $account->uid)); 975 $this->assertText(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), 'Expired cancel account request rejected.'); 976 $accounts = user_load_multiple(array($account->uid), array('status' => 1)); 977 $this->assertTrue(reset($accounts), 'User account was not canceled.'); 978 979 // Confirm user's content has not been altered. 980 $test_node = node_load($node->nid, NULL, TRUE); 981 $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), 'Node of the user has not been altered.'); 982 } 983 984 /** 985 * Disable account and keep all content. 986 */ 987 function testUserBlock() { 988 variable_set('user_cancel_method', 'user_cancel_block'); 989 990 // Create a user. 991 $web_user = $this->drupalCreateUser(array('cancel account')); 992 $this->drupalLogin($web_user); 993 994 // Load real user object. 995 $account = user_load($web_user->uid, TRUE); 996 997 // Attempt to cancel account. 998 $this->drupalGet('user/' . $account->uid . '/edit'); 999 $this->drupalPost(NULL, NULL, t('Cancel account')); 1000 $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.'); 1001 $this->assertText(t('Your account will be blocked and you will no longer be able to log in. All of your content will remain attributed to your user name.'), 'Informs that all content will be remain as is.'); 1002 $this->assertNoText(t('Select the method to cancel the account above.'), 'Does not allow user to select account cancellation method.'); 1003 1004 // Confirm account cancellation. 1005 $timestamp = time(); 1006 1007 $this->drupalPost(NULL, NULL, t('Cancel account')); 1008 $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.'); 1009 1010 // Confirm account cancellation request. 1011 $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid)); 1012 $account = user_load($account->uid, TRUE); 1013 $this->assertTrue($account->status == 0, 'User has been blocked.'); 1014 1015 // Confirm that the confirmation message made it through to the end user. 1016 $this->assertRaw(t('%name has been disabled.', array('%name' => $account->name)), "Confirmation message displayed to user."); 1017 } 1018 1019 /** 1020 * Disable account and unpublish all content. 1021 */ 1022 function testUserBlockUnpublish() { 1023 variable_set('user_cancel_method', 'user_cancel_block_unpublish'); 1024 1025 // Create a user. 1026 $account = $this->drupalCreateUser(array('cancel account')); 1027 $this->drupalLogin($account); 1028 // Load real user object. 1029 $account = user_load($account->uid, TRUE); 1030 1031 // Create a node with two revisions. 1032 $node = $this->drupalCreateNode(array('uid' => $account->uid)); 1033 $settings = get_object_vars($node); 1034 $settings['revision'] = 1; 1035 $node = $this->drupalCreateNode($settings); 1036 1037 // Attempt to cancel account. 1038 $this->drupalGet('user/' . $account->uid . '/edit'); 1039 $this->drupalPost(NULL, NULL, t('Cancel account')); 1040 $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.'); 1041 $this->assertText(t('Your account will be blocked and you will no longer be able to log in. All of your content will be hidden from everyone but administrators.'), 'Informs that all content will be unpublished.'); 1042 1043 // Confirm account cancellation. 1044 $timestamp = time(); 1045 $this->drupalPost(NULL, NULL, t('Cancel account')); 1046 $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.'); 1047 1048 // Confirm account cancellation request. 1049 $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid)); 1050 $account = user_load($account->uid, TRUE); 1051 $this->assertTrue($account->status == 0, 'User has been blocked.'); 1052 1053 // Confirm user's content has been unpublished. 1054 $test_node = node_load($node->nid, NULL, TRUE); 1055 $this->assertTrue($test_node->status == 0, 'Node of the user has been unpublished.'); 1056 $test_node = node_load($node->nid, $node->vid, TRUE); 1057 $this->assertTrue($test_node->status == 0, 'Node revision of the user has been unpublished.'); 1058 1059 // Confirm that the confirmation message made it through to the end user. 1060 $this->assertRaw(t('%name has been disabled.', array('%name' => $account->name)), "Confirmation message displayed to user."); 1061 } 1062 1063 /** 1064 * Delete account and anonymize all content. 1065 */ 1066 function testUserAnonymize() { 1067 variable_set('user_cancel_method', 'user_cancel_reassign'); 1068 1069 // Create a user. 1070 $account = $this->drupalCreateUser(array('cancel account')); 1071 $this->drupalLogin($account); 1072 // Load real user object. 1073 $account = user_load($account->uid, TRUE); 1074 1075 // Create a simple node. 1076 $node = $this->drupalCreateNode(array('uid' => $account->uid)); 1077 1078 // Create a node with two revisions, the initial one belonging to the 1079 // cancelling user. 1080 $revision_node = $this->drupalCreateNode(array('uid' => $account->uid)); 1081 $revision = $revision_node->vid; 1082 $settings = get_object_vars($revision_node); 1083 $settings['revision'] = 1; 1084 $settings['uid'] = 1; // Set new/current revision to someone else. 1085 $revision_node = $this->drupalCreateNode($settings); 1086 1087 // Attempt to cancel account. 1088 $this->drupalGet('user/' . $account->uid . '/edit'); 1089 $this->drupalPost(NULL, NULL, t('Cancel account')); 1090 $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.'); 1091 $this->assertRaw(t('Your account will be removed and all account information deleted. All of your content will be assigned to the %anonymous-name user.', array('%anonymous-name' => variable_get('anonymous', t('Anonymous')))), 'Informs that all content will be attributed to anonymous account.'); 1092 1093 // Confirm account cancellation. 1094 $timestamp = time(); 1095 $this->drupalPost(NULL, NULL, t('Cancel account')); 1096 $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.'); 1097 1098 // Confirm account cancellation request. 1099 $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid)); 1100 $this->assertFalse(user_load($account->uid, TRUE), 'User is not found in the database.'); 1101 1102 // Confirm that user's content has been attributed to anonymous user. 1103 $test_node = node_load($node->nid, NULL, TRUE); 1104 $this->assertTrue(($test_node->uid == 0 && $test_node->status == 1), 'Node of the user has been attributed to anonymous user.'); 1105 $test_node = node_load($revision_node->nid, $revision, TRUE); 1106 $this->assertTrue(($test_node->revision_uid == 0 && $test_node->status == 1), 'Node revision of the user has been attributed to anonymous user.'); 1107 $test_node = node_load($revision_node->nid, NULL, TRUE); 1108 $this->assertTrue(($test_node->uid != 0 && $test_node->status == 1), "Current revision of the user's node was not attributed to anonymous user."); 1109 1110 // Confirm that the confirmation message made it through to the end user. 1111 $this->assertRaw(t('%name has been deleted.', array('%name' => $account->name)), "Confirmation message displayed to user."); 1112 } 1113 1114 /** 1115 * Delete account and remove all content. 1116 */ 1117 function testUserDelete() { 1118 variable_set('user_cancel_method', 'user_cancel_delete'); 1119 1120 // Create a user. 1121 $account = $this->drupalCreateUser(array('cancel account', 'post comments', 'skip comment approval')); 1122 $this->drupalLogin($account); 1123 // Load real user object. 1124 $account = user_load($account->uid, TRUE); 1125 1126 // Create a simple node. 1127 $node = $this->drupalCreateNode(array('uid' => $account->uid)); 1128 1129 // Create comment. 1130 $langcode = LANGUAGE_NONE; 1131 $edit = array(); 1132 $edit['subject'] = $this->randomName(8); 1133 $edit['comment_body[' . $langcode . '][0][value]'] = $this->randomName(16); 1134 1135 $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview')); 1136 $this->drupalPost(NULL, array(), t('Save')); 1137 $this->assertText(t('Your comment has been posted.')); 1138 $comments = comment_load_multiple(array(), array('subject' => $edit['subject'])); 1139 $comment = reset($comments); 1140 $this->assertTrue($comment->cid, 'Comment found.'); 1141 1142 // Create a node with two revisions, the initial one belonging to the 1143 // cancelling user. 1144 $revision_node = $this->drupalCreateNode(array('uid' => $account->uid)); 1145 $revision = $revision_node->vid; 1146 $settings = get_object_vars($revision_node); 1147 $settings['revision'] = 1; 1148 $settings['uid'] = 1; // Set new/current revision to someone else. 1149 $revision_node = $this->drupalCreateNode($settings); 1150 1151 // Attempt to cancel account. 1152 $this->drupalGet('user/' . $account->uid . '/edit'); 1153 $this->drupalPost(NULL, NULL, t('Cancel account')); 1154 $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.'); 1155 $this->assertText(t('Your account will be removed and all account information deleted. All of your content will also be deleted.'), 'Informs that all content will be deleted.'); 1156 1157 // Confirm account cancellation. 1158 $timestamp = time(); 1159 $this->drupalPost(NULL, NULL, t('Cancel account')); 1160 $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.'); 1161 1162 // Confirm account cancellation request. 1163 $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid)); 1164 $this->assertFalse(user_load($account->uid, TRUE), 'User is not found in the database.'); 1165 1166 // Confirm that user's content has been deleted. 1167 $this->assertFalse(node_load($node->nid, NULL, TRUE), 'Node of the user has been deleted.'); 1168 $this->assertFalse(node_load($node->nid, $revision, TRUE), 'Node revision of the user has been deleted.'); 1169 $this->assertTrue(node_load($revision_node->nid, NULL, TRUE), "Current revision of the user's node was not deleted."); 1170 $this->assertFalse(comment_load($comment->cid), 'Comment of the user has been deleted.'); 1171 1172 // Confirm that the confirmation message made it through to the end user. 1173 $this->assertRaw(t('%name has been deleted.', array('%name' => $account->name)), "Confirmation message displayed to user."); 1174 } 1175 1176 /** 1177 * Create an administrative user and delete another user. 1178 */ 1179 function testUserCancelByAdmin() { 1180 variable_set('user_cancel_method', 'user_cancel_reassign'); 1181 1182 // Create a regular user. 1183 $account = $this->drupalCreateUser(array()); 1184 1185 // Create administrative user. 1186 $admin_user = $this->drupalCreateUser(array('administer users')); 1187 $this->drupalLogin($admin_user); 1188 1189 // Delete regular user. 1190 $this->drupalGet('user/' . $account->uid . '/edit'); 1191 $this->drupalPost(NULL, NULL, t('Cancel account')); 1192 $this->assertRaw(t('Are you sure you want to cancel the account %name?', array('%name' => $account->name)), 'Confirmation form to cancel account displayed.'); 1193 $this->assertText(t('Select the method to cancel the account above.'), 'Allows to select account cancellation method.'); 1194 1195 // Confirm deletion. 1196 $this->drupalPost(NULL, NULL, t('Cancel account')); 1197 $this->assertRaw(t('%name has been deleted.', array('%name' => $account->name)), 'User deleted.'); 1198 $this->assertFalse(user_load($account->uid), 'User is not found in the database.'); 1199 } 1200 1201 /** 1202 * Create an administrative user and mass-delete other users. 1203 */ 1204 function testMassUserCancelByAdmin() { 1205 variable_set('user_cancel_method', 'user_cancel_reassign'); 1206 // Enable account cancellation notification. 1207 variable_set('user_mail_status_canceled_notify', TRUE); 1208 1209 // Create administrative user. 1210 $admin_user = $this->drupalCreateUser(array('administer users')); 1211 $this->drupalLogin($admin_user); 1212 1213 // Create some users. 1214 $users = array(); 1215 for ($i = 0; $i < 3; $i++) { 1216 $account = $this->drupalCreateUser(array()); 1217 $users[$account->uid] = $account; 1218 } 1219 1220 // Cancel user accounts, including own one. 1221 $edit = array(); 1222 $edit['operation'] = 'cancel'; 1223 foreach ($users as $uid => $account) { 1224 $edit['accounts[' . $uid . ']'] = TRUE; 1225 } 1226 $edit['accounts[' . $admin_user->uid . ']'] = TRUE; 1227 // Also try to cancel uid 1. 1228 $edit['accounts[1]'] = TRUE; 1229 $this->drupalPost('admin/people', $edit, t('Update')); 1230 $this->assertText(t('Are you sure you want to cancel these user accounts?'), 'Confirmation form to cancel accounts displayed.'); 1231 $this->assertText(t('When cancelling these accounts'), 'Allows to select account cancellation method.'); 1232 $this->assertText(t('Require e-mail confirmation to cancel account.'), 'Allows to send confirmation mail.'); 1233 $this->assertText(t('Notify user when account is canceled.'), 'Allows to send notification mail.'); 1234 1235 // Confirm deletion. 1236 $this->drupalPost(NULL, NULL, t('Cancel accounts')); 1237 $status = TRUE; 1238 foreach ($users as $account) { 1239 $status = $status && (strpos($this->content, t('%name has been deleted.', array('%name' => $account->name))) !== FALSE); 1240 $status = $status && !user_load($account->uid, TRUE); 1241 } 1242 $this->assertTrue($status, 'Users deleted and not found in the database.'); 1243 1244 // Ensure that admin account was not cancelled. 1245 $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.'); 1246 $admin_user = user_load($admin_user->uid); 1247 $this->assertTrue($admin_user->status == 1, 'Administrative user is found in the database and enabled.'); 1248 1249 // Verify that uid 1's account was not cancelled. 1250 $user1 = user_load(1, TRUE); 1251 $this->assertEqual($user1->status, 1, 'User #1 still exists and is not blocked.'); 1252 } 1253} 1254 1255class UserPictureTestCase extends DrupalWebTestCase { 1256 protected $user; 1257 protected $_directory_test; 1258 1259 public static function getInfo() { 1260 return array( 1261 'name' => 'Upload user picture', 1262 'description' => 'Assure that dimension check, extension check and image scaling work as designed.', 1263 'group' => 'User' 1264 ); 1265 } 1266 1267 function setUp() { 1268 parent::setUp(); 1269 // Enable user pictures. 1270 variable_set('user_pictures', 1); 1271 1272 $this->user = $this->drupalCreateUser(); 1273 1274 // Test if directories specified in settings exist in filesystem. 1275 $file_dir = 'public://'; 1276 $file_check = file_prepare_directory($file_dir, FILE_CREATE_DIRECTORY); 1277 // TODO: Test public and private methods? 1278 1279 $picture_dir = variable_get('user_picture_path', 'pictures'); 1280 $picture_path = $file_dir . $picture_dir; 1281 1282 $pic_check = file_prepare_directory($picture_path, FILE_CREATE_DIRECTORY); 1283 $this->_directory_test = is_writable($picture_path); 1284 $this->assertTrue($this->_directory_test, "The directory $picture_path doesn't exist or is not writable. Further tests won't be made."); 1285 } 1286 1287 function testNoPicture() { 1288 $this->drupalLogin($this->user); 1289 1290 // Try to upload a file that is not an image for the user picture. 1291 $not_an_image = current($this->drupalGetTestFiles('html')); 1292 $this->saveUserPicture($not_an_image); 1293 $this->assertRaw(t('Only JPEG, PNG and GIF images are allowed.'), 'Non-image files are not accepted.'); 1294 } 1295 1296 /** 1297 * Do the test: 1298 * GD Toolkit is installed 1299 * Picture has invalid dimension 1300 * 1301 * results: The image should be uploaded because ImageGDToolkit resizes the picture 1302 */ 1303 function testWithGDinvalidDimension() { 1304 if ($this->_directory_test && image_get_toolkit()) { 1305 $this->drupalLogin($this->user); 1306 1307 $image = current($this->drupalGetTestFiles('image')); 1308 $info = image_get_info($image->uri); 1309 1310 // Set new variables: invalid dimensions, valid filesize (0 = no limit). 1311 $test_dim = ($info['width'] - 10) . 'x' . ($info['height'] - 10); 1312 variable_set('user_picture_dimensions', $test_dim); 1313 variable_set('user_picture_file_size', 0); 1314 1315 $pic_path = $this->saveUserPicture($image); 1316 // Check that the image was resized and is being displayed on the 1317 // user's profile page. 1318 $text = t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', array('%dimensions' => $test_dim)); 1319 $this->assertRaw($text, 'Image was resized.'); 1320 $alt = t("@user's picture", array('@user' => format_username($this->user))); 1321 $style = variable_get('user_picture_style', ''); 1322 $this->assertRaw(check_plain(image_style_url($style, $pic_path)), "Image is displayed in user's edit page"); 1323 1324 // Check if file is located in proper directory. 1325 $this->assertTrue(is_file($pic_path), "File is located in proper directory"); 1326 } 1327 } 1328 1329 /** 1330 * Do the test: 1331 * GD Toolkit is installed 1332 * Picture has invalid size 1333 * 1334 * results: The image should be uploaded because ImageGDToolkit resizes the picture 1335 */ 1336 function testWithGDinvalidSize() { 1337 if ($this->_directory_test && image_get_toolkit()) { 1338 $this->drupalLogin($this->user); 1339 1340 // Images are sorted first by size then by name. We need an image 1341 // bigger than 1 KB so we'll grab the last one. 1342 $files = $this->drupalGetTestFiles('image'); 1343 $image = end($files); 1344 $info = image_get_info($image->uri); 1345 1346 // Set new variables: valid dimensions, invalid filesize. 1347 $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10); 1348 $test_size = 1; 1349 variable_set('user_picture_dimensions', $test_dim); 1350 variable_set('user_picture_file_size', $test_size); 1351 1352 $pic_path = $this->saveUserPicture($image); 1353 1354 // Test that the upload failed and that the correct reason was cited. 1355 $text = t('The specified file %filename could not be uploaded.', array('%filename' => $image->filename)); 1356 $this->assertRaw($text, 'Upload failed.'); 1357 $text = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size(filesize($image->uri)), '%maxsize' => format_size($test_size * 1024))); 1358 $this->assertRaw($text, 'File size cited as reason for failure.'); 1359 1360 // Check if file is not uploaded. 1361 $this->assertFalse(is_file($pic_path), 'File was not uploaded.'); 1362 } 1363 } 1364 1365 /** 1366 * Do the test: 1367 * GD Toolkit is not installed 1368 * Picture has invalid size 1369 * 1370 * results: The image shouldn't be uploaded 1371 */ 1372 function testWithoutGDinvalidDimension() { 1373 if ($this->_directory_test && !image_get_toolkit()) { 1374 $this->drupalLogin($this->user); 1375 1376 $image = current($this->drupalGetTestFiles('image')); 1377 $info = image_get_info($image->uri); 1378 1379 // Set new variables: invalid dimensions, valid filesize (0 = no limit). 1380 $test_dim = ($info['width'] - 10) . 'x' . ($info['height'] - 10); 1381 variable_set('user_picture_dimensions', $test_dim); 1382 variable_set('user_picture_file_size', 0); 1383 1384 $pic_path = $this->saveUserPicture($image); 1385 1386 // Test that the upload failed and that the correct reason was cited. 1387 $text = t('The specified file %filename could not be uploaded.', array('%filename' => $image->filename)); 1388 $this->assertRaw($text, 'Upload failed.'); 1389 $text = t('The image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => $test_dim)); 1390 $this->assertRaw($text, 'Checking response on invalid image (dimensions).'); 1391 1392 // Check if file is not uploaded. 1393 $this->assertFalse(is_file($pic_path), 'File was not uploaded.'); 1394 } 1395 } 1396 1397 /** 1398 * Do the test: 1399 * GD Toolkit is not installed 1400 * Picture has invalid size 1401 * 1402 * results: The image shouldn't be uploaded 1403 */ 1404 function testWithoutGDinvalidSize() { 1405 if ($this->_directory_test && !image_get_toolkit()) { 1406 $this->drupalLogin($this->user); 1407 1408 $image = current($this->drupalGetTestFiles('image')); 1409 $info = image_get_info($image->uri); 1410 1411 // Set new variables: valid dimensions, invalid filesize. 1412 $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10); 1413 $test_size = 1; 1414 variable_set('user_picture_dimensions', $test_dim); 1415 variable_set('user_picture_file_size', $test_size); 1416 1417 $pic_path = $this->saveUserPicture($image); 1418 1419 // Test that the upload failed and that the correct reason was cited. 1420 $text = t('The specified file %filename could not be uploaded.', array('%filename' => $image->filename)); 1421 $this->assertRaw($text, 'Upload failed.'); 1422 $text = t('The file is %filesize exceeding the maximum file size of %maxsize.', array('%filesize' => format_size(filesize($image->uri)), '%maxsize' => format_size($test_size * 1024))); 1423 $this->assertRaw($text, 'File size cited as reason for failure.'); 1424 1425 // Check if file is not uploaded. 1426 $this->assertFalse(is_file($pic_path), 'File was not uploaded.'); 1427 } 1428 } 1429 1430 /** 1431 * Do the test: 1432 * Picture is valid (proper size and dimension) 1433 * 1434 * results: The image should be uploaded 1435 */ 1436 function testPictureIsValid() { 1437 if ($this->_directory_test) { 1438 $this->drupalLogin($this->user); 1439 1440 $image = current($this->drupalGetTestFiles('image')); 1441 $info = image_get_info($image->uri); 1442 1443 // Set new variables: valid dimensions, valid filesize (0 = no limit). 1444 $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10); 1445 variable_set('user_picture_dimensions', $test_dim); 1446 variable_set('user_picture_file_size', 0); 1447 1448 $pic_path = $this->saveUserPicture($image); 1449 1450 // Check if image is displayed in user's profile page. 1451 $this->drupalGet('user'); 1452 $this->assertRaw(file_uri_target($pic_path), "Image is displayed in user's profile page"); 1453 1454 // Check if file is located in proper directory. 1455 $this->assertTrue(is_file($pic_path), 'File is located in proper directory'); 1456 1457 // Set new picture dimensions. 1458 $test_dim = ($info['width'] + 5) . 'x' . ($info['height'] + 5); 1459 variable_set('user_picture_dimensions', $test_dim); 1460 1461 $pic_path2 = $this->saveUserPicture($image); 1462 $this->assertNotEqual($pic_path, $pic_path2, 'Filename of second picture is different.'); 1463 1464 // Check if user picture has a valid file ID after saving the user. 1465 $account = user_load($this->user->uid, TRUE); 1466 $this->assertTrue(is_object($account->picture), 'User picture object is valid after user load.'); 1467 $this->assertNotNull($account->picture->fid, 'User picture object has a FID after user load.'); 1468 $this->assertTrue(is_file($account->picture->uri), 'File is located in proper directory after user load.'); 1469 user_save($account); 1470 // Verify that the user save does not destroy the user picture object. 1471 $this->assertTrue(is_object($account->picture), 'User picture object is valid after user save.'); 1472 $this->assertNotNull($account->picture->fid, 'User picture object has a FID after user save.'); 1473 $this->assertTrue(is_file($account->picture->uri), 'File is located in proper directory after user save.'); 1474 } 1475 } 1476 1477 /** 1478 * Test HTTP schema working with user pictures. 1479 */ 1480 function testExternalPicture() { 1481 $this->drupalLogin($this->user); 1482 // Set the default picture to an URI with a HTTP schema. 1483 $images = $this->drupalGetTestFiles('image'); 1484 $image = $images[0]; 1485 $pic_path = file_create_url($image->uri); 1486 variable_set('user_picture_default', $pic_path); 1487 1488 // Check if image is displayed in user's profile page. 1489 $this->drupalGet('user'); 1490 1491 // Get the user picture image via xpath. 1492 $elements = $this->xpath('//div[@class="user-picture"]/img'); 1493 $this->assertEqual(count($elements), 1, "There is exactly one user picture on the user's profile page"); 1494 $this->assertEqual($pic_path, (string) $elements[0]['src'], "User picture source is correct."); 1495 } 1496 1497 /** 1498 * Tests deletion of user pictures. 1499 */ 1500 function testDeletePicture() { 1501 $this->drupalLogin($this->user); 1502 1503 $image = current($this->drupalGetTestFiles('image')); 1504 $info = image_get_info($image->uri); 1505 1506 // Set new variables: valid dimensions, valid filesize (0 = no limit). 1507 $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10); 1508 variable_set('user_picture_dimensions', $test_dim); 1509 variable_set('user_picture_file_size', 0); 1510 1511 // Save a new picture. 1512 $edit = array('files[picture_upload]' => drupal_realpath($image->uri)); 1513 $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save')); 1514 1515 // Load actual user data from database. 1516 $account = user_load($this->user->uid, TRUE); 1517 $pic_path = isset($account->picture) ? $account->picture->uri : NULL; 1518 1519 // Check if image is displayed in user's profile page. 1520 $this->drupalGet('user'); 1521 $this->assertRaw(file_uri_target($pic_path), "Image is displayed in user's profile page"); 1522 1523 // Check if file is located in proper directory. 1524 $this->assertTrue(is_file($pic_path), 'File is located in proper directory'); 1525 1526 $edit = array('picture_delete' => 1); 1527 $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save')); 1528 1529 // Load actual user data from database. 1530 $account1 = user_load($this->user->uid, TRUE); 1531 $this->assertNull($account1->picture, 'User object has no picture'); 1532 1533 $file = file_load($account->picture->fid); 1534 $this->assertFalse($file, 'File is removed from database'); 1535 1536 // Clear out PHP's file stat cache so we see the current value. 1537 clearstatcache(); 1538 $this->assertFalse(is_file($pic_path), 'File is removed from file system'); 1539 } 1540 1541 function saveUserPicture($image) { 1542 $edit = array('files[picture_upload]' => drupal_realpath($image->uri)); 1543 $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save')); 1544 1545 // Load actual user data from database. 1546 $account = user_load($this->user->uid, TRUE); 1547 return isset($account->picture) ? $account->picture->uri : NULL; 1548 } 1549 1550 /** 1551 * Tests the admin form validates user picture settings. 1552 */ 1553 function testUserPictureAdminFormValidation() { 1554 $this->drupalLogin($this->drupalCreateUser(array('administer users'))); 1555 1556 // The default values are valid. 1557 $this->drupalPost('admin/config/people/accounts', array(), t('Save configuration')); 1558 $this->assertText(t('The configuration options have been saved.'), 'The default values are valid.'); 1559 1560 // The form does not save with an invalid file size. 1561 $edit = array( 1562 'user_picture_file_size' => $this->randomName(), 1563 ); 1564 $this->drupalPost('admin/config/people/accounts', $edit, t('Save configuration')); 1565 $this->assertNoText(t('The configuration options have been saved.'), 'The form does not save with an invalid file size.'); 1566 } 1567} 1568 1569 1570class UserPermissionsTestCase extends DrupalWebTestCase { 1571 protected $admin_user; 1572 protected $rid; 1573 1574 public static function getInfo() { 1575 return array( 1576 'name' => 'Role permissions', 1577 'description' => 'Verify that role permissions can be added and removed via the permissions page.', 1578 'group' => 'User' 1579 ); 1580 } 1581 1582 function setUp() { 1583 parent::setUp(); 1584 1585 $this->admin_user = $this->drupalCreateUser(array('administer permissions', 'access user profiles', 'administer site configuration', 'administer modules', 'administer users')); 1586 1587 // Find the new role ID - it must be the maximum. 1588 $all_rids = array_keys($this->admin_user->roles); 1589 sort($all_rids); 1590 $this->rid = array_pop($all_rids); 1591 } 1592 1593 /** 1594 * Change user permissions and check user_access(). 1595 */ 1596 function testUserPermissionChanges() { 1597 $this->drupalLogin($this->admin_user); 1598 $rid = $this->rid; 1599 $account = $this->admin_user; 1600 1601 // Add a permission. 1602 $this->assertFalse(user_access('administer nodes', $account), 'User does not have "administer nodes" permission.'); 1603 $edit = array(); 1604 $edit[$rid . '[administer nodes]'] = TRUE; 1605 $this->drupalPost('admin/people/permissions', $edit, t('Save permissions')); 1606 $this->assertText(t('The changes have been saved.'), 'Successful save message displayed.'); 1607 drupal_static_reset('user_access'); 1608 drupal_static_reset('user_role_permissions'); 1609 $this->assertTrue(user_access('administer nodes', $account), 'User now has "administer nodes" permission.'); 1610 1611 // Remove a permission. 1612 $this->assertTrue(user_access('access user profiles', $account), 'User has "access user profiles" permission.'); 1613 $edit = array(); 1614 $edit[$rid . '[access user profiles]'] = FALSE; 1615 $this->drupalPost('admin/people/permissions', $edit, t('Save permissions')); 1616 $this->assertText(t('The changes have been saved.'), 'Successful save message displayed.'); 1617 drupal_static_reset('user_access'); 1618 drupal_static_reset('user_role_permissions'); 1619 $this->assertFalse(user_access('access user profiles', $account), 'User no longer has "access user profiles" permission.'); 1620 } 1621 1622 /** 1623 * Test assigning of permissions for the administrator role. 1624 */ 1625 function testAdministratorRole() { 1626 $this->drupalLogin($this->admin_user); 1627 $this->drupalGet('admin/config/people/accounts'); 1628 1629 // Set the user's role to be the administrator role. 1630 $edit = array(); 1631 $edit['user_admin_role'] = $this->rid; 1632 $this->drupalPost('admin/config/people/accounts', $edit, t('Save configuration')); 1633 1634 // Enable aggregator module and ensure the 'administer news feeds' 1635 // permission is assigned by default. 1636 $edit = array(); 1637 $edit['modules[Core][aggregator][enable]'] = TRUE; 1638 $this->drupalPost('admin/modules', $edit, t('Save configuration')); 1639 $this->assertTrue(user_access('administer news feeds', $this->admin_user), 'The permission was automatically assigned to the administrator role'); 1640 } 1641 1642 /** 1643 * Verify proper permission changes by user_role_change_permissions(). 1644 */ 1645 function testUserRoleChangePermissions() { 1646 $rid = $this->rid; 1647 $account = $this->admin_user; 1648 1649 // Verify current permissions. 1650 $this->assertFalse(user_access('administer nodes', $account), 'User does not have "administer nodes" permission.'); 1651 $this->assertTrue(user_access('access user profiles', $account), 'User has "access user profiles" permission.'); 1652 $this->assertTrue(user_access('administer site configuration', $account), 'User has "administer site configuration" permission.'); 1653 1654 // Change permissions. 1655 $permissions = array( 1656 'administer nodes' => 1, 1657 'access user profiles' => 0, 1658 ); 1659 user_role_change_permissions($rid, $permissions); 1660 1661 // Verify proper permission changes. 1662 $this->assertTrue(user_access('administer nodes', $account), 'User now has "administer nodes" permission.'); 1663 $this->assertFalse(user_access('access user profiles', $account), 'User no longer has "access user profiles" permission.'); 1664 $this->assertTrue(user_access('administer site configuration', $account), 'User still has "administer site configuration" permission.'); 1665 } 1666} 1667 1668class UserAdminTestCase extends DrupalWebTestCase { 1669 public static function getInfo() { 1670 return array( 1671 'name' => 'User administration', 1672 'description' => 'Test user administration page functionality.', 1673 'group' => 'User' 1674 ); 1675 } 1676 1677 /** 1678 * Registers a user and deletes it. 1679 */ 1680 function testUserAdmin() { 1681 1682 $user_a = $this->drupalCreateUser(array()); 1683 $user_b = $this->drupalCreateUser(array('administer taxonomy')); 1684 $user_c = $this->drupalCreateUser(array('administer taxonomy')); 1685 1686 // Create admin user to delete registered user. 1687 $admin_user = $this->drupalCreateUser(array('administer users')); 1688 $this->drupalLogin($admin_user); 1689 $this->drupalGet('admin/people'); 1690 $this->assertText($user_a->name, 'Found user A on admin users page'); 1691 $this->assertText($user_b->name, 'Found user B on admin users page'); 1692 $this->assertText($user_c->name, 'Found user C on admin users page'); 1693 $this->assertText($admin_user->name, 'Found Admin user on admin users page'); 1694 1695 // Test for existence of edit link in table. 1696 $link = l(t('edit'), "user/$user_a->uid/edit", array('query' => array('destination' => 'admin/people'))); 1697 $this->assertRaw($link, 'Found user A edit link on admin users page'); 1698 1699 // Filter the users by permission 'administer taxonomy'. 1700 $edit = array(); 1701 $edit['permission'] = 'administer taxonomy'; 1702 $this->drupalPost('admin/people', $edit, t('Filter')); 1703 1704 // Check if the correct users show up. 1705 $this->assertNoText($user_a->name, 'User A not on filtered by perm admin users page'); 1706 $this->assertText($user_b->name, 'Found user B on filtered by perm admin users page'); 1707 $this->assertText($user_c->name, 'Found user C on filtered by perm admin users page'); 1708 1709 // Filter the users by role. Grab the system-generated role name for User C. 1710 $edit['role'] = max(array_flip($user_c->roles)); 1711 $this->drupalPost('admin/people', $edit, t('Refine')); 1712 1713 // Check if the correct users show up when filtered by role. 1714 $this->assertNoText($user_a->name, 'User A not on filtered by role on admin users page'); 1715 $this->assertNoText($user_b->name, 'User B not on filtered by role on admin users page'); 1716 $this->assertText($user_c->name, 'User C on filtered by role on admin users page'); 1717 1718 // Test blocking of a user. 1719 $account = user_load($user_c->uid); 1720 $this->assertEqual($account->status, 1, 'User C not blocked'); 1721 $edit = array(); 1722 $edit['operation'] = 'block'; 1723 $edit['accounts[' . $account->uid . ']'] = TRUE; 1724 $this->drupalPost('admin/people', $edit, t('Update')); 1725 $account = user_load($user_c->uid, TRUE); 1726 $this->assertEqual($account->status, 0, 'User C blocked'); 1727 1728 // Test unblocking of a user from /admin/people page and sending of activation mail 1729 $editunblock = array(); 1730 $editunblock['operation'] = 'unblock'; 1731 $editunblock['accounts[' . $account->uid . ']'] = TRUE; 1732 $this->drupalPost('admin/people', $editunblock, t('Update')); 1733 $account = user_load($user_c->uid, TRUE); 1734 $this->assertEqual($account->status, 1, 'User C unblocked'); 1735 $this->assertMail("to", $account->mail, "Activation mail sent to user C"); 1736 1737 // Test blocking and unblocking another user from /user/[uid]/edit form and sending of activation mail 1738 $user_d = $this->drupalCreateUser(array()); 1739 $account1 = user_load($user_d->uid, TRUE); 1740 $this->drupalPost('user/' . $account1->uid . '/edit', array('status' => 0), t('Save')); 1741 $account1 = user_load($user_d->uid, TRUE); 1742 $this->assertEqual($account1->status, 0, 'User D blocked'); 1743 $this->drupalPost('user/' . $account1->uid . '/edit', array('status' => TRUE), t('Save')); 1744 $account1 = user_load($user_d->uid, TRUE); 1745 $this->assertEqual($account1->status, 1, 'User D unblocked'); 1746 $this->assertMail("to", $account1->mail, "Activation mail sent to user D"); 1747 } 1748} 1749 1750/** 1751 * Tests for user-configurable time zones. 1752 */ 1753class UserTimeZoneFunctionalTest extends DrupalWebTestCase { 1754 public static function getInfo() { 1755 return array( 1756 'name' => 'User time zones', 1757 'description' => 'Set a user time zone and verify that dates are displayed in local time.', 1758 'group' => 'User', 1759 ); 1760 } 1761 1762 /** 1763 * Tests the display of dates and time when user-configurable time zones are set. 1764 */ 1765 function testUserTimeZone() { 1766 // Setup date/time settings for Los Angeles time. 1767 variable_set('date_default_timezone', 'America/Los_Angeles'); 1768 variable_set('configurable_timezones', 1); 1769 1770 // Override the 'medium' date format, which is the default for node 1771 // creation time. Since we are testing time zones with Daylight Saving 1772 // Time, and need to future proof against changes to the zoneinfo database, 1773 // we choose the 'I' format placeholder instead of a human-readable zone 1774 // name. With 'I', a 1 means the date is in DST, and 0 if not. 1775 variable_set('date_format_medium', 'Y-m-d H:i I'); 1776 1777 // Create a user account and login. 1778 $web_user = $this->drupalCreateUser(); 1779 $this->drupalLogin($web_user); 1780 1781 // Create some nodes with different authored-on dates. 1782 // Two dates in PST (winter time): 1783 $date1 = '2007-03-09 21:00:00 -0800'; 1784 $date2 = '2007-03-11 01:00:00 -0800'; 1785 // One date in PDT (summer time): 1786 $date3 = '2007-03-20 21:00:00 -0700'; 1787 $node1 = $this->drupalCreateNode(array('created' => strtotime($date1), 'type' => 'article')); 1788 $node2 = $this->drupalCreateNode(array('created' => strtotime($date2), 'type' => 'article')); 1789 $node3 = $this->drupalCreateNode(array('created' => strtotime($date3), 'type' => 'article')); 1790 1791 // Confirm date format and time zone. 1792 $this->drupalGet("node/$node1->nid"); 1793 $this->assertText('2007-03-09 21:00 0', 'Date should be PST.'); 1794 $this->drupalGet("node/$node2->nid"); 1795 $this->assertText('2007-03-11 01:00 0', 'Date should be PST.'); 1796 $this->drupalGet("node/$node3->nid"); 1797 $this->assertText('2007-03-20 21:00 1', 'Date should be PDT.'); 1798 1799 // Change user time zone to Santiago time. 1800 $edit = array(); 1801 $edit['mail'] = $web_user->mail; 1802 $edit['timezone'] = 'America/Santiago'; 1803 $this->drupalPost("user/$web_user->uid/edit", $edit, t('Save')); 1804 $this->assertText(t('The changes have been saved.'), 'Time zone changed to Santiago time.'); 1805 1806 // Confirm date format and time zone. 1807 $this->drupalGet("node/$node1->nid"); 1808 $this->assertText('2007-03-10 02:00 1', 'Date should be Chile summer time; five hours ahead of PST.'); 1809 $this->drupalGet("node/$node2->nid"); 1810 $this->assertText('2007-03-11 05:00 0', 'Date should be Chile time; four hours ahead of PST'); 1811 $this->drupalGet("node/$node3->nid"); 1812 $this->assertText('2007-03-21 00:00 0', 'Date should be Chile time; three hours ahead of PDT.'); 1813 } 1814} 1815 1816/** 1817 * Test user autocompletion. 1818 */ 1819class UserAutocompleteTestCase extends DrupalWebTestCase { 1820 public static function getInfo() { 1821 return array( 1822 'name' => 'User autocompletion', 1823 'description' => 'Test user autocompletion functionality.', 1824 'group' => 'User' 1825 ); 1826 } 1827 1828 function setUp() { 1829 parent::setUp(); 1830 1831 // Set up two users with different permissions to test access. 1832 $this->unprivileged_user = $this->drupalCreateUser(); 1833 $this->privileged_user = $this->drupalCreateUser(array('access user profiles')); 1834 } 1835 1836 /** 1837 * Tests access to user autocompletion and verify the correct results. 1838 */ 1839 function testUserAutocomplete() { 1840 // Check access from unprivileged user, should be denied. 1841 $this->drupalLogin($this->unprivileged_user); 1842 $this->drupalGet('user/autocomplete/' . $this->unprivileged_user->name[0]); 1843 $this->assertResponse(403, 'Autocompletion access denied to user without permission.'); 1844 1845 // Check access from privileged user. 1846 $this->drupalLogout(); 1847 $this->drupalLogin($this->privileged_user); 1848 $this->drupalGet('user/autocomplete/' . $this->unprivileged_user->name[0]); 1849 $this->assertResponse(200, 'Autocompletion access allowed.'); 1850 1851 // Using first letter of the user's name, make sure the user's full name is in the results. 1852 $this->assertRaw($this->unprivileged_user->name, 'User name found in autocompletion results.'); 1853 } 1854} 1855 1856 1857/** 1858 * Tests user links in the secondary menu. 1859 */ 1860class UserAccountLinksUnitTests extends DrupalWebTestCase { 1861 public static function getInfo() { 1862 return array( 1863 'name' => 'User account links', 1864 'description' => 'Test user-account links.', 1865 'group' => 'User' 1866 ); 1867 } 1868 1869 function setUp() { 1870 parent::setUp('menu'); 1871 } 1872 1873 /** 1874 * Tests the secondary menu. 1875 */ 1876 function testSecondaryMenu() { 1877 // Create a regular user. 1878 $user = $this->drupalCreateUser(array()); 1879 1880 // Log in and get the homepage. 1881 $this->drupalLogin($user); 1882 $this->drupalGet('<front>'); 1883 1884 // For a logged-in user, expect the secondary menu to have links for "My 1885 // account" and "Log out". 1886 $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array( 1887 ':menu_id' => 'secondary-menu-links', 1888 ':href' => 'user', 1889 ':text' => 'My account', 1890 )); 1891 $this->assertEqual(count($link), 1, 'My account link is in secondary menu.'); 1892 1893 $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array( 1894 ':menu_id' => 'secondary-menu-links', 1895 ':href' => 'user/logout', 1896 ':text' => 'Log out', 1897 )); 1898 $this->assertEqual(count($link), 1, 'Log out link is in secondary menu.'); 1899 1900 // Log out and get the homepage. 1901 $this->drupalLogout(); 1902 $this->drupalGet('<front>'); 1903 1904 // For a logged-out user, expect no secondary links. 1905 $element = $this->xpath('//ul[@id=:menu_id]', array(':menu_id' => 'secondary-menu-links')); 1906 $this->assertEqual(count($element), 0, 'No secondary-menu for logged-out users.'); 1907 } 1908 1909 /** 1910 * Tests disabling the 'My account' link. 1911 */ 1912 function testDisabledAccountLink() { 1913 // Create an admin user and log in. 1914 $this->drupalLogin($this->drupalCreateUser(array('access administration pages', 'administer menu'))); 1915 1916 // Verify that the 'My account' link is enabled. 1917 $this->drupalGet('admin/structure/menu/manage/user-menu'); 1918 $label = $this->xpath('//label[contains(.,:text)]/@for', array(':text' => 'Enable My account menu link')); 1919 $this->assertFieldChecked((string) $label[0], "The 'My account' link is enabled by default."); 1920 1921 // Disable the 'My account' link. 1922 $input = $this->xpath('//input[@id=:field_id]/@name', array(':field_id' => (string)$label[0])); 1923 $edit = array( 1924 (string) $input[0] => FALSE, 1925 ); 1926 $this->drupalPost('admin/structure/menu/manage/user-menu', $edit, t('Save configuration')); 1927 1928 // Get the homepage. 1929 $this->drupalGet('<front>'); 1930 1931 // Verify that the 'My account' link does not appear when disabled. 1932 $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array( 1933 ':menu_id' => 'secondary-menu-links', 1934 ':href' => 'user', 1935 ':text' => 'My account', 1936 )); 1937 $this->assertEqual(count($link), 0, 'My account link is not in the secondary menu.'); 1938 } 1939 1940} 1941 1942/** 1943 * Test user blocks. 1944 */ 1945class UserBlocksUnitTests extends DrupalWebTestCase { 1946 public static function getInfo() { 1947 return array( 1948 'name' => 'User blocks', 1949 'description' => 'Test user blocks.', 1950 'group' => 'User' 1951 ); 1952 } 1953 1954 /** 1955 * Test the user login block. 1956 */ 1957 function testUserLoginBlock() { 1958 // Create a user with some permission that anonymous users lack. 1959 $user = $this->drupalCreateUser(array('administer permissions')); 1960 1961 // Log in using the block. 1962 $edit = array(); 1963 $edit['name'] = $user->name; 1964 $edit['pass'] = $user->pass_raw; 1965 $this->drupalPost('admin/people/permissions', $edit, t('Log in')); 1966 $this->assertNoText(t('User login'), 'Logged in.'); 1967 1968 // Check that we are still on the same page. 1969 $this->assertEqual(url('admin/people/permissions', array('absolute' => TRUE)), $this->getUrl(), 'Still on the same page after login for access denied page'); 1970 1971 // Now, log out and repeat with a non-403 page. 1972 $this->drupalLogout(); 1973 $this->drupalPost('filter/tips', $edit, t('Log in')); 1974 $this->assertNoText(t('User login'), 'Logged in.'); 1975 $this->assertPattern('!<title.*?' . t('Compose tips') . '.*?</title>!', 'Still on the same page after login for allowed page'); 1976 1977 // Check that the user login block is not vulnerable to information 1978 // disclosure to third party sites. 1979 $this->drupalLogout(); 1980 $this->drupalPost('http://example.com/', $edit, t('Log in'), array('external' => FALSE)); 1981 // Check that we remain on the site after login. 1982 $this->assertEqual(url('user/' . $user->uid, array('absolute' => TRUE)), $this->getUrl(), 'Redirected to user profile page after login from the frontpage'); 1983 } 1984 1985 /** 1986 * Test the Who's Online block. 1987 */ 1988 function testWhosOnlineBlock() { 1989 // Generate users and make sure there are no current user sessions. 1990 $user1 = $this->drupalCreateUser(array()); 1991 $user2 = $this->drupalCreateUser(array()); 1992 $user3 = $this->drupalCreateUser(array()); 1993 $this->assertEqual(db_query("SELECT COUNT(*) FROM {sessions}")->fetchField(), 0, 'Sessions table is empty.'); 1994 1995 // Insert a user with two sessions. 1996 $this->insertSession(array('uid' => $user1->uid)); 1997 $this->insertSession(array('uid' => $user1->uid)); 1998 $this->assertEqual(db_query("SELECT COUNT(*) FROM {sessions} WHERE uid = :uid", array(':uid' => $user1->uid))->fetchField(), 2, 'Duplicate user session has been inserted.'); 1999 2000 // Insert a user with only one session. 2001 $this->insertSession(array('uid' => $user2->uid, 'timestamp' => REQUEST_TIME + 1)); 2002 2003 // Insert an inactive logged-in user who should not be seen in the block. 2004 $this->insertSession(array('uid' => $user3->uid, 'timestamp' => (REQUEST_TIME - variable_get('user_block_seconds_online', 900) - 1))); 2005 2006 // Insert two anonymous user sessions. 2007 $this->insertSession(); 2008 $this->insertSession(); 2009 2010 // Test block output. 2011 $block = user_block_view('online'); 2012 $this->drupalSetContent($block['content']); 2013 $this->assertRaw(t('2 users'), 'Correct number of online users (2 users).'); 2014 $this->assertText($user1->name, 'Active user 1 found in online list.'); 2015 $this->assertText($user2->name, 'Active user 2 found in online list.'); 2016 $this->assertNoText($user3->name, "Inactive user not found in online list."); 2017 $this->assertTrue(strpos($this->drupalGetContent(), $user1->name) > strpos($this->drupalGetContent(), $user2->name), 'Online users are ordered correctly.'); 2018 } 2019 2020 /** 2021 * Insert a user session into the {sessions} table. This function is used 2022 * since we cannot log in more than one user at the same time in tests. 2023 */ 2024 private function insertSession(array $fields = array()) { 2025 $fields += array( 2026 'uid' => 0, 2027 'sid' => drupal_hash_base64(uniqid(mt_rand(), TRUE)), 2028 'timestamp' => REQUEST_TIME, 2029 ); 2030 db_insert('sessions') 2031 ->fields($fields) 2032 ->execute(); 2033 $this->assertEqual(db_query("SELECT COUNT(*) FROM {sessions} WHERE uid = :uid AND sid = :sid AND timestamp = :timestamp", array(':uid' => $fields['uid'], ':sid' => $fields['sid'], ':timestamp' => $fields['timestamp']))->fetchField(), 1, 'Session record inserted.'); 2034 } 2035} 2036 2037/** 2038 * Tests saving a user account. 2039 */ 2040class UserSaveTestCase extends DrupalWebTestCase { 2041 2042 public static function getInfo() { 2043 return array( 2044 'name' => 'User save test', 2045 'description' => 'Test user_save() for arbitrary new uid.', 2046 'group' => 'User', 2047 ); 2048 } 2049 2050 /** 2051 * Test creating a user with arbitrary uid. 2052 */ 2053 function testUserImport() { 2054 // User ID must be a number that is not in the database. 2055 $max_uid = db_query('SELECT MAX(uid) FROM {users}')->fetchField(); 2056 $test_uid = $max_uid + mt_rand(1000, 1000000); 2057 $test_name = $this->randomName(); 2058 2059 // Create the base user, based on drupalCreateUser(). 2060 $user = array( 2061 'name' => $test_name, 2062 'uid' => $test_uid, 2063 'mail' => $test_name . '@example.com', 2064 'is_new' => TRUE, 2065 'pass' => user_password(), 2066 'status' => 1, 2067 ); 2068 $user_by_return = user_save(drupal_anonymous_user(), $user); 2069 $this->assertTrue($user_by_return, 'Loading user by return of user_save().'); 2070 2071 // Test if created user exists. 2072 $user_by_uid = user_load($test_uid); 2073 $this->assertTrue($user_by_uid, 'Loading user by uid.'); 2074 2075 $user_by_name = user_load_by_name($test_name); 2076 $this->assertTrue($user_by_name, 'Loading user by name.'); 2077 } 2078} 2079 2080/** 2081 * Test the create user administration page. 2082 */ 2083class UserCreateTestCase extends DrupalWebTestCase { 2084 2085 public static function getInfo() { 2086 return array( 2087 'name' => 'User create', 2088 'description' => 'Test the create user administration page.', 2089 'group' => 'User', 2090 ); 2091 } 2092 2093 /** 2094 * Create a user through the administration interface and ensure that it 2095 * displays in the user list. 2096 */ 2097 protected function testUserAdd() { 2098 $user = $this->drupalCreateUser(array('administer users')); 2099 $this->drupalLogin($user); 2100 2101 foreach (array(FALSE, TRUE) as $notify) { 2102 $edit = array( 2103 'name' => $this->randomName(), 2104 'mail' => $this->randomName() . '@example.com', 2105 'pass[pass1]' => $pass = $this->randomString(), 2106 'pass[pass2]' => $pass, 2107 'notify' => $notify, 2108 ); 2109 $this->drupalPost('admin/people/create', $edit, t('Create new account')); 2110 2111 if ($notify) { 2112 $this->assertText(t('A welcome message with further instructions has been e-mailed to the new user @name.', array('@name' => $edit['name'])), 'User created'); 2113 $this->assertEqual(count($this->drupalGetMails()), 1, 'Notification e-mail sent'); 2114 } 2115 else { 2116 $this->assertText(t('Created a new user account for @name. No e-mail has been sent.', array('@name' => $edit['name'])), 'User created'); 2117 $this->assertEqual(count($this->drupalGetMails()), 0, 'Notification e-mail not sent'); 2118 } 2119 2120 $this->drupalGet('admin/people'); 2121 $this->assertText($edit['name'], 'User found in list of users'); 2122 } 2123 2124 // Test that the password '0' is considered a password. 2125 $name = $this->randomName(); 2126 $edit = array( 2127 'name' => $name, 2128 'mail' => $name . '@example.com', 2129 'pass[pass1]' => 0, 2130 'pass[pass2]' => 0, 2131 'notify' => FALSE, 2132 ); 2133 $this->drupalPost('admin/people/create', $edit, t('Create new account')); 2134 $this->assertText(t('Created a new user account for @name. No e-mail has been sent.', array('@name' => $edit['name'])), 'User created with password 0'); 2135 $this->assertNoText('Password field is required'); 2136 } 2137} 2138 2139/** 2140 * Tests editing a user account. 2141 */ 2142class UserEditTestCase extends DrupalWebTestCase { 2143 2144 public static function getInfo() { 2145 return array( 2146 'name' => 'User edit', 2147 'description' => 'Test user edit page.', 2148 'group' => 'User', 2149 ); 2150 } 2151 2152 /** 2153 * Test user edit page. 2154 */ 2155 function testUserEdit() { 2156 // Test user edit functionality with user pictures disabled. 2157 variable_set('user_pictures', 0); 2158 $user1 = $this->drupalCreateUser(array('change own username')); 2159 $user2 = $this->drupalCreateUser(array()); 2160 $this->drupalLogin($user1); 2161 2162 // Test that error message appears when attempting to use a non-unique user name. 2163 $edit['name'] = $user2->name; 2164 $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); 2165 $this->assertRaw(t('The name %name is already taken.', array('%name' => $edit['name']))); 2166 2167 // Repeat the test with user pictures enabled, which modifies the form. 2168 variable_set('user_pictures', 1); 2169 $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); 2170 $this->assertRaw(t('The name %name is already taken.', array('%name' => $edit['name']))); 2171 2172 // Check that filling out a single password field does not validate. 2173 $edit = array(); 2174 $edit['pass[pass1]'] = ''; 2175 $edit['pass[pass2]'] = $this->randomName(); 2176 $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); 2177 $this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.'); 2178 2179 $edit['pass[pass1]'] = $this->randomName(); 2180 $edit['pass[pass2]'] = ''; 2181 $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); 2182 $this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.'); 2183 2184 // Test that the error message appears when attempting to change the mail or 2185 // pass without the current password. 2186 $edit = array(); 2187 $edit['mail'] = $this->randomName() . '@new.example.com'; 2188 $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); 2189 $this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => t('E-mail address')))); 2190 2191 $edit['current_pass'] = $user1->pass_raw; 2192 $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); 2193 $this->assertRaw(t("The changes have been saved.")); 2194 2195 // Test that the user must enter current password before changing passwords. 2196 $edit = array(); 2197 $edit['pass[pass1]'] = $new_pass = $this->randomName(); 2198 $edit['pass[pass2]'] = $new_pass; 2199 $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); 2200 $this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => t('Password')))); 2201 2202 // Try again with the current password. 2203 $edit['current_pass'] = $user1->pass_raw; 2204 $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); 2205 $this->assertRaw(t("The changes have been saved.")); 2206 2207 // Make sure the user can log in with their new password. 2208 $this->drupalLogout(); 2209 $user1->pass_raw = $new_pass; 2210 $this->drupalLogin($user1); 2211 $this->drupalLogout(); 2212 } 2213 2214 /** 2215 * Tests setting the password to "0". 2216 */ 2217 public function testUserWith0Password() { 2218 $admin = $this->drupalCreateUser(array('administer users')); 2219 $this->drupalLogin($admin); 2220 // Create a regular user. 2221 $user1 = $this->drupalCreateUser(array()); 2222 2223 $edit = array('pass[pass1]' => '0', 'pass[pass2]' => '0'); 2224 $this->drupalPost("user/" . $user1->uid . "/edit", $edit, t('Save')); 2225 $this->assertRaw(t("The changes have been saved.")); 2226 2227 $this->drupalLogout(); 2228 $user1->pass_raw = '0'; 2229 $this->drupalLogin($user1); 2230 $this->drupalLogout(); 2231 } 2232} 2233 2234/** 2235 * Tests editing a user account with and without a form rebuild. 2236 */ 2237class UserEditRebuildTestCase extends DrupalWebTestCase { 2238 2239 public static function getInfo() { 2240 return array( 2241 'name' => 'User edit with form rebuild', 2242 'description' => 'Test user edit page when a form rebuild is triggered.', 2243 'group' => 'User', 2244 ); 2245 } 2246 2247 function setUp() { 2248 parent::setUp('user_form_test'); 2249 } 2250 2251 /** 2252 * Test user edit page when the form is set to rebuild. 2253 */ 2254 function testUserEditFormRebuild() { 2255 $user1 = $this->drupalCreateUser(array('change own username')); 2256 $this->drupalLogin($user1); 2257 2258 $roles = array_keys($user1->roles); 2259 // Save the user form twice. 2260 $edit = array(); 2261 $edit['current_pass'] = $user1->pass_raw; 2262 $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); 2263 $this->assertRaw(t("The changes have been saved.")); 2264 $this->drupalPost(NULL, $edit, t('Save')); 2265 $this->assertRaw(t("The changes have been saved.")); 2266 $saved_user1 = entity_load_unchanged('user', $user1->uid); 2267 $this->assertEqual(count($roles), count($saved_user1->roles), 'Count of user roles in database matches original count.'); 2268 $diff = array_diff(array_keys($saved_user1->roles), $roles); 2269 $this->assertTrue(empty($diff), format_string('User roles in database match original: @roles', array('@roles' => implode(', ', $saved_user1->roles)))); 2270 // Set variable that causes the form to be rebuilt in user_form_test.module. 2271 variable_set('user_form_test_user_profile_form_rebuild', TRUE); 2272 $this->drupalPost("user/$user1->uid/edit", $edit, t('Save')); 2273 $this->assertRaw(t("The changes have been saved.")); 2274 $this->drupalPost(NULL, $edit, t('Save')); 2275 $this->assertRaw(t("The changes have been saved.")); 2276 $saved_user1 = entity_load_unchanged('user', $user1->uid); 2277 $this->assertEqual(count($roles), count($saved_user1->roles), 'Count of user roles in database matches original count.'); 2278 $diff = array_diff(array_keys($saved_user1->roles), $roles); 2279 $this->assertTrue(empty($diff), format_string('User roles in database match original: @roles', array('@roles' => implode(', ', $saved_user1->roles)))); 2280 } 2281} 2282 2283/** 2284 * Test case for user signatures. 2285 */ 2286class UserSignatureTestCase extends DrupalWebTestCase { 2287 public static function getInfo() { 2288 return array( 2289 'name' => 'User signatures', 2290 'description' => 'Test user signatures.', 2291 'group' => 'User', 2292 ); 2293 } 2294 2295 function setUp() { 2296 parent::setUp('comment'); 2297 2298 // Enable user signatures. 2299 variable_set('user_signatures', 1); 2300 2301 // Prefetch text formats. 2302 $this->full_html_format = filter_format_load('full_html'); 2303 $this->plain_text_format = filter_format_load('plain_text'); 2304 2305 // Create regular and administrative users. 2306 $this->web_user = $this->drupalCreateUser(array()); 2307 $admin_permissions = array('administer comments'); 2308 foreach (filter_formats() as $format) { 2309 if ($permission = filter_permission_name($format)) { 2310 $admin_permissions[] = $permission; 2311 } 2312 } 2313 $this->admin_user = $this->drupalCreateUser($admin_permissions); 2314 } 2315 2316 /** 2317 * Test that a user can change their signature format and that it is respected 2318 * upon display. 2319 */ 2320 function testUserSignature() { 2321 // Create a new node with comments on. 2322 $node = $this->drupalCreateNode(array('comment' => COMMENT_NODE_OPEN)); 2323 2324 // Verify that user signature field is not displayed on registration form. 2325 $this->drupalGet('user/register'); 2326 $this->assertNoText(t('Signature')); 2327 2328 // Log in as a regular user and create a signature. 2329 $this->drupalLogin($this->web_user); 2330 $signature_text = "<h1>" . $this->randomName() . "</h1>"; 2331 $edit = array( 2332 'signature[value]' => $signature_text, 2333 'signature[format]' => $this->plain_text_format->format, 2334 ); 2335 $this->drupalPost('user/' . $this->web_user->uid . '/edit', $edit, t('Save')); 2336 2337 // Verify that values were stored. 2338 $this->assertFieldByName('signature[value]', $edit['signature[value]'], 'Submitted signature text found.'); 2339 $this->assertFieldByName('signature[format]', $edit['signature[format]'], 'Submitted signature format found.'); 2340 2341 // Create a comment. 2342 $langcode = LANGUAGE_NONE; 2343 $edit = array(); 2344 $edit['subject'] = $this->randomName(8); 2345 $edit['comment_body[' . $langcode . '][0][value]'] = $this->randomName(16); 2346 $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview')); 2347 $this->drupalPost(NULL, array(), t('Save')); 2348 2349 // Get the comment ID. (This technique is the same one used in the Comment 2350 // module's CommentHelperCase test case.) 2351 preg_match('/#comment-([0-9]+)/', $this->getURL(), $match); 2352 $comment_id = $match[1]; 2353 2354 // Log in as an administrator and edit the comment to use Full HTML, so 2355 // that the comment text itself is not filtered at all. 2356 $this->drupalLogin($this->admin_user); 2357 $edit['comment_body[' . $langcode . '][0][format]'] = $this->full_html_format->format; 2358 $this->drupalPost('comment/' . $comment_id . '/edit', $edit, t('Save')); 2359 2360 // Assert that the signature did not make it through unfiltered. 2361 $this->drupalGet('node/' . $node->nid); 2362 $this->assertNoRaw($signature_text, 'Unfiltered signature text not found.'); 2363 $this->assertRaw(check_markup($signature_text, $this->plain_text_format->format), 'Filtered signature text found.'); 2364 } 2365} 2366 2367/* 2368 * Test that a user, having editing their own account, can still log in. 2369 */ 2370class UserEditedOwnAccountTestCase extends DrupalWebTestCase { 2371 2372 public static function getInfo() { 2373 return array( 2374 'name' => 'User edited own account', 2375 'description' => 'Test user edited own account can still log in.', 2376 'group' => 'User', 2377 ); 2378 } 2379 2380 function testUserEditedOwnAccount() { 2381 // Change account setting 'Who can register accounts?' to Administrators 2382 // only. 2383 variable_set('user_register', USER_REGISTER_ADMINISTRATORS_ONLY); 2384 2385 // Create a new user account and log in. 2386 $account = $this->drupalCreateUser(array('change own username')); 2387 $this->drupalLogin($account); 2388 2389 // Change own username. 2390 $edit = array(); 2391 $edit['name'] = $this->randomName(); 2392 $this->drupalPost('user/' . $account->uid . '/edit', $edit, t('Save')); 2393 2394 // Log out. 2395 $this->drupalLogout(); 2396 2397 // Set the new name on the user account and attempt to log back in. 2398 $account->name = $edit['name']; 2399 $this->drupalLogin($account); 2400 } 2401} 2402 2403/** 2404 * Test case to test adding, editing and deleting roles. 2405 */ 2406class UserRoleAdminTestCase extends DrupalWebTestCase { 2407 2408 public static function getInfo() { 2409 return array( 2410 'name' => 'User role administration', 2411 'description' => 'Test adding, editing and deleting user roles and changing role weights.', 2412 'group' => 'User', 2413 ); 2414 } 2415 2416 function setUp() { 2417 parent::setUp(); 2418 $this->admin_user = $this->drupalCreateUser(array('administer permissions', 'administer users')); 2419 } 2420 2421 /** 2422 * Test adding, renaming and deleting roles. 2423 */ 2424 function testRoleAdministration() { 2425 $this->drupalLogin($this->admin_user); 2426 2427 // Test adding a role. (In doing so, we use a role name that happens to 2428 // correspond to an integer, to test that the role administration pages 2429 // correctly distinguish between role names and IDs.) 2430 $role_name = '123'; 2431 $edit = array('name' => $role_name); 2432 $this->drupalPost('admin/people/permissions/roles', $edit, t('Add role')); 2433 $this->assertText(t('The role has been added.'), 'The role has been added.'); 2434 $role = user_role_load_by_name($role_name); 2435 $this->assertTrue(is_object($role), 'The role was successfully retrieved from the database.'); 2436 2437 // Try adding a duplicate role. 2438 $this->drupalPost(NULL, $edit, t('Add role')); 2439 $this->assertRaw(t('The role name %name already exists. Choose another role name.', array('%name' => $role_name)), 'Duplicate role warning displayed.'); 2440 2441 // Test renaming a role. 2442 $old_name = $role_name; 2443 $role_name = '456'; 2444 $edit = array('name' => $role_name); 2445 $this->drupalPost("admin/people/permissions/roles/edit/{$role->rid}", $edit, t('Save role')); 2446 $this->assertText(t('The role has been renamed.'), 'The role has been renamed.'); 2447 $this->assertFalse(user_role_load_by_name($old_name), 'The role can no longer be retrieved from the database using its old name.'); 2448 $this->assertTrue(is_object(user_role_load_by_name($role_name)), 'The role can be retrieved from the database using its new name.'); 2449 2450 // Test deleting the default administrator role. 2451 $role_name = 'administrator'; 2452 $role = user_role_load_by_name($role_name); 2453 $this->drupalPost("admin/people/permissions/roles/edit/{$role->rid}", NULL, t('Delete role')); 2454 $this->drupalPost(NULL, NULL, t('Delete')); 2455 $this->assertText(t('The role has been deleted.'), 'The role has been deleted'); 2456 $this->assertNoLinkByHref("admin/people/permissions/roles/edit/{$role->rid}", 'Role edit link removed.'); 2457 $this->assertFalse(user_role_load_by_name($role_name), 'A deleted role can no longer be loaded.'); 2458 // Make sure this role is no longer configured as the administrator role. 2459 $this->assertNull(variable_get('user_admin_role'), 'The administrator role is no longer configured as the administrator role.'); 2460 2461 // Make sure that the system-defined roles cannot be edited via the user 2462 // interface. 2463 $this->drupalGet('admin/people/permissions/roles/edit/' . DRUPAL_ANONYMOUS_RID); 2464 $this->assertResponse(403, 'Access denied when trying to edit the built-in anonymous role.'); 2465 $this->drupalGet('admin/people/permissions/roles/edit/' . DRUPAL_AUTHENTICATED_RID); 2466 $this->assertResponse(403, 'Access denied when trying to edit the built-in authenticated role.'); 2467 } 2468 2469 /** 2470 * Test user role weight change operation. 2471 */ 2472 function testRoleWeightChange() { 2473 $this->drupalLogin($this->admin_user); 2474 2475 // Pick up a random role and get its weight. 2476 $rid = array_rand(user_roles()); 2477 $role = user_role_load($rid); 2478 $old_weight = $role->weight; 2479 2480 // Change the role weight and submit the form. 2481 $edit = array('roles['. $rid .'][weight]' => $old_weight + 1); 2482 $this->drupalPost('admin/people/permissions/roles', $edit, t('Save order')); 2483 $this->assertText(t('The role settings have been updated.'), 'The role settings form submitted successfully.'); 2484 2485 // Retrieve the saved role and compare its weight. 2486 $role = user_role_load($rid); 2487 $new_weight = $role->weight; 2488 $this->assertTrue(($old_weight + 1) == $new_weight, 'Role weight updated successfully.'); 2489 } 2490} 2491 2492/** 2493 * Test user token replacement in strings. 2494 */ 2495class UserTokenReplaceTestCase extends DrupalWebTestCase { 2496 public static function getInfo() { 2497 return array( 2498 'name' => 'User token replacement', 2499 'description' => 'Generates text using placeholders for dummy content to check user token replacement.', 2500 'group' => 'User', 2501 ); 2502 } 2503 2504 /** 2505 * Creates a user, then tests the tokens generated from it. 2506 */ 2507 function testUserTokenReplacement() { 2508 global $language; 2509 $url_options = array( 2510 'absolute' => TRUE, 2511 'language' => $language, 2512 ); 2513 2514 // Create two users and log them in one after another. 2515 $user1 = $this->drupalCreateUser(array()); 2516 $user2 = $this->drupalCreateUser(array()); 2517 $this->drupalLogin($user1); 2518 $this->drupalLogout(); 2519 $this->drupalLogin($user2); 2520 2521 $account = user_load($user1->uid); 2522 $global_account = user_load($GLOBALS['user']->uid); 2523 2524 // Generate and test sanitized tokens. 2525 $tests = array(); 2526 $tests['[user:uid]'] = $account->uid; 2527 $tests['[user:name]'] = check_plain(format_username($account)); 2528 $tests['[user:mail]'] = check_plain($account->mail); 2529 $tests['[user:url]'] = url("user/$account->uid", $url_options); 2530 $tests['[user:edit-url]'] = url("user/$account->uid/edit", $url_options); 2531 $tests['[user:last-login]'] = format_date($account->login, 'medium', '', NULL, $language->language); 2532 $tests['[user:last-login:short]'] = format_date($account->login, 'short', '', NULL, $language->language); 2533 $tests['[user:created]'] = format_date($account->created, 'medium', '', NULL, $language->language); 2534 $tests['[user:created:short]'] = format_date($account->created, 'short', '', NULL, $language->language); 2535 $tests['[current-user:name]'] = check_plain(format_username($global_account)); 2536 2537 // Test to make sure that we generated something for each token. 2538 $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.'); 2539 2540 foreach ($tests as $input => $expected) { 2541 $output = token_replace($input, array('user' => $account), array('language' => $language)); 2542 $this->assertEqual($output, $expected, format_string('Sanitized user token %token replaced.', array('%token' => $input))); 2543 } 2544 2545 // Generate and test unsanitized tokens. 2546 $tests['[user:name]'] = format_username($account); 2547 $tests['[user:mail]'] = $account->mail; 2548 $tests['[current-user:name]'] = format_username($global_account); 2549 2550 foreach ($tests as $input => $expected) { 2551 $output = token_replace($input, array('user' => $account), array('language' => $language, 'sanitize' => FALSE)); 2552 $this->assertEqual($output, $expected, format_string('Unsanitized user token %token replaced.', array('%token' => $input))); 2553 } 2554 } 2555} 2556 2557/** 2558 * Test user search. 2559 */ 2560class UserUserSearchTestCase extends DrupalWebTestCase { 2561 public static function getInfo() { 2562 return array( 2563 'name' => 'User search', 2564 'description' => 'Tests the user search page and verifies that sensitive information is hidden from unauthorized users.', 2565 'group' => 'User', 2566 ); 2567 } 2568 2569 function testUserSearch() { 2570 // Verify that a user without 'administer users' permission cannot search 2571 // for users by email address. Additionally, ensure that the username has a 2572 // plus sign to ensure searching works with that. 2573 $user1 = $this->drupalCreateUser(array('access user profiles', 'search content', 'use advanced search')); 2574 $edit['name'] = 'foo+bar'; 2575 $edit['mail'] = $edit['name'] . '@example.com'; 2576 user_save($user1, $edit); 2577 $this->drupalLogin($user1); 2578 $keys = $user1->mail; 2579 $edit = array('keys' => $keys); 2580 $this->drupalPost('search/user/', $edit, t('Search')); 2581 $this->assertNoText($keys); 2582 $this->drupalLogout(); 2583 2584 $user2 = $this->drupalCreateUser(array('administer users', 'access user profiles', 'search content', 'use advanced search')); 2585 $this->drupalLogin($user2); 2586 $keys = $user2->mail; 2587 $edit = array('keys' => $keys); 2588 $this->drupalPost('search/user/', $edit, t('Search')); 2589 $this->assertText($keys); 2590 2591 // Verify that wildcard search works. 2592 $keys = $user1->name; 2593 $keys = substr($keys, 0, 2) . '*' . substr($keys, 4, 2); 2594 $edit = array('keys' => $keys); 2595 $this->drupalPost('search/user/', $edit, t('Search')); 2596 $this->assertText($user1->name, 'Search for username wildcard resulted in user name on page for administrative user.'); 2597 2598 // Verify that wildcard search works for email. 2599 $keys = $user1->mail; 2600 $keys = substr($keys, 0, 2) . '*' . substr($keys, 4, 2); 2601 $edit = array('keys' => $keys); 2602 $this->drupalPost('search/user/', $edit, t('Search')); 2603 $this->assertText($user1->name, 'Search for email wildcard resulted in user name on page for administrative user.'); 2604 2605 // Create a blocked user. 2606 $blocked_user = $this->drupalCreateUser(); 2607 $edit = array('status' => 0); 2608 $blocked_user = user_save($blocked_user, $edit); 2609 2610 // Verify that users with "administer users" permissions can see blocked 2611 // accounts in search results. 2612 $edit = array('keys' => $blocked_user->name); 2613 $this->drupalPost('search/user/', $edit, t('Search')); 2614 $this->assertText($blocked_user->name, 'Blocked users are listed on the user search results for users with the "administer users" permission.'); 2615 2616 // Verify that users without "administer users" permissions do not see 2617 // blocked accounts in search results. 2618 $this->drupalLogin($user1); 2619 $edit = array('keys' => $blocked_user->name); 2620 $this->drupalPost('search/user/', $edit, t('Search')); 2621 $this->assertNoText($blocked_user->name, 'Blocked users are hidden from the user search results.'); 2622 2623 $this->drupalLogout(); 2624 } 2625} 2626 2627/** 2628 * Test role assignment. 2629 */ 2630class UserRolesAssignmentTestCase extends DrupalWebTestCase { 2631 protected $admin_user; 2632 2633 public static function getInfo() { 2634 return array( 2635 'name' => 'Role assignment', 2636 'description' => 'Tests that users can be assigned and unassigned roles.', 2637 'group' => 'User' 2638 ); 2639 } 2640 2641 function setUp() { 2642 parent::setUp(); 2643 $this->admin_user = $this->drupalCreateUser(array('administer permissions', 'administer users')); 2644 $this->drupalLogin($this->admin_user); 2645 } 2646 2647 /** 2648 * Tests that a user can be assigned a role and that the role can be removed 2649 * again. 2650 */ 2651 function testAssignAndRemoveRole() { 2652 $rid = $this->drupalCreateRole(array('administer content types')); 2653 $account = $this->drupalCreateUser(); 2654 2655 // Assign the role to the user. 2656 $this->drupalPost('user/' . $account->uid . '/edit', array("roles[$rid]" => $rid), t('Save')); 2657 $this->assertText(t('The changes have been saved.')); 2658 $this->assertFieldChecked('edit-roles-' . $rid, 'Role is assigned.'); 2659 $this->userLoadAndCheckRoleAssigned($account, $rid); 2660 2661 // Remove the role from the user. 2662 $this->drupalPost('user/' . $account->uid . '/edit', array("roles[$rid]" => FALSE), t('Save')); 2663 $this->assertText(t('The changes have been saved.')); 2664 $this->assertNoFieldChecked('edit-roles-' . $rid, 'Role is removed from user.'); 2665 $this->userLoadAndCheckRoleAssigned($account, $rid, FALSE); 2666 } 2667 2668 /** 2669 * Tests that when creating a user the role can be assigned. And that it can 2670 * be removed again. 2671 */ 2672 function testCreateUserWithRole() { 2673 $rid = $this->drupalCreateRole(array('administer content types')); 2674 // Create a new user and add the role at the same time. 2675 $edit = array( 2676 'name' => $this->randomName(), 2677 'mail' => $this->randomName() . '@example.com', 2678 'pass[pass1]' => $pass = $this->randomString(), 2679 'pass[pass2]' => $pass, 2680 "roles[$rid]" => $rid, 2681 ); 2682 $this->drupalPost('admin/people/create', $edit, t('Create new account')); 2683 $this->assertText(t('Created a new user account for !name.', array('!name' => $edit['name']))); 2684 // Get the newly added user. 2685 $account = user_load_by_name($edit['name']); 2686 2687 $this->drupalGet('user/' . $account->uid . '/edit'); 2688 $this->assertFieldChecked('edit-roles-' . $rid, 'Role is assigned.'); 2689 $this->userLoadAndCheckRoleAssigned($account, $rid); 2690 2691 // Remove the role again. 2692 $this->drupalPost('user/' . $account->uid . '/edit', array("roles[$rid]" => FALSE), t('Save')); 2693 $this->assertText(t('The changes have been saved.')); 2694 $this->assertNoFieldChecked('edit-roles-' . $rid, 'Role is removed from user.'); 2695 $this->userLoadAndCheckRoleAssigned($account, $rid, FALSE); 2696 } 2697 2698 /** 2699 * Check role on user object. 2700 * 2701 * @param object $account 2702 * The user account to check. 2703 * @param string $rid 2704 * The role ID to search for. 2705 * @param bool $is_assigned 2706 * (optional) Whether to assert that $rid exists (TRUE) or not (FALSE). 2707 * Defaults to TRUE. 2708 */ 2709 private function userLoadAndCheckRoleAssigned($account, $rid, $is_assigned = TRUE) { 2710 $account = user_load($account->uid, TRUE); 2711 if ($is_assigned) { 2712 $this->assertTrue(array_key_exists($rid, $account->roles), 'The role is present in the user object.'); 2713 } 2714 else { 2715 $this->assertFalse(array_key_exists($rid, $account->roles), 'The role is not present in the user object.'); 2716 } 2717 } 2718} 2719 2720 2721/** 2722 * Unit test for authmap assignment. 2723 */ 2724class UserAuthmapAssignmentTestCase extends DrupalWebTestCase { 2725 public static function getInfo() { 2726 return array( 2727 'name' => 'Authmap assignment', 2728 'description' => 'Tests that users can be assigned and unassigned authmaps.', 2729 'group' => 'User' 2730 ); 2731 } 2732 2733 /** 2734 * Test authmap assignment and retrieval. 2735 */ 2736 function testAuthmapAssignment() { 2737 $account = $this->drupalCreateUser(); 2738 2739 // Assign authmaps to the user. 2740 $authmaps = array( 2741 'authname_poll' => 'external username one', 2742 'authname_book' => 'external username two', 2743 ); 2744 user_set_authmaps($account, $authmaps); 2745 2746 // Test for expected authmaps. 2747 $expected_authmaps = array( 2748 'external username one' => array( 2749 'poll' => 'external username one', 2750 ), 2751 'external username two' => array( 2752 'book' => 'external username two', 2753 ), 2754 ); 2755 foreach ($expected_authmaps as $authname => $expected_output) { 2756 $this->assertIdentical(user_get_authmaps($authname), $expected_output, format_string('Authmap for authname %authname was set correctly.', array('%authname' => $authname))); 2757 } 2758 2759 // Remove authmap for module poll, add authmap for module blog. 2760 $authmaps = array( 2761 'authname_poll' => NULL, 2762 'authname_blog' => 'external username three', 2763 ); 2764 user_set_authmaps($account, $authmaps); 2765 2766 // Assert that external username one does not have authmaps. 2767 $remove_username = 'external username one'; 2768 unset($expected_authmaps[$remove_username]); 2769 $this->assertFalse(user_get_authmaps($remove_username), format_string('Authmap for %authname was removed.', array('%authname' => $remove_username))); 2770 2771 // Assert that a new authmap was created for external username three, and 2772 // existing authmaps for external username two were unchanged. 2773 $expected_authmaps['external username three'] = array('blog' => 'external username three'); 2774 foreach ($expected_authmaps as $authname => $expected_output) { 2775 $this->assertIdentical(user_get_authmaps($authname), $expected_output, format_string('Authmap for authname %authname was set correctly.', array('%authname' => $authname))); 2776 } 2777 } 2778} 2779 2780/** 2781 * Tests user_validate_current_pass on a custom form. 2782 */ 2783class UserValidateCurrentPassCustomForm extends DrupalWebTestCase { 2784 2785 public static function getInfo() { 2786 return array( 2787 'name' => 'User validate current pass custom form', 2788 'description' => 'Test that user_validate_current_pass is usable on a custom form.', 2789 'group' => 'User', 2790 ); 2791 } 2792 2793 /** 2794 * User with permission to view content. 2795 */ 2796 protected $accessUser; 2797 2798 /** 2799 * User permission to administer users. 2800 */ 2801 protected $adminUser; 2802 2803 function setUp() { 2804 parent::setUp('user_form_test'); 2805 // Create two users 2806 $this->accessUser = $this->drupalCreateUser(array('access content')); 2807 $this->adminUser = $this->drupalCreateUser(array('administer users')); 2808 } 2809 2810 /** 2811 * Tests that user_validate_current_pass can be reused on a custom form. 2812 */ 2813 function testUserValidateCurrentPassCustomForm() { 2814 $this->drupalLogin($this->adminUser); 2815 2816 // Submit the custom form with the admin user using the access user's password. 2817 $edit = array(); 2818 $edit['user_form_test_field'] = $this->accessUser->name; 2819 $edit['current_pass'] = $this->accessUser->pass_raw; 2820 $this->drupalPost('user_form_test_current_password/' . $this->accessUser->uid, $edit, t('Test')); 2821 $this->assertText(t('The password has been validated and the form submitted successfully.')); 2822 } 2823} 2824