/[drupal]/drupal/modules/user/user.test
ViewVC logotype

Contents of /drupal/modules/user/user.test

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.66 - (show annotations) (download) (as text)
Sun Nov 1 21:26:44 2009 UTC (3 weeks, 6 days ago) by webchick
Branch: MAIN
CVS Tags: DRUPAL-7-0-UNSTABLE-10
Changes since 1.65: +2 -2 lines
File MIME type: text/x-php
#192056 by effulgentsia, Dave Cohen, andypost, hswong3i, geodaniel, pwolanin, and dahacouk: Ensure user's raw login name is never output directly.
1 <?php
2 // $Id: user.test,v 1.65 2009/10/16 13:20:16 dries Exp $
3
4 class UserRegistrationTestCase extends DrupalWebTestCase {
5 public static function getInfo() {
6 return array(
7 'name' => 'User registration',
8 'description' => 'Test registration of user under different configurations.',
9 'group' => 'User'
10 );
11 }
12
13 function testRegistrationWithEmailVerification() {
14 // Require e-mail verification.
15 variable_set('user_email_verification', TRUE);
16
17 // Set registration to administrator only.
18 variable_set('user_register', 0);
19 $this->drupalGet('user/register');
20 $this->assertResponse(403, t('Registration page is inaccessible when only administrators can create accounts.'));
21
22 // Allow registration by site visitors without administrator approval.
23 variable_set('user_register', 1);
24 $edit = array();
25 $edit['name'] = $name = $this->randomName();
26 $edit['mail'] = $mail = $edit['name'] . '@example.com';
27 $this->drupalPost('user/register', $edit, t('Create new account'));
28 $this->assertText(t('Your password and further instructions have been sent to your e-mail address.'), t('User registered successfully.'));
29 $new_user = reset(user_load_multiple(array(), array('name' => $name, 'mail' => $mail)));
30 $this->assertTrue($new_user->status, t('New account is active after registration.'));
31
32 // Allow registration by site visitors, but require administrator approval.
33 variable_set('user_register', 2);
34 $edit = array();
35 $edit['name'] = $name = $this->randomName();
36 $edit['mail'] = $mail = $edit['name'] . '@example.com';
37 $this->drupalPost('user/register', $edit, t('Create new account'));
38 $new_user = reset(user_load_multiple(array(), array('name' => $name, 'mail' => $mail)));
39 $this->assertFalse($new_user->status, t('New account is blocked until approved by an administrator.'));
40 }
41
42 function testRegistrationWithoutEmailVerification() {
43 // Don't require e-mail verification.
44 variable_set('user_email_verification', FALSE);
45
46 // Allow registration by site visitors without administrator approval.
47 variable_set('user_register', 1);
48 $edit = array();
49 $edit['name'] = $name = $this->randomName();
50 $edit['mail'] = $mail = $edit['name'] . '@example.com';
51
52 // Try entering a mismatching password.
53 $edit['pass[pass1]'] = '99999.0';
54 $edit['pass[pass2]'] = '99999';
55 $this->drupalPost('user/register', $edit, t('Create new account'));
56 $this->assertText(t('The specified passwords do not match.'), t('Type mismatched passwords display an error message.'));
57
58 // Enter a correct password.
59 $edit['pass[pass1]'] = $new_pass = $this->randomName();
60 $edit['pass[pass2]'] = $new_pass;
61 $this->drupalPost('user/register', $edit, t('Create new account'));
62 $new_user = reset(user_load_multiple(array(), array('name' => $name, 'mail' => $mail)));
63 $this->assertText(t('Registration successful. You are now logged in.'), t('Users are logged in after registering.'));
64 $this->drupalLogout();
65
66 // Allow registration by site visitors, but require administrator approval.
67 variable_set('user_register', 2);
68 $edit = array();
69 $edit['name'] = $name = $this->randomName();
70 $edit['mail'] = $mail = $edit['name'] . '@example.com';
71 $edit['pass[pass1]'] = $pass = $this->randomName();
72 $edit['pass[pass2]'] = $pass;
73 $this->drupalPost('user/register', $edit, t('Create new account'));
74 $this->assertText(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.'), t('Users are notified of pending approval'));
75
76 // Try to login before administrator approval.
77 $auth = array(
78 'name' => $name,
79 'pass' => $pass,
80 );
81 $this->drupalPost('user/login', $auth, t('Log in'));
82 $this->assertText(t('The username @name has not been activated or is blocked.', array('@name' => $name)), t('User cannot login yet.'));
83
84 // Activate the new account.
85 $new_user = reset(user_load_multiple(array(), array('name' => $name, 'mail' => $mail)));
86 $admin_user = $this->drupalCreateUser(array('administer users'));
87 $this->drupalLogin($admin_user);
88 $edit = array(
89 'status' => 1,
90 );
91 $this->drupalPost('user/' . $new_user->uid . '/edit', $edit, t('Save'));
92 $this->drupalLogout();
93
94 // Login after administrator approval.
95 $this->drupalPost('user/login', $auth, t('Log in'));
96 $this->assertText(t('Member for'), t('User can log in after administrator approval.'));
97 }
98
99 function testRegistrationDefaultValues() {
100 // Allow registration by site visitors without administrator approval.
101 variable_set('user_register', 1);
102
103 // Don't require e-mail verification.
104 variable_set('user_email_verification', FALSE);
105
106 // Set the default timezone to Brussels.
107 variable_set('configurable_timezones', 1);
108 variable_set('date_default_timezone', 'Europe/Brussels');
109
110 $edit = array();
111 $edit['name'] = $name = $this->randomName();
112 $edit['mail'] = $mail = $edit['name'] . '@example.com';
113 $edit['pass[pass1]'] = $new_pass = $this->randomName();
114 $edit['pass[pass2]'] = $new_pass;
115 $this->drupalPost('user/register', $edit, t('Create new account'));
116
117 // Check user fields.
118 $new_user = reset(user_load_multiple(array(), array('name' => $name, 'mail' => $mail)));
119 $this->assertEqual($new_user->name, $name, t('Username matches.'));
120 $this->assertEqual($new_user->mail, $mail, t('E-mail address matches.'));
121 $this->assertEqual($new_user->theme, '', t('Correct theme field.'));
122 $this->assertEqual($new_user->signature, '', t('Correct signature field.'));
123 $this->assertTrue(($new_user->created > REQUEST_TIME - 20 ), t('Correct creation time.'));
124 $this->assertEqual($new_user->status, variable_get('user_register', 1) == 1 ? 1 : 0, t('Correct status field.'));
125 $this->assertEqual($new_user->timezone, variable_get('date_default_timezone'), t('Correct time zone field.'));
126 $this->assertEqual($new_user->language, '', t('Correct language field.'));
127 $this->assertEqual($new_user->picture, '', t('Correct picture field.'));
128 $this->assertEqual($new_user->init, $mail, t('Correct init field.'));
129 }
130 }
131
132 class UserValidationTestCase extends DrupalWebTestCase {
133 public static function getInfo() {
134 return array(
135 'name' => 'Username/e-mail validation',
136 'description' => 'Verify that username/email validity checks behave as designed.',
137 'group' => 'User'
138 );
139 }
140
141 // Username validation.
142 function testUsernames() {
143 $test_cases = array( // '<username>' => array('<description>', 'assert<testName>'),
144 'foo' => array('Valid username', 'assertNull'),
145 'FOO' => array('Valid username', 'assertNull'),
146 'Foo O\'Bar' => array('Valid username', 'assertNull'),
147 'foo@bar' => array('Valid username', 'assertNull'),
148 'foo@example.com' => array('Valid username', 'assertNull'),
149 'foo@-example.com' => array('Valid username', 'assertNull'), // invalid domains are allowed in usernames
150 'þòøÇߪř€' => array('Valid username', 'assertNull'),
151 'ᚠᛇᚻ᛫ᛒᛦᚦ' => array('Valid UTF8 username', 'assertNull'), // runes
152 ' foo' => array('Invalid username that starts with a space', 'assertNotNull'),
153 'foo ' => array('Invalid username that ends with a space', 'assertNotNull'),
154 'foo bar' => array('Invalid username that contains 2 spaces \'&nbsp;&nbsp;\'', 'assertNotNull'),
155 '' => array('Invalid empty username', 'assertNotNull'),
156 'foo/' => array('Invalid username containing invalid chars', 'assertNotNull'),
157 'foo' . chr(0) . 'bar' => array('Invalid username containing chr(0)', 'assertNotNull'), // NULL
158 'foo' . chr(13) . 'bar' => array('Invalid username containing chr(13)', 'assertNotNull'), // CR
159 str_repeat('x', USERNAME_MAX_LENGTH + 1) => array('Invalid excessively long username', 'assertNotNull'),
160 );
161 foreach ($test_cases as $name => $test_case) {
162 list($description, $test) = $test_case;
163 $result = user_validate_name($name);
164 $this->$test($result, $description . ' (' . $name . ')');
165 }
166 }
167
168 // Mail validation. More extensive tests can be found at common.test
169 function testMailAddresses() {
170 $test_cases = array( // '<username>' => array('<description>', 'assert<testName>'),
171 '' => array('Empty mail address', 'assertNotNull'),
172 'foo' => array('Invalid mail address', 'assertNotNull'),
173 'foo@example.com' => array('Valid mail address', 'assertNull'),
174 );
175 foreach ($test_cases as $name => $test_case) {
176 list($description, $test) = $test_case;
177 $result = user_validate_mail($name);
178 $this->$test($result, $description . ' (' . $name . ')');
179 }
180 }
181 }
182
183 /**
184 * Functional tests for user logins, including rate limiting of login attempts.
185 */
186 class UserLoginTestCase extends DrupalWebTestCase {
187 public static function getInfo() {
188 return array(
189 'name' => 'User login',
190 'description' => 'Ensure that login works as expected.',
191 'group' => 'User',
192 );
193 }
194
195 /**
196 * Test the global login flood control.
197 */
198 function testGlobalLoginFloodControl() {
199 // Set the global login limit.
200 variable_set('user_failed_login_ip_limit', 10);
201 // Set a high per-user limit out so that it is not relevant in the test.
202 variable_set('user_failed_login_user_limit', 4000);
203
204 $user1 = $this->drupalCreateUser(array());
205 $incorrect_user1 = clone $user1;
206 $incorrect_user1->pass_raw .= 'incorrect';
207
208 // Try 2 failed logins.
209 for ($i = 0; $i < 2; $i++) {
210 $this->assertFailedLogin($incorrect_user1);
211 }
212
213 // A successful login will not reset the IP-based flood control count.
214 $this->drupalLogin($user1);
215 $this->drupalLogout();
216
217 // Try 8 more failed logins, they should not trigger the flood control
218 // mechanism.
219 for ($i = 0; $i < 8; $i++) {
220 $this->assertFailedLogin($incorrect_user1);
221 }
222
223 // The next login trial should result in an IP-based flood error message.
224 $this->assertFailedLogin($incorrect_user1, 'ip');
225
226 // A login with the correct password should also result in a flood error
227 // message.
228 $this->assertFailedLogin($user1, 'ip');
229 }
230
231 /**
232 * Test the per-user login flood control.
233 */
234 function testPerUserLoginFloodControl() {
235 // Set a high global limit out so that it is not relevant in the test.
236 variable_set('user_failed_login_ip_limit', 4000);
237 // Set the per-user login limit.
238 variable_set('user_failed_login_user_limit', 3);
239
240 $user1 = $this->drupalCreateUser(array());
241 $incorrect_user1 = clone $user1;
242 $incorrect_user1->pass_raw .= 'incorrect';
243
244 $user2 = $this->drupalCreateUser(array());
245
246 // Try 2 failed logins.
247 for ($i = 0; $i < 2; $i++) {
248 $this->assertFailedLogin($incorrect_user1);
249 }
250
251 // A successful login will reset the per-user flood control count.
252 $this->drupalLogin($user1);
253 $this->drupalLogout();
254
255 // Try 3 failed logins for user 1, they will not trigger flood control.
256 for ($i = 0; $i < 3; $i++) {
257 $this->assertFailedLogin($incorrect_user1);
258 }
259
260 // Try one successful attempt for user 2, it should not trigger any
261 // flood control.
262 $this->drupalLogin($user2);
263 $this->drupalLogout();
264
265 // Try one more attempt for user 1, it should be rejected, even if the
266 // correct password has been used.
267 $this->assertFailedLogin($user1, 'user');
268 }
269
270 /**
271 * Make an unsuccessful login attempt.
272 *
273 * @param $account
274 * A user object with name and pass_raw attributes for the login attempt.
275 * @param $flood_trigger
276 * Whether or not to expect that the flood control mechanism will be
277 * triggered.
278 */
279 function assertFailedLogin($account, $flood_trigger = NULL) {
280 $edit = array(
281 'name' => $account->name,
282 'pass' => $account->pass_raw,
283 );
284 $this->drupalPost('user', $edit, t('Log in'));
285 if (isset($flood_trigger)) {
286 if ($flood_trigger == 'user') {
287 $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. Please 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. Please try again later, or <a href="@url">request a new password</a>.', array('@url' => url('user/password'))));
288 }
289 else {
290 // No uid, so the limit is IP-based.
291 $this->assertRaw(t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Please try again later, or <a href="@url">request a new password</a>.', array('@url' => url('user/password'))));
292 }
293 }
294 else {
295 $this->assertText(t('Sorry, unrecognized username or password. Have you forgotten your password?'));
296 }
297 }
298 }
299
300 class UserCancelTestCase extends DrupalWebTestCase {
301 public static function getInfo() {
302 return array(
303 'name' => 'Cancel account',
304 'description' => 'Ensure that account cancellation methods work as expected.',
305 'group' => 'User',
306 );
307 }
308
309 /**
310 * Attempt to cancel account without permission.
311 */
312 function testUserCancelWithoutPermission() {
313 variable_set('user_cancel_method', 'user_cancel_reassign');
314
315 // Create a user.
316 $account = $this->drupalCreateUser(array());
317 $this->drupalLogin($account);
318 // Load real user object.
319 $account = user_load($account->uid, TRUE);
320
321 // Create a node.
322 $node = $this->drupalCreateNode(array('uid' => $account->uid));
323
324 // Attempt to cancel account.
325 $this->drupalGet('user/' . $account->uid . '/edit');
326 $this->assertNoRaw(t('Cancel account'), t('No cancel account button displayed.'));
327
328 // Attempt bogus account cancellation request confirmation.
329 $timestamp = $account->login;
330 $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login));
331 $this->assertResponse(403, t('Bogus cancelling request rejected.'));
332 $account = user_load($account->uid);
333 $this->assertTrue($account->status == 1, t('User account was not canceled.'));
334
335 // Confirm user's content has not been altered.
336 $test_node = node_load($node->nid, NULL, TRUE);
337 $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), t('Node of the user has not been altered.'));
338 }
339
340 /**
341 * Attempt invalid account cancellations.
342 */
343 function testUserCancelInvalid() {
344 variable_set('user_cancel_method', 'user_cancel_reassign');
345
346 // Create a user.
347 $account = $this->drupalCreateUser(array('cancel account'));
348 $this->drupalLogin($account);
349 // Load real user object.
350 $account = user_load($account->uid, TRUE);
351
352 // Create a node.
353 $node = $this->drupalCreateNode(array('uid' => $account->uid));
354
355 // Attempt to cancel account.
356 $this->drupalPost('user/' . $account->uid . '/edit', NULL, t('Cancel account'));
357
358 // Confirm account cancellation.
359 $timestamp = time();
360 $this->drupalPost(NULL, NULL, t('Cancel account'));
361 $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), t('Account cancellation request mailed message displayed.'));
362
363 // Attempt bogus account cancellation request confirmation.
364 $bogus_timestamp = $timestamp + 60;
365 $this->drupalGet("user/$account->uid/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login));
366 $this->assertText(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), t('Bogus cancelling request rejected.'));
367 $account = user_load($account->uid);
368 $this->assertTrue($account->status == 1, t('User account was not canceled.'));
369
370 // Attempt expired account cancellation request confirmation.
371 $bogus_timestamp = $timestamp - 86400 - 60;
372 $this->drupalGet("user/$account->uid/cancel/confirm/$bogus_timestamp/" . user_pass_rehash($account->pass, $bogus_timestamp, $account->login));
373 $this->assertText(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'), t('Expired cancel account request rejected.'));
374 $this->assertTrue(reset(user_load_multiple(array($account->uid), array('status' => 1))), t('User account was not canceled.'));
375
376 // Confirm user's content has not been altered.
377 $test_node = node_load($node->nid, NULL, TRUE);
378 $this->assertTrue(($test_node->uid == $account->uid && $test_node->status == 1), t('Node of the user has not been altered.'));
379 }
380
381 /**
382 * Disable account and keep all content.
383 */
384 function testUserBlock() {
385 variable_set('user_cancel_method', 'user_cancel_block');
386
387 // Create a user.
388 $web_user = $this->drupalCreateUser(array('cancel account'));
389 $this->drupalLogin($web_user);
390
391 // Load real user object.
392 $account = user_load($web_user->uid, TRUE);
393
394 // Attempt to cancel account.
395 $this->drupalGet('user/' . $account->uid . '/edit');
396 $this->drupalPost(NULL, NULL, t('Cancel account'));
397 $this->assertText(t('Are you sure you want to cancel your account?'), t('Confirmation form to cancel account displayed.'));
398 $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.'), t('Informs that all content will be remain as is.'));
399 $this->assertNoText(t('Select the method to cancel the account above.'), t('Does not allow user to select account cancellation method.'));
400
401 // Confirm account cancellation.
402 $timestamp = time();
403
404 $this->drupalPost(NULL, NULL, t('Cancel account'));
405 $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), t('Account cancellation request mailed message displayed.'));
406
407 // Confirm account cancellation request.
408 $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login));
409 $account = user_load($account->uid, TRUE);
410 $this->assertTrue($account->status == 0, t('User has been blocked.'));
411
412 // Confirm user is logged out.
413 $this->assertNoText($account->name, t('Logged out.'));
414 }
415
416 /**
417 * Disable account and unpublish all content.
418 */
419 function testUserBlockUnpublish() {
420 variable_set('user_cancel_method', 'user_cancel_block_unpublish');
421
422 // Create a user.
423 $account = $this->drupalCreateUser(array('cancel account'));
424 $this->drupalLogin($account);
425 // Load real user object.
426 $account = user_load($account->uid, TRUE);
427
428 // Create a node with two revisions.
429 $node = $this->drupalCreateNode(array('uid' => $account->uid));
430 $settings = get_object_vars($node);
431 $settings['revision'] = 1;
432 $node = $this->drupalCreateNode($settings);
433
434 // Attempt to cancel account.
435 $this->drupalGet('user/' . $account->uid . '/edit');
436 $this->drupalPost(NULL, NULL, t('Cancel account'));
437 $this->assertText(t('Are you sure you want to cancel your account?'), t('Confirmation form to cancel account displayed.'));
438 $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.'), t('Informs that all content will be unpublished.'));
439
440 // Confirm account cancellation.
441 $timestamp = time();
442 $this->drupalPost(NULL, NULL, t('Cancel account'));
443 $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), t('Account cancellation request mailed message displayed.'));
444
445 // Confirm account cancellation request.
446 $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login));
447 $account = user_load($account->uid, TRUE);
448 $this->assertTrue($account->status == 0, t('User has been blocked.'));
449
450 // Confirm user's content has been unpublished.
451 $test_node = node_load($node->nid, NULL, TRUE);
452 $this->assertTrue($test_node->status == 0, t('Node of the user has been unpublished.'));
453 $test_node = node_load($node->nid, $node->vid, TRUE);
454 $this->assertTrue($test_node->status == 0, t('Node revision of the user has been unpublished.'));
455
456 // Confirm user is logged out.
457 $this->assertNoText($account->name, t('Logged out.'));
458 }
459
460 /**
461 * Delete account and anonymize all content.
462 */
463 function testUserAnonymize() {
464 variable_set('user_cancel_method', 'user_cancel_reassign');
465
466 // Create a user.
467 $account = $this->drupalCreateUser(array('cancel account'));
468 $this->drupalLogin($account);
469 // Load real user object.
470 $account = user_load($account->uid, TRUE);
471
472 // Create a simple node.
473 $node = $this->drupalCreateNode(array('uid' => $account->uid));
474
475 // Create a node with two revisions, the initial one belonging to the
476 // cancelling user.
477 $revision_node = $this->drupalCreateNode(array('uid' => $account->uid));
478 $revision = $revision_node->vid;
479 $settings = get_object_vars($revision_node);
480 $settings['revision'] = 1;
481 $settings['uid'] = 1; // Set new/current revision to someone else.
482 $revision_node = $this->drupalCreateNode($settings);
483
484 // Attempt to cancel account.
485 $this->drupalGet('user/' . $account->uid . '/edit');
486 $this->drupalPost(NULL, NULL, t('Cancel account'));
487 $this->assertText(t('Are you sure you want to cancel your account?'), t('Confirmation form to cancel account displayed.'));
488 $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')))), t('Informs that all content will be attributed to anonymous account.'));
489
490 // Confirm account cancellation.
491 $timestamp = time();
492 $this->drupalPost(NULL, NULL, t('Cancel account'));
493 $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), t('Account cancellation request mailed message displayed.'));
494
495 // Confirm account cancellation request.
496 $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login));
497 $this->assertFalse(user_load($account->uid, TRUE), t('User is not found in the database.'));
498
499 // Confirm that user's content has been attributed to anonymous user.
500 $test_node = node_load($node->nid, NULL, TRUE);
501 $this->assertTrue(($test_node->uid == 0 && $test_node->status == 1), t('Node of the user has been attributed to anonymous user.'));
502 $test_node = node_load($revision_node->nid, $revision, TRUE);
503 $this->assertTrue(($test_node->revision_uid == 0 && $test_node->status == 1), t('Node revision of the user has been attributed to anonymous user.'));
504 $test_node = node_load($revision_node->nid, NULL, TRUE);
505 $this->assertTrue(($test_node->uid != 0 && $test_node->status == 1), t("Current revision of the user's node was not attributed to anonymous user."));
506
507 // Confirm that user is logged out.
508 $this->assertNoText($account->name, t('Logged out.'));
509 }
510
511 /**
512 * Delete account and remove all content.
513 */
514 function testUserDelete() {
515 variable_set('user_cancel_method', 'user_cancel_delete');
516
517 // Create a user.
518 $account = $this->drupalCreateUser(array('cancel account'));
519 $this->drupalLogin($account);
520 // Load real user object.
521 $account = user_load($account->uid, TRUE);
522
523 // Create a simple node.
524 $node = $this->drupalCreateNode(array('uid' => $account->uid));
525
526 // Create comment.
527 module_load_include('test', 'comment');
528 $comment = CommentHelperCase::postComment($node, $this->randomName(32), '', TRUE);
529 $this->assertTrue(comment_load($comment->id), t('Comment found.'));
530
531 // Create a node with two revisions, the initial one belonging to the
532 // cancelling user.
533 $revision_node = $this->drupalCreateNode(array('uid' => $account->uid));
534 $revision = $revision_node->vid;
535 $settings = get_object_vars($revision_node);
536 $settings['revision'] = 1;
537 $settings['uid'] = 1; // Set new/current revision to someone else.
538 $revision_node = $this->drupalCreateNode($settings);
539
540 // Attempt to cancel account.
541 $this->drupalGet('user/' . $account->uid . '/edit');
542 $this->drupalPost(NULL, NULL, t('Cancel account'));
543 $this->assertText(t('Are you sure you want to cancel your account?'), t('Confirmation form to cancel account displayed.'));
544 $this->assertText(t('Your account will be removed and all account information deleted. All of your content will also be deleted.'), t('Informs that all content will be deleted.'));
545
546 // Confirm account cancellation.
547 $timestamp = time();
548 $this->drupalPost(NULL, NULL, t('Cancel account'));
549 $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), t('Account cancellation request mailed message displayed.'));
550
551 // Confirm account cancellation request.
552 $this->drupalGet("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login));
553 $this->assertFalse(user_load($account->uid, TRUE), t('User is not found in the database.'));
554
555 // Confirm that user's content has been deleted.
556 $this->assertFalse(node_load($node->nid, NULL, TRUE), t('Node of the user has been deleted.'));
557 $this->assertFalse(node_load($node->nid, $revision, TRUE), t('Node revision of the user has been deleted.'));
558 $this->assertTrue(node_load($revision_node->nid, NULL, TRUE), t("Current revision of the user's node was not deleted."));
559 $this->assertFalse(comment_load($comment->id), t('Comment of the user has been deleted.'));
560
561 // Confirm that user is logged out.
562 $this->assertNoText($account->name, t('Logged out.'));
563 }
564
565 /**
566 * Create an administrative user and delete another user.
567 */
568 function testUserCancelByAdmin() {
569 variable_set('user_cancel_method', 'user_cancel_reassign');
570
571 // Create a regular user.
572 $account = $this->drupalCreateUser(array());
573
574 // Create administrative user.
575 $admin_user = $this->drupalCreateUser(array('administer users'));
576 $this->drupalLogin($admin_user);
577
578 // Delete regular user.
579 $this->drupalGet('user/' . $account->uid . '/edit');
580 $this->drupalPost(NULL, NULL, t('Cancel account'));
581 $this->assertRaw(t('Are you sure you want to cancel the account %name?', array('%name' => $account->name)), t('Confirmation form to cancel account displayed.'));
582 $this->assertText(t('Select the method to cancel the account above.'), t('Allows to select account cancellation method.'));
583
584 // Confirm deletion.
585 $this->drupalPost(NULL, NULL, t('Cancel account'));
586 $this->assertRaw(t('%name has been deleted.', array('%name' => $account->name)), t('User deleted.'));
587 $this->assertFalse(user_load($account->uid), t('User is not found in the database.'));
588 }
589
590 /**
591 * Create an administrative user and mass-delete other users.
592 */
593 function testMassUserCancelByAdmin() {
594 variable_set('user_cancel_method', 'user_cancel_reassign');
595 // Enable account cancellation notification.
596 variable_set('user_mail_status_canceled_notify', TRUE);
597
598 // Create administrative user.
599 $admin_user = $this->drupalCreateUser(array('administer users'));
600 $this->drupalLogin($admin_user);
601
602 // Create some users.
603 $users = array();
604 for ($i = 0; $i < 3; $i++) {
605 $account = $this->drupalCreateUser(array());
606 $users[$account->uid] = $account;
607 }
608
609 // Cancel user accounts, including own one.
610 $edit = array();
611 $edit['operation'] = 'cancel';
612 foreach ($users as $uid => $account) {
613 $edit['accounts[' . $uid . ']'] = TRUE;
614 }
615 $edit['accounts[' . $admin_user->uid . ']'] = TRUE;
616 $this->drupalPost('admin/people', $edit, t('Update'));
617 $this->assertText(t('Are you sure you want to cancel these user accounts?'), t('Confirmation form to cancel accounts displayed.'));
618 $this->assertText(t('When cancelling these accounts'), t('Allows to select account cancellation method.'));
619 $this->assertText(t('Require e-mail confirmation to cancel account.'), t('Allows to send confirmation mail.'));
620 $this->assertText(t('Notify user when account is canceled.'), t('Allows to send notification mail.'));
621
622 // Confirm deletion.
623 $this->drupalPost(NULL, NULL, t('Cancel accounts'));
624 $status = TRUE;
625 foreach ($users as $account) {
626 $status = $status && (strpos($this->content, t('%name has been deleted.', array('%name' => $account->name))) !== FALSE);
627 $status = $status && !user_load($account->uid, TRUE);
628 }
629 $this->assertTrue($status, t('Users deleted and not found in the database.'));
630
631 // Ensure that admin account was not cancelled.
632 $this->assertText(t('A confirmation request to cancel your account has been sent to your e-mail address.'), t('Account cancellation request mailed message displayed.'));
633 $admin_user = user_load($admin_user->uid);
634 $this->assertTrue($admin_user->status == 1, t('Administrative user is found in the database and enabled.'));
635 }
636 }
637
638 class UserPictureTestCase extends DrupalWebTestCase {
639 protected $user;
640 protected $_directory_test;
641
642 public static function getInfo() {
643 return array(
644 'name' => 'Upload user picture',
645 'description' => 'Assure that dimension check, extension check and image scaling work as designed.',
646 'group' => 'User'
647 );
648 }
649
650 function setUp() {
651 parent::setUp();
652 // Enable user pictures.
653 variable_set('user_pictures', 1);
654
655 $this->user = $this->drupalCreateUser();
656
657 // Test if directories specified in settings exist in filesystem.
658 $file_dir = 'public://';
659 $file_check = file_prepare_directory($file_dir, FILE_CREATE_DIRECTORY);
660 // TODO: Test public and private methods?
661
662 $picture_dir = variable_get('user_picture_path', 'pictures');
663 $picture_path = $file_dir . $picture_dir;
664
665 $pic_check = file_prepare_directory($picture_path, FILE_CREATE_DIRECTORY);
666 $this->_directory_test = is_writable($picture_path);
667 $this->assertTrue($this->_directory_test, "The directory $picture_path doesn't exist or is not writable. Further tests won't be made.");
668 }
669
670 function testNoPicture() {
671 $this->drupalLogin($this->user);
672
673 // Try to upload a file that is not an image for the user picture.
674 $not_an_image = current($this->drupalGetTestFiles('html'));
675 $this->saveUserPicture($not_an_image);
676 $this->assertRaw(t('Only JPEG, PNG and GIF images are allowed.'), t('Non-image files are not accepted.'));
677 }
678
679 /**
680 * Do the test:
681 * GD Toolkit is installed
682 * Picture has invalid dimension
683 *
684 * results: The image should be uploaded because ImageGDToolkit resizes the picture
685 */
686 function testWithGDinvalidDimension() {
687 if ($this->_directory_test)
688 if (image_get_toolkit()) {
689 $this->drupalLogin($this->user);
690
691 $image = current($this->drupalGetTestFiles('image'));
692 $info = image_get_info($image->uri);
693
694 // Set new variables: invalid dimensions, valid filesize (0 = no limit).
695 $test_dim = ($info['width'] - 10) . 'x' . ($info['height'] - 10);
696 variable_set('user_picture_dimensions', $test_dim);
697 variable_set('user_picture_file_size', 0);
698
699 $pic_path = $this->saveUserPicture($image);
700 // Check that the image was resized and is being displayed on the
701 // user's profile page.
702 $text = t('The image was resized to fit within the maximum allowed dimensions of %dimensions pixels.', array('%dimensions' => $test_dim));
703 $this->assertRaw($text, t('Image was resized.'));
704 $alt = t("@user's picture", array('@user' => format_username($this->user)));
705 $style = variable_get('user_picture_style', '');
706 $this->assertRaw(image_style_url($style, $pic_path), t("Image is displayed in user's edit page"));
707
708 // Check if file is located in proper directory.
709 $this->assertTrue(is_file($pic_path), t("File is located in proper directory"));
710 }
711 }
712
713 /**
714 * Do the test:
715 * GD Toolkit is installed
716 * Picture has invalid size
717 *
718 * results: The image should be uploaded because ImageGDToolkit resizes the picture
719 */
720 function testWithGDinvalidSize() {
721 if ($this->_directory_test)
722 if (image_get_toolkit()) {
723
724 $this->drupalLogin($this->user);
725
726 // Images are sorted first by size then by name. We need an image
727 // bigger than 1 KB so we'll grab the last one.
728 $image = end($this->drupalGetTestFiles('image'));
729 $info = image_get_info($image->uri);
730
731 // Set new variables: valid dimensions, invalid filesize.
732 $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
733 $test_size = 1;
734 variable_set('user_picture_dimensions', $test_dim);
735 variable_set('user_picture_file_size', $test_size);
736
737 $pic_path = $this->saveUserPicture($image);
738
739 // Test that the upload failed and that the correct reason was cited.
740 $text = t('The specified file %filename could not be uploaded.', array('%filename' => $image->filename));
741 $this->assertRaw($text, t('Upload failed.'));
742 $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)));
743 $this->assertRaw($text, t('File size cited as reason for failure.'));
744
745 // Check if file is not uploaded.
746 $this->assertFalse(is_file($pic_path), t('File was not uploaded.'));
747 }
748 }
749
750 /**
751 * Do the test:
752 * GD Toolkit is not installed
753 * Picture has invalid size
754 *
755 * results: The image shouldn't be uploaded
756 */
757 function testWithoutGDinvalidDimension() {
758 if ($this->_directory_test)
759 if (!image_get_toolkit()) {
760
761 $this->drupalLogin($this->user);
762
763 $image = current($this->drupalGetTestFiles('image'));
764 $info = image_get_info($image->uri);
765
766 // Set new variables: invalid dimensions, valid filesize (0 = no limit).
767 $test_dim = ($info['width'] - 10) . 'x' . ($info['height'] - 10);
768 variable_set('user_picture_dimensions', $test_dim);
769 variable_set('user_picture_file_size', 0);
770
771 $pic_path = $this->saveUserPicture($image);
772
773 // Test that the upload failed and that the correct reason was cited.
774 $text = t('The specified file %filename could not be uploaded.', array('%filename' => $image->filename));
775 $this->assertRaw($text, t('Upload failed.'));
776 $text = t('The image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => $test_dim));
777 $this->assertRaw($text, t('Checking response on invalid image (dimensions).'));
778
779 // Check if file is not uploaded.
780 $this->assertFalse(is_file($pic_path), t('File was not uploaded.'));
781 }
782 }
783
784 /**
785 * Do the test:
786 * GD Toolkit is not installed
787 * Picture has invalid size
788 *
789 * results: The image shouldn't be uploaded
790 */
791 function testWithoutGDinvalidSize() {
792 if ($this->_directory_test)
793 if (!image_get_toolkit()) {
794 $this->drupalLogin($this->user);
795
796 $image = current($this->drupalGetTestFiles('image'));
797 $info = image_get_info($image->uri);
798
799 // Set new variables: valid dimensions, invalid filesize.
800 $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
801 $test_size = 1;
802 variable_set('user_picture_dimensions', $test_dim);
803 variable_set('user_picture_file_size', $test_size);
804
805 $pic_path = $this->saveUserPicture($image);
806
807 // Test that the upload failed and that the correct reason was cited.
808 $text = t('The specified file %filename could not be uploaded.', array('%filename' => $image->filename));
809 $this->assertRaw($text, t('Upload failed.'));
810 $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)));
811 $this->assertRaw($text, t('File size cited as reason for failure.'));
812
813 // Check if file is not uploaded.
814 $this->assertFalse(is_file($pic_path), t('File was not uploaded.'));
815 }
816 }
817
818 /**
819 * Do the test:
820 * Picture is valid (proper size and dimension)
821 *
822 * results: The image should be uploaded
823 */
824 function testPictureIsValid() {
825 if ($this->_directory_test) {
826 $this->drupalLogin($this->user);
827
828 $image = current($this->drupalGetTestFiles('image'));
829 $info = image_get_info($image->uri);
830
831 // Set new variables: valid dimensions, valid filesize (0 = no limit).
832 $test_dim = ($info['width'] + 10) . 'x' . ($info['height'] + 10);
833 variable_set('user_picture_dimensions', $test_dim);
834 variable_set('user_picture_file_size', 0);
835
836 $pic_path = $this->saveUserPicture($image);
837
838 // Check if image is displayed in user's profile page.
839 $this->drupalGet('user');
840 $this->assertRaw(file_uri_target($pic_path), t("Image is displayed in user's profile page"));
841
842 // Check if file is located in proper directory.
843 $this->assertTrue(is_file($pic_path), t('File is located in proper directory'));
844 }
845 }
846
847 function saveUserPicture($image) {
848 $edit = array('files[picture_upload]' => drupal_realpath($image->uri));
849 $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save'));
850
851 $img_info = image_get_info($image->uri);
852 $picture_dir = variable_get('user_picture_path', 'pictures');
853 $pic_path = 'public://' . $picture_dir . '/picture-' . $this->user->uid . '.' . $img_info['extension'];
854
855 return $pic_path;
856 }
857 }
858
859
860 class UserPermissionsTestCase extends DrupalWebTestCase {
861 protected $admin_user;
862 protected $rid;
863
864 public static function getInfo() {
865 return array(
866 'name' => 'Role permissions',
867 'description' => 'Verify that role permissions can be added and removed via the permissions page.',
868 'group' => 'User'
869 );
870 }
871
872 function setUp() {
873 parent::setUp();
874
875 $this->admin_user = $this->drupalCreateUser(array('administer permissions', 'access user profiles', 'administer site configuration', 'administer users'));
876
877 // Find the new role ID - it must be the maximum.
878 $all_rids = array_keys($this->admin_user->roles);
879 sort($all_rids);
880 $this->rid = array_pop($all_rids);
881 }
882
883 /**
884 * Change user permissions and check user_access().
885 */
886 function testUserPermissionChanges() {
887 $this->drupalLogin($this->admin_user);
888 $rid = $this->rid;
889 $account = $this->admin_user;
890
891 // Add a permission.
892 $this->assertFalse(user_access('administer nodes', $account), t('User does not have "administer nodes" permission.'));
893 $edit = array();
894 $edit[$rid . '[administer nodes]'] = TRUE;
895 $this->drupalPost('admin/config/people/permissions', $edit, t('Save permissions'));
896 $this->assertText(t('The changes have been saved.'), t('Successful save message displayed.'));
897 drupal_static_reset('user_access');
898 drupal_static_reset('user_role_permissions');
899 $this->assertTrue(user_access('administer nodes', $account), t('User now has "administer nodes" permission.'));
900
901 // Remove a permission.
902 $this->assertTrue(user_access('access user profiles', $account), t('User has "access user profiles" permission.'));
903 $edit = array();
904 $edit[$rid . '[access user profiles]'] = FALSE;
905 $this->drupalPost('admin/config/people/permissions', $edit, t('Save permissions'));
906 $this->assertText(t('The changes have been saved.'), t('Successful save message displayed.'));
907 drupal_static_reset('user_access');
908 drupal_static_reset('user_role_permissions');
909 $this->assertFalse(user_access('access user profiles', $account), t('User no longer has "access user profiles" permission.'));
910 }
911
912 /**
913 * Test assigning of permissions for the administrator role.
914 */
915 function testAdministratorRole() {
916 $this->drupalLogin($this->admin_user);
917 $this->drupalGet('admin/config/people/accounts');
918
919 // Set the user's role to be the administrator role.
920 $edit = array();
921 $edit['user_admin_role'] = $this->rid;
922 $this->drupalPost('admin/config/people/accounts', $edit, t('Save configuration'));
923
924 // Enable aggregator module and ensure the 'administer news feeds'
925 // permission is assigned by default.
926 $edit = array();
927 $edit['modules[Core][aggregator][enable]'] = TRUE;
928 $this->drupalPost('admin/config/modules', $edit, t('Save configuration'));
929 $this->assertTrue(user_access('administer news feeds', $this->admin_user), t('The permission was automatically assigned to the administrator role'));
930 }
931
932 /**
933 * Verify proper permission changes by user_role_change_permissions().
934 */
935 function testUserRoleChangePermissions() {
936 $rid = $this->rid;
937 $account = $this->admin_user;
938
939 // Verify current permissions.
940 $this->assertFalse(user_access('administer nodes', $account), t('User does not have "administer nodes" permission.'));
941 $this->assertTrue(user_access('access user profiles', $account), t('User has "access user profiles" permission.'));
942 $this->assertTrue(user_access('administer site configuration', $account), t('User has "administer site configuration" permission.'));
943
944 // Change permissions.
945 $permissions = array(
946 'administer nodes' => 1,
947 'access user profiles' => 0,
948 );
949 user_role_change_permissions($rid, $permissions);
950
951 // Verify proper permission changes.
952 $this->assertTrue(user_access('administer nodes', $account), t('User now has "administer nodes" permission.'));
953 $this->assertFalse(user_access('access user profiles', $account), t('User no longer has "access user profiles" permission.'));
954 $this->assertTrue(user_access('administer site configuration', $account), t('User still has "administer site configuration" permission.'));
955 }
956 }
957
958 class UserAdminTestCase extends DrupalWebTestCase {
959 public static function getInfo() {
960 return array(
961 'name' => 'User administration',
962 'description' => 'Test user administration page functionality.',
963 'group' => 'User'
964 );
965 }
966
967 /**
968 * Registers a user and deletes it.
969 */
970 function testUserAdmin() {
971
972 $user_a = $this->drupalCreateUser(array());
973 $user_b = $this->drupalCreateUser(array('administer taxonomy'));
974 $user_c = $this->drupalCreateUser(array('administer taxonomy'));
975
976 // Create admin user to delete registered user.
977 $admin_user = $this->drupalCreateUser(array('administer users'));
978 $this->drupalLogin($admin_user);
979 $this->drupalGet('admin/people');
980 $this->assertText($user_a->name, t('Found user A on admin users page'));
981 $this->assertText($user_b->name, t('Found user B on admin users page'));
982 $this->assertText($user_c->name, t('Found user C on admin users page'));
983 $this->assertText($admin_user->name, t('Found Admin user on admin users page'));
984
985 // Filter the users by permission 'administer taxonomy'.
986 $edit = array();
987 $edit['filter'] = 'permission';
988 $edit['permission'] = 'administer taxonomy';
989 $this->drupalPost('admin/people', $edit, t('Filter'));
990
991 // Check if the correct users show up.
992 $this->assertNoText($user_a->name, t('User A not on filtered by perm admin users page'));
993 $this->assertText($user_b->name, t('Found user B on filtered by perm admin users page'));
994 $this->assertText($user_c->name, t('Found user C on filtered by perm admin users page'));
995
996 // Test blocking of a user.
997 $account = user_load($user_b->uid);
998 $this->assertEqual($account->status, 1, 'User B not blocked');
999 $edit = array();
1000 $edit['operation'] = 'block';
1001 $edit['accounts[' . $account->uid . ']'] = TRUE;
1002 $this->drupalPost('admin/people', $edit, t('Update'));
1003 $account = user_load($user_b->uid, TRUE);
1004 $this->assertEqual($account->status, 0, 'User B blocked');
1005 }
1006 }
1007
1008 /**
1009 * Tests for user-configurable time zones.
1010 */
1011 class UserTimeZoneFunctionalTest extends DrupalWebTestCase {
1012 public static function getInfo() {
1013 return array(
1014 'name' => 'User time zones',
1015 'description' => 'Set a user time zone and verify that dates are displayed in local time.',
1016 'group' => 'User',
1017 );
1018 }
1019
1020 /**
1021 * Tests the display of dates and time when user-configurable time zones are set.
1022 */
1023 function testUserTimeZone() {
1024 // Setup date/time settings for Los Angeles time.
1025 variable_set('date_default_timezone', 'America/Los_Angeles');
1026 variable_set('configurable_timezones', 1);
1027 variable_set('date_format_medium', 'Y-m-d H:i T');
1028
1029 // Create a user account and login.
1030 $web_user = $this->drupalCreateUser();
1031 $this->drupalLogin($web_user);
1032
1033 // Create some nodes with different authored-on dates.
1034 // Two dates in PST (winter time):
1035 $date1 = '2007-03-09 21:00:00 -0800';
1036 $date2 = '2007-03-11 01:00:00 -0800';
1037 // One date in PDT (summer time):
1038 $date3 = '2007-03-20 21:00:00 -0700';
1039 $node1 = $this->drupalCreateNode(array('created' => strtotime($date1), 'type' => 'article'));
1040 $node2 = $this->drupalCreateNode(array('created' => strtotime($date2), 'type' => 'article'));
1041 $node3 = $this->drupalCreateNode(array('created' => strtotime($date3), 'type' => 'article'));
1042
1043 // Confirm date format and time zone.
1044 $this->drupalGet("node/$node1->nid");
1045 $this->assertText(