Project

General

Profile

Paste
Download (115 KB) Statistics
| Branch: | Revision:

root / drupal7 / modules / user / user.test @ c7768a53

1
<?php
2

    
3
/**
4
 * @file
5
 * Tests for user.module.
6
 */
7

    
8
class 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

    
260
class 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 \'&nbsp;&nbsp;\'', '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
 */
315
class 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
 */
480
class UserPasswordResetTestCase extends DrupalWebTestCase {
481
  protected $profile = 'standard';
482

    
483
  public static function getInfo() {
484
    return array(
485
      'name' => 'Reset password',
486
      'description' => 'Ensure that password reset methods work as expected.',
487
      'group' => 'User',
488
    );
489
  }
490

    
491
  /**
492
   * Retrieves password reset email and extracts the login link.
493
   */
494
  public function getResetURL() {
495
    // Assume the most recent email.
496
    $_emails = $this->drupalGetMails();
497
    $email = end($_emails);
498
    $urls = array();
499
    preg_match('#.+user/reset/.+#', $email['body'], $urls);
500

    
501
    return $urls[0];
502
  }
503

    
504
  /**
505
   * Tests password reset functionality.
506
   */
507
  function testUserPasswordReset() {
508
    // Create a user.
509
    $account = $this->drupalCreateUser();
510
    $this->drupalLogin($account);
511
    $this->drupalLogout();
512
    // Attempt to reset password.
513
    $edit = array('name' => $account->name);
514
    $this->drupalPost('user/password', $edit, t('E-mail new password'));
515
    // Confirm the password reset.
516
    $this->assertText(t('Further instructions have been sent to your e-mail address.'), 'Password reset instructions mailed message displayed.');
517
    // Ensure that flood control was not triggered.
518
    $this->assertNoText(t('is temporarily blocked. Try again later'), 'Flood control was not triggered by single password reset.');
519

    
520
    // Create an image field to enable an Ajax request on the user profile page.
521
    $field = array(
522
      'field_name' => 'field_avatar',
523
      'type' => 'image',
524
      'settings' => array(),
525
      'cardinality' => 1,
526
    );
527
    field_create_field($field);
528

    
529
    $instance = array(
530
      'field_name' => $field['field_name'],
531
      'entity_type' => 'user',
532
      'label' => 'Avatar',
533
      'bundle' => 'user',
534
      'required' => FALSE,
535
      'settings' => array(),
536
      'widget' => array(
537
        'type' => 'image_image',
538
        'settings' => array(),
539
      ),
540
    );
541
    field_create_instance($instance);
542

    
543
    $resetURL = $this->getResetURL();
544
    $this->drupalGet($resetURL);
545

    
546
    // Check successful login.
547
    $this->drupalPost(NULL, NULL, t('Log in'));
548

    
549
    // Make sure the Ajax request from uploading a file does not invalidate the
550
    // reset token.
551
    $image = current($this->drupalGetTestFiles('image'));
552
    $edit = array(
553
      'files[field_avatar_und_0]' => drupal_realpath($image->uri),
554
    );
555
    $this->drupalPostAJAX(NULL, $edit, 'field_avatar_und_0_upload_button');
556

    
557
    // Change the forgotten password.
558
    $password = user_password();
559
    $edit = array('pass[pass1]' => $password, 'pass[pass2]' => $password);
560
    $this->drupalPost(NULL, $edit, t('Save'));
561
    $this->assertText(t('The changes have been saved.'), 'Forgotten password changed.');
562
  }
563

    
564
  /**
565
   * Test user-based flood control on password reset.
566
   */
567
  function testPasswordResetFloodControlPerUser() {
568
    // Set a very low limit for testing.
569
    variable_set('user_pass_reset_user_limit', 2);
570

    
571
    // Create a user.
572
    $account = $this->drupalCreateUser();
573
    $this->drupalLogin($account);
574
    $this->drupalLogout();
575

    
576
    $edit = array('name' => $account->name);
577

    
578
    // Try 2 requests that should not trigger flood control.
579
    for ($i = 0; $i < 2; $i++) {
580
      $this->drupalPost('user/password', $edit, t('E-mail new password'));
581
      // Confirm the password reset.
582
      $this->assertText(t('Further instructions have been sent to your e-mail address.'), 'Password reset instructions mailed message displayed.');
583
      // Ensure that flood control was not triggered.
584
      $this->assertNoText(t('is temporarily blocked. Try again later'), 'Flood control was not triggered by password reset.');
585
    }
586

    
587
    // A successful password reset should clear flood events.
588
    $resetURL = $this->getResetURL();
589
    $this->drupalGet($resetURL);
590

    
591
    // Check successful login.
592
    $this->drupalPost(NULL, NULL, t('Log in'));
593
    $this->drupalLogout();
594

    
595
    // Try 2 requests that should not trigger flood control.
596
    for ($i = 0; $i < 2; $i++) {
597
      $this->drupalPost('user/password', $edit, t('E-mail new password'));
598
      // Confirm the password reset.
599
      $this->assertText(t('Further instructions have been sent to your e-mail address.'), 'Password reset instructions mailed message displayed.');
600
      // Ensure that flood control was not triggered.
601
      $this->assertNoText(t('is temporarily blocked. Try again later'), 'Flood control was not triggered by password reset.');
602
    }
603

    
604
    // The next request should trigger flood control
605
    $this->drupalPost('user/password', $edit, t('E-mail new password'));
606
    // Confirm the password reset was blocked.
607
    $this->assertNoText(t('Further instructions have been sent to your e-mail address.'), 'Password reset instructions mailed message not displayed for excessive password resets.');
608
    // Ensure that flood control was triggered.
609
    $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.');
610
  }
611

    
612
  /**
613
   * Test IP-based flood control on password reset.
614
   */
615
  function testPasswordResetFloodControlPerIp() {
616
    // Set a very low limit for testing.
617
    variable_set('user_pass_reset_ip_limit', 2);
618

    
619
    // Try 2 requests that should not trigger flood control.
620
    for ($i = 0; $i < 2; $i++) {
621
      $name = $this->randomName();
622
      $edit = array('name' => $name);
623
      $this->drupalPost('user/password', $edit, t('E-mail new password'));
624
      // Confirm the password reset was not blocked. Note that @name is used
625
      // instead of %name as assertText() works with plain text not HTML.
626
      $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.');
627
      // Ensure that flood control was not triggered.
628
      $this->assertNoText(t('is temporarily blocked. Try again later'), 'Flood control was not triggered by password reset.');
629
    }
630

    
631
    // The next request should trigger flood control
632
    $name = $this->randomName();
633
    $edit = array('name' => $name);
634
    $this->drupalPost('user/password', $edit, t('E-mail new password'));
635
    // Confirm the password reset was blocked early. Note that @name is used
636
    // instead of %name as assertText() works with plain text not HTML.
637
    $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.');
638
    // Ensure that flood control was triggered.
639
    $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.');
640
  }
641

    
642
  /**
643
   * Test user password reset while logged in.
644
   */
645
  function testUserPasswordResetLoggedIn() {
646
    $account = $this->drupalCreateUser();
647
    $this->drupalLogin($account);
648
    // Make sure the test account has a valid password.
649
    user_save($account, array('pass' => user_password()));
650

    
651
    // Generate one time login link.
652
    $reset_url = user_pass_reset_url($account);
653
    $this->drupalGet($reset_url);
654

    
655
    $this->assertText('Reset password');
656
    $this->drupalPost(NULL, NULL, t('Log in'));
657

    
658
    $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.');
659

    
660
    $pass = user_password();
661
    $edit = array(
662
      'pass[pass1]' => $pass,
663
      'pass[pass2]' => $pass,
664
    );
665
    $this->drupalPost(NULL, $edit, t('Save'));
666

    
667
    $this->assertText('The changes have been saved.');
668
  }
669

    
670
  /**
671
   * Attempts login using an expired password reset link.
672
   */
673
  function testUserPasswordResetExpired() {
674
    // Set password reset timeout variable to 43200 seconds = 12 hours.
675
    $timeout = 43200;
676
    variable_set('user_password_reset_timeout', $timeout);
677

    
678
    // Create a user.
679
    $account = $this->drupalCreateUser();
680
    $this->drupalLogin($account);
681
    // Load real user object.
682
    $account = user_load($account->uid, TRUE);
683
    $this->drupalLogout();
684

    
685
    // To attempt an expired password reset, create a password reset link as if
686
    // its request time was 60 seconds older than the allowed limit of timeout.
687
    $bogus_timestamp = REQUEST_TIME - variable_get('user_password_reset_timeout', 86400) - 60;
688
    $this->drupalGet("user/reset/$account->uid/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login, $account->uid));
689
    $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.');
690
  }
691

    
692
  /**
693
   * Prefill the text box on incorrect login via link to password reset page.
694
   */
695
  function testUserPasswordTextboxFilled() {
696
    $this->drupalGet('user/login');
697
    $edit = array(
698
      'name' => $this->randomName(),
699
      'pass' => $this->randomName(),
700
    );
701
    $this->drupalPost('user', $edit, t('Log in'));
702
    $this->assertRaw(t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>',
703
      array('@password' => url('user/password', array('query' => array('name' => $edit['name']))))));
704
    unset($edit['pass']);
705
    $this->drupalGet('user/password', array('query' => array('name' => $edit['name'])));
706
    $this->assertFieldByName('name', $edit['name'], 'User name found.');
707
  }
708

    
709
  /**
710
   * Make sure that users cannot forge password reset URLs of other users.
711
   */
712
  function testResetImpersonation() {
713
    // Make sure user 1 has a valid password, so it does not interfere with the
714
    // test user accounts that are created below.
715
    $account = user_load(1);
716
    user_save($account, array('pass' => user_password()));
717

    
718
    // Create two identical user accounts except for the user name. They must
719
    // have the same empty password, so we can't use $this->drupalCreateUser().
720
    $edit = array();
721
    $edit['name'] = $this->randomName();
722
    $edit['mail'] = $edit['name'] . '@example.com';
723
    $edit['status'] = 1;
724

    
725
    $user1 = user_save(drupal_anonymous_user(), $edit);
726

    
727
    $edit['name'] = $this->randomName();
728
    $user2 = user_save(drupal_anonymous_user(), $edit);
729

    
730
    // The password reset URL must not be valid for the second user when only
731
    // the user ID is changed in the URL.
732
    $reset_url = user_pass_reset_url($user1);
733
    $attack_reset_url = str_replace("user/reset/$user1->uid", "user/reset/$user2->uid", $reset_url);
734
    $this->drupalGet($attack_reset_url);
735
    $this->assertNoText($user2->name, 'The invalid password reset page does not show the user name.');
736
    $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
737
    $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.');
738

    
739
    // When legacy code calls user_pass_rehash() without providing the $uid
740
    // parameter, neither password reset URL should be valid since it is
741
    // impossible for the system to determine which user account the token was
742
    // intended for.
743
    $timestamp = REQUEST_TIME;
744
    // Pass an explicit NULL for the $uid parameter of user_pass_rehash()
745
    // rather than not passing it at all, to avoid triggering PHP warnings in
746
    // the test.
747
    $reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL);
748
    $reset_url = url("user/reset/$user1->uid/$timestamp/$reset_url_token", array('absolute' => TRUE));
749
    $this->drupalGet($reset_url);
750
    $this->assertNoText($user1->name, 'The invalid password reset page does not show the user name.');
751
    $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
752
    $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.');
753
    $attack_reset_url = str_replace("user/reset/$user1->uid", "user/reset/$user2->uid", $reset_url);
754
    $this->drupalGet($attack_reset_url);
755
    $this->assertNoText($user2->name, 'The invalid password reset page does not show the user name.');
756
    $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
757
    $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.');
758

    
759
    // To verify that user_pass_rehash() never returns a valid result in the
760
    // above situation (even if legacy code also called it to attempt to
761
    // validate the token, rather than just to generate the URL), check that a
762
    // second call with the same parameters produces a different result.
763
    $new_reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL);
764
    $this->assertNotEqual($reset_url_token, $new_reset_url_token);
765

    
766
    // However, when the duplicate account is removed, the password reset URL
767
    // should be valid.
768
    user_delete($user2->uid);
769
    $reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL);
770
    $reset_url = url("user/reset/$user1->uid/$timestamp/$reset_url_token", array('absolute' => TRUE));
771
    $this->drupalGet($reset_url);
772
    $this->assertText($user1->name, 'The valid password reset page shows the user name.');
