Project

General

Profile

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

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

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
      'ᚠᛇᚻ᛫ᛒᛦᚦ'                => array('Valid UTF8 username', 'assertNull'), // runes
280
      ' foo'                   => array('Invalid username that starts with a space', 'assertNotNull'),
281
      'foo '                   => array('Invalid username that ends with a space', 'assertNotNull'),
282
      'foo  bar'               => array('Invalid username that contains 2 spaces \'&nbsp;&nbsp;\'', 'assertNotNull'),
283
      ''                       => array('Invalid empty username', 'assertNotNull'),
284
      'foo/'                   => array('Invalid username containing invalid chars', 'assertNotNull'),
285
      'foo' . chr(0) . 'bar'   => array('Invalid username containing chr(0)', 'assertNotNull'), // NULL
286
      'foo' . chr(13) . 'bar'  => array('Invalid username containing chr(13)', 'assertNotNull'), // CR
287
      str_repeat('x', USERNAME_MAX_LENGTH + 1) => array('Invalid excessively long username', 'assertNotNull'),
288
    );
289
    foreach ($test_cases as $name => $test_case) {
290
      list($description, $test) = $test_case;
291
      $result = user_validate_name($name);
292
      $this->$test($result, $description . ' (' . $name . ')');
293
    }
294
  }
295

    
296
  // Mail validation. More extensive tests can be found at common.test
297
  function testMailAddresses() {
298
    $test_cases = array( // '<username>' => array('<description>', 'assert<testName>'),
299
      ''                => array('Empty mail address', 'assertNotNull'),
300
      'foo'             => array('Invalid mail address', 'assertNotNull'),
301
      'foo@example.com' => array('Valid mail address', 'assertNull'),
302
    );
303
    foreach ($test_cases as $name => $test_case) {
304
      list($description, $test) = $test_case;
305
      $result = user_validate_mail($name);
306
      $this->$test($result, $description . ' (' . $name . ')');
307
    }
308
  }
309
}
310

    
311
/**
312
 * Functional tests for user logins, including rate limiting of login attempts.
313
 */
314
class UserLoginTestCase extends DrupalWebTestCase {
315
  public static function getInfo() {
316
    return array(
317
      'name' => 'User login',
318
      'description' => 'Ensure that login works as expected.',
319
      'group' => 'User',
320
    );
321
  }
322

    
323
  /**
324
   * Test the global login flood control.
325
   */
326
  function testGlobalLoginFloodControl() {
327
    // Set the global login limit.
328
    variable_set('user_failed_login_ip_limit', 10);
329
    // Set a high per-user limit out so that it is not relevant in the test.
330
    variable_set('user_failed_login_user_limit', 4000);
331

    
332
    $user1 = $this->drupalCreateUser(array());
333
    $incorrect_user1 = clone $user1;
334
    $incorrect_user1->pass_raw .= 'incorrect';
335

    
336
    // Try 2 failed logins.
337
    for ($i = 0; $i < 2; $i++) {
338
      $this->assertFailedLogin($incorrect_user1);
339
    }
340

    
341
    // A successful login will not reset the IP-based flood control count.
342
    $this->drupalLogin($user1);
343
    $this->drupalLogout();
344

    
345
    // Try 8 more failed logins, they should not trigger the flood control
346
    // mechanism.
347
    for ($i = 0; $i < 8; $i++) {
348
      $this->assertFailedLogin($incorrect_user1);
349
    }
350

    
351
    // The next login trial should result in an IP-based flood error message.
352
    $this->assertFailedLogin($incorrect_user1, 'ip');
353

    
354
    // A login with the correct password should also result in a flood error
355
    // message.
356
    $this->assertFailedLogin($user1, 'ip');
357
  }
358

    
359
  /**
360
   * Test the per-user login flood control.
361
   */
362
  function testPerUserLoginFloodControl() {
363
    // Set a high global limit out so that it is not relevant in the test.
364
    variable_set('user_failed_login_ip_limit', 4000);
365
    // Set the per-user login limit.
366
    variable_set('user_failed_login_user_limit', 3);
367

    
368
    $user1 = $this->drupalCreateUser(array());
369
    $incorrect_user1 = clone $user1;
370
    $incorrect_user1->pass_raw .= 'incorrect';
371

    
372
    $user2 = $this->drupalCreateUser(array());
373

    
374
    // Try 2 failed logins.
375
    for ($i = 0; $i < 2; $i++) {
376
      $this->assertFailedLogin($incorrect_user1);
377
    }
378

    
379
    // A successful login will reset the per-user flood control count.
380
    $this->drupalLogin($user1);
381
    $this->drupalLogout();
382

    
383
    // Try 3 failed logins for user 1, they will not trigger flood control.
384
    for ($i = 0; $i < 3; $i++) {
385
      $this->assertFailedLogin($incorrect_user1);
386
    }
387

    
388
    // Try one successful attempt for user 2, it should not trigger any
389
    // flood control.
390
    $this->drupalLogin($user2);
391
    $this->drupalLogout();
392

    
393
    // Try one more attempt for user 1, it should be rejected, even if the
394
    // correct password has been used.
395
    $this->assertFailedLogin($user1, 'user');
396
  }
397

    
398
  /**
399
   * Test that user password is re-hashed upon login after changing $count_log2.
400
   */
401
  function testPasswordRehashOnLogin() {
402
    // Load password hashing API.
403
    require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
404
    // Set initial $count_log2 to the default, DRUPAL_HASH_COUNT.
405
    variable_set('password_count_log2', DRUPAL_HASH_COUNT);
406
    // Create a new user and authenticate.
407
    $account = $this->drupalCreateUser(array());
408
    $password = $account->pass_raw;
409
    $this->drupalLogin($account);
410
    $this->drupalLogout();
411
    // Load the stored user. The password hash should reflect $count_log2.
412
    $account = user_load($account->uid);
413
    $this->assertIdentical(_password_get_count_log2($account->pass), DRUPAL_HASH_COUNT);
414
    // Change $count_log2 and log in again.
415
    variable_set('password_count_log2', DRUPAL_HASH_COUNT + 1);
416
    $account->pass_raw = $password;
417
    $this->drupalLogin($account);
418
    // Load the stored user, which should have a different password hash now.
419
    $account = user_load($account->uid, TRUE);
420
    $this->assertIdentical(_password_get_count_log2($account->pass), DRUPAL_HASH_COUNT + 1);
421
  }
422

    
423
  /**
424
   * Make an unsuccessful login attempt.
425
   *
426
   * @param $account
427
   *   A user object with name and pass_raw attributes for the login attempt.
428
   * @param $flood_trigger
429
   *   Whether or not to expect that the flood control mechanism will be
430
   *   triggered.
431
   */
432
  function assertFailedLogin($account, $flood_trigger = NULL) {
433
    $edit = array(
434
      'name' => $account->name,
435
      'pass' => $account->pass_raw,
436
    );
437
    $this->drupalPost('user', $edit, t('Log in'));
438
    $this->assertNoFieldByXPath("//input[@name='pass' and @value!='']", NULL, 'Password value attribute is blank.');
439
    if (isset($flood_trigger)) {
440
      if ($flood_trigger == 'user') {
441
        $this->assertRaw(format_plural(variable_get('user_failed_login_user_limit', 5), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', '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'))));
442
      }
443
      else {
444
        // No uid, so the limit is IP-based.
445
        $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'))));
446
      }
447
    }
448
    else {
449
      $this->assertText(t('Sorry, unrecognized username or password. Have you forgotten your password?'));
450
    }
451
  }
452
}
453

    
454
/**
455
 * Tests resetting a user password.
456
 */
457
class UserPasswordResetTestCase extends DrupalWebTestCase {
458
  protected $profile = 'standard';
459

    
460
  public static function getInfo() {
461
    return array(
462
      'name' => 'Reset password',
463
      'description' => 'Ensure that password reset methods work as expected.',
464
      'group' => 'User',
465
    );
466
  }
467

    
468
  /**
469
   * Tests password reset functionality.
470
   */
471
  function testUserPasswordReset() {
472
    // Create a user.
473
    $account = $this->drupalCreateUser();
474
    $this->drupalLogin($account);
475
    $this->drupalLogout();
476
    // Attempt to reset password.
477
    $edit = array('name' => $account->name);
478
    $this->drupalPost('user/password', $edit, t('E-mail new password'));
479
    // Confirm the password reset.
480
    $this->assertText(t('Further instructions have been sent to your e-mail address.'), 'Password reset instructions mailed message displayed.');
481
  }
482

    
483
  /**
484
   * Test user password reset while logged in.
485
   */
486
  function testUserPasswordResetLoggedIn() {
487
    $account = $this->drupalCreateUser();
488
    $this->drupalLogin($account);
489
    // Make sure the test account has a valid password.
490
    user_save($account, array('pass' => user_password()));
491

    
492
    // Generate one time login link.
493
    $reset_url = user_pass_reset_url($account);
494
    $this->drupalGet($reset_url);
495

    
496
    $this->assertText('Reset password');
497
    $this->drupalPost(NULL, NULL, t('Log in'));
498

    
499
    $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.');
500

    
501
    $pass = user_password();
502
    $edit = array(
503
      'pass[pass1]' => $pass,
504
      'pass[pass2]' => $pass,
505
    );
506
    $this->drupalPost(NULL, $edit, t('Save'));
507

    
508
    $this->assertText('The changes have been saved.');
509
  }
510

    
511
  /**
512
   * Attempts login using an expired password reset link.
513
   */
514
  function testUserPasswordResetExpired() {
515
    // Set password reset timeout variable to 43200 seconds = 12 hours.
516
    $timeout = 43200;
517
    variable_set('user_password_reset_timeout', $timeout);
518

    
519
    // Create a user.
520
    $account = $this->drupalCreateUser();
521
    $this->drupalLogin($account);
522
    // Load real user object.
523
    $account = user_load($account->uid, TRUE);
524
    $this->drupalLogout();
525

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

    
533
  /**
534
   * Prefill the text box on incorrect login via link to password reset page.
535
   */
536
  function testUserPasswordTextboxFilled() {
537
    $this->drupalGet('user/login');
538
    $edit = array(
539
      'name' => $this->randomName(),
540
      'pass' => $this->randomName(),
541
    );
542
    $this->drupalPost('user', $edit, t('Log in'));
543
    $this->assertRaw(t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>',
544
      array('@password' => url('user/password', array('query' => array('name' => $edit['name']))))));
545
    unset($edit['pass']);
546
    $this->drupalGet('user/password', array('query' => array('name' => $edit['name'])));
547
    $this->assertFieldByName('name', $edit['name'], 'User name found.');
548
  }
549

    
550
  /**
551
   * Make sure that users cannot forge password reset URLs of other users.
552
   */
553
  function testResetImpersonation() {
554
    // Make sure user 1 has a valid password, so it does not interfere with the
555
    // test user accounts that are created below.
556
    $account = user_load(1);
557
    user_save($account, array('pass' => user_password()));
558

    
559
    // Create two identical user accounts except for the user name. They must
560
    // have the same empty password, so we can't use $this->drupalCreateUser().
561
    $edit = array();
562
    $edit['name'] = $this->randomName();
563
    $edit['mail'] = $edit['name'] . '@example.com';
564
    $edit['status'] = 1;
565

    
566
    $user1 = user_save(drupal_anonymous_user(), $edit);
567

    
568
    $edit['name'] = $this->randomName();
569
    $user2 = user_save(drupal_anonymous_user(), $edit);
570

    
571
    // The password reset URL must not be valid for the second user when only
572
    // the user ID is changed in the URL.
573
    $reset_url = user_pass_reset_url($user1);
574
    $attack_reset_url = str_replace("user/reset/$user1->uid", "user/reset/$user2->uid", $reset_url);
575
    $this->drupalGet($attack_reset_url);
576
    $this->assertNoText($user2->name, 'The invalid password reset page does not show the user name.');
577
    $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
578
    $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.');
579

    
580
    // When legacy code calls user_pass_rehash() without providing the $uid
581
    // parameter, neither password reset URL should be valid since it is
582
    // impossible for the system to determine which user account the token was
583
    // intended for.
584
    $timestamp = REQUEST_TIME;
585
    // Pass an explicit NULL for the $uid parameter of user_pass_rehash()
586
    // rather than not passing it at all, to avoid triggering PHP warnings in
587
    // the test.
588
    $reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL);
589
    $reset_url = url("user/reset/$user1->uid/$timestamp/$reset_url_token", array('absolute' => TRUE));
590
    $this->drupalGet($reset_url);
591
    $this->assertNoText($user1->name, 'The invalid password reset page does not show the user name.');
592
    $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
593
    $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.');
594
    $attack_reset_url = str_replace("user/reset/$user1->uid", "user/reset/$user2->uid", $reset_url);
595
    $this->drupalGet($attack_reset_url);
596
    $this->assertNoText($user2->name, 'The invalid password reset page does not show the user name.');
597
    $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
598
    $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.');
599

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

    
607
    // However, when the duplicate account is removed, the password reset URL
608
    // should be valid.
609
    user_delete($user2->uid);
610
    $reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL);
611
    $reset_url = url("user/reset/$user1->uid/$timestamp/$reset_url_token", array('absolute' => TRUE));
612
    $this->drupalGet($reset_url);
613
    $this->assertText($user1->name, 'The valid password reset page shows the user name.');
614
    $this->assertUrl($reset_url, array(), 'The user remains on the password reset login page.');
615
    $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.');
616
  }
617

    
618
}
619

    
620
/**
621
 * Test cancelling a user.
622
 */
