Projet

Général

Profil

Paste
Télécharger (103 ko) Statistiques
| Branche: | Révision:

root / drupal7 / modules / user / user.test @ 30d5b9c5

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
   * Attempts login using an expired password reset link.
485
   */
486
  function testUserPasswordResetExpired() {
487
    // Set password reset timeout variable to 43200 seconds = 12 hours.
488
    $timeout = 43200;
489
    variable_set('user_password_reset_timeout', $timeout);
490

    
491
    // Create a user.
492
    $account = $this->drupalCreateUser();
493
    $this->drupalLogin($account);
494
    // Load real user object.
495
    $account = user_load($account->uid, TRUE);
496
    $this->drupalLogout();
497

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

    
505
  /**
506
   * Prefill the text box on incorrect login via link to password reset page.
507
   */
508
  function testUserPasswordTextboxFilled() {
509
    $this->drupalGet('user/login');
510
    $edit = array(
511
      'name' => $this->randomName(),
512
      'pass' => $this->randomName(),
513
    );
514
    $this->drupalPost('user', $edit, t('Log in'));
515
    $this->assertRaw(t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>',
516
      array('@password' => url('user/password', array('query' => array('name' => $edit['name']))))));
517
    unset($edit['pass']);
518
    $this->drupalGet('user/password', array('query' => array('name' => $edit['name'])));
519
    $this->assertFieldByName('name', $edit['name'], 'User name found.');
520
  }
521

    
522
  /**
523
   * Make sure that users cannot forge password reset URLs of other users.
524
   */
525
  function testResetImpersonation() {
526
    // Make sure user 1 has a valid password, so it does not interfere with the
527
    // test user accounts that are created below.
528
    $account = user_load(1);
529
    user_save($account, array('pass' => user_password()));
530

    
531
    // Create two identical user accounts except for the user name. They must
532
    // have the same empty password, so we can't use $this->drupalCreateUser().
533
    $edit = array();
534
    $edit['name'] = $this->randomName();
535
    $edit['mail'] = $edit['name'] . '@example.com';
536
    $edit['status'] = 1;
537

    
538
    $user1 = user_save(drupal_anonymous_user(), $edit);
539

    
540
    $edit['name'] = $this->randomName();
541
    $user2 = user_save(drupal_anonymous_user(), $edit);
542

    
543
    // The password reset URL must not be valid for the second user when only
544
    // the user ID is changed in the URL.
545
    $reset_url = user_pass_reset_url($user1);
546
    $attack_reset_url = str_replace("user/reset/$user1->uid", "user/reset/$user2->uid", $reset_url);
547
    $this->drupalGet($attack_reset_url);
548
    $this->assertNoText($user2->name, 'The invalid password reset page does not show the user name.');
549
    $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
550
    $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.');
551

    
552
    // When legacy code calls user_pass_rehash() without providing the $uid
553
    // parameter, neither password reset URL should be valid since it is
554
    // impossible for the system to determine which user account the token was
555
    // intended for.
556
    $timestamp = REQUEST_TIME;
557
    // Pass an explicit NULL for the $uid parameter of user_pass_rehash()
558
    // rather than not passing it at all, to avoid triggering PHP warnings in
559
    // the test.
560
    $reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL);
561
    $reset_url = url("user/reset/$user1->uid/$timestamp/$reset_url_token", array('absolute' => TRUE));
562
    $this->drupalGet($reset_url);
563
    $this->assertNoText($user1->name, 'The invalid password reset page does not show the user name.');
564
    $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
565
    $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.');
566
    $attack_reset_url = str_replace("user/reset/$user1->uid", "user/reset/$user2->uid", $reset_url);
567
    $this->drupalGet($attack_reset_url);
568
    $this->assertNoText($user2->name, 'The invalid password reset page does not show the user name.');
569
    $this->assertUrl('user/password', array(), 'The user is redirected to the password reset request page.');
570
    $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.');
571

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

    
579
    // However, when the duplicate account is removed, the password reset URL
580
    // should be valid.
581
    user_delete($user2->uid);
582
    $reset_url_token = user_pass_rehash($user1->pass, $timestamp, $user1->login, NULL);
583
    $reset_url = url("user/reset/$user1->uid/$timestamp/$reset_url_token", array('absolute' => TRUE));
584
    $this->drupalGet($reset_url);
585
    $this->assertText($user1->name, 'The valid password reset page shows the user name.');
586
    $this->assertUrl($reset_url, array(), 'The user remains on the password reset login page.');
587
    $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.');
588
  }
589

    
590
}
591

    
592
/**
593
 * Test cancelling a user.
594
 */
595
class UserCancelTestCase extends DrupalWebTestCase {
596
  public static function getInfo() {
597
    return array(
598
      'name' => 'Cancel account',
599
      'description' => 'Ensure that account cancellation methods work as expected.',
600
      'group' => 'User',
601
    );
602
  }
603

    
604
  function setUp() {
605
    parent::setUp('comment');
606
  }
607

    
608
  /**
609
   * Attempt to cancel account without permission.
610
   */
611
  function testUserCancelWithoutPermission() {
612
    variable_set('user_cancel_method', 'user_cancel_reassign');
613

    
614
    // Create a user.
615
    $account = $this->drupalCreateUser(array());
616
    $this->drupalLogin($account);
617
    // Load real user object.
618
    $account = user_load($account->uid, TRUE);
619

    
620
    // Create a node.
621
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
622

    
623
    // Attempt to cancel account.
624
    $this->drupalGet('user/' . $account->uid . '/edit');
625
    $this->assertNoRaw(t('Cancel account'), 'No cancel account button displayed.');
626

    
627
    // Attempt bogus account cancellation request confirmation.
628
    $timestamp = $account->login;
629
    $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid));
630
    $this->assertResponse(403, 'Bogus cancelling request rejected.');
631
    $account = user_load($account->uid);
632
    $this->assertTrue($account->status == 1, 'User account was not canceled.');
633

    
634
    // Confirm user's content has not been altered.
635
    $test_node = node_load($node->nid, NULL, TRUE);
636
    $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), 'Node of the user has not been altered.');
637
  }
638

    
639
  /**
640
   * Tests that user account for uid 1 cannot be cancelled.
641
   *
642
   * This should never be possible, or the site owner would become unable to
643
   * administer the site.
644
   */
645
  function testUserCancelUid1() {
646
    // Update uid 1's name and password to we know it.
647
    $password = user_password();
648
    require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
649
    $account = array(
650
      'name' => 'user1',
651
      'pass' => user_hash_password(trim($password)),
652
    );
653
    // We cannot use user_save() here or the password would be hashed again.
654
    db_update('users')
655
      ->fields($account)
656
      ->condition('uid', 1)
657
      ->execute();
658

    
659
    // Reload and log in uid 1.
660
    $user1 = user_load(1, TRUE);
661
    $user1->pass_raw = $password;
662

    
663
    // Try to cancel uid 1's account with a different user.
664
    $this->admin_user = $this->drupalCreateUser(array('administer users'));
665
    $this->drupalLogin($this->admin_user);
666
    $edit = array(
667
      'operation' => 'cancel',
668
      'accounts[1]' => TRUE,
669
    );
670
    $this->drupalPost('admin/people', $edit, t('Update'));
671

    
672
    // Verify that uid 1's account was not cancelled.
673
    $user1 = user_load(1, TRUE);
674
    $this->assertEqual($user1->status, 1, 'User #1 still exists and is not blocked.');
675
  }
676

    
677
  /**
678
   * Attempt invalid account cancellations.
679
   */
680
  function testUserCancelInvalid() {
681
    variable_set('user_cancel_method', 'user_cancel_reassign');
682

    
683
    // Create a user.
684
    $account = $this->drupalCreateUser(array('cancel account'));
685
    $this->drupalLogin($account);
686
    // Load real user object.
687
    $account = user_load($account->uid, TRUE);
688

    
689
    // Create a node.
690
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
691

    
692
    // Attempt to cancel account.
693
    $this->drupalPost('user/' . $account->uid . '/edit', NULL, t('Cancel account'));
694

    
695
    // Confirm account cancellation.
696
    $timestamp = time();
697
    $this->drupalPost(NULL, NULL, t('Cancel account'));
698
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
699

    
700
    // Attempt bogus account cancellation request confirmation.
701
    $bogus_timestamp = $timestamp + 60;
702
    $this->drupalGet("user/$account->uid/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login, $account->uid));
703
    $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.');
704
    $account = user_load($account->uid);
705
    $this->assertTrue($account->status == 1, 'User account was not canceled.');
706

    
707
    // Attempt expired account cancellation request confirmation.
708
    $bogus_timestamp = $timestamp - 86400 - 60;
709
    $this->drupalGet("user/$account->uid/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login, $account->uid));
710
    $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.');
711
    $accounts = user_load_multiple(array($account->uid), array('status' => 1));
712
    $this->assertTrue(reset($accounts), 'User account was not canceled.');
713

    
714
    // Confirm user's content has not been altered.
715
    $test_node = node_load($node->nid, NULL, TRUE);
716
    $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), 'Node of the user has not been altered.');
717
  }
718

    
719
  /**
720
   * Disable account and keep all content.
721
   */
722
  function testUserBlock() {
723
    variable_set('user_cancel_method', 'user_cancel_block');
724

    
725
    // Create a user.
726
    $web_user = $this->drupalCreateUser(array('cancel account'));
727
    $this->drupalLogin($web_user);
728

    
729
    // Load real user object.
730
    $account = user_load($web_user->uid, TRUE);
731

    
732
    // Attempt to cancel account.
733
    $this->drupalGet('user/' . $account->uid . '/edit');
734
    $this->drupalPost(NULL, NULL, t('Cancel account'));
735
    $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
736
    $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.');
737
    $this->assertNoText(t('Select the method to cancel the account above.'), 'Does not allow user to select account cancellation method.');
738

    
739
    // Confirm account cancellation.
740
    $timestamp = time();
741

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

    
745
    // Confirm account cancellation request.
746
    $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid));
747
    $account = user_load($account->uid, TRUE);
748
    $this->assertTrue($account->status == 0, 'User has been blocked.');
749

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

    
754
  /**
755
   * Disable account and unpublish all content.
756
   */