773
    $this->assertUrl($reset_url, array(), 'The user remains on the password reset login page.');
774
    $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.');
775
  }
776

    
777
}
778

    
779
/**
780
 * Test cancelling a user.
781
 */
782
class UserCancelTestCase extends DrupalWebTestCase {
783
  public static function getInfo() {
784
    return array(
785
      'name' => 'Cancel account',
786
      'description' => 'Ensure that account cancellation methods work as expected.',
787
      'group' => 'User',
788
    );
789
  }
790

    
791
  function setUp() {
792
    parent::setUp('comment');
793
  }
794

    
795
  /**
796
   * Attempt to cancel account without permission.
797
   */
798
  function testUserCancelWithoutPermission() {
799
    variable_set('user_cancel_method', 'user_cancel_reassign');
800

    
801
    // Create a user.
802
    $account = $this->drupalCreateUser(array());
803
    $this->drupalLogin($account);
804
    // Load real user object.
805
    $account = user_load($account->uid, TRUE);
806

    
807
    // Create a node.
808
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
809

    
810
    // Attempt to cancel account.
811
    $this->drupalGet('user/' . $account->uid . '/edit');
812
    $this->assertNoRaw(t('Cancel account'), 'No cancel account button displayed.');
813

    
814
    // Attempt bogus account cancellation request confirmation.
815
    $timestamp = $account->login;
816
    $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid));
817
    $this->assertResponse(403, 'Bogus cancelling request rejected.');
818
    $account = user_load($account->uid);
819
    $this->assertTrue($account->status == 1, 'User account was not canceled.');
820

    
821
    // Confirm user's content has not been altered.
822
    $test_node = node_load($node->nid, NULL, TRUE);
823
    $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), 'Node of the user has not been altered.');
824
  }
825

    
826
  /**
827
   * Tests that user account for uid 1 cannot be cancelled.
828
   *
829
   * This should never be possible, or the site owner would become unable to
830
   * administer the site.
831
   */
832
  function testUserCancelUid1() {
833
    // Update uid 1's name and password to we know it.
834
    $password = user_password();
835
    require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
836
    $account = array(
837
      'name' => 'user1',
838
      'pass' => user_hash_password(trim($password)),
839
    );
840
    // We cannot use user_save() here or the password would be hashed again.
841
    db_update('users')
842
      ->fields($account)
843
      ->condition('uid', 1)
844
      ->execute();
845

    
846
    // Reload and log in uid 1.
847
    $user1 = user_load(1, TRUE);
848
    $user1->pass_raw = $password;
849

    
850
    // Try to cancel uid 1's account with a different user.
851
    $this->admin_user = $this->drupalCreateUser(array('administer users'));
852
    $this->drupalLogin($this->admin_user);
853
    $edit = array(
854
      'operation' => 'cancel',
855
      'accounts[1]' => TRUE,
856
    );
857
    $this->drupalPost('admin/people', $edit, t('Update'));
858

    
859
    // Verify that uid 1's account was not cancelled.
860
    $user1 = user_load(1, TRUE);
861
    $this->assertEqual($user1->status, 1, 'User #1 still exists and is not blocked.');
862
  }
863

    
864
  /**
865
   * Attempt invalid account cancellations.
866
   */
867
  function testUserCancelInvalid() {
868
    variable_set('user_cancel_method', 'user_cancel_reassign');
869

    
870
    // Create a user.
871
    $account = $this->drupalCreateUser(array('cancel account'));
872
    $this->drupalLogin($account);
873
    // Load real user object.
874
    $account = user_load($account->uid, TRUE);
875

    
876
    // Create a node.
877
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
878

    
879
    // Attempt to cancel account.
880
    $this->drupalPost('user/' . $account->uid . '/edit', NULL, t('Cancel account'));
881

    
882
    // Confirm account cancellation.
883
    $timestamp = time();
884
    $this->drupalPost(NULL, NULL, t('Cancel account'));
885
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
886

    
887
    // Attempt bogus account cancellation request confirmation.
888
    $bogus_timestamp = $timestamp + 60;
889
    $this->drupalGet("user/$account->uid/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login, $account->uid));
890
    $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.');
891
    $account = user_load($account->uid);
892
    $this->assertTrue($account->status == 1, 'User account was not canceled.');
893

    
894
    // Attempt expired account cancellation request confirmation.
895
    $bogus_timestamp = $timestamp - 86400 - 60;
896
    $this->drupalGet("user/$account->uid/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login, $account->uid));
897
    $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.');
898
    $accounts = user_load_multiple(array($account->uid), array('status' => 1));
899
    $this->assertTrue(reset($accounts), 'User account was not canceled.');
900

    
901
    // Confirm user's content has not been altered.
902
    $test_node = node_load($node->nid, NULL, TRUE);
903
    $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), 'Node of the user has not been altered.');
904
  }
905

    
906
  /**
907
   * Disable account and keep all content.
908
   */
909
  function testUserBlock() {
910
    variable_set('user_cancel_method', 'user_cancel_block');
911

    
912
    // Create a user.
913
    $web_user = $this->drupalCreateUser(array('cancel account'));
914
    $this->drupalLogin($web_user);
915

    
916
    // Load real user object.
917
    $account = user_load($web_user->uid, TRUE);
918

    
919
    // Attempt to cancel account.
920
    $this->drupalGet('user/' . $account->uid . '/edit');
921
    $this->drupalPost(NULL, NULL, t('Cancel account'));
922
    $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
923
    $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.');
924
    $this->assertNoText(t('Select the method to cancel the account above.'), 'Does not allow user to select account cancellation method.');
925

    
926
    // Confirm account cancellation.
927
    $timestamp = time();
928

    
929
    $this->drupalPost(NULL, NULL, t('Cancel account'));
930
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
931

    
932
    // Confirm account cancellation request.
933
    $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid));
934
    $account = user_load($account->uid, TRUE);
935
    $this->assertTrue($account->status == 0, 'User has been blocked.');
936

    
937
    // Confirm that the confirmation message made it through to the end user.
938
    $this->assertRaw(t('%name has been disabled.', array('%name' => $account->name)), "Confirmation message displayed to user.");
939
  }
940

    
941
  /**
942
   * Disable account and unpublish all content.
943
   */
944
  function testUserBlockUnpublish() {
945
    variable_set('user_cancel_method', 'user_cancel_block_unpublish');
946

    
947
    // Create a user.
948
    $account = $this->drupalCreateUser(array('cancel account'));
949
    $this->drupalLogin($account);
950
    // Load real user object.
951
    $account = user_load($account->uid, TRUE);
952

    
953
    // Create a node with two revisions.
954
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
955
    $settings = get_object_vars($node);
956
    $settings['revision'] = 1;
957
    $node = $this->drupalCreateNode($settings);
958

    
959
    // Attempt to cancel account.
960
    $this->drupalGet('user/' . $account->uid . '/edit');
961
    $this->drupalPost(NULL, NULL, t('Cancel account'));
962
    $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
963
    $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.');
964

    
965
    // Confirm account cancellation.
966
    $timestamp = time();
967
    $this->drupalPost(NULL, NULL, t('Cancel account'));
968
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
969

    
970
    // Confirm account cancellation request.
971
    $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid));
972
    $account = user_load($account->uid, TRUE);
973
    $this->assertTrue($account->status == 0, 'User has been blocked.');
974

    
975
    // Confirm user's content has been unpublished.
976
    $test_node = node_load($node->nid, NULL, TRUE);
977
    $this->assertTrue($test_node->status == 0, 'Node of the user has been unpublished.');
978
    $test_node = node_load($node->nid, $node->vid, TRUE);
979
    $this->assertTrue($test_node->status == 0, 'Node revision of the user has been unpublished.');
980

    
981
    // Confirm that the confirmation message made it through to the end user.
982
    $this->assertRaw(t('%name has been disabled.', array('%name' => $account->name)), "Confirmation message displayed to user.");
983
  }
984

    
985
  /**
986
   * Delete account and anonymize all content.
987
   */
988
  function testUserAnonymize() {
989
    variable_set('user_cancel_method', 'user_cancel_reassign');
990

    
991
    // Create a user.
992
    $account = $this->drupalCreateUser(array('cancel account'));
993
    $this->drupalLogin($account);
994
    // Load real user object.
995
    $account = user_load($account->uid, TRUE);
996

    
997
    // Create a simple node.
998
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
999

    
1000
    // Create a node with two revisions, the initial one belonging to the
1001
    // cancelling user.
1002
    $revision_node = $this->drupalCreateNode(array('uid' => $account->uid));
1003
    $revision = $revision_node->vid;
1004
    $settings = get_object_vars($revision_node);
1005
    $settings['revision'] = 1;
1006
    $settings['uid'] = 1; // Set new/current revision to someone else.
1007
    $revision_node = $this->drupalCreateNode($settings);
1008

    
1009
    // Attempt to cancel account.
1010
    $this->drupalGet('user/' . $account->uid . '/edit');
1011
    $this->drupalPost(NULL, NULL, t('Cancel account'));
1012
    $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
1013
    $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.');
1014

    
1015
    // Confirm account cancellation.
1016
    $timestamp = time();
1017
    $this->drupalPost(NULL, NULL, t('Cancel account'));
1018
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
1019

    
1020
    // Confirm account cancellation request.
1021
    $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid));
1022
    $this->assertFalse(user_load($account->uid, TRUE), 'User is not found in the database.');
1023

    
1024
    // Confirm that user's content has been attributed to anonymous user.
1025
    $test_node = node_load($node->nid, NULL, TRUE);
1026
    $this->assertTrue(($test_node->uid == 0 && $test_node->status == 1), 'Node of the user has been attributed to anonymous user.');
1027
    $test_node = node_load($revision_node->nid, $revision, TRUE);
1028
    $this->assertTrue(($test_node->revision_uid == 0 && $test_node->status == 1), 'Node revision of the user has been attributed to anonymous user.');
1029
    $test_node = node_load($revision_node->nid, NULL, TRUE);
1030
    $this->assertTrue(($test_node->uid != 0 && $test_node->status == 1), "Current revision of the user's node was not attributed to anonymous user.");
1031

    
1032
    // Confirm that the confirmation message made it through to the end user.
1033
    $this->assertRaw(t('%name has been deleted.', array('%name' => $account->name)), "Confirmation message displayed to user.");
1034
  }
1035

    
1036
  /**
1037
   * Delete account and remove all content.
1038
   */
1039
  function testUserDelete() {
1040
    variable_set('user_cancel_method', 'user_cancel_delete');
1041

    
1042
    // Create a user.
1043
    $account = $this->drupalCreateUser(array('cancel account', 'post comments', 'skip comment approval'));
1044
    $this->drupalLogin($account);
1045
    // Load real user object.
1046
    $account = user_load($account->uid, TRUE);
1047

    
1048
    // Create a simple node.
1049
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
1050

    
1051
    // Create comment.
1052
    $langcode = LANGUAGE_NONE;
1053
    $edit = array();
1054
    $edit['subject'] = $this->randomName(8);
1055
    $edit['comment_body[' . $langcode . '][0][value]'] = $this->randomName(16);
1056

    
1057
    $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview'));
1058
    $this->drupalPost(NULL, array(), t('Save'));
1059
    $this->assertText(t('Your comment has been posted.'));
1060
    $comments = comment_load_multiple(array(), array('subject' => $edit['subject']));
1061
    $comment = reset($comments);
1062
    $this->assertTrue($comment->cid, 'Comment found.');
1063

    
1064
    // Create a node with two revisions, the initial one belonging to the
1065
    // cancelling user.
1066
    $revision_node = $this->drupalCreateNode(array('uid' => $account->uid));
1067
    $revision = $revision_node->vid;
1068
    $settings = get_object_vars($revision_node);
1069
    $settings['revision'] = 1;
1070
    $settings['uid'] = 1; // Set new/current revision to someone else.
1071
    $revision_node = $this->drupalCreateNode($settings);
1072

    
1073
    // Attempt to cancel account.
1074
    $this->drupalGet('user/' . $account->uid . '/edit');
1075
    $this->drupalPost(NULL, NULL, t('Cancel account'));
1076
    $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
1077
    $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.');
1078

    
1079
    // Confirm account cancellation.
1080
    $timestamp = time();
1081
    $this->drupalPost(NULL, NULL, t('Cancel account'));
1082
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
1083

    
1084
    // Confirm account cancellation request.
1085
    $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid));
1086
    $this->assertFalse(user_load($account->uid, TRUE), 'User is not found in the database.');