623
class UserCancelTestCase extends DrupalWebTestCase {
624
  public static function getInfo() {
625
    return array(
626
      'name' => 'Cancel account',
627
      'description' => 'Ensure that account cancellation methods work as expected.',
628
      'group' => 'User',
629
    );
630
  }
631

    
632
  function setUp() {
633
    parent::setUp('comment');
634
  }
635

    
636
  /**
637
   * Attempt to cancel account without permission.
638
   */
639
  function testUserCancelWithoutPermission() {
640
    variable_set('user_cancel_method', 'user_cancel_reassign');
641

    
642
    // Create a user.
643
    $account = $this->drupalCreateUser(array());
644
    $this->drupalLogin($account);
645
    // Load real user object.
646
    $account = user_load($account->uid, TRUE);
647

    
648
    // Create a node.
649
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
650

    
651
    // Attempt to cancel account.
652
    $this->drupalGet('user/' . $account->uid . '/edit');
653
    $this->assertNoRaw(t('Cancel account'), 'No cancel account button displayed.');
654

    
655
    // Attempt bogus account cancellation request confirmation.
656
    $timestamp = $account->login;
657
    $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid));
658
    $this->assertResponse(403, 'Bogus cancelling request rejected.');
659
    $account = user_load($account->uid);
660
    $this->assertTrue($account->status == 1, 'User account was not canceled.');
661

    
662
    // Confirm user's content has not been altered.
663
    $test_node = node_load($node->nid, NULL, TRUE);
664
    $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), 'Node of the user has not been altered.');
665
  }
666

    
667
  /**
668
   * Tests that user account for uid 1 cannot be cancelled.
669
   *
670
   * This should never be possible, or the site owner would become unable to
671
   * administer the site.
672
   */
673
  function testUserCancelUid1() {
674
    // Update uid 1's name and password to we know it.
675
    $password = user_password();
676
    require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
677
    $account = array(
678
      'name' => 'user1',
679
      'pass' => user_hash_password(trim($password)),
680
    );
681
    // We cannot use user_save() here or the password would be hashed again.
682
    db_update('users')
683
      ->fields($account)
684
      ->condition('uid', 1)
685
      ->execute();
686

    
687
    // Reload and log in uid 1.
688
    $user1 = user_load(1, TRUE);
689
    $user1->pass_raw = $password;
690

    
691
    // Try to cancel uid 1's account with a different user.
692
    $this->admin_user = $this->drupalCreateUser(array('administer users'));
693
    $this->drupalLogin($this->admin_user);
694
    $edit = array(
695
      'operation' => 'cancel',
696
      'accounts[1]' => TRUE,
697
    );
698
    $this->drupalPost('admin/people', $edit, t('Update'));
699

    
700
    // Verify that uid 1's account was not cancelled.
701
    $user1 = user_load(1, TRUE);
702
    $this->assertEqual($user1->status, 1, 'User #1 still exists and is not blocked.');
703
  }
704

    
705
  /**
706
   * Attempt invalid account cancellations.
707
   */
708
  function testUserCancelInvalid() {
709
    variable_set('user_cancel_method', 'user_cancel_reassign');
710

    
711
    // Create a user.
712
    $account = $this->drupalCreateUser(array('cancel account'));
713
    $this->drupalLogin($account);
714
    // Load real user object.
715
    $account = user_load($account->uid, TRUE);
716

    
717
    // Create a node.
718
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
719

    
720
    // Attempt to cancel account.
721
    $this->drupalPost('user/' . $account->uid . '/edit', NULL, t('Cancel account'));
722

    
723
    // Confirm account cancellation.
724
    $timestamp = time();
725
    $this->drupalPost(NULL, NULL, t('Cancel account'));
726
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
727

    
728
    // Attempt bogus account cancellation request confirmation.
729
    $bogus_timestamp = $timestamp + 60;
730
    $this->drupalGet("user/$account->uid/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login, $account->uid));
731
    $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.');
732
    $account = user_load($account->uid);
733
    $this->assertTrue($account->status == 1, 'User account was not canceled.');
734

    
735
    // Attempt expired account cancellation request confirmation.
736
    $bogus_timestamp = $timestamp - 86400 - 60;
737
    $this->drupalGet("user/$account->uid/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login, $account->uid));
738
    $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.');
739
    $accounts = user_load_multiple(array($account->uid), array('status' => 1));
740
    $this->assertTrue(reset($accounts), 'User account was not canceled.');
741

    
742
    // Confirm user's content has not been altered.
743
    $test_node = node_load($node->nid, NULL, TRUE);
744
    $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), 'Node of the user has not been altered.');
745
  }
746

    
747
  /**
748
   * Disable account and keep all content.
749
   */
750
  function testUserBlock() {
751
    variable_set('user_cancel_method', 'user_cancel_block');
752

    
753
    // Create a user.
754
    $web_user = $this->drupalCreateUser(array('cancel account'));
755
    $this->drupalLogin($web_user);
756

    
757
    // Load real user object.
758
    $account = user_load($web_user->uid, TRUE);
759

    
760
    // Attempt to cancel account.
761
    $this->drupalGet('user/' . $account->uid . '/edit');
762
    $this->drupalPost(NULL, NULL, t('Cancel account'));
763
    $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
764
    $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.');
765
    $this->assertNoText(t('Select the method to cancel the account above.'), 'Does not allow user to select account cancellation method.');
766

    
767
    // Confirm account cancellation.
768
    $timestamp = time();
769

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

    
773
    // Confirm account cancellation request.
774
    $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid));
775
    $account = user_load($account->uid, TRUE);
776
    $this->assertTrue($account->status == 0, 'User has been blocked.');
777

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

    
782
  /**
783
   * Disable account and unpublish all content.
784
   */
785
  function testUserBlockUnpublish() {
786
    variable_set('user_cancel_method', 'user_cancel_block_unpublish');
787

    
788
    // Create a user.
789
    $account = $this->drupalCreateUser(array('cancel account'));
790
    $this->drupalLogin($account);
791
    // Load real user object.
792
    $account = user_load($account->uid, TRUE);
793

    
794
    // Create a node with two revisions.
795
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
796
    $settings = get_object_vars($node);
797
    $settings['revision'] = 1;
798
    $node = $this->drupalCreateNode($settings);
799

    
800
    // Attempt to cancel account.
801
    $this->drupalGet('user/' . $account->uid . '/edit');
802
    $this->drupalPost(NULL, NULL, t('Cancel account'));
803
    $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
804
    $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.');
805

    
806
    // Confirm account cancellation.
807
    $timestamp = time();
808
    $this->drupalPost(NULL, NULL, t('Cancel account'));
809
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
810

    
811
    // Confirm account cancellation request.
812
    $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid));
813
    $account = user_load($account->uid, TRUE);
814
    $this->assertTrue($account->status == 0, 'User has been blocked.');
815

    
816
    // Confirm user's content has been unpublished.
817
    $test_node = node_load($node->nid, NULL, TRUE);
818
    $this->assertTrue($test_node->status == 0, 'Node of the user has been unpublished.');
819
    $test_node = node_load($node->nid, $node->vid, TRUE);
820
    $this->assertTrue($test_node->status == 0, 'Node revision of the user has been unpublished.');
821

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

    
826
  /**
827
   * Delete account and anonymize all content.
828
   */