757
  function testUserBlockUnpublish() {
758
    variable_set('user_cancel_method', 'user_cancel_block_unpublish');
759

    
760
    // Create a user.
761
    $account = $this->drupalCreateUser(array('cancel account'));
762
    $this->drupalLogin($account);
763
    // Load real user object.
764
    $account = user_load($account->uid, TRUE);
765

    
766
    // Create a node with two revisions.
767
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
768
    $settings = get_object_vars($node);
769
    $settings['revision'] = 1;
770
    $node = $this->drupalCreateNode($settings);
771

    
772
    // Attempt to cancel account.
773
    $this->drupalGet('user/' . $account->uid . '/edit');
774
    $this->drupalPost(NULL, NULL, t('Cancel account'));
775
    $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
776
    $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.');
777

    
778
    // Confirm account cancellation.
779
    $timestamp = time();
780
    $this->drupalPost(NULL, NULL, t('Cancel account'));
781
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
782

    
783
    // Confirm account cancellation request.
784
    $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login, $account->uid));
785
    $account = user_load($account->uid, TRUE);
786
    $this->assertTrue($account->status == 0, 'User has been blocked.');
787

    
788
    // Confirm user's content has been unpublished.
789
    $test_node = node_load($node->nid, NULL, TRUE);
790
    $this->assertTrue($test_node->status == 0, 'Node of the user has been unpublished.');
791
    $test_node = node_load($node->nid, $node->vid, TRUE);
792
    $this->assertTrue($test_node->status == 0, 'Node revision of the user has been unpublished.');
793

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

    
798
  /**
799
   * Delete account and anonymize all content.
800
   */
801
  function testUserAnonymize() {
802
    variable_set('user_cancel_method', 'user_cancel_reassign');
803

    
804
    // Create a user.
805
    $account = $this->drupalCreateUser(array('cancel account'));
806
    $this->drupalLogin($account);
807
    // Load real user object.
808
    $account = user_load($account->uid, TRUE);
809

    
810
    // Create a simple node.
811
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
812

    
813
    // Create a node with two revisions, the initial one belonging to the
814
    // cancelling user.
815
    $revision_node = $this->drupalCreateNode(array('uid' => $account->uid));
816
    $revision = $revision_node->vid;
817
    $settings = get_object_vars($revision_node);
818
    $settings['revision'] = 1;
819
    $settings['uid'] = 1; // Set new/current revision to someone else.
820
    $revision_node = $this->drupalCreateNode($settings);
821

    
822
    // Attempt to cancel account.
823
    $this->drupalGet('user/' . $account->uid . '/edit');
824
    $this->drupalPost(NULL, NULL, t('Cancel account'));
825
    $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
826
    $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.');
827

    
828
    // Confirm account cancellation.
829
    $timestamp = time();
830
    $this->drupalPost(NULL, NULL, t('Cancel account'));
831
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
832

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

    
837
    // Confirm that user's content has been attributed to anonymous user.
838
    $test_node = node_load($node->nid, NULL, TRUE);
839
    $this->assertTrue(($test_node->uid == 0 && $test_node->status == 1), 'Node of the user has been attributed to anonymous user.');
840
    $test_node = node_load($revision_node->nid, $revision, TRUE);
841
    $this->assertTrue(($test_node->revision_uid == 0 && $test_node->status == 1), 'Node revision of the user has been attributed to anonymous user.');
842
    $test_node = node_load($revision_node->nid, NULL, TRUE);
843
    $this->assertTrue(($test_node->uid != 0 && $test_node->status == 1), "Current revision of the user's node was not attributed to anonymous user.");
844

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

    
849
  /**
850
   * Delete account and remove all content.
851
   */
852
  function testUserDelete() {
853
    variable_set('user_cancel_method', 'user_cancel_delete');
854

    
855
    // Create a user.
856
    $account = $this->drupalCreateUser(array('cancel account', 'post comments', 'skip comment approval'));
857
    $this->drupalLogin($account);
858
    // Load real user object.
859
    $account = user_load($account->uid, TRUE);
860

    
861
    // Create a simple node.
862
    $node = $this->drupalCreateNode(array('uid' => $account->uid));
863

    
864
    // Create comment.
865
    $langcode = LANGUAGE_NONE;
866
    $edit = array();
867
    $edit['subject'] = $this->randomName(8);
868
    $edit['comment_body[' . $langcode . '][0][value]'] = $this->randomName(16);
869

    
870
    $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview'));
871
    $this->drupalPost(NULL, array(), t('Save'));
872
    $this->assertText(t('Your comment has been posted.'));
873
    $comments = comment_load_multiple(array(), array('subject' => $edit['subject']));
874
    $comment = reset($comments);
875
    $this->assertTrue($comment->cid, 'Comment found.');
876

    
877
    // Create a node with two revisions, the initial one belonging to the
878
    // cancelling user.
879
    $revision_node = $this->drupalCreateNode(array('uid' => $account->uid));
880
    $revision = $revision_node->vid;
881
    $settings = get_object_vars($revision_node);
882
    $settings['revision'] = 1;
883
    $settings['uid'] = 1; // Set new/current revision to someone else.
884
    $revision_node = $this->drupalCreateNode($settings);
885

    
886
    // Attempt to cancel account.
887
    $this->drupalGet('user/' . $account->uid . '/edit');
888
    $this->drupalPost(NULL, NULL, t('Cancel account'));
889
    $this->assertText(t('Are you sure you want to cancel your account?'), 'Confirmation form to cancel account displayed.');
890
    $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.');
891

    
892
    // Confirm account cancellation.
893
    $timestamp = time();
894
    $this->drupalPost(NULL, NULL, t('Cancel account'));
895
    $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), 'Account cancellation request mailed message displayed.');
896

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

    
901
    // Confirm that user's content has been deleted.
902
    $this->assertFalse(node_load($node->nid, NULL, TRUE), 'Node of the user has been deleted.');
903
    $this->assertFalse(node_load($node->nid, $revision, TRUE), 'Node revision of the user has been deleted.');
904
    $this->assertTrue(node_load($revision_node->nid, NULL, TRUE), "Current revision of the user's node was not deleted.");
905
    $this->assertFalse(comment_load($comment->cid), 'Comment of the user has been deleted.');
906

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

    
911
  /**
912
   * Create an administrative user and delete another user.
913
   */
914
  function testUserCancelByAdmin() {
915
    variable_set('user_cancel_method', 'user_cancel_reassign');
916

    
917
    // Create a regular user.
918
    $account = $this->drupalCreateUser(array());
919

    
920
    // Create administrative user.
921
    $admin_user = $this->drupalCreateUser(array('administer users'));
922
    $this->drupalLogin($admin_user);
923

    
924
    // Delete regular user.
925
    $this->drupalGet('user/' . $account->uid . '/edit');
926
    $this->drupalPost(NULL, NULL, t('Cancel account'));
927
    $this->assertRaw(t('Are you sure you want to cancel the account %name?', array('%name' => $account->name)), 'Confirmation form to cancel account displayed.');
928
    $this->assertText(t('Select the method to cancel the account above.'), 'Allows to select account cancellation method.');
929

    
930
    // Confirm deletion.
931
    $this->drupalPost(NULL, NULL, t('Cancel account'));
932
    $this->assertRaw(t('%name has been deleted.', array('%name' => $account->name)), 'User deleted.');
933
    $this->assertFalse(user_load($account->uid), 'User is not found in the database.');
934
  }
935

    
936
  /**
937
   * Create an administrative user and mass-delete other users.
938
   */
939
  function testMassUserCancelByAdmin() {
940
    variable_set('user_cancel_method', 'user_cancel_reassign');
941
    // Enable account cancellation notification.
942
    variable_set('user_mail_status_canceled_notify', TRUE);
943

    
944
    // Create administrative user.
945
    $admin_user = $this->drupalCreateUser(array('administer users'));
946
    $this->drupalLogin($admin_user);
947

    
948
    // Create some users.
949
    $users = array();
950
    for ($i = 0; $i < 3; $i++) {
951
      $account = $this->drupalCreateUser(array());
952
      $users[$account->uid] = $account;
953
    }
954

    
955
    // Cancel user accounts, including own one.
956
    $edit = array();
957
    $edit['operation'] = 'cancel';
958
    foreach ($users as $uid => $account) {
959
      $edit['accounts[' . $uid . ']'] = TRUE;
960
    }
961
    $edit['accounts[' . $admin_user->uid . ']'] = TRUE;
962
    // Also try to cancel uid 1.
963
    $edit['accounts[1]'] = TRUE;
964
    $this->drupalPost('admin/people', $edit, t('Update'));
965
    $this->assertText(t('Are you sure you want to cancel these user accounts?'), 'Confirmation form to cancel accounts displayed.');
966
    $this->assertText(t('When cancelling these accounts'), 'Allows to select account cancellation method.');
967
    $this->assertText(t('Require e-mail confirmation to cancel account.'), 'Allows to send confirmation mail.');
968
    $this->assertText(t('Notify user when account is canceled.'), 'Allows to send notification mail.');
969

    
970
    // Confirm deletion.
971
    $this->drupalPost(NULL, NULL, t('Cancel accounts'));
972
    $status = TRUE;
973
    foreach ($users as $account) {
974
      $status = $status && (strpos($this->content, t('%name has been deleted.', array('%name' => $account->name))) !== FALSE);
975
      $status = $status && !user_load($account->uid, TRUE);
976
    }
977
    $this->assertTrue($status, 'Users deleted and not found in the database.');
978

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

    
984
    // Verify that uid 1's account was not cancelled.
985
    $user1 = user_load(1, TRUE);
986
    $this->assertEqual($user1->status, 1, 'User #1 still exists and is not blocked.');
987
  }