1087

    
1088
    // Confirm that user's content has been deleted.
1089
    $this->assertFalse(node_load($node->nid, NULL, TRUE), 'Node of the user has been deleted.');
1090
    $this->assertFalse(node_load($node->nid, $revision, TRUE), 'Node revision of the user has been deleted.');
1091
    $this->assertTrue(node_load($revision_node->nid, NULL, TRUE), "Current revision of the user's node was not deleted.");
1092
    $this->assertFalse(comment_load($comment->cid), 'Comment of the user has been deleted.');
1093

    
1094
    // Confirm that the confirmation message made it through to the end user.
1095
    $this->assertRaw(t('%name has been deleted.', array('%name' => $account->name)), "Confirmation message displayed to user.");
1096
  }
1097

    
1098
  /**
1099
   * Create an administrative user and delete another user.
1100
   */
1101
  function testUserCancelByAdmin() {
1102
    variable_set('user_cancel_method', 'user_cancel_reassign');
1103

    
1104
    // Create a regular user.
1105
    $account = $this->drupalCreateUser(array());
1106

    
1107
    // Create administrative user.
1108
    $admin_user = $this->drupalCreateUser(array('administer users'));
1109
    $this->drupalLogin($admin_user);
1110

    
1111
    // Delete regular user.
1112
    $this->drupalGet('user/' . $account->uid . '/edit');
1113
    $this->drupalPost(NULL, NULL, t('Cancel account'));
1114
    $this->assertRaw(t('Are you sure you want to cancel the account %name?', array('%name' => $account->name)), 'Confirmation form to cancel account displayed.');
1115
    $this->assertText(t('Select the method to cancel the account above.'), 'Allows to select account cancellation method.');
1116

    
1117
    // Confirm deletion.
1118
    $this->drupalPost(NULL, NULL, t('Cancel account'));
1119
    $this->assertRaw(t('%name has been deleted.', array('%name' => $account->name)), 'User deleted.');
1120
    $this->assertFalse(user_load($account->uid), 'User is not found in the database.');
1121
  }
1122

    
1123
  /**
1124
   * Create an administrative user and mass-delete other users.
1125
   */
1126
  function testMassUserCancelByAdmin() {
1127
    variable_set('user_cancel_method', 'user_cancel_reassign');
1128
    // Enable account cancellation notification.
1129
    variable_set('user_mail_status_canceled_notify', TRUE);
1130

    
1131
    // Create administrative user.
1132
    $admin_user = $this->drupalCreateUser(array('administer users'));
1133
    $this->drupalLogin($admin_user);
1134

    
1135
    // Create some users.
1136
    $users = array();
1137
    for ($i = 0; $i < 3; $i++) {
1138
      $account = $this->drupalCreateUser(array());
1139
      $users[$account->uid] = $account;
1140
    }
1141

    
1142
    // Cancel user accounts, including own one.
1143
    $edit = array();
1144
    $edit['operation'] = 'cancel';
1145
    foreach ($users as $uid => $account) {
1146
      $edit['accounts[' . $uid . ']'] = TRUE;
1147
    }
1148
    $edit['accounts[' . $admin_user->uid . ']'] = TRUE;
1149
    // Also try to cancel uid 1.
1150
    $edit['accounts[1]'] = TRUE;
1151
    $this->drupalPost('admin/people', $edit, t('Update'));
1152
    $this->assertText(t('Are you sure you want to cancel these user accounts?'), 'Confirmation form to cancel accounts displayed.');
1153
    $this->assertText(t('When cancelling these accounts'), 'Allows to select account cancellation method.');
1154
    $this->assertText(t('Require e-mail confirmation to cancel account.'), 'Allows to send confirmation mail.');
1155
    $this->assertText(t('Notify user when account is canceled.'), 'Allows to send notification mail.');
1156

    
1157
    // Confirm deletion.
1158
    $this->drupalPost(NULL, NULL, t('Cancel accounts'));
1159
    $status = TRUE;
1160
    foreach ($users as $account) {
1161
      $status = $status && (strpos($this->content, t('%name has been deleted.', array('%name' => $account->name))) !== FALSE);
1162
      $status = $status && !user_load($account->uid, TRUE);
1163
    }
1164
    $this->assertTrue($status, 'Users deleted and not found in the database.');
1165

    
1166
    // Ensure that admin account was not cancelled.
1167
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
1168
    $admin_user = user_load($admin_user->uid);
1169
    $this->assertTrue($admin_user->status == 1, 'Administrative user is found in the database and enabled.');
1170

    
1171
    // Verify that uid 1's account was not cancelled.
1172
    $user1 = user_load(1, TRUE);
1173
    $this->assertEqual($user1->status, 1, 'User #1 still exists and is not blocked.');
1174
  }
1175
}
1176

    
1177
class UserPictureTestCase extends DrupalWebTestCase {
1178
  protected $user;
1179
  protected $_directory_test;
1180

    
1181
  public static function getInfo() {
1182
    return array(
1183
      'name' => 'Upload user picture',
1184
      'description' => 'Assure that dimension check, extension check and image scaling work as designed.',
1185
      'group' => 'User'
1186
    );
1187
  }
1188

    
1189
  function setUp() {
1190
    parent::setUp();
1191
    // Enable user pictures.
1192
    variable_set('user_pictures', 1);
1193

    
1194
    $this->user = $this->drupalCreateUser();
1195

    
1196
    // Test if directories specified in settings exist in filesystem.
1197
    $file_dir = 'public://';
1198
    $file_check = file_prepare_directory($file_dir, FILE_CREATE_DIRECTORY);
1199
    // TODO: Test public and private methods?
1200

    
1201
    $picture_dir = variable_get('user_picture_path', 'pictures');
1202
    $picture_path = $file_dir . $picture_dir;
1203

    
1204
    $pic_check = file_prepare_directory($picture_path, FILE_CREATE_DIRECTORY);
1205
    $this->_directory_test = is_writable($picture_path);
1206
    $this->assertTrue($this->_directory_test, "The directory $picture_path doesn't exist or is not writable. Further tests won't be made.");
1207
  }
1208

    
1209
  function testNoPicture() {
1210
    $this->drupalLogin($this->user);
1211

    
1212
    // Try to upload a file that is not an image for the user picture.
1213
    $not_an_image = current($this->drupalGetTestFiles('html'));
1214
    $this->saveUserPicture($not_an_image);
1215
    $this->assertRaw(t('Only JPEG, PNG and GIF images are allowed.'), 'Non-image files are not accepted.');
1216
  }
1217

    
1218
  /**
1219
   * Do the test:
1220
   *  GD Toolkit is installed
1221
   *  Picture has invalid dimension
1222
   *
1223
   * results: The image should be uploaded because ImageGDToolkit resizes the picture
1224
   */
1225
  function testWithGDinvalidDimension() {
1226
    if ($this->_directory_test && image_get_toolkit()) {
1227
      $this->drupalLogin($this->user);
1228

    
1229
      $image = current($this->drupalGetTestFiles('image'));
1230
      $info = image_get_info($image->uri);
1231

    
1232
      // Set new variables: invalid dimensions, valid filesize (0 = no limit).
1233
      $test_dim = ($info['width'] - 10) . 'x' . ($info['height'] - 10);
1234
      variable_set('user_picture_dimensions', $test_dim);
1235
      variable_set('user_picture_file_size', 0);
1236

    
1237
      $pic_path = $this->saveUserPicture($image);
1238
      // Check that the image was resized and is being displayed on the
1239
      // user's profile page.
1240
      $text = t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', array('%dimensions' => $test_dim));
1241
      $this->assertRaw($text, 'Image was resized.');
1242
      $alt = t("@user's picture", array('@user' => format_username($this->user)));
1243
      $style = variable_get('user_picture_style', '');
1244
      $this->assertRaw(check_plain(image_style_url($style, $pic_path)), "Image is displayed in user's edit page");
1245

    
1246
      // Check if file is located in proper directory.
1247
      $this->assertTrue(is_file($pic_path), "File is located in proper directory");
1248
    }
1249
  }
1250

    
1251
  /**
1252
   * Do the test:
1253
   *  GD Toolkit is installed
1254
   *  Picture has invalid size
1255
   *
1256
   * results: The image should be uploaded because ImageGDToolkit resizes the picture
1257
   */
1258
  function testWithGDinvalidSize() {
1259
    if ($this->_directory_test && image_get_toolkit()) {
1260
      $this->drupalLogin($this->user);
1261

    
1262
      // Images are sorted first by size then by name. We need an image
1263
      // bigger than 1 KB so we'll grab the last one.
1264
      $files = $this->drupalGetTestFiles('image');
1265
      $image = end($files);
1266
      $info = image_get_info($image->uri);
1267

    
1268
      // Set new variables: valid dimensions, invalid filesize.
1269
      $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
1270
      $test_size = 1;
1271
      variable_set('user_picture_dimensions', $test_dim);
1272
      variable_set('user_picture_file_size', $test_size);
1273

    
1274
      $pic_path = $this->saveUserPicture($image);
1275

    
1276
      // Test that the upload failed and that the correct reason was cited.
1277
      $text = t('The specified file %filename could not be uploaded.', array('%filename' => $image->filename));
1278
      $this->assertRaw($text, 'Upload failed.');
1279
      $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)));
1280
      $this->assertRaw($text, 'File size cited as reason for failure.');
1281

    
1282
      // Check if file is not uploaded.
1283
      $this->assertFalse(is_file($pic_path), 'File was not uploaded.');
1284
    }
1285
  }
1286

    
1287
  /**
1288
   * Do the test:
1289
   *  GD Toolkit is not installed
1290
   *  Picture has invalid size
1291
   *
1292
   * results: The image shouldn't be uploaded
1293
   */
1294
  function testWithoutGDinvalidDimension() {
1295
    if ($this->_directory_test && !image_get_toolkit()) {
1296
      $this->drupalLogin($this->user);
1297

    
1298
      $image = current($this->drupalGetTestFiles('image'));
1299
      $info = image_get_info($image->uri);
1300

    
1301
      // Set new variables: invalid dimensions, valid filesize (0 = no limit).
1302
      $test_dim = ($info['width'] - 10) . 'x' . ($info['height'] - 10);
1303
      variable_set('user_picture_dimensions', $test_dim);
1304
      variable_set('user_picture_file_size', 0);
1305

    
1306
      $pic_path = $this->saveUserPicture($image);
1307

    
1308
      // Test that the upload failed and that the correct reason was cited.
1309
      $text = t('The specified file %filename could not be uploaded.', array('%filename' => $image->filename));
1310
      $this->assertRaw($text, 'Upload failed.');
1311
      $text = t('The image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => $test_dim));
1312
      $this->assertRaw($text, 'Checking response on invalid image (dimensions).');
1313

    
1314
      // Check if file is not uploaded.
1315
      $this->assertFalse(is_file($pic_path), 'File was not uploaded.');
1316
    }
1317
  }
1318

    
1319
  /**
1320
   * Do the test:
1321
   *  GD Toolkit is not installed
1322
   *  Picture has invalid size
1323
   *
1324
   * results: The image shouldn't be uploaded
1325
   */
1326
  function testWithoutGDinvalidSize() {
1327
    if ($this->_directory_test && !image_get_toolkit()) {
1328
      $this->drupalLogin($this->user);
1329

    
1330
      $image = current($this->drupalGetTestFiles('image'));
1331
      $info = image_get_info($image->uri);
1332

    
1333
      // Set new variables: valid dimensions, invalid filesize.
1334
      $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
1335
      $test_size = 1;
1336
      variable_set('user_picture_dimensions', $test_dim);
1337
      variable_set('user_picture_file_size', $test_size);
1338

    
1339
      $pic_path = $this->saveUserPicture($image);
1340

    
1341
      // Test that the upload failed and that the correct reason was cited.
1342
      $text = t('The specified file %filename could not be uploaded.', array('%filename' => $image->filename));
1343
      $this->assertRaw($text, 'Upload failed.');
1344
      $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)));
1345
      $this->assertRaw($text, 'File size cited as reason for failure.');
1346

    
1347
      // Check if file is not uploaded.
1348
      $this->assertFalse(is_file($pic_path), 'File was not uploaded.');
1349
    }
1350
  }
1351

    
1352
  /**
1353
   * Do the test:
1354
   *  Picture is valid (proper size and dimension)
1355
   *
1356
   * results: The image should be uploaded
1357
   */