829
  function testUserAnonymize() {
830
    variable_set('user_cancel_method', 'user_cancel_reassign');
831

    
832
    // Create a user.
833
    $account = $this->drupalCreateUser(array('cancel account'));
834
    $this->drupalLogin($account);
835
    // Load real user object.
836
    $account = user_load($account->uid, TRUE);
837

    
838
    // Create a simple node.
839
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
840

    
841
    // Create a node with two revisions, the initial one belonging to the
842
    // cancelling user.
843
    $revision_node = $this->drupalCreateNode(array('uid' => $account->uid));
844
    $revision = $revision_node->vid;
845
    $settings = get_object_vars($revision_node);
846
    $settings['revision'] = 1;
847
    $settings['uid'] = 1; // Set new/current revision to someone else.
848
    $revision_node = $this->drupalCreateNode($settings);
849

    
850
    // Attempt to cancel account.
851
    $this->drupalGet('user/' . $account->uid . '/edit');
852
    $this->drupalPost(NULL, NULL, t('Cancel account'));
853
    $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
854
    $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.');
855

    
856
    // Confirm account cancellation.
857
    $timestamp = time();
858
    $this->drupalPost(NULL, NULL, t('Cancel account'));
859
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
860

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

    
865
    // Confirm that user's content has been attributed to anonymous user.
866
    $test_node = node_load($node->nid, NULL, TRUE);
867
    $this->assertTrue(($test_node->uid == 0 && $test_node->status == 1), 'Node of the user has been attributed to anonymous user.');
868
    $test_node = node_load($revision_node->nid, $revision, TRUE);
869
    $this->assertTrue(($test_node->revision_uid == 0 && $test_node->status == 1), 'Node revision of the user has been attributed to anonymous user.');
870
    $test_node = node_load($revision_node->nid, NULL, TRUE);
871
    $this->assertTrue(($test_node->uid != 0 && $test_node->status == 1), "Current revision of the user's node was not attributed to anonymous user.");
872

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

    
877
  /**
878
   * Delete account and remove all content.
879
   */
880
  function testUserDelete() {
881
    variable_set('user_cancel_method', 'user_cancel_delete');
882

    
883
    // Create a user.
884
    $account = $this->drupalCreateUser(array('cancel account', 'post comments', 'skip comment approval'));
885
    $this->drupalLogin($account);
886
    // Load real user object.
887
    $account = user_load($account->uid, TRUE);
888

    
889
    // Create a simple node.
890
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
891

    
892
    // Create comment.
893
    $langcode = LANGUAGE_NONE;
894
    $edit = array();
895
    $edit['subject'] = $this->randomName(8);
896
    $edit['comment_body[' . $langcode . '][0][value]'] = $this->randomName(16);
897

    
898
    $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview'));
899
    $this->drupalPost(NULL, array(), t('Save'));
900
    $this->assertText(t('Your comment has been posted.'));
901
    $comments = comment_load_multiple(array(), array('subject' => $edit['subject']));
902
    $comment = reset($comments);
903
    $this->assertTrue($comment->cid, 'Comment found.');
904

    
905
    // Create a node with two revisions, the initial one belonging to the
906
    // cancelling user.
907
    $revision_node = $this->drupalCreateNode(array('uid' => $account->uid));
908
    $revision = $revision_node->vid;
909
    $settings = get_object_vars($revision_node);
910
    $settings['revision'] = 1;
911
    $settings['uid'] = 1; // Set new/current revision to someone else.
912
    $revision_node = $this->drupalCreateNode($settings);
913

    
914
    // Attempt to cancel account.
915
    $this->drupalGet('user/' . $account->uid . '/edit');
916
    $this->drupalPost(NULL, NULL, t('Cancel account'));
917
    $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
918
    $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.');
919

    
920
    // Confirm account cancellation.
921
    $timestamp = time();
922
    $this->drupalPost(NULL, NULL, t('Cancel account'));
923
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
924

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

    
929
    // Confirm that user's content has been deleted.
930
    $this->assertFalse(node_load($node->nid, NULL, TRUE), 'Node of the user has been deleted.');
931
    $this->assertFalse(node_load($node->nid, $revision, TRUE), 'Node revision of the user has been deleted.');
932
    $this->assertTrue(node_load($revision_node->nid, NULL, TRUE), "Current revision of the user's node was not deleted.");
933
    $this->assertFalse(comment_load($comment->cid), 'Comment of the user has been deleted.');
934

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

    
939
  /**
940
   * Create an administrative user and delete another user.
941
   */
942
  function testUserCancelByAdmin() {
943
    variable_set('user_cancel_method', 'user_cancel_reassign');
944

    
945
    // Create a regular user.
946
    $account = $this->drupalCreateUser(array());
947

    
948
    // Create administrative user.
949
    $admin_user = $this->drupalCreateUser(array('administer users'));
950
    $this->drupalLogin($admin_user);
951

    
952
    // Delete regular user.
953
    $this->drupalGet('user/' . $account->uid . '/edit');
954
    $this->drupalPost(NULL, NULL, t('Cancel account'));
955
    $this->assertRaw(t('Are you sure you want to cancel the account %name?', array('%name' => $account->name)), 'Confirmation form to cancel account displayed.');
956
    $this->assertText(t('Select the method to cancel the account above.'), 'Allows to select account cancellation method.');
957

    
958
    // Confirm deletion.
959
    $this->drupalPost(NULL, NULL, t('Cancel account'));
960
    $this->assertRaw(t('%name has been deleted.', array('%name' => $account->name)), 'User deleted.');
961
    $this->assertFalse(user_load($account->uid), 'User is not found in the database.');
962
  }
963

    
964
  /**
965
   * Create an administrative user and mass-delete other users.
966
   */
967
  function testMassUserCancelByAdmin() {
968
    variable_set('user_cancel_method', 'user_cancel_reassign');
969
    // Enable account cancellation notification.
970
    variable_set('user_mail_status_canceled_notify', TRUE);
971

    
972
    // Create administrative user.
973
    $admin_user = $this->drupalCreateUser(array('administer users'));
974
    $this->drupalLogin($admin_user);
975

    
976
    // Create some users.
977
    $users = array();
978
    for ($i = 0; $i < 3; $i++) {
979
      $account = $this->drupalCreateUser(array());
980
      $users[$account->uid] = $account;
981
    }
982

    
983
    // Cancel user accounts, including own one.
984
    $edit = array();
985
    $edit['operation'] = 'cancel';
986
    foreach ($users as $uid => $account) {
987
      $edit['accounts[' . $uid . ']'] = TRUE;
988
    }
989
    $edit['accounts[' . $admin_user->uid . ']'] = TRUE;
990
    // Also try to cancel uid 1.
991
    $edit['accounts[1]'] = TRUE;
992
    $this->drupalPost('admin/people', $edit, t('Update'));
993
    $this->assertText(t('Are you sure you want to cancel these user accounts?'), 'Confirmation form to cancel accounts displayed.');
994
    $this->assertText(t('When cancelling these accounts'), 'Allows to select account cancellation method.');
995
    $this->assertText(t('Require e-mail confirmation to cancel account.'), 'Allows to send confirmation mail.');
996
    $this->assertText(t('Notify user when account is canceled.'), 'Allows to send notification mail.');
997

    
998
    // Confirm deletion.
999
    $this->drupalPost(NULL, NULL, t('Cancel accounts'));
1000
    $status = TRUE;
1001
    foreach ($users as $account) {
1002
      $status = $status && (strpos($this->content, t('%name has been deleted.', array('%name' => $account->name))) !== FALSE);
1003
      $status = $status && !user_load($account->uid, TRUE);
1004
    }
1005
    $this->assertTrue($status, 'Users deleted and not found in the database.');
1006

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

    
1012
    // Verify that uid 1's account was not cancelled.
1013
    $user1 = user_load(1, TRUE);
1014
    $this->assertEqual($user1->status, 1, 'User #1 still exists and is not blocked.');
1015
  }
1016
}
1017

    
1018
class UserPictureTestCase extends DrupalWebTestCase {
1019
  protected $user;
1020
  protected $_directory_test;
1021

    
1022
  public static function getInfo() {
1023
    return array(
1024
      'name' => 'Upload user picture',
1025
      'description' => 'Assure that dimension check, extension check and image scaling work as designed.',
1026
      'group' => 'User'
1027
    );
1028
  }
1029

    
1030
  function setUp() {
1031
    parent::setUp();
1032
    // Enable user pictures.
1033
    variable_set('user_pictures', 1);
1034

    
1035
    $this->user = $this->drupalCreateUser();
1036

    
1037
    // Test if directories specified in settings exist in filesystem.
1038
    $file_dir = 'public://';
1039
    $file_check = file_prepare_directory($file_dir, FILE_CREATE_DIRECTORY);
1040
    // TODO: Test public and private methods?
1041

    
1042
    $picture_dir = variable_get('user_picture_path', 'pictures');
1043
    $picture_path = $file_dir . $picture_dir;
1044

    
1045
    $pic_check = file_prepare_directory($picture_path, FILE_CREATE_DIRECTORY);
1046
    $this->_directory_test = is_writable($picture_path);
1047
    $this->assertTrue($this->_directory_test, "The directory $picture_path doesn't exist or is not writable. Further tests won't be made.");
1048
  }
1049

    
1050
  function testNoPicture() {
1051
    $this->drupalLogin($this->user);
1052

    
1053
    // Try to upload a file that is not an image for the user picture.
1054
    $not_an_image = current($this->drupalGetTestFiles('html'));
1055
    $this->saveUserPicture($not_an_image);
1056
    $this->assertRaw(t('Only JPEG, PNG and GIF images are allowed.'), 'Non-image files are not accepted.');
1057
  }
1058

    
1059
  /**
1060
   * Do the test:
1061
   *  GD Toolkit is installed
1062
   *  Picture has invalid dimension
1063
   *
1064
   * results: The image should be uploaded because ImageGDToolkit resizes the picture
1065
   */
1066
  function testWithGDinvalidDimension() {
1067
    if ($this->_directory_test && image_get_toolkit()) {
1068
      $this->drupalLogin($this->user);
1069

    
1070
      $image = current($this->drupalGetTestFiles('image'));
1071
      $info = image_get_info($image->uri);
1072

    
1073
      // Set new variables: invalid dimensions, valid filesize (0 = no limit).
1074
      $test_dim = ($info['width'] - 10) . 'x' . ($info['height'] - 10);
1075
      variable_set('user_picture_dimensions', $test_dim);
1076
      variable_set('user_picture_file_size', 0);
1077

    
1078
      $pic_path = $this->saveUserPicture($image);
1079
      // Check that the image was resized and is being displayed on the
1080
      // user's profile page.
1081
      $text = t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', array('%dimensions' => $test_dim));
1082
      $this->assertRaw($text, 'Image was resized.');
1083
      $alt = t("@user's picture", array('@user' => format_username($this->user)));
1084
      $style = variable_get('user_picture_style', '');
1085
      $this->assertRaw(check_plain(image_style_url($style, $pic_path)), "Image is displayed in user's edit page");
1086

    
1087
      // Check if file is located in proper directory.
1088
      $this->assertTrue(is_file($pic_path), "File is located in proper directory");
1089
    }
1090
  }
1091

    
1092
  /**
1093
   * Do the test:
1094
   *  GD Toolkit is installed
1095
   *  Picture has invalid size
1096
   *
1097
   * results: The image should be uploaded because ImageGDToolkit resizes the picture
1098
   */
1099
  function testWithGDinvalidSize() {
1100
    if ($this->_directory_test && image_get_toolkit()) {
1101
      $this->drupalLogin($this->user);
1102

    
1103
      // Images are sorted first by size then by name. We need an image
1104
      // bigger than 1 KB so we'll grab the last one.
1105
      $files = $this->drupalGetTestFiles('image');
1106
      $image = end($files);
1107
      $info = image_get_info($image->uri);
1108

    
1109
      // Set new variables: valid dimensions, invalid filesize.
1110
      $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
1111
      $test_size = 1;
1112
      variable_set('user_picture_dimensions', $test_dim);
1113
      variable_set('user_picture_file_size', $test_size);
1114

    
1115
      $pic_path = $this->saveUserPicture($image);
1116

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

    
1123
      // Check if file is not uploaded.
1124
      $this->assertFalse(is_file($pic_path), 'File was not uploaded.');
1125
    }
1126
  }
1127

    
1128
  /**
1129
   * Do the test:
1130
   *  GD Toolkit is not installed
1131
   *  Picture has invalid size
1132
   *
1133
   * results: The image shouldn't be uploaded
1134
   */
1135
  function testWithoutGDinvalidDimension() {
1136
    if ($this->_directory_test && !image_get_toolkit()) {
1137
      $this->drupalLogin($this->user);
1138

    
1139
      $image = current($this->drupalGetTestFiles('image'));
1140
      $info = image_get_info($image->uri);
1141

    
1142
      // Set new variables: invalid dimensions, valid filesize (0 = no limit).
1143
      $test_dim = ($info['width'] - 10) . 'x' . ($info['height'] - 10);
1144
      variable_set('user_picture_dimensions', $test_dim);
1145
      variable_set('user_picture_file_size', 0);
1146

    
1147
      $pic_path = $this->saveUserPicture($image);
1148

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

    
1155
      // Check if file is not uploaded.
1156
      $this->assertFalse(is_file($pic_path), 'File was not uploaded.');
1157
    }
1158
  }
1159

    
1160
  /**
1161
   * Do the test:
1162
   *  GD Toolkit is not installed
1163
   *  Picture has invalid size
1164
   *
1165
   * results: The image shouldn't be uploaded
1166
   */
1167
  function testWithoutGDinvalidSize() {
1168
    if ($this->_directory_test && !image_get_toolkit()) {
1169
      $this->drupalLogin($this->user);
1170

    
1171
      $image = current($this->drupalGetTestFiles('image'));
1172
      $info = image_get_info($image->uri);
1173

    
1174
      // Set new variables: valid dimensions, invalid filesize.
1175
      $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
1176
      $test_size = 1;
1177
      variable_set('user_picture_dimensions', $test_dim);
1178
      variable_set('user_picture_file_size', $test_size);
1179

    
1180
      $pic_path = $this->saveUserPicture($image);
1181

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

    
1188
      // Check if file is not uploaded.
1189
      $this->assertFalse(is_file($pic_path), 'File was not uploaded.');
1190
    }
1191
  }
1192

    
1193
  /**
1194
   * Do the test:
1195
   *  Picture is valid (proper size and dimension)
1196
   *
1197
   * results: The image should be uploaded
1198
   */
1199
  function testPictureIsValid() {
1200
    if ($this->_directory_test) {
1201
      $this->drupalLogin($this->user);
1202

    
1203
      $image = current($this->drupalGetTestFiles('image'));
1204
      $info = image_get_info($image->uri);
1205

    
1206
      // Set new variables: valid dimensions, valid filesize (0 = no limit).
1207
      $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
1208
      variable_set('user_picture_dimensions', $test_dim);
1209
      variable_set('user_picture_file_size', 0);
1210

    
1211
      $pic_path = $this->saveUserPicture($image);
1212

    
1213
      // Check if image is displayed in user's profile page.
1214
      $this->drupalGet('user');
1215
      $this->assertRaw(file_uri_target($pic_path), "Image is displayed in user's profile page");
1216

    
1217
      // Check if file is located in proper directory.
1218
      $this->assertTrue(is_file($pic_path), 'File is located in proper directory');
1219

    
1220
      // Set new picture dimensions.
1221
      $test_dim = ($info['width'] + 5) . 'x' . ($info['height'] + 5);
1222
      variable_set('user_picture_dimensions', $test_dim);
1223

    
1224
      $pic_path2 = $this->saveUserPicture($image);
1225
      $this->assertNotEqual($pic_path, $pic_path2, 'Filename of second picture is different.');
1226

    
1227
      // Check if user picture has a valid file ID after saving the user.
1228
      $account = user_load($this->user->uid, TRUE);
1229
      $this->assertTrue(is_object($account->picture), 'User picture object is valid after user load.');
1230
      $this->assertNotNull($account->picture->fid, 'User picture object has a FID after user load.');
1231
      $this->assertTrue(is_file($account->picture->uri), 'File is located in proper directory after user load.');
1232
      user_save($account);
1233
      // Verify that the user save does not destroy the user picture object.
1234
      $this->assertTrue(is_object($account->picture), 'User picture object is valid after user save.');
1235
      $this->assertNotNull($account->picture->fid, 'User picture object has a FID after user save.');
1236
      $this->assertTrue(is_file($account->picture->uri), 'File is located in proper directory after user save.');
1237
    }
1238
  }
1239

    
1240
  /**
1241
   * Test HTTP schema working with user pictures.
1242
   */
1243
  function testExternalPicture() {
1244
    $this->drupalLogin($this->user);
1245
    // Set the default picture to an URI with a HTTP schema.
1246
    $images = $this->drupalGetTestFiles('image');
1247
    $image = $images[0];
1248
    $pic_path = file_create_url($image->uri);
1249
    variable_set('user_picture_default', $pic_path);
1250

    
1251
    // Check if image is displayed in user's profile page.
1252
    $this->drupalGet('user');
1253

    
1254
    // Get the user picture image via xpath.
1255
    $elements = $this->xpath('//div[@class="user-picture"]/img');
1256
    $this->assertEqual(count($elements), 1, "There is exactly one user picture on the user's profile page");
1257
    $this->assertEqual($pic_path, (string) $elements[0]['src'], "User picture source is correct.");
1258
  }
1259

    
1260
  /**
1261
   * Tests deletion of user pictures.
1262
   */
1263
  function testDeletePicture() {
1264
    $this->drupalLogin($this->user);
1265

    
1266
    $image = current($this->drupalGetTestFiles('image'));
1267
    $info = image_get_info($image->uri);
1268

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

    
1274
    // Save a new picture.
1275
    $edit = array('files[picture_upload]' => drupal_realpath($image->uri));
1276
    $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save'));
1277

    
1278
    // Load actual user data from database.
1279
    $account = user_load($this->user->uid, TRUE);
1280
    $pic_path = isset($account->picture) ? $account->picture->uri : NULL;
1281

    
1282
    // Check if image is displayed in user's profile page.
1283
    $this->drupalGet('user');
1284
    $this->assertRaw(file_uri_target($pic_path), "Image is displayed in user's profile page");
1285

    
1286
    // Check if file is located in proper directory.
1287
    $this->assertTrue(is_file($pic_path), 'File is located in proper directory');
1288

    
1289
    $edit = array('picture_delete' => 1);
1290
    $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save'));
1291

    
1292
    // Load actual user data from database.
1293
    $account1 = user_load($this->user->uid, TRUE);
1294
    $this->assertNull($account1->picture, 'User object has no picture');
1295

    
1296
    $file = file_load($account->picture->fid);
1297
    $this->assertFalse($file, 'File is removed from database');
1298

    
1299
    // Clear out PHP's file stat cache so we see the current value.
1300
    clearstatcache();
1301
    $this->assertFalse(is_file($pic_path), 'File is removed from file system');
1302
  }
1303

    
1304
  function saveUserPicture($image) {
1305
    $edit = array('files[picture_upload]' => drupal_realpath($image->uri));
1306
    $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save'));
1307

    
1308
    // Load actual user data from database.
1309
    $account = user_load($this->user->uid, TRUE);
1310
    return isset($account->picture) ? $account->picture->uri : NULL;
1311
  }
1312

    
1313
  /**
1314
   * Tests the admin form validates user picture settings.
1315
   */
1316
  function testUserPictureAdminFormValidation() {
1317
    $this->drupalLogin($this->drupalCreateUser(array('administer users')));
1318

    
1319
    // The default values are valid.
1320
    $this->drupalPost('admin/config/people/accounts', array(), t('Save configuration'));
1321
    $this->assertText(t('The configuration options have been saved.'), 'The default values are valid.');
1322

    
1323
    // The form does not save with an invalid file size.
1324
    $edit = array(
1325
      'user_picture_file_size' => $this->randomName(),
1326
    );
1327
    $this->drupalPost('admin/config/people/accounts', $edit, t('Save configuration'));
1328
    $this->assertNoText(t('The configuration options have been saved.'), 'The form does not save with an invalid file size.');
1329
  }
1330
}
1331

    
1332

    
1333
class UserPermissionsTestCase extends DrupalWebTestCase {
1334
  protected $admin_user;
1335
  protected $rid;
1336

    
1337
  public static function getInfo() {
1338
    return array(
1339
      'name' => 'Role permissions',
1340
      'description' => 'Verify that role permissions can be added and removed via the permissions page.',
1341
      'group' => 'User'
1342
    );
1343
  }
1344

    
1345
  function setUp() {
1346
    parent::setUp();
1347

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

    
1350
    // Find the new role ID - it must be the maximum.
1351
    $all_rids = array_keys($this->admin_user->roles);
1352
    sort($all_rids);
1353
    $this->rid = array_pop($all_rids);
1354
  }
1355

    
1356
  /**
1357
   * Change user permissions and check user_access().
1358
   */
1359
  function testUserPermissionChanges() {
1360
    $this->drupalLogin($this->admin_user);
1361
    $rid = $this->rid;
1362
    $account = $this->admin_user;
1363

    
1364
    // Add a permission.
1365
    $this->assertFalse(user_access('administer nodes', $account), 'User does not have "administer nodes" permission.');
1366
    $edit = array();
1367
    $edit[$rid . '[administer nodes]'] = TRUE;
1368
    $this->drupalPost('admin/people/permissions', $edit, t('Save permissions'));
1369
    $this->assertText(t('The changes have been saved.'), 'Successful save message displayed.');
1370
    drupal_static_reset('user_access');
1371
    drupal_static_reset('user_role_permissions');
1372
    $this->assertTrue(user_access('administer nodes', $account), 'User now has "administer nodes" permission.');
1373

    
1374
    // Remove a permission.
1375
    $this->assertTrue(user_access('access user profiles', $account), 'User has "access user profiles" permission.');
1376
    $edit = array();
1377
    $edit[$rid . '[access user profiles]'] = FALSE;
1378
    $this->drupalPost('admin/people/permissions', $edit, t('Save permissions'));
1379
    $this->assertText(t('The changes have been saved.'), 'Successful save message displayed.');
1380
    drupal_static_reset('user_access');
1381
    drupal_static_reset('user_role_permissions');
1382
    $this->assertFalse(user_access('access user profiles', $account), 'User no longer has "access user profiles" permission.');
1383
  }
1384

    
1385
  /**
1386
   * Test assigning of permissions for the administrator role.
1387
   */
1388
  function testAdministratorRole() {
1389
    $this->drupalLogin($this->admin_user);
1390
    $this->drupalGet('admin/config/people/accounts');
1391

    
1392
    // Set the user's role to be the administrator role.
1393
    $edit = array();
1394
    $edit['user_admin_role'] = $this->rid;
1395
    $this->drupalPost('admin/config/people/accounts', $edit, t('Save configuration'));
1396

    
1397
    // Enable aggregator module and ensure the 'administer news feeds'
1398
    // permission is assigned by default.
1399
    $edit = array();
1400
    $edit['modules[Core][aggregator][enable]'] = TRUE;
1401
    $this->drupalPost('admin/modules', $edit, t('Save configuration'));
1402
    $this->assertTrue(user_access('administer news feeds', $this->admin_user), 'The permission was automatically assigned to the administrator role');
1403
  }
1404

    
1405
  /**
1406
   * Verify proper permission changes by user_role_change_permissions().
1407
   */
1408
  function testUserRoleChangePermissions() {
1409
    $rid = $this->rid;
1410
    $account = $this->admin_user;
1411

    
1412
    // Verify current permissions.
1413
    $this->assertFalse(user_access('administer nodes', $account), 'User does not have "administer nodes" permission.');
1414
    $this->assertTrue(user_access('access user profiles', $account), 'User has "access user profiles" permission.');
1415
    $this->assertTrue(user_access('administer site configuration', $account), 'User has "administer site configuration" permission.');
1416

    
1417
    // Change permissions.
1418
    $permissions = array(
1419
      'administer nodes' => 1,
1420
      'access user profiles' => 0,
1421
    );
1422
    user_role_change_permissions($rid, $permissions);
1423

    
1424
    // Verify proper permission changes.
1425
    $this->assertTrue(user_access('administer nodes', $account), 'User now has "administer nodes" permission.');
1426
    $this->assertFalse(user_access('access user profiles', $account), 'User no longer has "access user profiles" permission.');
1427
    $this->assertTrue(user_access('administer site configuration', $account), 'User still has "administer site configuration" permission.');
1428
  }
1429
}
1430

    
1431
class UserAdminTestCase extends DrupalWebTestCase {
1432
  public static function getInfo() {
1433
    return array(
1434
      'name' => 'User administration',
1435
      'description' => 'Test user administration page functionality.',
1436
      'group' => 'User'
1437
    );
1438
  }
1439

    
1440
  /**
1441
   * Registers a user and deletes it.
1442
   */
1443
  function testUserAdmin() {
1444

    
1445
    $user_a = $this->drupalCreateUser(array());
1446
    $user_b = $this->drupalCreateUser(array('administer taxonomy'));
1447
    $user_c = $this->drupalCreateUser(array('administer taxonomy'));
1448

    
1449
    // Create admin user to delete registered user.
1450
    $admin_user = $this->drupalCreateUser(array('administer users'));
1451
    $this->drupalLogin($admin_user);
1452
    $this->drupalGet('admin/people');
1453
    $this->assertText($user_a->name, 'Found user A on admin users page');
1454
    $this->assertText($user_b->name, 'Found user B on admin users page');
1455
    $this->assertText($user_c->name, 'Found user C on admin users page');
1456
    $this->assertText($admin_user->name, 'Found Admin user on admin users page');
1457

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

    
1462
    // Filter the users by permission 'administer taxonomy'.
1463
    $edit = array();
1464
    $edit['permission'] = 'administer taxonomy';
1465
    $this->drupalPost('admin/people', $edit, t('Filter'));
1466

    
1467
    // Check if the correct users show up.
1468
    $this->assertNoText($user_a->name, 'User A not on filtered by perm admin users page');
1469
    $this->assertText($user_b->name, 'Found user B on filtered by perm admin users page');
1470
    $this->assertText($user_c->name, 'Found user C on filtered by perm admin users page');
1471

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

    
1476
    // Check if the correct users show up when filtered by role.
1477
    $this->assertNoText($user_a->name, 'User A not on filtered by role on admin users page');
1478
    $this->assertNoText($user_b->name, 'User B not on filtered by role on admin users page');
1479
    $this->assertText($user_c->name, 'User C on filtered by role on admin users page');
1480

    
1481
    // Test blocking of a user.
1482
    $account = user_load($user_c->uid);
1483
    $this->assertEqual($account->status, 1, 'User C not blocked');
1484
    $edit = array();
1485
    $edit['operation'] = 'block';
1486
    $edit['accounts[' . $account->uid . ']'] = TRUE;
1487
    $this->drupalPost('admin/people', $edit, t('Update'));
1488
    $account = user_load($user_c->uid, TRUE);
1489
    $this->assertEqual($account->status, 0, 'User C blocked');
1490

    
1491
    // Test unblocking of a user from /admin/people page and sending of activation mail
1492
    $editunblock = array();
1493
    $editunblock['operation'] = 'unblock';
1494
    $editunblock['accounts[' . $account->uid . ']'] = TRUE;
1495
    $this->drupalPost('admin/people', $editunblock, t('Update'));
1496
    $account = user_load($user_c->uid, TRUE);
1497
    $this->assertEqual($account->status, 1, 'User C unblocked');
1498
    $this->assertMail("to", $account->mail, "Activation mail sent to user C");
1499

    
1500
    // Test blocking and unblocking another user from /user/[uid]/edit form and sending of activation mail
1501
    $user_d = $this->drupalCreateUser(array());
1502
    $account1 = user_load($user_d->uid, TRUE);
1503
    $this->drupalPost('user/' . $account1->uid . '/edit', array('status' => 0), t('Save'));
1504
    $account1 = user_load($user_d->uid, TRUE);
1505
    $this->assertEqual($account1->status, 0, 'User D blocked');
1506
    $this->drupalPost('user/' . $account1->uid . '/edit', array('status' => TRUE), t('Save'));
1507
    $account1 = user_load($user_d->uid, TRUE);
1508
    $this->assertEqual($account1->status, 1, 'User D unblocked');
1509
    $this->assertMail("to", $account1->mail, "Activation mail sent to user D");
1510
  }
1511
}
1512

    
1513
/**
1514
 * Tests for user-configurable time zones.
1515
 */
1516
class UserTimeZoneFunctionalTest extends DrupalWebTestCase {
1517
  public static function getInfo() {
1518
    return array(
1519
      'name' => 'User time zones',
1520
      'description' => 'Set a user time zone and verify that dates are displayed in local time.',
1521
      'group' => 'User',
1522
    );
1523
  }
1524

    
1525
  /**
1526
   * Tests the display of dates and time when user-configurable time zones are set.
1527
   */
1528
  function testUserTimeZone() {
1529
    // Setup date/time settings for Los Angeles time.
1530
    variable_set('date_default_timezone', 'America/Los_Angeles');
1531
    variable_set('configurable_timezones', 1);
1532
    variable_set('date_format_medium', 'Y-m-d H:i T');
1533

    
1534
    // Create a user account and login.
1535
    $web_user = $this->drupalCreateUser();
1536
    $this->drupalLogin($web_user);
1537

    
1538
    // Create some nodes with different authored-on dates.
1539
    // Two dates in PST (winter time):
1540
    $date1 = '2007-03-09 21:00:00 -0800';
1541
    $date2 = '2007-03-11 01:00:00 -0800';
1542
    // One date in PDT (summer time):
1543
    $date3 = '2007-03-20 21:00:00 -0700';
1544
    $node1 = $this->drupalCreateNode(array('created' => strtotime($date1), 'type' => 'article'));
1545
    $node2 = $this->drupalCreateNode(array('created' => strtotime($date2), 'type' => 'article'));
1546
    $node3 = $this->drupalCreateNode(array('created' => strtotime($date3), 'type' => 'article'));
1547

    
1548
    // Confirm date format and time zone.
1549
    $this->drupalGet("node/$node1->nid");
1550
    $this->assertText('2007-03-09 21:00 PST', 'Date should be PST.');
1551
    $this->drupalGet("node/$node2->nid");
1552
    $this->assertText('2007-03-11 01:00 PST', 'Date should be PST.');
1553
    $this->drupalGet("node/$node3->nid");
1554
    $this->assertText('2007-03-20 21:00 PDT', 'Date should be PDT.');
1555

    
1556
    // Change user time zone to Santiago time.
1557
    $edit = array();
1558
    $edit['mail'] = $web_user->mail;
1559
    $edit['timezone'] = 'America/Santiago';
1560
    $this->drupalPost("user/$web_user->uid/edit", $edit, t('Save'));
1561
    $this->assertText(t('The changes have been saved.'), 'Time zone changed to Santiago time.');
1562

    
1563
    // Confirm date format and time zone.
1564
    $this->drupalGet("node/$node1->nid");
1565
    $this->assertText('2007-03-10 02:00 CLST', 'Date should be Chile summer time; five hours ahead of PST.');
1566
    $this->drupalGet("node/$node2->nid");
1567
    $this->assertText('2007-03-11 05:00 CLT', 'Date should be Chile time; four hours ahead of PST');
1568
    $this->drupalGet("node/$node3->nid");
1569
    $this->assertText('2007-03-21 00:00 CLT', 'Date should be Chile time; three hours ahead of PDT.');
1570
  }
1571
}
1572

    
1573
/**
1574
 * Test user autocompletion.
1575
 */
1576
class UserAutocompleteTestCase extends DrupalWebTestCase {
1577
  public static function getInfo() {
1578
    return array(
1579
      'name' => 'User autocompletion',
1580
      'description' => 'Test user autocompletion functionality.',
1581
      'group' => 'User'
1582
    );
1583
  }
1584

    
1585
  function setUp() {
1586
    parent::setUp();
1587

    
1588
    // Set up two users with different permissions to test access.
1589
    $this->unprivileged_user = $this->drupalCreateUser();
1590
    $this->privileged_user = $this->drupalCreateUser(array('access user profiles'));
1591
  }
1592

    
1593
  /**
1594
   * Tests access to user autocompletion and verify the correct results.
1595
   */
1596
  function testUserAutocomplete() {
1597
    // Check access from unprivileged user, should be denied.
1598
    $this->drupalLogin($this->unprivileged_user);
1599
    $this->drupalGet('user/autocomplete/' . $this->unprivileged_user->name[0]);
1600
    $this->assertResponse(403, 'Autocompletion access denied to user without permission.');
1601

    
1602
    // Check access from privileged user.
1603
    $this->drupalLogout();
1604
    $this->drupalLogin($this->privileged_user);
1605
    $this->drupalGet('user/autocomplete/' . $this->unprivileged_user->name[0]);
1606
    $this->assertResponse(200, 'Autocompletion access allowed.');
1607

    
1608
    // Using first letter of the user's name, make sure the user's full name is in the results.
1609
    $this->assertRaw($this->unprivileged_user->name, 'User name found in autocompletion results.');
1610
  }
1611
}
1612

    
1613

    
1614
/**
1615
 * Tests user links in the secondary menu.
1616
 */
1617
class UserAccountLinksUnitTests extends DrupalWebTestCase {
1618
  public static function getInfo() {
1619
    return array(
1620
      'name' => 'User account links',
1621
      'description' => 'Test user-account links.',
1622
      'group' => 'User'
1623
    );
1624
  }
1625

    
1626
  function setUp() {
1627
    parent::setUp('menu');
1628
  }
1629

    
1630
  /**
1631
   * Tests the secondary menu.
1632
   */
1633
  function testSecondaryMenu() {
1634
    // Create a regular user.
1635
    $user = $this->drupalCreateUser(array());
1636

    
1637
    // Log in and get the homepage.
1638
    $this->drupalLogin($user);
1639
    $this->drupalGet('<front>');
1640

    
1641
    // For a logged-in user, expect the secondary menu to have links for "My
1642
    // account" and "Log out".
1643
    $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array(
1644
      ':menu_id' => 'secondary-menu-links',
1645
      ':href' => 'user',
1646
      ':text' => 'My account',
1647
    ));
1648
    $this->assertEqual(count($link), 1, 'My account link is in secondary menu.');
1649

    
1650
    $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array(
1651
      ':menu_id' => 'secondary-menu-links',
1652
      ':href' => 'user/logout',
1653
      ':text' => 'Log out',
1654
    ));
1655
    $this->assertEqual(count($link), 1, 'Log out link is in secondary menu.');
1656

    
1657
    // Log out and get the homepage.
1658
    $this->drupalLogout();
1659
    $this->drupalGet('<front>');
1660

    
1661
    // For a logged-out user, expect no secondary links.
1662
    $element = $this->xpath('//ul[@id=:menu_id]', array(':menu_id' => 'secondary-menu-links'));
1663
    $this->assertEqual(count($element), 0, 'No secondary-menu for logged-out users.');
1664
  }