988
}
989

    
990
class UserPictureTestCase extends DrupalWebTestCase {
991
  protected $user;
992
  protected $_directory_test;
993

    
994
  public static function getInfo() {
995
    return array(
996
      'name' => 'Upload user picture',
997
      'description' => 'Assure that dimension check, extension check and image scaling work as designed.',
998
      'group' => 'User'
999
    );
1000
  }
1001

    
1002
  function setUp() {
1003
    parent::setUp();
1004
    // Enable user pictures.
1005
    variable_set('user_pictures', 1);
1006

    
1007
    $this->user = $this->drupalCreateUser();
1008

    
1009
    // Test if directories specified in settings exist in filesystem.
1010
    $file_dir = 'public://';
1011
    $file_check = file_prepare_directory($file_dir, FILE_CREATE_DIRECTORY);
1012
    // TODO: Test public and private methods?
1013

    
1014
    $picture_dir = variable_get('user_picture_path', 'pictures');
1015
    $picture_path = $file_dir . $picture_dir;
1016

    
1017
    $pic_check = file_prepare_directory($picture_path, FILE_CREATE_DIRECTORY);
1018
    $this->_directory_test = is_writable($picture_path);
1019
    $this->assertTrue($this->_directory_test, "The directory $picture_path doesn't exist or is not writable. Further tests won't be made.");
1020
  }
1021

    
1022
  function testNoPicture() {
1023
    $this->drupalLogin($this->user);
1024

    
1025
    // Try to upload a file that is not an image for the user picture.
1026
    $not_an_image = current($this->drupalGetTestFiles('html'));
1027
    $this->saveUserPicture($not_an_image);
1028
    $this->assertRaw(t('Only JPEG, PNG and GIF images are allowed.'), 'Non-image files are not accepted.');
1029
  }
1030

    
1031
  /**
1032
   * Do the test:
1033
   *  GD Toolkit is installed
1034
   *  Picture has invalid dimension
1035
   *
1036
   * results: The image should be uploaded because ImageGDToolkit resizes the picture
1037
   */
1038
  function testWithGDinvalidDimension() {
1039
    if ($this->_directory_test && image_get_toolkit()) {
1040
      $this->drupalLogin($this->user);
1041

    
1042
      $image = current($this->drupalGetTestFiles('image'));
1043
      $info = image_get_info($image->uri);
1044

    
1045
      // Set new variables: invalid dimensions, valid filesize (0 = no limit).
1046
      $test_dim = ($info['width'] - 10) . 'x' . ($info['height'] - 10);
1047
      variable_set('user_picture_dimensions', $test_dim);
1048
      variable_set('user_picture_file_size', 0);
1049

    
1050
      $pic_path = $this->saveUserPicture($image);
1051
      // Check that the image was resized and is being displayed on the
1052
      // user's profile page.
1053
      $text = t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', array('%dimensions' => $test_dim));
1054
      $this->assertRaw($text, 'Image was resized.');
1055
      $alt = t("@user's picture", array('@user' => format_username($this->user)));
1056
      $style = variable_get('user_picture_style', '');
1057
      $this->assertRaw(check_plain(image_style_url($style, $pic_path)), "Image is displayed in user's edit page");
1058

    
1059
      // Check if file is located in proper directory.
1060
      $this->assertTrue(is_file($pic_path), "File is located in proper directory");
1061
    }
1062
  }
1063

    
1064
  /**
1065
   * Do the test:
1066
   *  GD Toolkit is installed
1067
   *  Picture has invalid size
1068
   *
1069
   * results: The image should be uploaded because ImageGDToolkit resizes the picture
1070
   */
1071
  function testWithGDinvalidSize() {
1072
    if ($this->_directory_test && image_get_toolkit()) {
1073
      $this->drupalLogin($this->user);
1074

    
1075
      // Images are sorted first by size then by name. We need an image
1076
      // bigger than 1 KB so we'll grab the last one.
1077
      $files = $this->drupalGetTestFiles('image');
1078
      $image = end($files);
1079
      $info = image_get_info($image->uri);
1080

    
1081
      // Set new variables: valid dimensions, invalid filesize.
1082
      $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
1083
      $test_size = 1;
1084
      variable_set('user_picture_dimensions', $test_dim);
1085
      variable_set('user_picture_file_size', $test_size);
1086

    
1087
      $pic_path = $this->saveUserPicture($image);
1088

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

    
1095
      // Check if file is not uploaded.
1096
      $this->assertFalse(is_file($pic_path), 'File was not uploaded.');
1097
    }
1098
  }
1099

    
1100
  /**
1101
   * Do the test:
1102
   *  GD Toolkit is not installed
1103
   *  Picture has invalid size
1104
   *
1105
   * results: The image shouldn't be uploaded
1106
   */
1107
  function testWithoutGDinvalidDimension() {
1108
    if ($this->_directory_test && !image_get_toolkit()) {
1109
      $this->drupalLogin($this->user);
1110

    
1111
      $image = current($this->drupalGetTestFiles('image'));
1112
      $info = image_get_info($image->uri);
1113

    
1114
      // Set new variables: invalid dimensions, valid filesize (0 = no limit).
1115
      $test_dim = ($info['width'] - 10) . 'x' . ($info['height'] - 10);
1116
      variable_set('user_picture_dimensions', $test_dim);
1117
      variable_set('user_picture_file_size', 0);
1118

    
1119
      $pic_path = $this->saveUserPicture($image);
1120

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

    
1127
      // Check if file is not uploaded.
1128
      $this->assertFalse(is_file($pic_path), 'File was not uploaded.');
1129
    }
1130
  }
1131

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

    
1143
      $image = current($this->drupalGetTestFiles('image'));
1144
      $info = image_get_info($image->uri);
1145

    
1146
      // Set new variables: valid dimensions, invalid filesize.
1147
      $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
1148
      $test_size = 1;
1149
      variable_set('user_picture_dimensions', $test_dim);
1150
      variable_set('user_picture_file_size', $test_size);
1151

    
1152
      $pic_path = $this->saveUserPicture($image);
1153

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

    
1160
      // Check if file is not uploaded.
1161
      $this->assertFalse(is_file($pic_path), 'File was not uploaded.');
1162
    }
1163
  }
1164

    
1165
  /**
1166
   * Do the test:
1167
   *  Picture is valid (proper size and dimension)
1168
   *
1169
   * results: The image should be uploaded
1170
   */
1171
  function testPictureIsValid() {
1172
    if ($this->_directory_test) {
1173
      $this->drupalLogin($this->user);
1174

    
1175
      $image = current($this->drupalGetTestFiles('image'));
1176
      $info = image_get_info($image->uri);
1177

    
1178
      // Set new variables: valid dimensions, valid filesize (0 = no limit).
1179
      $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
1180
      variable_set('user_picture_dimensions', $test_dim);
1181
      variable_set('user_picture_file_size', 0);
1182

    
1183
      $pic_path = $this->saveUserPicture($image);
1184

    
1185
      // Check if image is displayed in user's profile page.
1186
      $this->drupalGet('user');
1187
      $this->assertRaw(file_uri_target($pic_path), "Image is displayed in user's profile page");
1188

    
1189
      // Check if file is located in proper directory.
1190
      $this->assertTrue(is_file($pic_path), 'File is located in proper directory');
1191

    
1192
      // Set new picture dimensions.
1193
      $test_dim = ($info['width'] + 5) . 'x' . ($info['height'] + 5);
1194
      variable_set('user_picture_dimensions', $test_dim);
1195

    
1196
      $pic_path2 = $this->saveUserPicture($image);
1197
      $this->assertNotEqual($pic_path, $pic_path2, 'Filename of second picture is different.');
1198

    
1199
      // Check if user picture has a valid file ID after saving the user.
1200
      $account = user_load($this->user->uid, TRUE);
1201
      $this->assertTrue(is_object($account->picture), 'User picture object is valid after user load.');
1202
      $this->assertNotNull($account->picture->fid, 'User picture object has a FID after user load.');
1203
      $this->assertTrue(is_file($account->picture->uri), 'File is located in proper directory after user load.');
1204
      user_save($account);
1205
      // Verify that the user save does not destroy the user picture object.
1206
      $this->assertTrue(is_object($account->picture), 'User picture object is valid after user save.');
1207
      $this->assertNotNull($account->picture->fid, 'User picture object has a FID after user save.');
1208
      $this->assertTrue(is_file($account->picture->uri), 'File is located in proper directory after user save.');
1209
    }
1210
  }
1211

    
1212
  /**
1213
   * Test HTTP schema working with user pictures.
1214
   */
1215
  function testExternalPicture() {
1216
    $this->drupalLogin($this->user);
1217
    // Set the default picture to an URI with a HTTP schema.
1218
    $images = $this->drupalGetTestFiles('image');
1219
    $image = $images[0];
1220
    $pic_path = file_create_url($image->uri);
1221
    variable_set('user_picture_default', $pic_path);
1222

    
1223
    // Check if image is displayed in user's profile page.
1224
    $this->drupalGet('user');
1225

    
1226
    // Get the user picture image via xpath.
1227
    $elements = $this->xpath('//div[@class="user-picture"]/img');
1228
    $this->assertEqual(count($elements), 1, "There is exactly one user picture on the user's profile page");
1229
    $this->assertEqual($pic_path, (string) $elements[0]['src'], "User picture source is correct.");
1230
  }
1231

    
1232
  /**
1233
   * Tests deletion of user pictures.
1234
   */
1235
  function testDeletePicture() {
1236
    $this->drupalLogin($this->user);
1237

    
1238
    $image = current($this->drupalGetTestFiles('image'));
1239
    $info = image_get_info($image->uri);
1240

    
1241
    // Set new variables: valid dimensions, valid filesize (0 = no limit).
1242
    $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
1243
    variable_set('user_picture_dimensions', $test_dim);
1244
    variable_set('user_picture_file_size', 0);
1245

    
1246
    // Save a new picture.
1247
    $edit = array('files[picture_upload]' => drupal_realpath($image->uri));
1248
    $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save'));
1249

    
1250
    // Load actual user data from database.
1251
    $account = user_load($this->user->uid, TRUE);
1252
    $pic_path = isset($account->picture) ? $account->picture->uri : NULL;
1253

    
1254
    // Check if image is displayed in user's profile page.
1255
    $this->drupalGet('user');
1256
    $this->assertRaw(file_uri_target($pic_path), "Image is displayed in user's profile page");
1257

    
1258
    // Check if file is located in proper directory.
1259
    $this->assertTrue(is_file($pic_path), 'File is located in proper directory');
1260

    
1261
    $edit = array('picture_delete' => 1);
1262
    $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save'));
1263

    
1264
    // Load actual user data from database.
1265
    $account1 = user_load($this->user->uid, TRUE);
1266
    $this->assertNull($account1->picture, 'User object has no picture');
1267

    
1268
    $file = file_load($account->picture->fid);
1269
    $this->assertFalse($file, 'File is removed from database');
1270

    
1271
    // Clear out PHP's file stat cache so we see the current value.
1272
    clearstatcache();
1273
    $this->assertFalse(is_file($pic_path), 'File is removed from file system');
1274
  }