1358
  function testPictureIsValid() {
1359
    if ($this->_directory_test) {
1360
      $this->drupalLogin($this->user);
1361

    
1362
      $image = current($this->drupalGetTestFiles('image'));
1363
      $info = image_get_info($image->uri);
1364

    
1365
      // Set new variables: valid dimensions, valid filesize (0 = no limit).
1366
      $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
1367
      variable_set('user_picture_dimensions', $test_dim);
1368
      variable_set('user_picture_file_size', 0);
1369

    
1370
      $pic_path = $this->saveUserPicture($image);
1371

    
1372
      // Check if image is displayed in user's profile page.
1373
      $this->drupalGet('user');
1374
      $this->assertRaw(file_uri_target($pic_path), "Image is displayed in user's profile page");
1375

    
1376
      // Check if file is located in proper directory.
1377
      $this->assertTrue(is_file($pic_path), 'File is located in proper directory');
1378

    
1379
      // Set new picture dimensions.
1380
      $test_dim = ($info['width'] + 5) . 'x' . ($info['height'] + 5);
1381
      variable_set('user_picture_dimensions', $test_dim);
1382

    
1383
      $pic_path2 = $this->saveUserPicture($image);
1384
      $this->assertNotEqual($pic_path, $pic_path2, 'Filename of second picture is different.');
1385

    
1386
      // Check if user picture has a valid file ID after saving the user.
1387
      $account = user_load($this->user->uid, TRUE);
1388
      $this->assertTrue(is_object($account->picture), 'User picture object is valid after user load.');
1389
      $this->assertNotNull($account->picture->fid, 'User picture object has a FID after user load.');
1390
      $this->assertTrue(is_file($account->picture->uri), 'File is located in proper directory after user load.');
1391
      user_save($account);
1392
      // Verify that the user save does not destroy the user picture object.
1393
      $this->assertTrue(is_object($account->picture), 'User picture object is valid after user save.');
1394
      $this->assertNotNull($account->picture->fid, 'User picture object has a FID after user save.');
1395
      $this->assertTrue(is_file($account->picture->uri), 'File is located in proper directory after user save.');
1396
    }
1397
  }
1398

    
1399
  /**
1400
   * Test HTTP schema working with user pictures.
1401
   */
1402
  function testExternalPicture() {
1403
    $this->drupalLogin($this->user);
1404
    // Set the default picture to an URI with a HTTP schema.
1405
    $images = $this->drupalGetTestFiles('image');
1406
    $image = $images[0];
1407
    $pic_path = file_create_url($image->uri);
1408
    variable_set('user_picture_default', $pic_path);
1409

    
1410
    // Check if image is displayed in user's profile page.
1411
    $this->drupalGet('user');
1412

    
1413
    // Get the user picture image via xpath.
1414
    $elements = $this->xpath('//div[@class="user-picture"]/img');
1415
    $this->assertEqual(count($elements), 1, "There is exactly one user picture on the user's profile page");
1416
    $this->assertEqual($pic_path, (string) $elements[0]['src'], "User picture source is correct.");
1417
  }
1418

    
1419
  /**
1420
   * Tests deletion of user pictures.
1421
   */
1422
  function testDeletePicture() {
1423
    $this->drupalLogin($this->user);
1424

    
1425
    $image = current($this->drupalGetTestFiles('image'));
1426
    $info = image_get_info($image->uri);
1427

    
1428
    // Set new variables: valid dimensions, valid filesize (0 = no limit).
1429
    $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
1430
    variable_set('user_picture_dimensions', $test_dim);
1431
    variable_set('user_picture_file_size', 0);
1432

    
1433
    // Save a new picture.
1434
    $edit = array('files[picture_upload]' => drupal_realpath($image->uri));
1435
    $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save'));
1436

    
1437
    // Load actual user data from database.
1438
    $account = user_load($this->user->uid, TRUE);
1439
    $pic_path = isset($account->picture) ? $account->picture->uri : NULL;
1440

    
1441
    // Check if image is displayed in user's profile page.
1442
    $this->drupalGet('user');
1443
    $this->assertRaw(file_uri_target($pic_path), "Image is displayed in user's profile page");
1444

    
1445
    // Check if file is located in proper directory.
1446
    $this->assertTrue(is_file($pic_path), 'File is located in proper directory');
1447

    
1448
    $edit = array('picture_delete' => 1);
1449
    $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save'));
1450

    
1451
    // Load actual user data from database.
1452
    $account1 = user_load($this->user->uid, TRUE);
1453
    $this->assertNull($account1->picture, 'User object has no picture');
1454

    
1455
    $file = file_load($account->picture->fid);
1456
    $this->assertFalse($file, 'File is removed from database');
1457

    
1458
    // Clear out PHP's file stat cache so we see the current value.
1459
    clearstatcache();
1460
    $this->assertFalse(is_file($pic_path), 'File is removed from file system');
1461
  }
1462

    
1463
  function saveUserPicture($image) {
1464
    $edit = array('files[picture_upload]' => drupal_realpath($image->uri));
1465
    $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save'));
1466

    
1467
    // Load actual user data from database.
1468
    $account = user_load($this->user->uid, TRUE);
1469
    return isset($account->picture) ? $account->picture->uri : NULL;
1470
  }
1471

    
1472
  /**
1473
   * Tests the admin form validates user picture settings.
1474
   */
1475
  function testUserPictureAdminFormValidation() {
1476
    $this->drupalLogin($this->drupalCreateUser(array('administer users')));
1477

    
1478
    // The default values are valid.
1479
    $this->drupalPost('admin/config/people/accounts', array(), t('Save configuration'));
1480
    $this->assertText(t('The configuration options have been saved.'), 'The default values are valid.');
1481

    
1482
    // The form does not save with an invalid file size.
1483
    $edit = array(
1484
      'user_picture_file_size' => $this->randomName(),
1485
    );
1486
    $this->drupalPost('admin/config/people/accounts', $edit, t('Save configuration'));
1487
    $this->assertNoText(t('The configuration options have been saved.'), 'The form does not save with an invalid file size.');
1488
  }
1489
}
1490

    
1491

    
1492
class UserPermissionsTestCase extends DrupalWebTestCase {
1493
  protected $admin_user;
1494
  protected $rid;
1495

    
1496
  public static function getInfo() {
1497
    return array(
1498
      'name' => 'Role permissions',
1499
      'description' => 'Verify that role permissions can be added and removed via the permissions page.',
1500
      'group' => 'User'
1501
    );
1502
  }
1503

    
1504
  function setUp() {
1505
    parent::setUp();
1506

    
1507
    $this->admin_user = $this->drupalCreateUser(array('administer permissions', 'access user profiles', 'administer site configuration', 'administer modules', 'administer users'));
1508

    
1509
    // Find the new role ID - it must be the maximum.
1510
    $all_rids = array_keys($this->admin_user->roles);
1511
    sort($all_rids);
1512
    $this->rid = array_pop($all_rids);
1513
  }
1514

    
1515
  /**
1516
   * Change user permissions and check user_access().
1517
   */
1518
  function testUserPermissionChanges() {
1519
    $this->drupalLogin($this->admin_user);
1520
    $rid = $this->rid;
1521
    $account = $this->admin_user;
1522

    
1523
    // Add a permission.
1524
    $this->assertFalse(user_access('administer nodes', $account), 'User does not have "administer nodes" permission.');
1525
    $edit = array();
1526
    $edit[$rid . '[administer nodes]'] = TRUE;
1527
    $this->drupalPost('admin/people/permissions', $edit, t('Save permissions'));
1528
    $this->assertText(t('The changes have been saved.'), 'Successful save message displayed.');
1529
    drupal_static_reset('user_access');
1530
    drupal_static_reset('user_role_permissions');
1531
    $this->assertTrue(user_access('administer nodes', $account), 'User now has "administer nodes" permission.');
1532

    
1533
    // Remove a permission.
1534
    $this->assertTrue(user_access('access user profiles', $account), 'User has "access user profiles" permission.');
1535
    $edit = array();
1536
    $edit[$rid . '[access user profiles]'] = FALSE;
1537
    $this->drupalPost('admin/people/permissions', $edit, t('Save permissions'));
1538
    $this->assertText(t('The changes have been saved.'), 'Successful save message displayed.');
1539
    drupal_static_reset('user_access');
1540
    drupal_static_reset('user_role_permissions');
1541
    $this->assertFalse(user_access('access user profiles', $account), 'User no longer has "access user profiles" permission.');
1542
  }
1543

    
1544
  /**
1545
   * Test assigning of permissions for the administrator role.
1546
   */
1547
  function testAdministratorRole() {
1548
    $this->drupalLogin($this->admin_user);
1549
    $this->drupalGet('admin/config/people/accounts');
1550

    
1551
    // Set the user's role to be the administrator role.
1552
    $edit = array();
1553
    $edit['user_admin_role'] = $this->rid;
1554
    $this->drupalPost('admin/config/people/accounts', $edit, t('Save configuration'));
1555

    
1556
    // Enable aggregator module and ensure the 'administer news feeds'
1557
    // permission is assigned by default.
1558
    $edit = array();
1559
    $edit['modules[Core][aggregator][enable]'] = TRUE;
1560
    $this->drupalPost('admin/modules', $edit, t('Save configuration'));
1561
    $this->assertTrue(user_access('administer news feeds', $this->admin_user), 'The permission was automatically assigned to the administrator role');
1562
  }
1563

    
1564
  /**
1565
   * Verify proper permission changes by user_role_change_permissions().
1566
   */
1567
  function testUserRoleChangePermissions() {
1568
    $rid = $this->rid;
1569
    $account = $this->admin_user;
1570

    
1571
    // Verify current permissions.
1572
    $this->assertFalse(user_access('administer nodes', $account), 'User does not have "administer nodes" permission.');
1573
    $this->assertTrue(user_access('access user profiles', $account), 'User has "access user profiles" permission.');
1574
    $this->assertTrue(user_access('administer site configuration', $account), 'User has "administer site configuration" permission.');
1575

    
1576
    // Change permissions.
1577
    $permissions = array(
1578
      'administer nodes' => 1,
1579
      'access user profiles' => 0,
1580
    );
1581
    user_role_change_permissions($rid, $permissions);
1582

    
1583
    // Verify proper permission changes.
1584
    $this->assertTrue(user_access('administer nodes', $account), 'User now has "administer nodes" permission.');
1585
    $this->assertFalse(user_access('access user profiles', $account), 'User no longer has "access user profiles" permission.');
1586
    $this->assertTrue(user_access('administer site configuration', $account), 'User still has "administer site configuration" permission.');
1587
  }
1588
}
1589

    
1590
class UserAdminTestCase extends DrupalWebTestCase {
1591
  public static function getInfo() {
1592
    return array(
1593
      'name' => 'User administration',
1594
      'description' => 'Test user administration page functionality.',
1595
      'group' => 'User'
1596
    );
1597
  }
1598

    
1599
  /**
1600
   * Registers a user and deletes it.
1601
   */
1602
  function testUserAdmin() {
1603

    
1604
    $user_a = $this->drupalCreateUser(array());
1605
    $user_b = $this->drupalCreateUser(array('administer taxonomy'));
1606
    $user_c = $this->drupalCreateUser(array('administer taxonomy'));
1607

    
1608
    // Create admin user to delete registered user.
1609
    $admin_user = $this->drupalCreateUser(array('administer users'));
1610
    $this->drupalLogin($admin_user);
1611
    $this->drupalGet('admin/people');
1612
    $this->assertText($user_a->name, 'Found user A on admin users page');
1613
    $this->assertText($user_b->name, 'Found user B on admin users page');
1614
    $this->assertText($user_c->name, 'Found user C on admin users page');
1615
    $this->assertText($admin_user->name, 'Found Admin user on admin users page');
1616

    
1617
    // Test for existence of edit link in table.
1618
    $link = l(t('edit'), "user/$user_a->uid/edit", array('query' => array('destination' => 'admin/people')));
1619
    $this->assertRaw($link, 'Found user A edit link on admin users page');
1620

    
1621
    // Filter the users by permission 'administer taxonomy'.
1622
    $edit = array();
1623
    $edit['permission'] = 'administer taxonomy';
1624
    $this->drupalPost('admin/people', $edit, t('Filter'));
1625

    
1626
    // Check if the correct users show up.
1627
    $this->assertNoText($user_a->name, 'User A not on filtered by perm admin users page');
1628
    $this->assertText($user_b->name, 'Found user B on filtered by perm admin users page');
1629
    $this->assertText($user_c->name, 'Found user C on filtered by perm admin users page');
1630

    
1631
    // Filter the users by role. Grab the system-generated role name for User C.
1632
    $edit['role'] = max(array_flip($user_c->roles));
1633
    $this->drupalPost('admin/people', $edit, t('Refine'));
1634

    
1635
    // Check if the correct users show up when filtered by role.
1636
    $this->assertNoText($user_a->name, 'User A not on filtered by role on admin users page');
1637
    $this->assertNoText($user_b->name, 'User B not on filtered by role on admin users page');
1638
    $this->assertText($user_c->name, 'User C on filtered by role on admin users page');
1639

    
1640
    // Test blocking of a user.
1641
    $account = user_load($user_c->uid);
1642
    $this->assertEqual($account->status, 1, 'User C not blocked');
1643
    $edit = array();
1644
    $edit['operation'] = 'block';
1645
    $edit['accounts[' . $account->uid . ']'] = TRUE;
1646
    $this->drupalPost('admin/people', $edit, t('Update'));
1647
    $account = user_load($user_c->uid, TRUE);
1648
    $this->assertEqual($account->status, 0, 'User C blocked');
1649

    
1650
    // Test unblocking of a user from /admin/people page and sending of activation mail
1651
    $editunblock = array();
1652
    $editunblock['operation'] = 'unblock';
1653
    $editunblock['accounts[' . $account->uid . ']'] = TRUE;
1654
    $this->drupalPost('admin/people', $editunblock, t('Update'));
1655
    $account = user_load($user_c->uid, TRUE);
1656
    $this->assertEqual($account->status, 1, 'User C unblocked');
1657
    $this->assertMail("to", $account->mail, "Activation mail sent to user C");
1658

    
1659
    // Test blocking and unblocking another user from /user/[uid]/edit form and sending of activation mail
1660
    $user_d = $this->drupalCreateUser(array());
1661
    $account1 = user_load($user_d->uid, TRUE);
1662
    $this->drupalPost('user/' . $account1->uid . '/edit', array('status' => 0), t('Save'));
1663
    $account1 = user_load($user_d->uid, TRUE);
1664
    $this->assertEqual($account1->status, 0, 'User D blocked');
1665
    $this->drupalPost('user/' . $account1->uid . '/edit', array('status' => TRUE), t('Save'));
1666
    $account1 = user_load($user_d->uid, TRUE);
1667
    $this->assertEqual($account1->status, 1, 'User D unblocked');
1668
    $this->assertMail("to", $account1->mail, "Activation mail sent to user D");
1669
  }
1670
}
1671

    
1672
/**
1673
 * Tests for user-configurable time zones.
1674
 */