1665

    
1666
  /**
1667
   * Tests disabling the 'My account' link.
1668
   */
1669
  function testDisabledAccountLink() {
1670
    // Create an admin user and log in.
1671
    $this->drupalLogin($this->drupalCreateUser(array('access administration pages', 'administer menu')));
1672

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

    
1678
    // Disable the 'My account' link.
1679
    $input = $this->xpath('//input[@id=:field_id]/@name', array(':field_id' => (string)$label[0]));
1680
    $edit = array(
1681
      (string) $input[0] => FALSE,
1682
    );
1683
    $this->drupalPost('admin/structure/menu/manage/user-menu', $edit, t('Save configuration'));
1684

    
1685
    // Get the homepage.
1686
    $this->drupalGet('<front>');
1687

    
1688
    // Verify that the 'My account' link does not appear when disabled.
1689
    $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array(
1690
      ':menu_id' => 'secondary-menu-links',
1691
      ':href' => 'user',
1692
      ':text' => 'My account',
1693
    ));
1694
    $this->assertEqual(count($link), 0, 'My account link is not in the secondary menu.');
1695
  }
1696

    
1697
}
1698

    
1699
/**
1700
 * Test user blocks.
1701
 */
1702
class UserBlocksUnitTests extends DrupalWebTestCase {
1703
  public static function getInfo() {
1704
    return array(
1705
      'name' => 'User blocks',
1706
      'description' => 'Test user blocks.',
1707
      'group' => 'User'
1708
    );
1709
  }
1710

    
1711
  /**
1712
   * Test the user login block.
1713
   */
1714
  function testUserLoginBlock() {
1715
    // Create a user with some permission that anonymous users lack.
1716
    $user = $this->drupalCreateUser(array('administer permissions'));
1717

    
1718
    // Log in using the block.
1719
    $edit = array();
1720
    $edit['name'] = $user->name;
1721
    $edit['pass'] = $user->pass_raw;
1722
    $this->drupalPost('admin/people/permissions', $edit, t('Log in'));
1723
    $this->assertNoText(t('User login'), 'Logged in.');
1724

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

    
1728
    // Now, log out and repeat with a non-403 page.
1729
    $this->drupalLogout();
1730
    $this->drupalPost('filter/tips', $edit, t('Log in'));
1731
    $this->assertNoText(t('User login'), 'Logged in.');
1732
    $this->assertPattern('!<title.*?' . t('Compose tips') . '.*?</title>!', 'Still on the same page after login for allowed page');
1733

    
1734
    // Check that the user login block is not vulnerable to information
1735
    // disclosure to third party sites.
1736
    $this->drupalLogout();
1737
    $this->drupalPost('http://example.com/', $edit, t('Log in'), array('external' => FALSE));
1738
    // Check that we remain on the site after login.
1739
    $this->assertEqual(url('user/' . $user->uid, array('absolute' => TRUE)), $this->getUrl(), 'Redirected to user profile page after login from the frontpage');
1740
  }
1741

    
1742
  /**
1743
   * Test the Who's Online block.
1744
   */
1745
  function testWhosOnlineBlock() {
1746
    // Generate users and make sure there are no current user sessions.
1747
    $user1 = $this->drupalCreateUser(array());
1748
    $user2 = $this->drupalCreateUser(array());
1749
    $user3 = $this->drupalCreateUser(array());
1750
    $this->assertEqual(db_query("SELECT COUNT(*) FROM {sessions}")->fetchField(), 0, 'Sessions table is empty.');
1751

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

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

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

    
1763
    // Insert two anonymous user sessions.
1764
    $this->insertSession();
1765
    $this->insertSession();
1766

    
1767
    // Test block output.
1768
    $block = user_block_view('online');
1769
    $this->drupalSetContent($block['content']);
1770
    $this->assertRaw(t('2 users'), 'Correct number of online users (2 users).');
1771
    $this->assertText($user1->name, 'Active user 1 found in online list.');
1772
    $this->assertText($user2->name, 'Active user 2 found in online list.');
1773
    $this->assertNoText($user3->name, "Inactive user not found in online list.");
1774
    $this->assertTrue(strpos($this->drupalGetContent(), $user1->name) > strpos($this->drupalGetContent(), $user2->name), 'Online users are ordered correctly.');
1775
  }
1776

    
1777
  /**
1778
   * Insert a user session into the {sessions} table. This function is used
1779
   * since we cannot log in more than one user at the same time in tests.
1780
   */
1781
  private function insertSession(array $fields = array()) {
1782
    $fields += array(
1783
      'uid' => 0,
1784
      'sid' => drupal_hash_base64(uniqid(mt_rand(), TRUE)),
1785
      'timestamp' => REQUEST_TIME,
1786
    );
1787
    db_insert('sessions')
1788
      ->fields($fields)
1789
      ->execute();
1790
    $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.');
1791
  }
1792
}
1793

    
1794
/**
1795
 * Tests saving a user account.
1796
 */