1275

    
1276
  function saveUserPicture($image) {
1277
    $edit = array('files[picture_upload]' => drupal_realpath($image->uri));
1278
    $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save'));
1279

    
1280
    // Load actual user data from database.
1281
    $account = user_load($this->user->uid, TRUE);
1282
    return isset($account->picture) ? $account->picture->uri : NULL;
1283
  }
1284

    
1285
  /**
1286
   * Tests the admin form validates user picture settings.
1287
   */
1288
  function testUserPictureAdminFormValidation() {
1289
    $this->drupalLogin($this->drupalCreateUser(array('administer users')));
1290

    
1291
    // The default values are valid.
1292
    $this->drupalPost('admin/config/people/accounts', array(), t('Save configuration'));
1293
    $this->assertText(t('The configuration options have been saved.'), 'The default values are valid.');
1294

    
1295
    // The form does not save with an invalid file size.
1296
    $edit = array(
1297
      'user_picture_file_size' => $this->randomName(),
1298
    );
1299
    $this->drupalPost('admin/config/people/accounts', $edit, t('Save configuration'));
1300
    $this->assertNoText(t('The configuration options have been saved.'), 'The form does not save with an invalid file size.');
1301
  }
1302
}
1303

    
1304

    
1305
class UserPermissionsTestCase extends DrupalWebTestCase {
1306
  protected $admin_user;
1307
  protected $rid;
1308

    
1309
  public static function getInfo() {
1310
    return array(
1311
      'name' => 'Role permissions',
1312
      'description' => 'Verify that role permissions can be added and removed via the permissions page.',
1313
      'group' => 'User'
1314
    );
1315
  }
1316

    
1317
  function setUp() {
1318
    parent::setUp();
1319

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

    
1322
    // Find the new role ID - it must be the maximum.
1323
    $all_rids = array_keys($this->admin_user->roles);
1324
    sort($all_rids);
1325
    $this->rid = array_pop($all_rids);
1326
  }
1327

    
1328
  /**
1329
   * Change user permissions and check user_access().
1330
   */
1331
  function testUserPermissionChanges() {
1332
    $this->drupalLogin($this->admin_user);
1333
    $rid = $this->rid;
1334
    $account = $this->admin_user;
1335

    
1336
    // Add a permission.
1337
    $this->assertFalse(user_access('administer nodes', $account), 'User does not have "administer nodes" permission.');
1338
    $edit = array();
1339
    $edit[$rid . '[administer nodes]'] = TRUE;
1340
    $this->drupalPost('admin/people/permissions', $edit, t('Save permissions'));
1341
    $this->assertText(t('The changes have been saved.'), 'Successful save message displayed.');
1342
    drupal_static_reset('user_access');
1343
    drupal_static_reset('user_role_permissions');
1344
    $this->assertTrue(user_access('administer nodes', $account), 'User now has "administer nodes" permission.');
1345

    
1346
    // Remove a permission.
1347
    $this->assertTrue(user_access('access user profiles', $account), 'User has "access user profiles" permission.');
1348
    $edit = array();
1349
    $edit[$rid . '[access user profiles]'] = FALSE;
1350
    $this->drupalPost('admin/people/permissions', $edit, t('Save permissions'));
1351
    $this->assertText(t('The changes have been saved.'), 'Successful save message displayed.');
1352
    drupal_static_reset('user_access');
1353
    drupal_static_reset('user_role_permissions');
1354
    $this->assertFalse(user_access('access user profiles', $account), 'User no longer has "access user profiles" permission.');
1355
  }
1356

    
1357
  /**
1358
   * Test assigning of permissions for the administrator role.
1359
   */
1360
  function testAdministratorRole() {
1361
    $this->drupalLogin($this->admin_user);
1362
    $this->drupalGet('admin/config/people/accounts');
1363

    
1364
    // Set the user's role to be the administrator role.
1365
    $edit = array();
1366
    $edit['user_admin_role'] = $this->rid;
1367
    $this->drupalPost('admin/config/people/accounts', $edit, t('Save configuration'));
1368

    
1369
    // Enable aggregator module and ensure the 'administer news feeds'
1370
    // permission is assigned by default.
1371
    $edit = array();
1372
    $edit['modules[Core][aggregator][enable]'] = TRUE;
1373
    $this->drupalPost('admin/modules', $edit, t('Save configuration'));
1374
    $this->assertTrue(user_access('administer news feeds', $this->admin_user), 'The permission was automatically assigned to the administrator role');
1375
  }
1376

    
1377
  /**
1378
   * Verify proper permission changes by user_role_change_permissions().
1379
   */
1380
  function testUserRoleChangePermissions() {
1381
    $rid = $this->rid;
1382
    $account = $this->admin_user;
1383

    
1384
    // Verify current permissions.
1385
    $this->assertFalse(user_access('administer nodes', $account), 'User does not have "administer nodes" permission.');
1386
    $this->assertTrue(user_access('access user profiles', $account), 'User has "access user profiles" permission.');
1387
    $this->assertTrue(user_access('administer site configuration', $account), 'User has "administer site configuration" permission.');
1388

    
1389
    // Change permissions.
1390
    $permissions = array(
1391
      'administer nodes' => 1,
1392
      'access user profiles' => 0,
1393
    );
1394
    user_role_change_permissions($rid, $permissions);
1395

    
1396
    // Verify proper permission changes.
1397
    $this->assertTrue(user_access('administer nodes', $account), 'User now has "administer nodes" permission.');
1398
    $this->assertFalse(user_access('access user profiles', $account), 'User no longer has "access user profiles" permission.');
1399
    $this->assertTrue(user_access('administer site configuration', $account), 'User still has "administer site configuration" permission.');
1400
  }
1401
}
1402

    
1403
class UserAdminTestCase extends DrupalWebTestCase {
1404
  public static function getInfo() {
1405
    return array(
1406
      'name' => 'User administration',
1407
      'description' => 'Test user administration page functionality.',
1408
      'group' => 'User'
1409
    );
1410
  }
1411

    
1412
  /**
1413
   * Registers a user and deletes it.
1414
   */
1415
  function testUserAdmin() {
1416

    
1417
    $user_a = $this->drupalCreateUser(array());
1418
    $user_b = $this->drupalCreateUser(array('administer taxonomy'));
1419
    $user_c = $this->drupalCreateUser(array('administer taxonomy'));
1420

    
1421
    // Create admin user to delete registered user.
1422
    $admin_user = $this->drupalCreateUser(array('administer users'));
1423
    $this->drupalLogin($admin_user);
1424
    $this->drupalGet('admin/people');
1425
    $this->assertText($user_a->name, 'Found user A on admin users page');
1426
    $this->assertText($user_b->name, 'Found user B on admin users page');
1427
    $this->assertText($user_c->name, 'Found user C on admin users page');
1428
    $this->assertText($admin_user->name, 'Found Admin user on admin users page');
1429

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

    
1434
    // Filter the users by permission 'administer taxonomy'.
1435
    $edit = array();
1436
    $edit['permission'] = 'administer taxonomy';
1437
    $this->drupalPost('admin/people', $edit, t('Filter'));
1438

    
1439
    // Check if the correct users show up.
1440
    $this->assertNoText($user_a->name, 'User A not on filtered by perm admin users page');
1441
    $this->assertText($user_b->name, 'Found user B on filtered by perm admin users page');
1442
    $this->assertText($user_c->name, 'Found user C on filtered by perm admin users page');
1443

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

    
1448
    // Check if the correct users show up when filtered by role.
1449
    $this->assertNoText($user_a->name, 'User A not on filtered by role on admin users page');
1450
    $this->assertNoText($user_b->name, 'User B not on filtered by role on admin users page');
1451
    $this->assertText($user_c->name, 'User C on filtered by role on admin users page');
1452

    
1453
    // Test blocking of a user.
1454
    $account = user_load($user_c->uid);
1455
    $this->assertEqual($account->status, 1, 'User C not blocked');
1456
    $edit = array();
1457
    $edit['operation'] = 'block';
1458
    $edit['accounts[' . $account->uid . ']'] = TRUE;
1459
    $this->drupalPost('admin/people', $edit, t('Update'));
1460
    $account = user_load($user_c->uid, TRUE);
1461
    $this->assertEqual($account->status, 0, 'User C blocked');
1462

    
1463
    // Test unblocking of a user from /admin/people page and sending of activation mail
1464
    $editunblock = array();
1465
    $editunblock['operation'] = 'unblock';
1466
    $editunblock['accounts[' . $account->uid . ']'] = TRUE;
1467
    $this->drupalPost('admin/people', $editunblock, t('Update'));
1468
    $account = user_load($user_c->uid, TRUE);
1469
    $this->assertEqual($account->status, 1, 'User C unblocked');
1470
    $this->assertMail("to", $account->mail, "Activation mail sent to user C");
1471

    
1472
    // Test blocking and unblocking another user from /user/[uid]/edit form and sending of activation mail
1473
    $user_d = $this->drupalCreateUser(array());
1474
    $account1 = user_load($user_d->uid, TRUE);
1475
    $this->drupalPost('user/' . $account1->uid . '/edit', array('status' => 0), t('Save'));
1476
    $account1 = user_load($user_d->uid, TRUE);
1477
    $this->assertEqual($account1->status, 0, 'User D blocked');
1478
    $this->drupalPost('user/' . $account1->uid . '/edit', array('status' => TRUE), t('Save'));
1479
    $account1 = user_load($user_d->uid, TRUE);
1480
    $this->assertEqual($account1->status, 1, 'User D unblocked');
1481
    $this->assertMail("to", $account1->mail, "Activation mail sent to user D");
1482
  }
1483
}
1484

    
1485
/**
1486
 * Tests for user-configurable time zones.
1487
 */