1675
class UserTimeZoneFunctionalTest extends DrupalWebTestCase {
1676
  public static function getInfo() {
1677
    return array(
1678
      'name' => 'User time zones',
1679
      'description' => 'Set a user time zone and verify that dates are displayed in local time.',
1680
      'group' => 'User',
1681
    );
1682
  }
1683

    
1684
  /**
1685
   * Tests the display of dates and time when user-configurable time zones are set.
1686
   */
1687
  function testUserTimeZone() {
1688
    // Setup date/time settings for Los Angeles time.
1689
    variable_set('date_default_timezone', 'America/Los_Angeles');
1690
    variable_set('configurable_timezones', 1);
1691

    
1692
    // Override the 'medium' date format, which is the default for node
1693
    // creation time. Since we are testing time zones with Daylight Saving
1694
    // Time, and need to future proof against changes to the zoneinfo database,
1695
    // we choose the 'I' format placeholder instead of a human-readable zone
1696
    // name. With 'I', a 1 means the date is in DST, and 0 if not.
1697
    variable_set('date_format_medium', 'Y-m-d H:i I');
1698

    
1699
    // Create a user account and login.
1700
    $web_user = $this->drupalCreateUser();
1701
    $this->drupalLogin($web_user);
1702

    
1703
    // Create some nodes with different authored-on dates.
1704
    // Two dates in PST (winter time):
1705
    $date1 = '2007-03-09 21:00:00 -0800';
1706
    $date2 = '2007-03-11 01:00:00 -0800';
1707
    // One date in PDT (summer time):
1708
    $date3 = '2007-03-20 21:00:00 -0700';
1709
    $node1 = $this->drupalCreateNode(array('created' => strtotime($date1), 'type' => 'article'));
1710
    $node2 = $this->drupalCreateNode(array('created' => strtotime($date2), 'type' => 'article'));
1711
    $node3 = $this->drupalCreateNode(array('created' => strtotime($date3), 'type' => 'article'));
1712

    
1713
    // Confirm date format and time zone.
1714
    $this->drupalGet("node/$node1->nid");
1715
    $this->assertText('2007-03-09 21:00 0', 'Date should be PST.');
1716
    $this->drupalGet("node/$node2->nid");
1717
    $this->assertText('2007-03-11 01:00 0', 'Date should be PST.');
1718
    $this->drupalGet("node/$node3->nid");
1719
    $this->assertText('2007-03-20 21:00 1', 'Date should be PDT.');
1720

    
1721
    // Change user time zone to Santiago time.
1722
    $edit = array();
1723
    $edit['mail'] = $web_user->mail;
1724
    $edit['timezone'] = 'America/Santiago';
1725
    $this->drupalPost("user/$web_user->uid/edit", $edit, t('Save'));
1726
    $this->assertText(t('The changes have been saved.'), 'Time zone changed to Santiago time.');
1727

    
1728
    // Confirm date format and time zone.
1729
    $this->drupalGet("node/$node1->nid");
1730
    $this->assertText('2007-03-10 02:00 1', 'Date should be Chile summer time; five hours ahead of PST.');
1731
    $this->drupalGet("node/$node2->nid");
1732
    $this->assertText('2007-03-11 05:00 0', 'Date should be Chile time; four hours ahead of PST');
1733
    $this->drupalGet("node/$node3->nid");
1734
    $this->assertText('2007-03-21 00:00 0', 'Date should be Chile time; three hours ahead of PDT.');
1735
  }
1736
}
1737

    
1738
/**
1739
 * Test user autocompletion.
1740
 */
1741
class UserAutocompleteTestCase extends DrupalWebTestCase {
1742
  public static function getInfo() {
1743
    return array(
1744
      'name' => 'User autocompletion',
1745
      'description' => 'Test user autocompletion functionality.',
1746
      'group' => 'User'
1747
    );
1748
  }
1749

    
1750
  function setUp() {
1751
    parent::setUp();
1752

    
1753
    // Set up two users with different permissions to test access.
1754
    $this->unprivileged_user = $this->drupalCreateUser();
1755
    $this->privileged_user = $this->drupalCreateUser(array('access user profiles'));
1756
  }
1757

    
1758
  /**
1759
   * Tests access to user autocompletion and verify the correct results.
1760
   */
1761
  function testUserAutocomplete() {
1762
    // Check access from unprivileged user, should be denied.
1763
    $this->drupalLogin($this->unprivileged_user);
1764
    $this->drupalGet('user/autocomplete/' . $this->unprivileged_user->name[0]);
1765
    $this->assertResponse(403, 'Autocompletion access denied to user without permission.');
1766

    
1767
    // Check access from privileged user.
1768
    $this->drupalLogout();
1769
    $this->drupalLogin($this->privileged_user);
1770
    $this->drupalGet('user/autocomplete/' . $this->unprivileged_user->name[0]);
1771
    $this->assertResponse(200, 'Autocompletion access allowed.');
1772

    
1773
    // Using first letter of the user's name, make sure the user's full name is in the results.
1774
    $this->assertRaw($this->unprivileged_user->name, 'User name found in autocompletion results.');
1775
  }
1776
}
1777

    
1778

    
1779
/**
1780
 * Tests user links in the secondary menu.
1781
 */
1782
class UserAccountLinksUnitTests extends DrupalWebTestCase {
1783
  public static function getInfo() {
1784
    return array(
1785
      'name' => 'User account links',
1786
      'description' => 'Test user-account links.',
1787
      'group' => 'User'
1788
    );
1789
  }
1790

    
1791
  function setUp() {
1792
    parent::setUp('menu');
1793
  }
1794

    
1795
  /**
1796
   * Tests the secondary menu.
1797
   */
1798
  function testSecondaryMenu() {
1799
    // Create a regular user.
1800
    $user = $this->drupalCreateUser(array());
1801

    
1802
    // Log in and get the homepage.
1803
    $this->drupalLogin($user);
1804
    $this->drupalGet('<front>');
1805

    
1806
    // For a logged-in user, expect the secondary menu to have links for "My
1807
    // account" and "Log out".
1808
    $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array(
1809
      ':menu_id' => 'secondary-menu-links',
1810
      ':href' => 'user',
1811
      ':text' => 'My account',
1812
    ));
1813
    $this->assertEqual(count($link), 1, 'My account link is in secondary menu.');
1814

    
1815
    $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array(
1816
      ':menu_id' => 'secondary-menu-links',
1817
      ':href' => 'user/logout',
1818
      ':text' => 'Log out',
1819
    ));
1820
    $this->assertEqual(count($link), 1, 'Log out link is in secondary menu.');
1821

    
1822
    // Log out and get the homepage.
1823
    $this->drupalLogout();
1824
    $this->drupalGet('<front>');
1825

    
1826
    // For a logged-out user, expect no secondary links.
1827
    $element = $this->xpath('//ul[@id=:menu_id]', array(':menu_id' => 'secondary-menu-links'));
1828
    $this->assertEqual(count($element), 0, 'No secondary-menu for logged-out users.');
1829
  }
1830

    
1831
  /**
1832
   * Tests disabling the 'My account' link.
1833
   */
1834
  function testDisabledAccountLink() {
1835
    // Create an admin user and log in.
1836
    $this->drupalLogin($this->drupalCreateUser(array('access administration pages', 'administer menu')));
1837

    
1838
    // Verify that the 'My account' link is enabled.
1839
    $this->drupalGet('admin/structure/menu/manage/user-menu');
1840
    $label = $this->xpath('//label[contains(.,:text)]/@for', array(':text' => 'Enable My account menu link'));
1841
    $this->assertFieldChecked((string) $label[0], "The 'My account' link is enabled by default.");
1842

    
1843
    // Disable the 'My account' link.
1844
    $input = $this->xpath('//input[@id=:field_id]/@name', array(':field_id' => (string)$label[0]));
1845
    $edit = array(
1846
      (string) $input[0] => FALSE,
1847
    );
1848
    $this->drupalPost('admin/structure/menu/manage/user-menu', $edit, t('Save configuration'));
1849

    
1850
    // Get the homepage.
1851
    $this->drupalGet('<front>');
1852

    
1853
    // Verify that the 'My account' link does not appear when disabled.
1854
    $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array(
1855
      ':menu_id' => 'secondary-menu-links',
1856
      ':href' => 'user',
1857
      ':text' => 'My account',
1858
    ));
1859
    $this->assertEqual(count($link), 0, 'My account link is not in the secondary menu.');
1860
  }
1861

    
1862
}
1863

    
1864
/**
1865
 * Test user blocks.
1866
 */