1797
class UserSaveTestCase extends DrupalWebTestCase {
1798

    
1799
  public static function getInfo() {
1800
    return array(
1801
      'name' => 'User save test',
1802
      'description' => 'Test user_save() for arbitrary new uid.',
1803
      'group' => 'User',
1804
    );
1805
  }
1806

    
1807
  /**
1808
   * Test creating a user with arbitrary uid.
1809
   */
1810
  function testUserImport() {
1811
    // User ID must be a number that is not in the database.
1812
    $max_uid = db_query('SELECT MAX(uid) FROM {users}')->fetchField();
1813
    $test_uid = $max_uid + mt_rand(1000, 1000000);
1814
    $test_name = $this->randomName();
1815

    
1816
    // Create the base user, based on drupalCreateUser().
1817
    $user = array(
1818
      'name' => $test_name,
1819
      'uid' => $test_uid,
1820
      'mail' => $test_name . '@example.com',
1821
      'is_new' => TRUE,
1822
      'pass' => user_password(),
1823
      'status' => 1,
1824
    );
1825
    $user_by_return = user_save(drupal_anonymous_user(), $user);
1826
    $this->assertTrue($user_by_return, 'Loading user by return of user_save().');
1827

    
1828
    // Test if created user exists.
1829
    $user_by_uid = user_load($test_uid);
1830
    $this->assertTrue($user_by_uid, 'Loading user by uid.');
1831

    
1832
    $user_by_name = user_load_by_name($test_name);
1833
    $this->assertTrue($user_by_name, 'Loading user by name.');
1834
  }
1835
}
1836

    
1837
/**
1838
 * Test the create user administration page.
1839
 */