1488
class UserTimeZoneFunctionalTest extends DrupalWebTestCase {
1489
  public static function getInfo() {
1490
    return array(
1491
      'name' => 'User time zones',
1492
      'description' => 'Set a user time zone and verify that dates are displayed in local time.',
1493
      'group' => 'User',
1494
    );
1495
  }
1496

    
1497
  /**
1498
   * Tests the display of dates and time when user-configurable time zones are set.
1499
   */
1500
  function testUserTimeZone() {
1501
    // Setup date/time settings for Los Angeles time.
1502
    variable_set('date_default_timezone', 'America/Los_Angeles');
1503
    variable_set('configurable_timezones', 1);
1504
    variable_set('date_format_medium', 'Y-m-d H:i T');
1505

    
1506
    // Create a user account and login.
1507
    $web_user = $this->drupalCreateUser();
1508
    $this->drupalLogin($web_user);
1509

    
1510
    // Create some nodes with different authored-on dates.
1511
    // Two dates in PST (winter time):
1512
    $date1 = '2007-03-09 21:00:00 -0800';
1513
    $date2 = '2007-03-11 01:00:00 -0800';
1514
    // One date in PDT (summer time):
1515
    $date3 = '2007-03-20 21:00:00 -0700';
1516
    $node1 = $this->drupalCreateNode(array('created' => strtotime($date1), 'type' => 'article'));
1517
    $node2 = $this->drupalCreateNode(array('created' => strtotime($date2), 'type' => 'article'));
1518
    $node3 = $this->drupalCreateNode(array('created' => strtotime($date3), 'type' => 'article'));
1519

    
1520
    // Confirm date format and time zone.
1521
    $this->drupalGet("node/$node1->nid");
1522
    $this->assertText('2007-03-09 21:00 PST', 'Date should be PST.');
1523
    $this->drupalGet("node/$node2->nid");
1524
    $this->assertText('2007-03-11 01:00 PST', 'Date should be PST.');
1525
    $this->drupalGet("node/$node3->nid");
1526
    $this->assertText('2007-03-20 21:00 PDT', 'Date should be PDT.');
1527

    
1528
    // Change user time zone to Santiago time.
1529
    $edit = array();
1530
    $edit['mail'] = $web_user->mail;
1531
    $edit['timezone'] = 'America/Santiago';
1532
    $this->drupalPost("user/$web_user->uid/edit", $edit, t('Save'));
1533
    $this->assertText(t('The changes have been saved.'), 'Time zone changed to Santiago time.');
1534

    
1535
    // Confirm date format and time zone.
1536
    $this->drupalGet("node/$node1->nid");
1537
    $this->assertText('2007-03-10 02:00 CLST', 'Date should be Chile summer time; five hours ahead of PST.');
1538
    $this->drupalGet("node/$node2->nid");
1539
    $this->assertText('2007-03-11 05:00 CLT', 'Date should be Chile time; four hours ahead of PST');
1540
    $this->drupalGet("node/$node3->nid");
1541
    $this->assertText('2007-03-21 00:00 CLT', 'Date should be Chile time; three hours ahead of PDT.');
1542
  }
1543
}
1544

    
1545
/**
1546
 * Test user autocompletion.
1547
 */
1548
class UserAutocompleteTestCase extends DrupalWebTestCase {
1549
  public static function getInfo() {
1550
    return array(
1551
      'name' => 'User autocompletion',
1552
      'description' => 'Test user autocompletion functionality.',
1553
      'group' => 'User'
1554
    );
1555
  }
1556

    
1557
  function setUp() {
1558
    parent::setUp();
1559

    
1560
    // Set up two users with different permissions to test access.
1561
    $this->unprivileged_user = $this->drupalCreateUser();
1562
    $this->privileged_user = $this->drupalCreateUser(array('access user profiles'));
1563
  }
1564

    
1565
  /**
1566
   * Tests access to user autocompletion and verify the correct results.
1567
   */
1568
  function testUserAutocomplete() {
1569
    // Check access from unprivileged user, should be denied.
1570
    $this->drupalLogin($this->unprivileged_user);
1571
    $this->drupalGet('user/autocomplete/' . $this->unprivileged_user->name[0]);
1572
    $this->assertResponse(403, 'Autocompletion access denied to user without permission.');
1573

    
1574
    // Check access from privileged user.
1575
    $this->drupalLogout();
1576
    $this->drupalLogin($this->privileged_user);
1577
    $this->drupalGet('user/autocomplete/' . $this->unprivileged_user->name[0]);
1578
    $this->assertResponse(200, 'Autocompletion access allowed.');
1579

    
1580
    // Using first letter of the user's name, make sure the user's full name is in the results.
1581
    $this->assertRaw($this->unprivileged_user->name, 'User name found in autocompletion results.');
1582
  }
1583
}
1584

    
1585

    
1586
/**
1587
 * Tests user links in the secondary menu.
1588
 */
1589
class UserAccountLinksUnitTests extends DrupalWebTestCase {
1590
  public static function getInfo() {
1591
    return array(
1592
      'name' => 'User account links',
1593
      'description' => 'Test user-account links.',
1594
      'group' => 'User'
1595
    );
1596
  }
1597

    
1598
  function setUp() {
1599
    parent::setUp('menu');
1600
  }
1601

    
1602
  /**
1603
   * Tests the secondary menu.
1604
   */
1605
  function testSecondaryMenu() {
1606
    // Create a regular user.
1607
    $user = $this->drupalCreateUser(array());
1608

    
1609
    // Log in and get the homepage.
1610
    $this->drupalLogin($user);
1611
    $this->drupalGet('<front>');
1612

    
1613
    // For a logged-in user, expect the secondary menu to have links for "My
1614
    // account" and "Log out".
1615
    $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array(
1616
      ':menu_id' => 'secondary-menu-links',
1617
      ':href' => 'user',
1618
      ':text' => 'My account',
1619
    ));
1620
    $this->assertEqual(count($link), 1, 'My account link is in secondary menu.');
1621

    
1622
    $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array(
1623
      ':menu_id' => 'secondary-menu-links',
1624
      ':href' => 'user/logout',
1625
      ':text' => 'Log out',
1626
    ));
1627
    $this->assertEqual(count($link), 1, 'Log out link is in secondary menu.');
1628

    
1629
    // Log out and get the homepage.
1630
    $this->drupalLogout();
1631
    $this->drupalGet('<front>');
1632

    
1633
    // For a logged-out user, expect no secondary links.
1634
    $element = $this->xpath('//ul[@id=:menu_id]', array(':menu_id' => 'secondary-menu-links'));
1635
    $this->assertEqual(count($element), 0, 'No secondary-menu for logged-out users.');
1636
  }
1637

    
1638
  /**
1639
   * Tests disabling the 'My account' link.
1640
   */
1641
  function testDisabledAccountLink() {
1642
    // Create an admin user and log in.
1643
    $this->drupalLogin($this->drupalCreateUser(array('access administration pages', 'administer menu')));
1644

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

    
1650
    // Disable the 'My account' link.
1651
    $input = $this->xpath('//input[@id=:field_id]/@name', array(':field_id' => (string)$label[0]));
1652
    $edit = array(
1653
      (string) $input[0] => FALSE,
1654
    );
1655
    $this->drupalPost('admin/structure/menu/manage/user-menu', $edit, t('Save configuration'));
1656

    
1657
    // Get the homepage.
1658
    $this->drupalGet('<front>');
1659

    
1660
    // Verify that the 'My account' link does not appear when disabled.
1661
    $link = $this->xpath('//ul[@id=:menu_id]/li/a[contains(@href, :href) and text()=:text]', array(
1662
      ':menu_id' => 'secondary-menu-links',
1663
      ':href' => 'user',
1664
      ':text' => 'My account',
1665
    ));
1666
    $this->assertEqual(count($link), 0, 'My account link is not in the secondary menu.');
1667
  }
1668

    
1669
}
1670

    
1671
/**
1672
 * Test user blocks.
1673
 */
1674
class UserBlocksUnitTests extends DrupalWebTestCase {
1675
  public static function getInfo() {
1676
    return array(
1677
      'name' => 'User blocks',
1678
      'description' => 'Test user blocks.',
1679
      'group' => 'User'
1680
    );
1681
  }
1682

    
1683
  /**
1684
   * Test the user login block.
1685
   */
1686
  function testUserLoginBlock() {
1687
    // Create a user with some permission that anonymous users lack.
1688
    $user = $this->drupalCreateUser(array('administer permissions'));
1689

    
1690
    // Log in using the block.
1691
    $edit = array();
1692
    $edit['name'] = $user->name;
1693
    $edit['pass'] = $user->pass_raw;
1694
    $this->drupalPost('admin/people/permissions', $edit, t('Log in'));
1695
    $this->assertNoText(t('User login'), 'Logged in.');
1696

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

    
1700
    // Now, log out and repeat with a non-403 page.
1701
    $this->drupalLogout();
1702
    $this->drupalPost('filter/tips', $edit, t('Log in'));
1703
    $this->assertNoText(t('User login'), 'Logged in.');
1704
    $this->assertPattern('!<title.*?' . t('Compose tips') . '.*?</title>!', 'Still on the same page after login for allowed page');
1705

    
1706
    // Check that the user login block is not vulnerable to information
1707
    // disclosure to third party sites.
1708
    $this->drupalLogout();
1709
    $this->drupalPost('http://example.com/', $edit, t('Log in'), array('external' => FALSE));
1710
    // Check that we remain on the site after login.
1711
    $this->assertEqual(url('user/' . $user->uid, array('absolute' => TRUE)), $this->getUrl(), 'Redirected to user profile page after login from the frontpage');
1712
  }
1713

    
1714
  /**
1715
   * Test the Who's Online block.
1716
   */
1717
  function testWhosOnlineBlock() {
1718
    // Generate users and make sure there are no current user sessions.
1719
    $user1 = $this->drupalCreateUser(array());
1720
    $user2 = $this->drupalCreateUser(array());
1721
    $user3 = $this->drupalCreateUser(array());
1722
    $this->assertEqual(db_query("SELECT COUNT(*) FROM {sessions}")->fetchField(), 0, 'Sessions table is empty.');
1723

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

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

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

    
1735
    // Insert two anonymous user sessions.
1736
    $this->insertSession();
1737
    $this->insertSession();
1738

    
1739
    // Test block output.
1740
    $block = user_block_view('online');
1741
    $this->drupalSetContent($block['content']);
1742
    $this->assertRaw(t('2 users'), 'Correct number of online users (2 users).');
1743
    $this->assertText($user1->name, 'Active user 1 found in online list.');
1744
    $this->assertText($user2->name, 'Active user 2 found in online list.');
1745
    $this->assertNoText($user3->name, "Inactive user not found in online list.");
1746
    $this->assertTrue(strpos($this->drupalGetContent(), $user1->name) > strpos($this->drupalGetContent(), $user2->name), 'Online users are ordered correctly.');
1747
  }
1748

    
1749
  /**
1750
   * Insert a user session into the {sessions} table. This function is used
1751
   * since we cannot log in more than one user at the same time in tests.
1752
   */
1753
  private function insertSession(array $fields = array()) {
1754
    $fields += array(
1755
      'uid' => 0,
1756
      'sid' => drupal_hash_base64(uniqid(mt_rand(), TRUE)),
1757
      'timestamp' => REQUEST_TIME,
1758
    );
1759
    db_insert('sessions')
1760
      ->fields($fields)
1761
      ->execute();
1762
    $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.');
1763
  }
1764
}
1765

    
1766
/**
1767
 * Tests saving a user account.
1768
 */