1867
class UserBlocksUnitTests extends DrupalWebTestCase {
1868
  public static function getInfo() {
1869
    return array(
1870
      'name' => 'User blocks',
1871
      'description' => 'Test user blocks.',
1872
      'group' => 'User'
1873
    );
1874
  }
1875

    
1876
  /**
1877
   * Test the user login block.
1878
   */
1879
  function testUserLoginBlock() {
1880
    // Create a user with some permission that anonymous users lack.
1881
    $user = $this->drupalCreateUser(array('administer permissions'));
1882

    
1883
    // Log in using the block.
1884
    $edit = array();
1885
    $edit['name'] = $user->name;
1886
    $edit['pass'] = $user->pass_raw;
1887
    $this->drupalPost('admin/people/permissions', $edit, t('Log in'));
1888
    $this->assertNoText(t('User login'), 'Logged in.');
1889

    
1890
    // Check that we are still on the same page.
1891
    $this->assertEqual(url('admin/people/permissions', array('absolute' => TRUE)), $this->getUrl(), 'Still on the same page after login for access denied page');
1892

    
1893
    // Now, log out and repeat with a non-403 page.
1894
    $this->drupalLogout();
1895
    $this->drupalPost('filter/tips', $edit, t('Log in'));
1896
    $this->assertNoText(t('User login'), 'Logged in.');
1897
    $this->assertPattern('!<title.*?' . t('Compose tips') . '.*?</title>!', 'Still on the same page after login for allowed page');
1898

    
1899
    // Check that the user login block is not vulnerable to information
1900
    // disclosure to third party sites.
1901
    $this->drupalLogout();
1902
    $this->drupalPost('http://example.com/', $edit, t('Log in'), array('external' => FALSE));
1903
    // Check that we remain on the site after login.
1904
    $this->assertEqual(url('user/' . $user->uid, array('absolute' => TRUE)), $this->getUrl(), 'Redirected to user profile page after login from the frontpage');
1905
  }
1906

    
1907
  /**
1908
   * Test the Who's Online block.
1909
   */
1910
  function testWhosOnlineBlock() {
1911
    // Generate users and make sure there are no current user sessions.
1912
    $user1 = $this->drupalCreateUser(array());
1913
    $user2 = $this->drupalCreateUser(array());
1914
    $user3 = $this->drupalCreateUser(array());
1915
    $this->assertEqual(db_query("SELECT COUNT(*) FROM {sessions}")->fetchField(), 0, 'Sessions table is empty.');
1916

    
1917
    // Insert a user with two sessions.
1918
    $this->insertSession(array('uid' => $user1->uid));
1919
    $this->insertSession(array('uid' => $user1->uid));
1920
    $this->assertEqual(db_query("SELECT COUNT(*) FROM {sessions} WHERE uid = :uid", array(':uid' => $user1->uid))->fetchField(), 2, 'Duplicate user session has been inserted.');
1921

    
1922
    // Insert a user with only one session.
1923
    $this->insertSession(array('uid' => $user2->uid, 'timestamp' => REQUEST_TIME + 1));
1924

    
1925
    // Insert an inactive logged-in user who should not be seen in the block.
1926
    $this->insertSession(array('uid' => $user3->uid, 'timestamp' => (REQUEST_TIME - variable_get('user_block_seconds_online', 900) - 1)));
1927

    
1928
    // Insert two anonymous user sessions.
1929
    $this->insertSession();
1930
    $this->insertSession();
1931

    
1932
    // Test block output.
1933
    $block = user_block_view('online');
1934
    $this->drupalSetContent($block['content']);
1935
    $this->assertRaw(t('2 users'), 'Correct number of online users (2 users).');
1936
    $this->assertText($user1->name, 'Active user 1 found in online list.');
1937
    $this->assertText($user2->name, 'Active user 2 found in online list.');
1938
    $this->assertNoText($user3->name, "Inactive user not found in online list.");
1939
    $this->assertTrue(strpos($this->drupalGetContent(), $user1->name) > strpos($this->drupalGetContent(), $user2->name), 'Online users are ordered correctly.');
1940
  }
1941

    
1942
  /**
1943
   * Insert a user session into the {sessions} table. This function is used
1944
   * since we cannot log in more than one user at the same time in tests.
1945
   */
1946
  private function insertSession(array $fields = array()) {
1947
    $fields += array(
1948
      'uid' => 0,
1949
      'sid' => drupal_hash_base64(uniqid(mt_rand(), TRUE)),
1950
      'timestamp' => REQUEST_TIME,
1951
    );
1952
    db_insert('sessions')
1953
      ->fields($fields)
1954
      ->execute();
1955
    $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.');
1956
  }
1957
}
1958

    
1959
/**
1960
 * Tests saving a user account.
1961
 */
1962
class UserSaveTestCase extends DrupalWebTestCase {
1963

    
1964
  public static function getInfo() {
1965
    return array(
1966
      'name' => 'User save test',
1967
      'description' => 'Test user_save() for arbitrary new uid.',
1968
      'group' => 'User',
1969
    );
1970
  }
1971

    
1972
  /**
1973
   * Test creating a user with arbitrary uid.
1974
   */
1975
  function testUserImport() {
1976
    // User ID must be a number that is not in the database.
1977
    $max_uid = db_query('SELECT MAX(uid) FROM {users}')->fetchField();
1978
    $test_uid = $max_uid + mt_rand(1000, 1000000);
1979
    $test_name = $this->randomName();
1980

    
1981
    // Create the base user, based on drupalCreateUser().
1982
    $user = array(
1983
      'name' => $test_name,
1984
      'uid' => $test_uid,
1985
      'mail' => $test_name . '@example.com',
1986
      'is_new' => TRUE,
1987
      'pass' => user_password(),
1988
      'status' => 1,
1989
    );
1990
    $user_by_return = user_save(drupal_anonymous_user(), $user);
1991
    $this->assertTrue($user_by_return, 'Loading user by return of user_save().');
1992

    
1993
    // Test if created user exists.
1994
    $user_by_uid = user_load($test_uid);
1995
    $this->assertTrue($user_by_uid, 'Loading user by uid.');
1996

    
1997
    $user_by_name = user_load_by_name($test_name);
1998
    $this->assertTrue($user_by_name, 'Loading user by name.');
1999
  }
2000
}
2001

    
2002
/**
2003
 * Test the create user administration page.
2004
 */
2005
class UserCreateTestCase extends DrupalWebTestCase {
2006

    
2007
  public static function getInfo() {
2008
    return array(
2009
      'name' => 'User create',
2010
      'description' => 'Test the create user administration page.',
2011
      'group' => 'User',
2012
    );
2013
  }
2014

    
2015
  /**
2016
   * Create a user through the administration interface and ensure that it
2017
   * displays in the user list.
2018
   */
2019
  protected function testUserAdd() {
2020
    $user = $this->drupalCreateUser(array('administer users'));
2021
    $this->drupalLogin($user);
2022

    
2023
    foreach (array(FALSE, TRUE) as $notify) {
2024
      $edit = array(
2025
        'name' => $this->randomName(),
2026
        'mail' => $this->randomName() . '@example.com',
2027
        'pass[pass1]' => $pass = $this->randomString(),
2028
        'pass[pass2]' => $pass,
2029
        'notify' => $notify,
2030
      );
2031
      $this->drupalPost('admin/people/create', $edit, t('Create new account'));
2032

    
2033
      if ($notify) {
2034
        $this->assertText(t('A welcome message with further instructions has been e-mailed to the new user @name.', array('@name' => $edit['name'])), 'User created');
2035
        $this->assertEqual(count($this->drupalGetMails()), 1, 'Notification e-mail sent');
2036
      }
2037
      else {
2038
        $this->assertText(t('Created a new user account for @name. No e-mail has been sent.', array('@name' => $edit['name'])), 'User created');
2039
        $this->assertEqual(count($this->drupalGetMails()), 0, 'Notification e-mail not sent');
2040
      }
2041

    
2042
      $this->drupalGet('admin/people');
2043
      $this->assertText($edit['name'], 'User found in list of users');
2044
    }
2045

    
2046
    // Test that the password '0' is considered a password.
2047
    $name = $this->randomName();
2048
    $edit = array(
2049
      'name' => $name,
2050
      'mail' => $name . '@example.com',
2051
      'pass[pass1]' => 0,
2052
      'pass[pass2]' => 0,
2053
      'notify' => FALSE,
2054
    );
2055
    $this->drupalPost('admin/people/create', $edit, t('Create new account'));
2056
    $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');
2057
    $this->assertNoText('Password field is required');
2058
  }
2059
}
2060

    
2061
/**
2062
 * Tests editing a user account.
2063
 */
2064
class UserEditTestCase extends DrupalWebTestCase {
2065

    
2066
  public static function getInfo() {
2067
    return array(
2068
      'name' => 'User edit',
2069
      'description' => 'Test user edit page.',
2070
      'group' => 'User',
2071
    );
2072
  }
2073

    
2074
  /**
2075
   * Test user edit page.
2076
   */
2077
  function testUserEdit() {
2078
    // Test user edit functionality with user pictures disabled.
2079
    variable_set('user_pictures', 0);
2080
    $user1 = $this->drupalCreateUser(array('change own username'));
2081
    $user2 = $this->drupalCreateUser(array());
2082
    $this->drupalLogin($user1);
2083

    
2084
    // Test that error message appears when attempting to use a non-unique user name.
2085
    $edit['name'] = $user2->name;
2086
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
2087
    $this->assertRaw(t('The name %name is already taken.', array('%name' => $edit['name'])));
2088

    
2089
    // Repeat the test with user pictures enabled, which modifies the form.
2090
    variable_set('user_pictures', 1);
2091
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
2092
    $this->assertRaw(t('The name %name is already taken.', array('%name' => $edit['name'])));
2093

    
2094
    // Check that filling out a single password field does not validate.
2095
    $edit = array();
2096
    $edit['pass[pass1]'] = '';
2097
    $edit['pass[pass2]'] = $this->randomName();
2098
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
2099
    $this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.');
2100

    
2101
    $edit['pass[pass1]'] = $this->randomName();
2102
    $edit['pass[pass2]'] = '';
2103
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
2104
    $this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.');
2105

    
2106
    // Test that the error message appears when attempting to change the mail or
2107
    // pass without the current password.
2108
    $edit = array();
2109
    $edit['mail'] = $this->randomName() . '@new.example.com';
2110
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
2111
    $this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => t('E-mail address'))));
2112

    
2113
    $edit['current_pass'] = $user1->pass_raw;
2114
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
2115
    $this->assertRaw(t("The changes have been saved."));
2116

    
2117
    // Test that the user must enter current password before changing passwords.
2118
    $edit = array();
2119
    $edit['pass[pass1]'] = $new_pass = $this->randomName();
2120
    $edit['pass[pass2]'] = $new_pass;
2121
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
2122
    $this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => t('Password'))));
2123

    
2124
    // Try again with the current password.
2125
    $edit['current_pass'] = $user1->pass_raw;
2126
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
2127
    $this->assertRaw(t("The changes have been saved."));
2128

    
2129
    // Make sure the user can log in with their new password.
2130
    $this->drupalLogout();
2131
    $user1->pass_raw = $new_pass;
2132
    $this->drupalLogin($user1);
2133
    $this->drupalLogout();
2134
  }
2135

    
2136
  /**
2137
   * Tests setting the password to "0".
2138
   */
2139
  public function testUserWith0Password() {
2140
    $admin = $this->drupalCreateUser(array('administer users'));
2141
    $this->drupalLogin($admin);
2142
    // Create a regular user.
2143
    $user1 = $this->drupalCreateUser(array());
2144

    
2145
    $edit = array('pass[pass1]' => '0', 'pass[pass2]' => '0');
2146
    $this->drupalPost("user/" . $user1->uid . "/edit", $edit, t('Save'));
2147
    $this->assertRaw(t("The changes have been saved."));
2148

    
2149
    $this->drupalLogout();
2150
    $user1->pass_raw = '0';
2151
    $this->drupalLogin($user1);
2152
    $this->drupalLogout();
2153
  }
2154
}
2155

    
2156
/**
2157
 * Tests editing a user account with and without a form rebuild.
2158
 */
2159
class UserEditRebuildTestCase extends DrupalWebTestCase {
2160

    
2161
  public static function getInfo() {
2162
    return array(
2163
      'name' => 'User edit with form rebuild',
2164
      'description' => 'Test user edit page when a form rebuild is triggered.',
2165
      'group' => 'User',
2166
    );
2167
  }
2168

    
2169
  function setUp() {
2170
    parent::setUp('user_form_test');
2171
  }
2172

    
2173
  /**
2174
   * Test user edit page when the form is set to rebuild.
2175
   */
2176
  function testUserEditFormRebuild() {
2177
    $user1 = $this->drupalCreateUser(array('change own username'));
2178
    $this->drupalLogin($user1);
2179

    
2180
    $roles = array_keys($user1->roles);
2181
    // Save the user form twice.
2182
    $edit = array();
2183
    $edit['current_pass'] = $user1->pass_raw;
2184
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
2185
    $this->assertRaw(t("The changes have been saved."));
2186
    $this->drupalPost(NULL, $edit, t('Save'));
2187
    $this->assertRaw(t("The changes have been saved."));
2188
    $saved_user1 = entity_load_unchanged('user', $user1->uid);
2189
    $this->assertEqual(count($roles), count($saved_user1->roles), 'Count of user roles in database matches original count.');
2190
    $diff = array_diff(array_keys($saved_user1->roles), $roles);
2191
    $this->assertTrue(empty($diff), format_string('User roles in database match original: @roles', array('@roles' => implode(', ', $saved_user1->roles))));
2192
    // Set variable that causes the form to be rebuilt in user_form_test.module.
2193
    variable_set('user_form_test_user_profile_form_rebuild', TRUE);
2194
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
2195
    $this->assertRaw(t("The changes have been saved."));
2196
    $this->drupalPost(NULL, $edit, t('Save'));
2197
    $this->assertRaw(t("The changes have been saved."));
2198
    $saved_user1 = entity_load_unchanged('user', $user1->uid);
2199
    $this->assertEqual(count($roles), count($saved_user1->roles), 'Count of user roles in database matches original count.');
2200
    $diff = array_diff(array_keys($saved_user1->roles), $roles);
2201
    $this->assertTrue(empty($diff), format_string('User roles in database match original: @roles', array('@roles' => implode(', ', $saved_user1->roles))));
2202
  }
2203
}
2204

    
2205
/**
2206
 * Test case for user signatures.
2207
 */