1840
class UserCreateTestCase extends DrupalWebTestCase {
1841

    
1842
  public static function getInfo() {
1843
    return array(
1844
      'name' => 'User create',
1845
      'description' => 'Test the create user administration page.',
1846
      'group' => 'User',
1847
    );
1848
  }
1849

    
1850
  /**
1851
   * Create a user through the administration interface and ensure that it
1852
   * displays in the user list.
1853
   */
1854
  protected function testUserAdd() {
1855
    $user = $this->drupalCreateUser(array('administer users'));
1856
    $this->drupalLogin($user);
1857

    
1858
    foreach (array(FALSE, TRUE) as $notify) {
1859
      $edit = array(
1860
        'name' => $this->randomName(),
1861
        'mail' => $this->randomName() . '@example.com',
1862
        'pass[pass1]' => $pass = $this->randomString(),
1863
        'pass[pass2]' => $pass,
1864
        'notify' => $notify,
1865
      );
1866
      $this->drupalPost('admin/people/create', $edit, t('Create new account'));
1867

    
1868
      if ($notify) {
1869
        $this->assertText(t('A welcome message with further instructions has been e-mailed to the new user @name.', array('@name' => $edit['name'])), 'User created');
1870
        $this->assertEqual(count($this->drupalGetMails()), 1, 'Notification e-mail sent');
1871
      }
1872
      else {
1873
        $this->assertText(t('Created a new user account for @name. No e-mail has been sent.', array('@name' => $edit['name'])), 'User created');
1874
        $this->assertEqual(count($this->drupalGetMails()), 0, 'Notification e-mail not sent');
1875
      }
1876

    
1877
      $this->drupalGet('admin/people');
1878
      $this->assertText($edit['name'], 'User found in list of users');
1879
    }
1880

    
1881
    // Test that the password '0' is considered a password.
1882
    $name = $this->randomName();
1883
    $edit = array(
1884
      'name' => $name,
1885
      'mail' => $name . '@example.com',
1886
      'pass[pass1]' => 0,
1887
      'pass[pass2]' => 0,
1888
      'notify' => FALSE,
1889
    );
1890
    $this->drupalPost('admin/people/create', $edit, t('Create new account'));
1891
    $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');
1892
    $this->assertNoText('Password field is required');
1893
  }
1894
}
1895

    
1896
/**
1897
 * Tests editing a user account.
1898
 */
1899
class UserEditTestCase extends DrupalWebTestCase {
1900

    
1901
  public static function getInfo() {
1902
    return array(
1903
      'name' => 'User edit',
1904
      'description' => 'Test user edit page.',
1905
      'group' => 'User',
1906
    );
1907
  }
1908

    
1909
  /**
1910
   * Test user edit page.
1911
   */
1912
  function testUserEdit() {
1913
    // Test user edit functionality with user pictures disabled.
1914
    variable_set('user_pictures', 0);
1915
    $user1 = $this->drupalCreateUser(array('change own username'));
1916
    $user2 = $this->drupalCreateUser(array());
1917
    $this->drupalLogin($user1);
1918

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

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

    
1929
    // Check that filling out a single password field does not validate.
1930
    $edit = array();
1931
    $edit['pass[pass1]'] = '';
1932
    $edit['pass[pass2]'] = $this->randomName();
1933
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
1934
    $this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.');
1935

    
1936
    $edit['pass[pass1]'] = $this->randomName();
1937
    $edit['pass[pass2]'] = '';
1938
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
1939
    $this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.');
1940

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

    
1948
    $edit['current_pass'] = $user1->pass_raw;
1949
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
1950
    $this->assertRaw(t("The changes have been saved."));
1951

    
1952
    // Test that the user must enter current password before changing passwords.
1953
    $edit = array();
1954
    $edit['pass[pass1]'] = $new_pass = $this->randomName();
1955
    $edit['pass[pass2]'] = $new_pass;
1956
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
1957
    $this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => t('Password'))));
1958

    
1959
    // Try again with the current password.
1960
    $edit['current_pass'] = $user1->pass_raw;
1961
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
1962
    $this->assertRaw(t("The changes have been saved."));
1963

    
1964
    // Make sure the user can log in with their new password.
1965
    $this->drupalLogout();
1966
    $user1->pass_raw = $new_pass;
1967
    $this->drupalLogin($user1);
1968
    $this->drupalLogout();
1969
  }
1970

    
1971
  /**
1972
   * Tests setting the password to "0".
1973
   */
1974
  public function testUserWith0Password() {
1975
    $admin = $this->drupalCreateUser(array('administer users'));
1976
    $this->drupalLogin($admin);
1977
    // Create a regular user.
1978
    $user1 = $this->drupalCreateUser(array());
1979

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

    
1984
    $this->drupalLogout();
1985
    $user1->pass_raw = '0';
1986
    $this->drupalLogin($user1);
1987
    $this->drupalLogout();
1988
  }
1989
}
1990

    
1991
/**
1992
 * Tests editing a user account with and without a form rebuild.
1993
 */
1994
class UserEditRebuildTestCase extends DrupalWebTestCase {
1995

    
1996
  public static function getInfo() {
1997
    return array(
1998
      'name' => 'User edit with form rebuild',
1999
      'description' => 'Test user edit page when a form rebuild is triggered.',
2000
      'group' => 'User',
2001
    );
2002
  }
2003

    
2004
  function setUp() {
2005
    parent::setUp('user_form_test');
2006
  }
2007

    
2008
  /**
2009
   * Test user edit page when the form is set to rebuild.
2010
   */
2011
  function testUserEditFormRebuild() {
2012
    $user1 = $this->drupalCreateUser(array('change own username'));
2013
    $this->drupalLogin($user1);
2014

    
2015
    $roles = array_keys($user1->roles);
2016
    // Save the user form twice.
2017
    $edit = array();
2018
    $edit['current_pass'] = $user1->pass_raw;
2019
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
2020
    $this->assertRaw(t("The changes have been saved."));
2021
    $this->drupalPost(NULL, $edit, t('Save'));
2022
    $this->assertRaw(t("The changes have been saved."));
2023
    $saved_user1 = entity_load_unchanged('user', $user1->uid);
2024
    $this->assertEqual(count($roles), count($saved_user1->roles), 'Count of user roles in database matches original count.');
2025
    $diff = array_diff(array_keys($saved_user1->roles), $roles);
2026
    $this->assertTrue(empty($diff), format_string('User roles in database match original: @roles', array('@roles' => implode(', ', $saved_user1->roles))));
2027
    // Set variable that causes the form to be rebuilt in user_form_test.module.
2028
    variable_set('user_form_test_user_profile_form_rebuild', TRUE);
2029
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
2030
    $this->assertRaw(t("The changes have been saved."));
2031
    $this->drupalPost(NULL, $edit, t('Save'));
2032
    $this->assertRaw(t("The changes have been saved."));
2033
    $saved_user1 = entity_load_unchanged('user', $user1->uid);
2034
    $this->assertEqual(count($roles), count($saved_user1->roles), 'Count of user roles in database matches original count.');
2035
    $diff = array_diff(array_keys($saved_user1->roles), $roles);
2036
    $this->assertTrue(empty($diff), format_string('User roles in database match original: @roles', array('@roles' => implode(', ', $saved_user1->roles))));
2037
  }
2038
}
2039

    
2040
/**
2041
 * Test case for user signatures.
2042
 */