1769
class UserSaveTestCase extends DrupalWebTestCase {
1770

    
1771
  public static function getInfo() {
1772
    return array(
1773
      'name' => 'User save test',
1774
      'description' => 'Test user_save() for arbitrary new uid.',
1775
      'group' => 'User',
1776
    );
1777
  }
1778

    
1779
  /**
1780
   * Test creating a user with arbitrary uid.
1781
   */
1782
  function testUserImport() {
1783
    // User ID must be a number that is not in the database.
1784
    $max_uid = db_query('SELECT MAX(uid) FROM {users}')->fetchField();
1785
    $test_uid = $max_uid + mt_rand(1000, 1000000);
1786
    $test_name = $this->randomName();
1787

    
1788
    // Create the base user, based on drupalCreateUser().
1789
    $user = array(
1790
      'name' => $test_name,
1791
      'uid' => $test_uid,
1792
      'mail' => $test_name . '@example.com',
1793
      'is_new' => TRUE,
1794
      'pass' => user_password(),
1795
      'status' => 1,
1796
    );
1797
    $user_by_return = user_save(drupal_anonymous_user(), $user);
1798
    $this->assertTrue($user_by_return, 'Loading user by return of user_save().');
1799

    
1800
    // Test if created user exists.
1801
    $user_by_uid = user_load($test_uid);
1802
    $this->assertTrue($user_by_uid, 'Loading user by uid.');
1803

    
1804
    $user_by_name = user_load_by_name($test_name);
1805
    $this->assertTrue($user_by_name, 'Loading user by name.');
1806
  }
1807
}
1808

    
1809
/**
1810
 * Test the create user administration page.
1811
 */
1812
class UserCreateTestCase extends DrupalWebTestCase {
1813

    
1814
  public static function getInfo() {
1815
    return array(
1816
      'name' => 'User create',
1817
      'description' => 'Test the create user administration page.',
1818
      'group' => 'User',
1819
    );
1820
  }
1821

    
1822
  /**
1823
   * Create a user through the administration interface and ensure that it
1824
   * displays in the user list.
1825
   */
1826
  protected function testUserAdd() {
1827
    $user = $this->drupalCreateUser(array('administer users'));
1828
    $this->drupalLogin($user);
1829

    
1830
    foreach (array(FALSE, TRUE) as $notify) {
1831
      $edit = array(
1832
        'name' => $this->randomName(),
1833
        'mail' => $this->randomName() . '@example.com',
1834
        'pass[pass1]' => $pass = $this->randomString(),
1835
        'pass[pass2]' => $pass,
1836
        'notify' => $notify,
1837
      );
1838
      $this->drupalPost('admin/people/create', $edit, t('Create new account'));
1839

    
1840
      if ($notify) {
1841
        $this->assertText(t('A welcome message with further instructions has been e-mailed to the new user @name.', array('@name' => $edit['name'])), 'User created');
1842
        $this->assertEqual(count($this->drupalGetMails()), 1, 'Notification e-mail sent');
1843
      }
1844
      else {
1845
        $this->assertText(t('Created a new user account for @name. No e-mail has been sent.', array('@name' => $edit['name'])), 'User created');
1846
        $this->assertEqual(count($this->drupalGetMails()), 0, 'Notification e-mail not sent');
1847
      }
1848

    
1849
      $this->drupalGet('admin/people');
1850
      $this->assertText($edit['name'], 'User found in list of users');
1851
    }
1852
  }
1853
}
1854

    
1855
/**
1856
 * Tests editing a user account.
1857
 */
1858
class UserEditTestCase extends DrupalWebTestCase {
1859

    
1860
  public static function getInfo() {
1861
    return array(
1862
      'name' => 'User edit',
1863
      'description' => 'Test user edit page.',
1864
      'group' => 'User',
1865
    );
1866
  }
1867

    
1868
  /**
1869
   * Test user edit page.
1870
   */
1871
  function testUserEdit() {
1872
    // Test user edit functionality with user pictures disabled.
1873
    variable_set('user_pictures', 0);
1874
    $user1 = $this->drupalCreateUser(array('change own username'));
1875
    $user2 = $this->drupalCreateUser(array());
1876
    $this->drupalLogin($user1);
1877

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

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

    
1888
    // Check that filling out a single password field does not validate.
1889
    $edit = array();
1890
    $edit['pass[pass1]'] = '';
1891
    $edit['pass[pass2]'] = $this->randomName();
1892
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
1893
    $this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.');
1894

    
1895
    $edit['pass[pass1]'] = $this->randomName();
1896
    $edit['pass[pass2]'] = '';
1897
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
1898
    $this->assertText(t("The specified passwords do not match."), 'Typing mismatched passwords displays an error message.');
1899

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

    
1907
    $edit['current_pass'] = $user1->pass_raw;
1908
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
1909
    $this->assertRaw(t("The changes have been saved."));
1910

    
1911
    // Test that the user must enter current password before changing passwords.
1912
    $edit = array();
1913
    $edit['pass[pass1]'] = $new_pass = $this->randomName();
1914
    $edit['pass[pass2]'] = $new_pass;
1915
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
1916
    $this->assertRaw(t("Your current password is missing or incorrect; it's required to change the %name.", array('%name' => t('Password'))));
1917

    
1918
    // Try again with the current password.
1919
    $edit['current_pass'] = $user1->pass_raw;
1920
    $this->drupalPost("user/$user1->uid/edit", $edit, t('Save'));
1921
    $this->assertRaw(t("The changes have been saved."));
1922

    
1923
    // Make sure the user can log in with their new password.
1924
    $this->drupalLogout();
1925
    $user1->pass_raw = $new_pass;
1926
    $this->drupalLogin($user1);
1927
    $this->drupalLogout();
1928
  }
1929
}
1930

    
1931
/**
1932
 * Test case for user signatures.
1933
 */
1934
class UserSignatureTestCase extends DrupalWebTestCase {
1935
  public static function getInfo() {
1936
    return array(
1937
      'name' => 'User signatures',
1938
      'description' => 'Test user signatures.',
1939
      'group' => 'User',
1940
    );
1941
  }
1942

    
1943
  function setUp() {
1944
    parent::setUp('comment');
1945

    
1946
    // Enable user signatures.
1947
    variable_set('user_signatures', 1);
1948

    
1949
    // Prefetch text formats.
1950
    $this->full_html_format = filter_format_load('full_html');
1951
    $this->plain_text_format = filter_format_load('plain_text');
1952

    
1953
    // Create regular and administrative users.
1954
    $this->web_user = $this->drupalCreateUser(array());
1955
    $admin_permissions = array('administer comments');
1956
    foreach (filter_formats() as $format) {
1957
      if ($permission = filter_permission_name($format)) {
1958
        $admin_permissions[] = $permission;
1959
      }
1960
    }
1961
    $this->admin_user = $this->drupalCreateUser($admin_permissions);
1962
  }
1963

    
1964
  /**
1965
   * Test that a user can change their signature format and that it is respected
1966
   * upon display.
1967
   */
1968
  function testUserSignature() {
1969
    // Create a new node with comments on.
1970
    $node = $this->drupalCreateNode(array('comment' => COMMENT_NODE_OPEN));
1971

    
1972
    // Verify that user signature field is not displayed on registration form.
1973
    $this->drupalGet('user/register');
1974
    $this->assertNoText(t('Signature'));
1975

    
1976
    // Log in as a regular user and create a signature.
1977
    $this->drupalLogin($this->web_user);
1978
    $signature_text = "<h1>" . $this->randomName() . "</h1>";
1979
    $edit = array(
1980
      'signature[value]' => $signature_text,
1981
      'signature[format]' => $this->plain_text_format->format,
1982
    );
1983
    $this->drupalPost('user/' . $this->web_user->uid . '/edit', $edit, t('Save'));
1984

    
1985
    // Verify that values were stored.
1986
    $this->assertFieldByName('signature[value]', $edit['signature[value]'], 'Submitted signature text found.');
1987
    $this->assertFieldByName('signature[format]', $edit['signature[format]'], 'Submitted signature format found.');
1988

    
1989
    // Create a comment.
1990
    $langcode = LANGUAGE_NONE;
1991
    $edit = array();
1992
    $edit['subject'] = $this->randomName(8);
1993
    $edit['comment_body[' . $langcode . '][0][value]'] = $this->randomName(16);
1994
    $this->drupalPost('comment/reply/' . $node->nid, $edit, t('Preview'));
1995
    $this->drupalPost(NULL, array(), t('Save'));
1996

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

    
2002
    // Log in as an administrator and edit the comment to use Full HTML, so
2003
    // that the comment text itself is not filtered at all.
2004
    $this->drupalLogin($this->admin_user);
2005
    $edit['comment_body[' . $langcode . '][0][format]'] = $this->full_html_format->format;
2006
    $this->drupalPost('comment/' . $comment_id . '/edit', $edit, t('Save'));
2007

    
2008
    // Assert that the signature did not make it through unfiltered.
2009
    $this->drupalGet('node/' . $node->nid);
2010
    $this->assertNoRaw($signature_text, 'Unfiltered signature text not found.');
2011
    $this->assertRaw(check_markup($signature_text, $this->plain_text_format->format), 'Filtered signature text found.');
2012
  }
2013
}
2014

    
2015
/*
2016
 * Test that a user, having editing their own account, can still log in.
2017
 */