2208
class UserSignatureTestCase extends DrupalWebTestCase {
2209
  public static function getInfo() {
2210
    return array(
2211
      'name' => 'User signatures',
2212
      'description' => 'Test user signatures.',
2213
      'group' => 'User',
2214
    );
2215
  }
2216

    
2217
  function setUp() {
2218
    parent::setUp('comment');
2219

    
2220
    // Enable user signatures.
2221
    variable_set('user_signatures', 1);
2222

    
2223
    // Prefetch text formats.
2224
    $this->full_html_format = filter_format_load('full_html');
2225
    $this->plain_text_format = filter_format_load('plain_text');
2226

    
2227
    // Create regular and administrative users.
2228
    $this->web_user = $this->drupalCreateUser(array());
2229
    $admin_permissions = array('administer comments');
2230
    foreach (filter_formats() as $format) {
2231
      if ($permission = filter_permission_name($format)) {
2232
        $admin_permissions[] = $permission;
2233
      }
2234
    }
2235
    $this->admin_user = $this->drupalCreateUser($admin_permissions);
2236
  }
2237

    
2238
  /**
2239
   * Test that a user can change their signature format and that it is respected
2240
   * upon display.
2241
   */
2242
  function testUserSignature() {
2243
    // Create a new node with comments on.
2244
    $node = $this->drupalCreateNode(array('comment' => COMMENT_NODE_OPEN));
2245

    
2246
    // Verify that user signature field is not displayed on registration form.
2247
    $this->drupalGet('user/register');
2248
    $this->assertNoText(t('Signature'));
2249

    
2250
    // Log in as a regular user and create a signature.
2251
    $this->drupalLogin($this->web_user);
2252
    $signature_text = "<h1>" . $this->randomName() . "</h1>";
2253
    $edit = array(
2254
      'signature[value]' => $signature_text,
2255
      'signature[format]' => $this->plain_text_format->format,
2256
    );
2257
    $this->drupalPost('user/' . $this->web_user->uid . '/edit', $edit, t('Save'));
2258

    
2259
    // Verify that values were stored.
2260
    $this->assertFieldByName('signature[value]', $edit['signature[value]'], 'Submitted signature text found.');
2261
    $this->assertFieldByName('signature[format]', $edit['signature[format]'], 'Submitted signature format found.');
2262

    
2263
    // Create a comment.
2264
    $langcode = LANGUAGE_NONE;
2265
    $edit = array();
2266
    $edit['subject'] = $this->randomName(8);
2267
    $edit['comment_body[' . $langcode . '][0][value]'] = $this->randomName(16);
2268
    $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview'));
2269
    $this->drupalPost(NULL, array(), t('Save'));
2270

    
2271
    // Get the comment ID. (This technique is the same one used in the Comment
2272
    // module's CommentHelperCase test case.)
2273
    preg_match('/#comment-([0-9]+)/', $this->getURL(), $match);
2274
    $comment_id = $match[1];
2275

    
2276
    // Log in as an administrator and edit the comment to use Full HTML, so
2277
    // that the comment text itself is not filtered at all.
2278
    $this->drupalLogin($this->admin_user);
2279
    $edit['comment_body[' . $langcode . '][0][format]'] = $this->full_html_format->format;
2280
    $this->drupalPost('comment/' . $comment_id . '/edit', $edit, t('Save'));
2281

    
2282
    // Assert that the signature did not make it through unfiltered.
2283
    $this->drupalGet('node/' . $node->nid);
2284
    $this->assertNoRaw($signature_text, 'Unfiltered signature text not found.');
2285
    $this->assertRaw(check_markup($signature_text, $this->plain_text_format->format), 'Filtered signature text found.');
2286
  }
2287
}
2288

    
2289
/*
2290
 * Test that a user, having editing their own account, can still log in.
2291
 */
2292
class UserEditedOwnAccountTestCase extends DrupalWebTestCase {
2293

    
2294
  public static function getInfo() {
2295
    return array(
2296
      'name' => 'User edited own account',
2297
      'description' => 'Test user edited own account can still log in.',
2298
      'group' => 'User',
2299
    );
2300
  }
2301

    
2302
  function testUserEditedOwnAccount() {
2303
    // Change account setting 'Who can register accounts?' to Administrators
2304
    // only.
2305
    variable_set('user_register', USER_REGISTER_ADMINISTRATORS_ONLY);
2306

    
2307
    // Create a new user account and log in.
2308
    $account = $this->drupalCreateUser(array('change own username'));
2309
    $this->drupalLogin($account);
2310

    
2311
    // Change own username.
2312
    $edit = array();
2313
    $edit['name'] = $this->randomName();
2314
    $this->drupalPost('user/' . $account->uid . '/edit', $edit, t('Save'));
2315

    
2316
    // Log out.
2317
    $this->drupalLogout();
2318

    
2319
    // Set the new name on the user account and attempt to log back in.
2320
    $account->name = $edit['name'];
2321
    $this->drupalLogin($account);
2322
  }
2323
}
2324

    
2325
/**
2326
 * Test case to test adding, editing and deleting roles.
2327
 */
2328
class UserRoleAdminTestCase extends DrupalWebTestCase {
2329

    
2330
  public static function getInfo() {
2331
    return array(
2332
      'name' => 'User role administration',
2333
      'description' => 'Test adding, editing and deleting user roles and changing role weights.',
2334
      'group' => 'User',
2335
    );
2336
  }
2337

    
2338
  function setUp() {
2339
    parent::setUp();
2340
    $this->admin_user = $this->drupalCreateUser(array('administer permissions', 'administer users'));
2341
  }
2342

    
2343
  /**
2344
   * Test adding, renaming and deleting roles.
2345
   */
2346
  function testRoleAdministration() {
2347
    $this->drupalLogin($this->admin_user);
2348

    
2349
    // Test adding a role. (In doing so, we use a role name that happens to
2350
    // correspond to an integer, to test that the role administration pages
2351
    // correctly distinguish between role names and IDs.)
2352
    $role_name = '123';
2353
    $edit = array('name' => $role_name);
2354
    $this->drupalPost('admin/people/permissions/roles', $edit, t('Add role'));
2355
    $this->assertText(t('The role has been added.'), 'The role has been added.');
2356
    $role = user_role_load_by_name($role_name);
2357
    $this->assertTrue(is_object($role), 'The role was successfully retrieved from the database.');
2358

    
2359
    // Try adding a duplicate role.
2360
    $this->drupalPost(NULL, $edit, t('Add role'));
2361
    $this->assertRaw(t('The role name %name already exists. Choose another role name.', array('%name' => $role_name)), 'Duplicate role warning displayed.');
2362

    
2363
    // Test renaming a role.
2364
    $old_name = $role_name;
2365
    $role_name = '456';
2366
    $edit = array('name' => $role_name);
2367
    $this->drupalPost("admin/people/permissions/roles/edit/{$role->rid}", $edit, t('Save role'));
2368
    $this->assertText(t('The role has been renamed.'), 'The role has been renamed.');
2369
    $this->assertFalse(user_role_load_by_name($old_name), 'The role can no longer be retrieved from the database using its old name.');
2370
    $this->assertTrue(is_object(user_role_load_by_name($role_name)), 'The role can be retrieved from the database using its new name.');
2371

    
2372
    // Test deleting the default administrator role.
2373
    $role_name = 'administrator';
2374
    $role = user_role_load_by_name($role_name);
2375
    $this->drupalPost("admin/people/permissions/roles/edit/{$role->rid}", NULL, t('Delete role'));
2376
    $this->drupalPost(NULL, NULL, t('Delete'));
2377
    $this->assertText(t('The role has been deleted.'), 'The role has been deleted');
2378
    $this->assertNoLinkByHref("admin/people/permissions/roles/edit/{$role->rid}", 'Role edit link removed.');
2379
    $this->assertFalse(user_role_load_by_name($role_name), 'A deleted role can no longer be loaded.');
2380
    // Make sure this role is no longer configured as the administrator role.
2381
    $this->assertNull(variable_get('user_admin_role'), 'The administrator role is no longer configured as the administrator role.');
2382

    
2383
    // Make sure that the system-defined roles cannot be edited via the user
2384
    // interface.
2385
    $this->drupalGet('admin/people/permissions/roles/edit/' . DRUPAL_ANONYMOUS_RID);
2386
    $this->assertResponse(403, 'Access denied when trying to edit the built-in anonymous role.');
2387
    $this->drupalGet('admin/people/permissions/roles/edit/' . DRUPAL_AUTHENTICATED_RID);
2388
    $this->assertResponse(403, 'Access denied when trying to edit the built-in authenticated role.');
2389
  }
2390

    
2391
  /**
2392
   * Test user role weight change operation.
2393
   */
2394
  function testRoleWeightChange() {
2395
    $this->drupalLogin($this->admin_user);
2396

    
2397
    // Pick up a random role and get its weight.
2398
    $rid = array_rand(user_roles());
2399
    $role = user_role_load($rid);
2400
    $old_weight = $role->weight;
2401

    
2402
    // Change the role weight and submit the form.
2403
    $edit = array('roles['. $rid .'][weight]' => $old_weight + 1);
2404
    $this->drupalPost('admin/people/permissions/roles', $edit, t('Save order'));
2405
    $this->assertText(t('The role settings have been updated.'), 'The role settings form submitted successfully.');
2406

    
2407
    // Retrieve the saved role and compare its weight.
2408
    $role = user_role_load($rid);
2409
    $new_weight = $role->weight;
2410
    $this->assertTrue(($old_weight + 1) == $new_weight, 'Role weight updated successfully.');
2411
  }
2412
}
2413

    
2414
/**
2415
 * Test user token replacement in strings.
2416
 */
2417
class UserTokenReplaceTestCase extends DrupalWebTestCase {
2418
  public static function getInfo() {
2419
    return array(
2420
      'name' => 'User token replacement',
2421
      'description' => 'Generates text using placeholders for dummy content to check user token replacement.',
2422
      'group' => 'User',
2423
    );
2424
  }
2425

    
2426
  /**
2427
   * Creates a user, then tests the tokens generated from it.
2428
   */
2429
  function testUserTokenReplacement() {
2430
    global $language;
2431
    $url_options = array(
2432
      'absolute' => TRUE,
2433
      'language' => $language,
2434
    );
2435

    
2436
    // Create two users and log them in one after another.
2437
    $user1 = $this->drupalCreateUser(array());
2438
    $user2 = $this->drupalCreateUser(array());
2439
    $this->drupalLogin($user1);
2440
    $this->drupalLogout();
2441
    $this->drupalLogin($user2);
2442

    
2443
    $account = user_load($user1->uid);
2444
    $global_account = user_load($GLOBALS['user']->uid);
2445

    
2446
    // Generate and test sanitized tokens.
2447
    $tests = array();
2448
    $tests['[user:uid]'] = $account->uid;
2449
    $tests['[user:name]'] = check_plain(format_username($account));
2450
    $tests['[user:mail]'] = check_plain($account->mail);
2451
    $tests['[user:url]'] = url("user/$account->uid", $url_options);
2452
    $tests['[user:edit-url]'] = url("user/$account->uid/edit", $url_options);
2453
    $tests['[user:last-login]'] = format_date($account->login, 'medium', '', NULL, $language->language);
2454
    $tests['[user:last-login:short]'] = format_date($account->login, 'short', '', NULL, $language->language);
2455
    $tests['[user:created]'] = format_date($account->created, 'medium', '', NULL, $language->language);
2456
    $tests['[user:created:short]'] = format_date($account->created, 'short', '', NULL, $language->language);
2457
    $tests['[current-user:name]'] = check_plain(format_username($global_account));
2458

    
2459
    // Test to make sure that we generated something for each token.
2460
    $this->assertFalse(in_array(0, array_map('strlen', $tests)), 'No empty tokens generated.');
2461

    
2462
    foreach ($tests as $input => $expected) {
2463
      $output = token_replace($input, array('user' => $account), array('language' => $language));
2464
      $this->assertEqual($output, $expected, format_string('Sanitized user token %token replaced.', array('%token' => $input)));
2465
    }
2466

    
2467
    // Generate and test unsanitized tokens.
2468
    $tests['[user:name]'] = format_username($account);
2469
    $tests['[user:mail]'] = $account->mail;
2470
    $tests['[current-user:name]'] = format_username($global_account);
2471

    
2472
    foreach ($tests as $input => $expected) {
2473
      $output = token_replace($input, array('user' => $account), array('language' => $language, 'sanitize' => FALSE));
2474
      $this->assertEqual($output, $expected, format_string('Unsanitized user token %token replaced.', array('%token' => $input)));
2475
    }
2476
  }
2477
}
2478

    
2479
/**
2480
 * Test user search.
2481
 */