2043
class UserSignatureTestCase extends DrupalWebTestCase {
2044
  public static function getInfo() {
2045
    return array(
2046
      'name' => 'User signatures',
2047
      'description' => 'Test user signatures.',
2048
      'group' => 'User',
2049
    );
2050
  }
2051

    
2052
  function setUp() {
2053
    parent::setUp('comment');
2054

    
2055
    // Enable user signatures.
2056
    variable_set('user_signatures', 1);
2057

    
2058
    // Prefetch text formats.
2059
    $this->full_html_format = filter_format_load('full_html');
2060
    $this->plain_text_format = filter_format_load('plain_text');
2061

    
2062
    // Create regular and administrative users.
2063
    $this->web_user = $this->drupalCreateUser(array());
2064
    $admin_permissions = array('administer comments');
2065
    foreach (filter_formats() as $format) {
2066
      if ($permission = filter_permission_name($format)) {
2067
        $admin_permissions[] = $permission;
2068
      }
2069
    }
2070
    $this->admin_user = $this->drupalCreateUser($admin_permissions);
2071
  }
2072

    
2073
  /**
2074
   * Test that a user can change their signature format and that it is respected
2075
   * upon display.
2076
   */
2077
  function testUserSignature() {
2078
    // Create a new node with comments on.
2079
    $node = $this->drupalCreateNode(array('comment' => COMMENT_NODE_OPEN));
2080

    
2081
    // Verify that user signature field is not displayed on registration form.
2082
    $this->drupalGet('user/register');
2083
    $this->assertNoText(t('Signature'));
2084

    
2085
    // Log in as a regular user and create a signature.
2086
    $this->drupalLogin($this->web_user);
2087
    $signature_text = "<h1>" . $this->randomName() . "</h1>";
2088
    $edit = array(
2089
      'signature[value]' => $signature_text,
2090
      'signature[format]' => $this->plain_text_format->format,
2091
    );
2092
    $this->drupalPost('user/' . $this->web_user->uid . '/edit', $edit, t('Save'));
2093

    
2094
    // Verify that values were stored.
2095
    $this->assertFieldByName('signature[value]', $edit['signature[value]'], 'Submitted signature text found.');
2096
    $this->assertFieldByName('signature[format]', $edit['signature[format]'], 'Submitted signature format found.');
2097

    
2098
    // Create a comment.
2099
    $langcode = LANGUAGE_NONE;
2100
    $edit = array();
2101
    $edit['subject'] = $this->randomName(8);
2102
    $edit['comment_body[' . $langcode . '][0][value]'] = $this->randomName(16);
2103
    $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview'));
2104
    $this->drupalPost(NULL, array(), t('Save'));
2105

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

    
2111
    // Log in as an administrator and edit the comment to use Full HTML, so
2112
    // that the comment text itself is not filtered at all.
2113
    $this->drupalLogin($this->admin_user);
2114
    $edit['comment_body[' . $langcode . '][0][format]'] = $this->full_html_format->format;
2115
    $this->drupalPost('comment/' . $comment_id . '/edit', $edit, t('Save'));
2116

    
2117
    // Assert that the signature did not make it through unfiltered.
2118
    $this->drupalGet('node/' . $node->nid);
2119
    $this->assertNoRaw($signature_text, 'Unfiltered signature text not found.');
2120
    $this->assertRaw(check_markup($signature_text, $this->plain_text_format->format), 'Filtered signature text found.');
2121
  }
2122
}
2123

    
2124
/*
2125
 * Test that a user, having editing their own account, can still log in.
2126
 */
2127
class UserEditedOwnAccountTestCase extends DrupalWebTestCase {
2128

    
2129
  public static function getInfo() {
2130
    return array(
2131
      'name' => 'User edited own account',
2132
      'description' => 'Test user edited own account can still log in.',
2133
      'group' => 'User',
2134
    );
2135
  }
2136

    
2137
  function testUserEditedOwnAccount() {
2138
    // Change account setting 'Who can register accounts?' to Administrators
2139
    // only.
2140
    variable_set('user_register', USER_REGISTER_ADMINISTRATORS_ONLY);
2141

    
2142
    // Create a new user account and log in.
2143
    $account = $this->drupalCreateUser(array('change own username'));
2144
    $this->drupalLogin($account);
2145

    
2146
    // Change own username.
2147
    $edit = array();
2148
    $edit['name'] = $this->randomName();
2149
    $this->drupalPost('user/' . $account->uid . '/edit', $edit, t('Save'));
2150

    
2151
    // Log out.
2152
    $this->drupalLogout();
2153

    
2154
    // Set the new name on the user account and attempt to log back in.
2155
    $account->name = $edit['name'];
2156
    $this->drupalLogin($account);
2157
  }
2158
}
2159

    
2160
/**
2161
 * Test case to test adding, editing and deleting roles.
2162
 */
2163
class UserRoleAdminTestCase extends DrupalWebTestCase {
2164

    
2165
  public static function getInfo() {
2166
    return array(
2167
      'name' => 'User role administration',
2168
      'description' => 'Test adding, editing and deleting user roles and changing role weights.',
2169
      'group' => 'User',
2170
    );
2171
  }
2172

    
2173
  function setUp() {
2174
    parent::setUp();
2175
    $this->admin_user = $this->drupalCreateUser(array('administer permissions', 'administer users'));
2176
  }
2177

    
2178
  /**
2179
   * Test adding, renaming and deleting roles.
2180
   */
2181
  function testRoleAdministration() {
2182
    $this->drupalLogin($this->admin_user);
2183

    
2184
    // Test adding a role. (In doing so, we use a role name that happens to
2185
    // correspond to an integer, to test that the role administration pages
2186
    // correctly distinguish between role names and IDs.)
2187
    $role_name = '123';
2188
    $edit = array('name' => $role_name);
2189
    $this->drupalPost('admin/people/permissions/roles', $edit, t('Add role'));
2190
    $this->assertText(t('The role has been added.'), 'The role has been added.');
2191
    $role = user_role_load_by_name($role_name);
2192
    $this->assertTrue(is_object($role), 'The role was successfully retrieved from the database.');
2193

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

    
2198
    // Test renaming a role.
2199
    $old_name = $role_name;
2200
    $role_name = '456';
2201
    $edit = array('name' => $role_name);
2202
    $this->drupalPost("admin/people/permissions/roles/edit/{$role->rid}", $edit, t('Save role'));
2203
    $this->assertText(t('The role has been renamed.'), 'The role has been renamed.');
2204
    $this->assertFalse(user_role_load_by_name($old_name), 'The role can no longer be retrieved from the database using its old name.');
2205
    $this->assertTrue(is_object(user_role_load_by_name($role_name)), 'The role can be retrieved from the database using its new name.');
2206

    
2207
    // Test deleting the default administrator role.
2208
    $role_name = 'administrator';
2209
    $role = user_role_load_by_name($role_name);
2210
    $this->drupalPost("admin/people/permissions/roles/edit/{$role->rid}", NULL, t('Delete role'));
2211
    $this->drupalPost(NULL, NULL, t('Delete'));
2212
    $this->assertText(t('The role has been deleted.'), 'The role has been deleted');
2213
    $this->assertNoLinkByHref("admin/people/permissions/roles/edit/{$role->rid}", 'Role edit link removed.');
2214
    $this->assertFalse(user_role_load_by_name($role_name), 'A deleted role can no longer be loaded.');
2215
    // Make sure this role is no longer configured as the administrator role.
2216
    $this->assertNull(variable_get('user_admin_role'), 'The administrator role is no longer configured as the administrator role.');
2217

    
2218
    // Make sure that the system-defined roles cannot be edited via the user
2219
    // interface.
2220
    $this->drupalGet('admin/people/permissions/roles/edit/' . DRUPAL_ANONYMOUS_RID);
2221
    $this->assertResponse(403, 'Access denied when trying to edit the built-in anonymous role.');
2222
    $this->drupalGet('admin/people/permissions/roles/edit/' . DRUPAL_AUTHENTICATED_RID);
2223
    $this->assertResponse(403, 'Access denied when trying to edit the built-in authenticated role.');
2224
  }
2225

    
2226
  /**
2227
   * Test user role weight change operation.
2228
   */
2229
  function testRoleWeightChange() {
2230
    $this->drupalLogin($this->admin_user);
2231

    
2232
    // Pick up a random role and get its weight.
2233
    $rid = array_rand(user_roles());
2234
    $role = user_role_load($rid);
2235
    $old_weight = $role->weight;
2236

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

    
2242
    // Retrieve the saved role and compare its weight.
2243
    $role = user_role_load($rid);
2244
    $new_weight = $role->weight;
2245
    $this->assertTrue(($old_weight + 1) == $new_weight, 'Role weight updated successfully.');
2246
  }
2247
}
2248

    
2249
/**
2250
 * Test user token replacement in strings.
2251
 */
2252
class UserTokenReplaceTestCase extends DrupalWebTestCase {
2253
  public static function getInfo() {
2254
    return array(
2255
      'name' => 'User token replacement',
2256
      'description' => 'Generates text using placeholders for dummy content to check user token replacement.',
2257
      'group' => 'User',
2258
    );
2259
  }
2260

    
2261
  /**
2262
   * Creates a user, then tests the tokens generated from it.
2263
   */
2264
  function testUserTokenReplacement() {
2265
    global $language;
2266
    $url_options = array(
2267
      'absolute' => TRUE,
2268
      'language' => $language,
2269
    );
2270

    
2271
    // Create two users and log them in one after another.
2272
    $user1 = $this->drupalCreateUser(array());
2273
    $user2 = $this->drupalCreateUser(array());
2274
    $this->drupalLogin($user1);
2275
    $this->drupalLogout();
2276
    $this->drupalLogin($user2);
2277

    
2278
    $account = user_load($user1->uid);
2279
    $global_account = user_load($GLOBALS['user']->uid);
2280

    
2281
    // Generate and test sanitized tokens.
2282
    $tests = array();
2283
    $tests['[user:uid]'] = $account->uid;
2284
    $tests['[user:name]'] = check_plain(format_username($account));
2285
    $tests['[user:mail]'] = check_plain($account->mail);
2286
    $tests['[user:url]'] = url("user/$account->uid", $url_options);
2287
    $tests['[user:edit-url]'] = url("user/$account->uid/edit", $url_options);
2288
    $tests['[user:last-login]'] = format_date($account->login, 'medium', '', NULL, $language->language);
2289
    $tests['[user:last-login:short]'] = format_date($account->login, 'short', '', NULL, $language->language);
2290
    $tests['[user:created]'] = format_date($account->created, 'medium', '', NULL, $language->language);
2291
    $tests['[user:created:short]'] = format_date($account->created, 'short', '', NULL, $language->language);
2292
    $tests['[current-user:name]'] = check_plain(format_username($global_account));
2293

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

    
2297
    foreach ($tests as $input => $expected) {
2298
      $output = token_replace($input, array('user' => $account), array('language' => $language));
2299
      $this->assertEqual($output, $expected, format_string('Sanitized user token %token replaced.', array('%token' => $input)));
2300
    }
2301

    
2302
    // Generate and test unsanitized tokens.
2303
    $tests['[user:name]'] = format_username($account);
2304
    $tests['[user:mail]'] = $account->mail;
2305
    $tests['[current-user:name]'] = format_username($global_account);
2306

    
2307
    foreach ($tests as $input => $expected) {
2308
      $output = token_replace($input, array('user' => $account), array('language' => $language, 'sanitize' => FALSE));
2309
      $this->assertEqual($output, $expected, format_string('Unsanitized user token %token replaced.', array('%token' => $input)));
2310
    }
2311
  }
2312
}
2313

    
2314
/**
2315
 * Test user search.
2316
 */