2018
class UserEditedOwnAccountTestCase extends DrupalWebTestCase {
2019

    
2020
  public static function getInfo() {
2021
    return array(
2022
      'name' => 'User edited own account',
2023
      'description' => 'Test user edited own account can still log in.',
2024
      'group' => 'User',
2025
    );
2026
  }
2027

    
2028
  function testUserEditedOwnAccount() {
2029
    // Change account setting 'Who can register accounts?' to Administrators
2030
    // only.
2031
    variable_set('user_register', USER_REGISTER_ADMINISTRATORS_ONLY);
2032

    
2033
    // Create a new user account and log in.
2034
    $account = $this->drupalCreateUser(array('change own username'));
2035
    $this->drupalLogin($account);
2036

    
2037
    // Change own username.
2038
    $edit = array();
2039
    $edit['name'] = $this->randomName();
2040
    $this->drupalPost('user/' . $account->uid . '/edit', $edit, t('Save'));
2041

    
2042
    // Log out.
2043
    $this->drupalLogout();
2044

    
2045
    // Set the new name on the user account and attempt to log back in.
2046
    $account->name = $edit['name'];
2047
    $this->drupalLogin($account);
2048
  }
2049
}
2050

    
2051
/**
2052
 * Test case to test adding, editing and deleting roles.
2053
 */
2054
class UserRoleAdminTestCase extends DrupalWebTestCase {
2055

    
2056
  public static function getInfo() {
2057
    return array(
2058
      'name' => 'User role administration',
2059
      'description' => 'Test adding, editing and deleting user roles and changing role weights.',
2060
      'group' => 'User',
2061
    );
2062
  }
2063

    
2064
  function setUp() {
2065
    parent::setUp();
2066
    $this->admin_user = $this->drupalCreateUser(array('administer permissions', 'administer users'));
2067
  }
2068

    
2069
  /**
2070
   * Test adding, renaming and deleting roles.
2071
   */
2072
  function testRoleAdministration() {
2073
    $this->drupalLogin($this->admin_user);
2074

    
2075
    // Test adding a role. (In doing so, we use a role name that happens to
2076
    // correspond to an integer, to test that the role administration pages
2077
    // correctly distinguish between role names and IDs.)
2078
    $role_name = '123';
2079
    $edit = array('name' => $role_name);
2080
    $this->drupalPost('admin/people/permissions/roles', $edit, t('Add role'));
2081
    $this->assertText(t('The role has been added.'), 'The role has been added.');
2082
    $role = user_role_load_by_name($role_name);
2083
    $this->assertTrue(is_object($role), 'The role was successfully retrieved from the database.');
2084

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

    
2089
    // Test renaming a role.
2090
    $old_name = $role_name;
2091
    $role_name = '456';
2092
    $edit = array('name' => $role_name);
2093
    $this->drupalPost("admin/people/permissions/roles/edit/{$role->rid}", $edit, t('Save role'));
2094
    $this->assertText(t('The role has been renamed.'), 'The role has been renamed.');
2095
    $this->assertFalse(user_role_load_by_name($old_name), 'The role can no longer be retrieved from the database using its old name.');
2096
    $this->assertTrue(is_object(user_role_load_by_name($role_name)), 'The role can be retrieved from the database using its new name.');
2097

    
2098
    // Test deleting the default administrator role.
2099
    $role_name = 'administrator';
2100
    $role = user_role_load_by_name($role_name);
2101
    $this->drupalPost("admin/people/permissions/roles/edit/{$role->rid}", NULL, t('Delete role'));
2102
    $this->drupalPost(NULL, NULL, t('Delete'));
2103
    $this->assertText(t('The role has been deleted.'), 'The role has been deleted');
2104
    $this->assertNoLinkByHref("admin/people/permissions/roles/edit/{$role->rid}", 'Role edit link removed.');
2105
    $this->assertFalse(user_role_load_by_name($role_name), 'A deleted role can no longer be loaded.');
2106
    // Make sure this role is no longer configured as the administrator role.
2107
    $this->assertNull(variable_get('user_admin_role'), 'The administrator role is no longer configured as the administrator role.');
2108

    
2109
    // Make sure that the system-defined roles cannot be edited via the user
2110
    // interface.
2111
    $this->drupalGet('admin/people/permissions/roles/edit/' . DRUPAL_ANONYMOUS_RID);
2112
    $this->assertResponse(403, 'Access denied when trying to edit the built-in anonymous role.');
2113
    $this->drupalGet('admin/people/permissions/roles/edit/' . DRUPAL_AUTHENTICATED_RID);
2114
    $this->assertResponse(403, 'Access denied when trying to edit the built-in authenticated role.');
2115
  }
2116

    
2117
  /**
2118
   * Test user role weight change operation.
2119
   */
2120
  function testRoleWeightChange() {
2121
    $this->drupalLogin($this->admin_user);
2122

    
2123
    // Pick up a random role and get its weight.
2124
    $rid = array_rand(user_roles());
2125
    $role = user_role_load($rid);
2126
    $old_weight = $role->weight;
2127

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

    
2133
    // Retrieve the saved role and compare its weight.
2134
    $role = user_role_load($rid);
2135
    $new_weight = $role->weight;
2136
    $this->assertTrue(($old_weight + 1) == $new_weight, 'Role weight updated successfully.');
2137
  }
2138
}
2139

    
2140
/**
2141
 * Test user token replacement in strings.
2142
 */
2143
class UserTokenReplaceTestCase extends DrupalWebTestCase {
2144
  public static function getInfo() {
2145
    return array(
2146
      'name' => 'User token replacement',
2147
      'description' => 'Generates text using placeholders for dummy content to check user token replacement.',
2148
      'group' => 'User',
2149
    );
2150
  }
2151

    
2152
  /**
2153
   * Creates a user, then tests the tokens generated from it.
2154
   */
2155
  function testUserTokenReplacement() {
2156
    global $language;
2157
    $url_options = array(
2158
      'absolute' => TRUE,
2159
      'language' => $language,
2160
    );
2161

    
2162
    // Create two users and log them in one after another.
2163
    $user1 = $this->drupalCreateUser(array());
2164
    $user2 = $this->drupalCreateUser(array());
2165
    $this->drupalLogin($user1);
2166
    $this->drupalLogout();
2167
    $this->drupalLogin($user2);
2168

    
2169
    $account = user_load($user1->uid);
2170
    $global_account = user_load($GLOBALS['user']->uid);
2171

    
2172
    // Generate and test sanitized tokens.
2173
    $tests = array();
2174
    $tests['[user:uid]'] = $account->uid;
2175
    $tests['[user:name]'] = check_plain(format_username($account));
2176
    $tests['[user:mail]'] = check_plain($account->mail);
2177
    $tests['[user:url]'] = url("user/$account->uid", $url_options);
2178
    $tests['[user:edit-url]'] = url("user/$account->uid/edit", $url_options);
2179
    $tests['[user:last-login]'] = format_date($account->login, 'medium', '', NULL, $language->language);
2180
    $tests['[user:last-login:short]'] = format_date($account->login, 'short', '', NULL, $language->language);
2181
    $tests['[user:created]'] = format_date($account->created, 'medium', '', NULL, $language->language);
2182
    $tests['[user:created:short]'] = format_date($account->created, 'short', '', NULL, $language->language);
2183
    $tests['[current-user:name]'] = check_plain(format_username($global_account));
2184

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

    
2188
    foreach ($tests as $input => $expected) {
2189
      $output = token_replace($input, array('user' => $account), array('language' => $language));
2190
      $this->assertEqual($output, $expected, format_string('Sanitized user token %token replaced.', array('%token' => $input)));
2191
    }
2192

    
2193
    // Generate and test unsanitized tokens.
2194
    $tests['[user:name]'] = format_username($account);
2195
    $tests['[user:mail]'] = $account->mail;
2196
    $tests['[current-user:name]'] = format_username($global_account);
2197

    
2198
    foreach ($tests as $input => $expected) {
2199
      $output = token_replace($input, array('user' => $account), array('language' => $language, 'sanitize' => FALSE));
2200
      $this->assertEqual($output, $expected, format_string('Unsanitized user token %token replaced.', array('%token' => $input)));
2201
    }
2202
  }
2203
}
2204

    
2205
/**
2206
 * Test user search.
2207
 */