2482
class UserUserSearchTestCase extends DrupalWebTestCase {
2483
  public static function getInfo() {
2484
    return array(
2485
      'name' => 'User search',
2486
      'description' => 'Tests the user search page and verifies that sensitive information is hidden from unauthorized users.',
2487
      'group' => 'User',
2488
    );
2489
  }
2490

    
2491
  function testUserSearch() {
2492
    // Verify that a user without 'administer users' permission cannot search
2493
    // for users by email address. Additionally, ensure that the username has a
2494
    // plus sign to ensure searching works with that.
2495
    $user1 = $this->drupalCreateUser(array('access user profiles', 'search content', 'use advanced search'));
2496
    $edit['name'] = 'foo+bar';
2497
    $edit['mail'] = $edit['name'] . '@example.com';
2498
    user_save($user1, $edit);
2499
    $this->drupalLogin($user1);
2500
    $keys = $user1->mail;
2501
    $edit = array('keys' => $keys);
2502
    $this->drupalPost('search/user/', $edit, t('Search'));
2503
    $this->assertNoText($keys);
2504
    $this->drupalLogout();
2505

    
2506
    $user2 = $this->drupalCreateUser(array('administer users', 'access user profiles', 'search content', 'use advanced search'));
2507
    $this->drupalLogin($user2);
2508
    $keys = $user2->mail;
2509
    $edit = array('keys' => $keys);
2510
    $this->drupalPost('search/user/', $edit, t('Search'));
2511
    $this->assertText($keys);
2512

    
2513
    // Verify that wildcard search works.
2514
    $keys = $user1->name;
2515
    $keys = substr($keys, 0, 2) . '*' . substr($keys, 4, 2);
2516
    $edit = array('keys' => $keys);
2517
    $this->drupalPost('search/user/', $edit, t('Search'));
2518
    $this->assertText($user1->name, 'Search for username wildcard resulted in user name on page for administrative user.');
2519

    
2520
    // Verify that wildcard search works for email.
2521
    $keys = $user1->mail;
2522
    $keys = substr($keys, 0, 2) . '*' . substr($keys, 4, 2);
2523
    $edit = array('keys' => $keys);
2524
    $this->drupalPost('search/user/', $edit, t('Search'));
2525
    $this->assertText($user1->name, 'Search for email wildcard resulted in user name on page for administrative user.');
2526

    
2527
    // Create a blocked user.
2528
    $blocked_user = $this->drupalCreateUser();
2529
    $edit = array('status' => 0);
2530
    $blocked_user = user_save($blocked_user, $edit);
2531

    
2532
    // Verify that users with "administer users" permissions can see blocked
2533
    // accounts in search results.
2534
    $edit = array('keys' => $blocked_user->name);
2535
    $this->drupalPost('search/user/', $edit, t('Search'));
2536
    $this->assertText($blocked_user->name, 'Blocked users are listed on the user search results for users with the "administer users" permission.');
2537

    
2538
    // Verify that users without "administer users" permissions do not see
2539
    // blocked accounts in search results.
2540
    $this->drupalLogin($user1);
2541
    $edit = array('keys' => $blocked_user->name);
2542
    $this->drupalPost('search/user/', $edit, t('Search'));
2543
    $this->assertNoText($blocked_user->name, 'Blocked users are hidden from the user search results.');
2544

    
2545
    $this->drupalLogout();
2546
  }
2547
}
2548

    
2549
/**
2550
 * Test role assignment.
2551
 */
2552
class UserRolesAssignmentTestCase extends DrupalWebTestCase {
2553
  protected $admin_user;
2554

    
2555
  public static function getInfo() {
2556
    return array(
2557
      'name' => 'Role assignment',
2558
      'description' => 'Tests that users can be assigned and unassigned roles.',
2559
      'group' => 'User'
2560
    );
2561
  }
2562

    
2563
  function setUp() {
2564
    parent::setUp();
2565
    $this->admin_user = $this->drupalCreateUser(array('administer permissions', 'administer users'));
2566
    $this->drupalLogin($this->admin_user);
2567
  }
2568

    
2569
  /**
2570
   * Tests that a user can be assigned a role and that the role can be removed
2571
   * again.
2572
   */
2573
  function testAssignAndRemoveRole()  {
2574
    $rid = $this->drupalCreateRole(array('administer content types'));
2575
    $account = $this->drupalCreateUser();
2576

    
2577
    // Assign the role to the user.
2578
    $this->drupalPost('user/' . $account->uid . '/edit', array("roles[$rid]" => $rid), t('Save'));
2579
    $this->assertText(t('The changes have been saved.'));
2580
    $this->assertFieldChecked('edit-roles-' . $rid, 'Role is assigned.');
2581
    $this->userLoadAndCheckRoleAssigned($account, $rid);
2582

    
2583
    // Remove the role from the user.
2584
    $this->drupalPost('user/' . $account->uid . '/edit', array("roles[$rid]" => FALSE), t('Save'));
2585
    $this->assertText(t('The changes have been saved.'));
2586
    $this->assertNoFieldChecked('edit-roles-' . $rid, 'Role is removed from user.');
2587
    $this->userLoadAndCheckRoleAssigned($account, $rid, FALSE);
2588
  }
2589

    
2590
  /**
2591
   * Tests that when creating a user the role can be assigned. And that it can
2592
   * be removed again.
2593
   */
2594
  function testCreateUserWithRole() {
2595
    $rid = $this->drupalCreateRole(array('administer content types'));
2596
    // Create a new user and add the role at the same time.
2597
    $edit = array(
2598
      'name' => $this->randomName(),
2599
      'mail' => $this->randomName() . '@example.com',
2600
      'pass[pass1]' => $pass = $this->randomString(),
2601
      'pass[pass2]' => $pass,
2602
      "roles[$rid]" => $rid,
2603
    );
2604
    $this->drupalPost('admin/people/create', $edit, t('Create new account'));
2605
    $this->assertText(t('Created a new user account for !name.', array('!name' => $edit['name'])));
2606
    // Get the newly added user.
2607
    $account = user_load_by_name($edit['name']);
2608

    
2609
    $this->drupalGet('user/' . $account->uid . '/edit');
2610
    $this->assertFieldChecked('edit-roles-' . $rid, 'Role is assigned.');
2611
    $this->userLoadAndCheckRoleAssigned($account, $rid);
2612

    
2613
    // Remove the role again.
2614
    $this->drupalPost('user/' . $account->uid . '/edit', array("roles[$rid]" => FALSE), t('Save'));
2615
    $this->assertText(t('The changes have been saved.'));
2616
    $this->assertNoFieldChecked('edit-roles-' . $rid, 'Role is removed from user.');
2617
    $this->userLoadAndCheckRoleAssigned($account, $rid, FALSE);
2618
  }
2619

    
2620
  /**
2621
   * Check role on user object.
2622
   *
2623
   * @param object $account
2624
   *   The user account to check.
2625
   * @param string $rid
2626
   *   The role ID to search for.
2627
   * @param bool $is_assigned
2628
   *   (optional) Whether to assert that $rid exists (TRUE) or not (FALSE).
2629
   *   Defaults to TRUE.
2630
   */
2631
  private function userLoadAndCheckRoleAssigned($account, $rid, $is_assigned = TRUE) {
2632
    $account = user_load($account->uid, TRUE);
2633
    if ($is_assigned) {
2634
      $this->assertTrue(array_key_exists($rid, $account->roles), 'The role is present in the user object.');
2635
    }
2636
    else {
2637
      $this->assertFalse(array_key_exists($rid, $account->roles), 'The role is not present in the user object.');
2638
    }
2639
  }
2640
}
2641

    
2642

    
2643
/**
2644
 * Unit test for authmap assignment.
2645
 */
2646
class UserAuthmapAssignmentTestCase extends DrupalWebTestCase {
2647
  public static function getInfo() {
2648
    return array(
2649
      'name' => 'Authmap assignment',
2650
      'description' => 'Tests that users can be assigned and unassigned authmaps.',
2651
      'group' => 'User'
2652
    );
2653
  }
2654

    
2655
  /**
2656
   * Test authmap assignment and retrieval.
2657
   */
2658
  function testAuthmapAssignment()  {
2659
    $account = $this->drupalCreateUser();
2660

    
2661
    // Assign authmaps to the user.
2662
    $authmaps = array(
2663
      'authname_poll' => 'external username one',
2664
      'authname_book' => 'external username two',
2665
    );
2666
    user_set_authmaps($account, $authmaps);
2667

    
2668
    // Test for expected authmaps.
2669
    $expected_authmaps = array(
2670
      'external username one' => array(
2671
        'poll' => 'external username one',
2672
      ),
2673
      'external username two' => array(
2674
        'book' => 'external username two',
2675
      ),
2676
    );
2677
    foreach ($expected_authmaps as $authname => $expected_output) {
2678
      $this->assertIdentical(user_get_authmaps($authname), $expected_output, format_string('Authmap for authname %authname was set correctly.', array('%authname' => $authname)));
2679
    }
2680

    
2681
    // Remove authmap for module poll, add authmap for module blog.
2682
    $authmaps = array(
2683
      'authname_poll' => NULL,
2684
      'authname_blog' => 'external username three',
2685
    );
2686
    user_set_authmaps($account, $authmaps);
2687

    
2688
    // Assert that external username one does not have authmaps.
2689
    $remove_username = 'external username one';
2690
    unset($expected_authmaps[$remove_username]);
2691
    $this->assertFalse(user_get_authmaps($remove_username), format_string('Authmap for %authname was removed.', array('%authname' => $remove_username)));
2692

    
2693
    // Assert that a new authmap was created for external username three, and
2694
    // existing authmaps for external username two were unchanged.
2695
    $expected_authmaps['external username three'] = array('blog' => 'external username three');
2696
    foreach ($expected_authmaps as $authname => $expected_output) {
2697
      $this->assertIdentical(user_get_authmaps($authname), $expected_output, format_string('Authmap for authname %authname was set correctly.', array('%authname' => $authname)));
2698
    }
2699
  }
2700
}
2701

    
2702
/**
2703
 * Tests user_validate_current_pass on a custom form.
2704
 */
2705
class UserValidateCurrentPassCustomForm extends DrupalWebTestCase {
2706

    
2707
  public static function getInfo() {
2708
    return array(
2709
      'name' => 'User validate current pass custom form',
2710
      'description' => 'Test that user_validate_current_pass is usable on a custom form.',
2711
      'group' => 'User',
2712
    );
2713
  }
2714

    
2715
  /**
2716
   * User with permission to view content.
2717
   */
2718
  protected $accessUser;
2719

    
2720
  /**
2721
   * User permission to administer users.
2722
   */
2723
  protected $adminUser;
2724

    
2725
  function setUp() {
2726
    parent::setUp('user_form_test');
2727
    // Create two users
2728
    $this->accessUser = $this->drupalCreateUser(array('access content'));
2729
    $this->adminUser = $this->drupalCreateUser(array('administer users'));
2730
  }
2731

    
2732
  /**
2733
   * Tests that user_validate_current_pass can be reused on a custom form.
2734
   */
2735
  function testUserValidateCurrentPassCustomForm() {
2736
    $this->drupalLogin($this->adminUser);
2737

    
2738
    // Submit the custom form with the admin user using the access user's password.
2739
    $edit = array();
2740
    $edit['user_form_test_field'] = $this->accessUser->name;
2741
    $edit['current_pass'] = $this->accessUser->pass_raw;
2742
    $this->drupalPost('user_form_test_current_password/' . $this->accessUser->uid, $edit, t('Test'));
2743
    $this->assertText(t('The password has been validated and the form submitted successfully.'));
2744
  }
2745
}