2317
class UserUserSearchTestCase extends DrupalWebTestCase {
2318
  public static function getInfo() {
2319
    return array(
2320
      'name' => 'User search',
2321
      'description' => 'Tests the user search page and verifies that sensitive information is hidden from unauthorized users.',
2322
      'group' => 'User',
2323
    );
2324
  }
2325

    
2326
  function testUserSearch() {
2327
    $user1 = $this->drupalCreateUser(array('access user profiles', 'search content', 'use advanced search'));
2328
    $this->drupalLogin($user1);
2329
    $keys = $user1->mail;
2330
    $edit = array('keys' => $keys);
2331
    $this->drupalPost('search/user/', $edit, t('Search'));
2332
    $this->assertNoText($keys);
2333
    $this->drupalLogout();
2334

    
2335
    $user2 = $this->drupalCreateUser(array('administer users', 'access user profiles', 'search content', 'use advanced search'));
2336
    $this->drupalLogin($user2);
2337
    $keys = $user2->mail;
2338
    $edit = array('keys' => $keys);
2339
    $this->drupalPost('search/user/', $edit, t('Search'));
2340
    $this->assertText($keys);
2341

    
2342
    // Verify that wildcard search works.
2343
    $keys = $user1->name;
2344
    $keys = substr($keys, 0, 2) . '*' . substr($keys, 4, 2);
2345
    $edit = array('keys' => $keys);
2346
    $this->drupalPost('search/user/', $edit, t('Search'));
2347
    $this->assertText($user1->name, 'Search for username wildcard resulted in user name on page for administrative user.');
2348

    
2349
    // Verify that wildcard search works for email.
2350
    $keys = $user1->mail;
2351
    $keys = substr($keys, 0, 2) . '*' . substr($keys, 4, 2);
2352
    $edit = array('keys' => $keys);
2353
    $this->drupalPost('search/user/', $edit, t('Search'));
2354
    $this->assertText($user1->name, 'Search for email wildcard resulted in user name on page for administrative user.');
2355

    
2356
    // Create a blocked user.
2357
    $blocked_user = $this->drupalCreateUser();
2358
    $edit = array('status' => 0);
2359
    $blocked_user = user_save($blocked_user, $edit);
2360

    
2361
    // Verify that users with "administer users" permissions can see blocked
2362
    // accounts in search results.
2363
    $edit = array('keys' => $blocked_user->name);
2364
    $this->drupalPost('search/user/', $edit, t('Search'));
2365
    $this->assertText($blocked_user->name, 'Blocked users are listed on the user search results for users with the "administer users" permission.');
2366

    
2367
    // Verify that users without "administer users" permissions do not see
2368
    // blocked accounts in search results.
2369
    $this->drupalLogin($user1);
2370
    $edit = array('keys' => $blocked_user->name);
2371
    $this->drupalPost('search/user/', $edit, t('Search'));
2372
    $this->assertNoText($blocked_user->name, 'Blocked users are hidden from the user search results.');
2373

    
2374
    $this->drupalLogout();
2375
  }
2376
}
2377

    
2378
/**
2379
 * Test role assignment.
2380
 */
2381
class UserRolesAssignmentTestCase extends DrupalWebTestCase {
2382
  protected $admin_user;
2383

    
2384
  public static function getInfo() {
2385
    return array(
2386
      'name' => 'Role assignment',
2387
      'description' => 'Tests that users can be assigned and unassigned roles.',
2388
      'group' => 'User'
2389
    );
2390
  }
2391

    
2392
  function setUp() {
2393
    parent::setUp();
2394
    $this->admin_user = $this->drupalCreateUser(array('administer permissions', 'administer users'));
2395
    $this->drupalLogin($this->admin_user);
2396
  }
2397

    
2398
  /**
2399
   * Tests that a user can be assigned a role and that the role can be removed
2400
   * again.
2401
   */
2402
  function testAssignAndRemoveRole()  {
2403
    $rid = $this->drupalCreateRole(array('administer content types'));
2404
    $account = $this->drupalCreateUser();
2405

    
2406
    // Assign the role to the user.
2407
    $this->drupalPost('user/' . $account->uid . '/edit', array("roles[$rid]" => $rid), t('Save'));
2408
    $this->assertText(t('The changes have been saved.'));
2409
    $this->assertFieldChecked('edit-roles-' . $rid, 'Role is assigned.');
2410
    $this->userLoadAndCheckRoleAssigned($account, $rid);
2411

    
2412
    // Remove the role from the user.
2413
    $this->drupalPost('user/' . $account->uid . '/edit', array("roles[$rid]" => FALSE), t('Save'));
2414
    $this->assertText(t('The changes have been saved.'));
2415
    $this->assertNoFieldChecked('edit-roles-' . $rid, 'Role is removed from user.');
2416
    $this->userLoadAndCheckRoleAssigned($account, $rid, FALSE);
2417
  }
2418

    
2419
  /**
2420
   * Tests that when creating a user the role can be assigned. And that it can
2421
   * be removed again.
2422
   */
2423
  function testCreateUserWithRole() {
2424
    $rid = $this->drupalCreateRole(array('administer content types'));
2425
    // Create a new user and add the role at the same time.
2426
    $edit = array(
2427
      'name' => $this->randomName(),
2428
      'mail' => $this->randomName() . '@example.com',
2429
      'pass[pass1]' => $pass = $this->randomString(),
2430
      'pass[pass2]' => $pass,
2431
      "roles[$rid]" => $rid,
2432
    );
2433
    $this->drupalPost('admin/people/create', $edit, t('Create new account'));
2434
    $this->assertText(t('Created a new user account for !name.', array('!name' => $edit['name'])));
2435
    // Get the newly added user.
2436
    $account = user_load_by_name($edit['name']);
2437

    
2438
    $this->drupalGet('user/' . $account->uid . '/edit');
2439
    $this->assertFieldChecked('edit-roles-' . $rid, 'Role is assigned.');
2440
    $this->userLoadAndCheckRoleAssigned($account, $rid);
2441

    
2442
    // Remove the role again.
2443
    $this->drupalPost('user/' . $account->uid . '/edit', array("roles[$rid]" => FALSE), t('Save'));
2444
    $this->assertText(t('The changes have been saved.'));
2445
    $this->assertNoFieldChecked('edit-roles-' . $rid, 'Role is removed from user.');
2446
    $this->userLoadAndCheckRoleAssigned($account, $rid, FALSE);
2447
  }
2448

    
2449
  /**
2450
   * Check role on user object.
2451
   *
2452
   * @param object $account
2453
   *   The user account to check.
2454
   * @param string $rid
2455
   *   The role ID to search for.
2456
   * @param bool $is_assigned
2457
   *   (optional) Whether to assert that $rid exists (TRUE) or not (FALSE).
2458
   *   Defaults to TRUE.
2459
   */
2460
  private function userLoadAndCheckRoleAssigned($account, $rid, $is_assigned = TRUE) {
2461
    $account = user_load($account->uid, TRUE);
2462
    if ($is_assigned) {
2463
      $this->assertTrue(array_key_exists($rid, $account->roles), 'The role is present in the user object.');
2464
    }
2465
    else {
2466
      $this->assertFalse(array_key_exists($rid, $account->roles), 'The role is not present in the user object.');
2467
    }
2468
  }
2469
}
2470

    
2471

    
2472
/**
2473
 * Unit test for authmap assignment.
2474
 */
2475
class UserAuthmapAssignmentTestCase extends DrupalWebTestCase {
2476
  public static function getInfo() {
2477
    return array(
2478
      'name' => 'Authmap assignment',
2479
      'description' => 'Tests that users can be assigned and unassigned authmaps.',
2480
      'group' => 'User'
2481
    );
2482
  }
2483

    
2484
  /**
2485
   * Test authmap assignment and retrieval.
2486
   */
2487
  function testAuthmapAssignment()  {
2488
    $account = $this->drupalCreateUser();
2489

    
2490
    // Assign authmaps to the user.
2491
    $authmaps = array(
2492
      'authname_poll' => 'external username one',
2493
      'authname_book' => 'external username two',
2494
    );
2495
    user_set_authmaps($account, $authmaps);
2496

    
2497
    // Test for expected authmaps.
2498
    $expected_authmaps = array(
2499
      'external username one' => array(
2500
        'poll' => 'external username one',
2501
      ),
2502
      'external username two' => array(
2503
        'book' => 'external username two',
2504
      ),
2505
    );
2506
    foreach ($expected_authmaps as $authname => $expected_output) {
2507
      $this->assertIdentical(user_get_authmaps($authname), $expected_output, format_string('Authmap for authname %authname was set correctly.', array('%authname' => $authname)));
2508
    }
2509

    
2510
    // Remove authmap for module poll, add authmap for module blog.
2511
    $authmaps = array(
2512
      'authname_poll' => NULL,
2513
      'authname_blog' => 'external username three',
2514
    );
2515
    user_set_authmaps($account, $authmaps);
2516

    
2517
    // Assert that external username one does not have authmaps.
2518
    $remove_username = 'external username one';
2519
    unset($expected_authmaps[$remove_username]);
2520
    $this->assertFalse(user_get_authmaps($remove_username), format_string('Authmap for %authname was removed.', array('%authname' => $remove_username)));
2521

    
2522
    // Assert that a new authmap was created for external username three, and
2523
    // existing authmaps for external username two were unchanged.
2524
    $expected_authmaps['external username three'] = array('blog' => 'external username three');
2525
    foreach ($expected_authmaps as $authname => $expected_output) {
2526
      $this->assertIdentical(user_get_authmaps($authname), $expected_output, format_string('Authmap for authname %authname was set correctly.', array('%authname' => $authname)));
2527
    }
2528
  }
2529
}
2530

    
2531
/**
2532
 * Tests user_validate_current_pass on a custom form.
2533
 */
2534
class UserValidateCurrentPassCustomForm extends DrupalWebTestCase {
2535

    
2536
  public static function getInfo() {
2537
    return array(
2538
      'name' => 'User validate current pass custom form',
2539
      'description' => 'Test that user_validate_current_pass is usable on a custom form.',
2540
      'group' => 'User',
2541
    );
2542
  }
2543

    
2544
  /**
2545
   * User with permission to view content.
2546
   */
2547
  protected $accessUser;
2548

    
2549
  /**
2550
   * User permission to administer users.
2551
   */
2552
  protected $adminUser;
2553

    
2554
  function setUp() {
2555
    parent::setUp('user_form_test');
2556
    // Create two users
2557
    $this->accessUser = $this->drupalCreateUser(array('access content'));
2558
    $this->adminUser = $this->drupalCreateUser(array('administer users'));
2559
  }
2560

    
2561
  /**
2562
   * Tests that user_validate_current_pass can be reused on a custom form.
2563
   */
2564
  function testUserValidateCurrentPassCustomForm() {
2565
    $this->drupalLogin($this->adminUser);
2566

    
2567
    // Submit the custom form with the admin user using the access user's password.
2568
    $edit = array();
2569
    $edit['user_form_test_field'] = $this->accessUser->name;
2570
    $edit['current_pass'] = $this->accessUser->pass_raw;
2571
    $this->drupalPost('user_form_test_current_password/' . $this->accessUser->uid, $edit, t('Test'));
2572
    $this->assertText(t('The password has been validated and the form submitted successfully.'));
2573
  }
2574
}