2208
class UserUserSearchTestCase extends DrupalWebTestCase {
2209
  public static function getInfo() {
2210
    return array(
2211
      'name' => 'User search',
2212
      'description' => 'Tests the user search page and verifies that sensitive information is hidden from unauthorized users.',
2213
      'group' => 'User',
2214
    );
2215
  }
2216

    
2217
  function testUserSearch() {
2218
    $user1 = $this->drupalCreateUser(array('access user profiles', 'search content', 'use advanced search'));
2219
    $this->drupalLogin($user1);
2220
    $keys = $user1->mail;
2221
    $edit = array('keys' => $keys);
2222
    $this->drupalPost('search/user/', $edit, t('Search'));
2223
    $this->assertNoText($keys);
2224
    $this->drupalLogout();
2225

    
2226
    $user2 = $this->drupalCreateUser(array('administer users', 'access user profiles', 'search content', 'use advanced search'));
2227
    $this->drupalLogin($user2);
2228
    $keys = $user2->mail;
2229
    $edit = array('keys' => $keys);
2230
    $this->drupalPost('search/user/', $edit, t('Search'));
2231
    $this->assertText($keys);
2232

    
2233
    // Verify that wildcard search works.
2234
    $keys = $user1->name;
2235
    $keys = substr($keys, 0, 2) . '*' . substr($keys, 4, 2);
2236
    $edit = array('keys' => $keys);
2237
    $this->drupalPost('search/user/', $edit, t('Search'));
2238
    $this->assertText($user1->name, 'Search for username wildcard resulted in user name on page for administrative user.');
2239

    
2240
    // Verify that wildcard search works for email.
2241
    $keys = $user1->mail;
2242
    $keys = substr($keys, 0, 2) . '*' . substr($keys, 4, 2);
2243
    $edit = array('keys' => $keys);
2244
    $this->drupalPost('search/user/', $edit, t('Search'));
2245
    $this->assertText($user1->name, 'Search for email wildcard resulted in user name on page for administrative user.');
2246

    
2247
    // Create a blocked user.
2248
    $blocked_user = $this->drupalCreateUser();
2249
    $edit = array('status' => 0);
2250
    $blocked_user = user_save($blocked_user, $edit);
2251

    
2252
    // Verify that users with "administer users" permissions can see blocked
2253
    // accounts in search results.
2254
    $edit = array('keys' => $blocked_user->name);
2255
    $this->drupalPost('search/user/', $edit, t('Search'));
2256
    $this->assertText($blocked_user->name, 'Blocked users are listed on the user search results for users with the "administer users" permission.');
2257

    
2258
    // Verify that users without "administer users" permissions do not see
2259
    // blocked accounts in search results.
2260
    $this->drupalLogin($user1);
2261
    $edit = array('keys' => $blocked_user->name);
2262
    $this->drupalPost('search/user/', $edit, t('Search'));
2263
    $this->assertNoText($blocked_user->name, 'Blocked users are hidden from the user search results.');
2264

    
2265
    $this->drupalLogout();
2266
  }
2267
}
2268

    
2269
/**
2270
 * Test role assignment.
2271
 */
2272
class UserRolesAssignmentTestCase extends DrupalWebTestCase {
2273
  protected $admin_user;
2274

    
2275
  public static function getInfo() {
2276
    return array(
2277
      'name' => 'Role assignment',
2278
      'description' => 'Tests that users can be assigned and unassigned roles.',
2279
      'group' => 'User'
2280
    );
2281
  }
2282

    
2283
  function setUp() {
2284
    parent::setUp();
2285
    $this->admin_user = $this->drupalCreateUser(array('administer permissions', 'administer users'));
2286
    $this->drupalLogin($this->admin_user);
2287
  }
2288

    
2289
  /**
2290
   * Tests that a user can be assigned a role and that the role can be removed
2291
   * again.
2292
   */
2293
  function testAssignAndRemoveRole()  {
2294
    $rid = $this->drupalCreateRole(array('administer content types'));
2295
    $account = $this->drupalCreateUser();
2296

    
2297
    // Assign the role to the user.
2298
    $this->drupalPost('user/' . $account->uid . '/edit', array("roles[$rid]" => $rid), t('Save'));
2299
    $this->assertText(t('The changes have been saved.'));
2300
    $this->assertFieldChecked('edit-roles-' . $rid, 'Role is assigned.');
2301
    $this->userLoadAndCheckRoleAssigned($account, $rid);
2302

    
2303
    // Remove the role from the user.
2304
    $this->drupalPost('user/' . $account->uid . '/edit', array("roles[$rid]" => FALSE), t('Save'));
2305
    $this->assertText(t('The changes have been saved.'));
2306
    $this->assertNoFieldChecked('edit-roles-' . $rid, 'Role is removed from user.');
2307
    $this->userLoadAndCheckRoleAssigned($account, $rid, FALSE);
2308
  }
2309

    
2310
  /**
2311
   * Tests that when creating a user the role can be assigned. And that it can
2312
   * be removed again.
2313
   */
2314
  function testCreateUserWithRole() {
2315
    $rid = $this->drupalCreateRole(array('administer content types'));
2316
    // Create a new user and add the role at the same time.
2317
    $edit = array(
2318
      'name' => $this->randomName(),
2319
      'mail' => $this->randomName() . '@example.com',
2320
      'pass[pass1]' => $pass = $this->randomString(),
2321
      'pass[pass2]' => $pass,
2322
      "roles[$rid]" => $rid,
2323
    );
2324
    $this->drupalPost('admin/people/create', $edit, t('Create new account'));
2325
    $this->assertText(t('Created a new user account for !name.', array('!name' => $edit['name'])));
2326
    // Get the newly added user.
2327
    $account = user_load_by_name($edit['name']);
2328

    
2329
    $this->drupalGet('user/' . $account->uid . '/edit');
2330
    $this->assertFieldChecked('edit-roles-' . $rid, 'Role is assigned.');
2331
    $this->userLoadAndCheckRoleAssigned($account, $rid);
2332

    
2333
    // Remove the role again.
2334
    $this->drupalPost('user/' . $account->uid . '/edit', array("roles[$rid]" => FALSE), t('Save'));
2335
    $this->assertText(t('The changes have been saved.'));
2336
    $this->assertNoFieldChecked('edit-roles-' . $rid, 'Role is removed from user.');
2337
    $this->userLoadAndCheckRoleAssigned($account, $rid, FALSE);
2338
  }
2339

    
2340
  /**
2341
   * Check role on user object.
2342
   *
2343
   * @param object $account
2344
   *   The user account to check.
2345
   * @param string $rid
2346
   *   The role ID to search for.
2347
   * @param bool $is_assigned
2348
   *   (optional) Whether to assert that $rid exists (TRUE) or not (FALSE).
2349
   *   Defaults to TRUE.
2350
   */
2351
  private function userLoadAndCheckRoleAssigned($account, $rid, $is_assigned = TRUE) {
2352
    $account = user_load($account->uid, TRUE);
2353
    if ($is_assigned) {
2354
      $this->assertTrue(array_key_exists($rid, $account->roles), 'The role is present in the user object.');
2355
    }
2356
    else {
2357
      $this->assertFalse(array_key_exists($rid, $account->roles), 'The role is not present in the user object.');
2358
    }
2359
  }
2360
}
2361

    
2362

    
2363
/**
2364
 * Unit test for authmap assignment.
2365
 */
2366
class UserAuthmapAssignmentTestCase extends DrupalWebTestCase {
2367
  public static function getInfo() {
2368
    return array(
2369
      'name' => 'Authmap assignment',
2370
      'description' => 'Tests that users can be assigned and unassigned authmaps.',
2371
      'group' => 'User'
2372
    );
2373
  }
2374

    
2375
  /**
2376
   * Test authmap assignment and retrieval.
2377
   */
2378
  function testAuthmapAssignment()  {
2379
    $account = $this->drupalCreateUser();
2380

    
2381
    // Assign authmaps to the user.
2382
    $authmaps = array(
2383
      'authname_poll' => 'external username one',
2384
      'authname_book' => 'external username two',
2385
    );
2386
    user_set_authmaps($account, $authmaps);
2387

    
2388
    // Test for expected authmaps.
2389
    $expected_authmaps = array(
2390
      'external username one' => array(
2391
        'poll' => 'external username one',
2392
      ),
2393
      'external username two' => array(
2394
        'book' => 'external username two',
2395
      ),
2396
    );
2397
    foreach ($expected_authmaps as $authname => $expected_output) {
2398
      $this->assertIdentical(user_get_authmaps($authname), $expected_output, format_string('Authmap for authname %authname was set correctly.', array('%authname' => $authname)));
2399
    }
2400

    
2401
    // Remove authmap for module poll, add authmap for module blog.
2402
    $authmaps = array(
2403
      'authname_poll' => NULL,
2404
      'authname_blog' => 'external username three',
2405
    );
2406
    user_set_authmaps($account, $authmaps);
2407

    
2408
    // Assert that external username one does not have authmaps.
2409
    $remove_username = 'external username one';
2410
    unset($expected_authmaps[$remove_username]);
2411
    $this->assertFalse(user_get_authmaps($remove_username), format_string('Authmap for %authname was removed.', array('%authname' => $remove_username)));
2412

    
2413
    // Assert that a new authmap was created for external username three, and
2414
    // existing authmaps for external username two were unchanged.
2415
    $expected_authmaps['external username three'] = array('blog' => 'external username three');
2416
    foreach ($expected_authmaps as $authname => $expected_output) {
2417
      $this->assertIdentical(user_get_authmaps($authname), $expected_output, format_string('Authmap for authname %authname was set correctly.', array('%authname' => $authname)));
2418
    }
2419
  }
2420
}
2421

    
2422
/**
2423
 * Tests user_validate_current_pass on a custom form.
2424
 */
2425
class UserValidateCurrentPassCustomForm extends DrupalWebTestCase {
2426

    
2427
  public static function getInfo() {
2428
    return array(
2429
      'name' => 'User validate current pass custom form',
2430
      'description' => 'Test that user_validate_current_pass is usable on a custom form.',
2431
      'group' => 'User',
2432
    );
2433
  }
2434

    
2435
  /**
2436
   * User with permission to view content.
2437
   */
2438
  protected $accessUser;
2439

    
2440
  /**
2441
   * User permission to administer users.
2442
   */
2443
  protected $adminUser;
2444

    
2445
  function setUp() {
2446
    parent::setUp('user_form_test');
2447
    // Create two users
2448
    $this->accessUser = $this->drupalCreateUser(array('access content'));
2449
    $this->adminUser = $this->drupalCreateUser(array('administer users'));
2450
  }
2451

    
2452
  /**
2453
   * Tests that user_validate_current_pass can be reused on a custom form.
2454
   */
2455
  function testUserValidateCurrentPassCustomForm() {
2456
    $this->drupalLogin($this->adminUser);
2457

    
2458
    // Submit the custom form with the admin user using the access user's password.
2459
    $edit = array();
2460
    $edit['user_form_test_field'] = $this->accessUser->name;
2461
    $edit['current_pass'] = $this->accessUser->pass_raw;
2462
    $this->drupalPost('user_form_test_current_password/' . $this->accessUser->uid, $edit, t('Test'));
2463
    $this->assertText(t('The password has been validated and the form submitted successfully.'));
2464
  }
2465
}