0a8bd772d9d226c659d733c93b8e825160c66e87
[moodle.git] / user / tests / userlib_test.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Unit tests for user/lib.php.
19  *
20  * @package    core_user
21  * @category   phpunit
22  * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->dirroot.'/user/lib.php');
31 /**
32  * Unit tests for user lib api.
33  *
34  * @package    core_user
35  * @category   phpunit
36  * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
37  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38  */
39 class core_userliblib_testcase extends advanced_testcase {
40     /**
41      * Test user_get_user_details_courses
42      */
43     public function test_user_get_user_details_courses() {
44         global $DB;
46         $this->resetAfterTest();
48         // Create user and modify user profile.
49         $user1 = $this->getDataGenerator()->create_user();
50         $user2 = $this->getDataGenerator()->create_user();
52         $course1 = $this->getDataGenerator()->create_course();
53         $coursecontext = context_course::instance($course1->id);
54         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
55         $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
56         $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
57         role_assign($teacherrole->id, $user1->id, $coursecontext->id);
58         role_assign($teacherrole->id, $user2->id, $coursecontext->id);
60         accesslib_clear_all_caches_for_unit_testing();
62         // Get user2 details as a user with super system capabilities.
63         $result = user_get_user_details_courses($user2);
64         $this->assertEquals($user2->id, $result['id']);
65         $this->assertEquals(fullname($user2), $result['fullname']);
66         $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
68         $this->setUser($user1);
69         // Get user2 details as a user who can only see this user in a course.
70         $result = user_get_user_details_courses($user2);
71         $this->assertEquals($user2->id, $result['id']);
72         $this->assertEquals(fullname($user2), $result['fullname']);
73         $this->assertEquals($course1->id, $result['enrolledcourses'][0]['id']);
75     }
77     /**
78      * Test user_update_user.
79      */
80     public function test_user_update_user() {
81         global $DB;
83         $this->resetAfterTest();
85         // Create user and modify user profile.
86         $user = $this->getDataGenerator()->create_user();
87         $user->firstname = 'Test';
88         $user->password = 'M00dLe@T';
90         // Update user and capture event.
91         $sink = $this->redirectEvents();
92         user_update_user($user);
93         $events = $sink->get_events();
94         $sink->close();
95         $event = array_pop($events);
97         // Test updated value.
98         $dbuser = $DB->get_record('user', array('id' => $user->id));
99         $this->assertSame($user->firstname, $dbuser->firstname);
100         $this->assertNotSame('M00dLe@T', $dbuser->password);
102         // Test event.
103         $this->assertInstanceOf('\core\event\user_updated', $event);
104         $this->assertSame($user->id, $event->objectid);
105         $this->assertSame('user_updated', $event->get_legacy_eventname());
106         $this->assertEventLegacyData($dbuser, $event);
107         $this->assertEquals(context_user::instance($user->id), $event->get_context());
108         $expectedlogdata = array(SITEID, 'user', 'update', 'view.php?id='.$user->id, '');
109         $this->assertEventLegacyLogData($expectedlogdata, $event);
111         // Update user with no password update.
112         $password = $user->password = hash_internal_user_password('M00dLe@T');
113         user_update_user($user, false);
114         $dbuser = $DB->get_record('user', array('id' => $user->id));
115         $this->assertSame($password, $dbuser->password);
117         // Verify event is not triggred by user_update_user when needed.
118         $sink = $this->redirectEvents();
119         user_update_user($user, false, false);
120         $events = $sink->get_events();
121         $sink->close();
122         $this->assertCount(0, $events);
124         // With password, there should be 1 event.
125         $sink = $this->redirectEvents();
126         user_update_user($user, true, false);
127         $events = $sink->get_events();
128         $sink->close();
129         $this->assertCount(1, $events);
130         $event = array_pop($events);
131         $this->assertInstanceOf('\core\event\user_password_updated', $event);
133         // Test user data validation.
134         $user->username = 'johndoe123';
135         $user->auth = 'shibolth';
136         $user->country = 'WW';
137         $user->lang = 'xy';
138         $user->theme = 'somewrongthemename';
139         $user->timezone = 'Paris';
140         $user->url = 'wwww.somewrong@#$url.com.aus';
141         $debugmessages = $this->getDebuggingMessages();
142         user_update_user($user, true, false);
143         $this->assertDebuggingCalledCount(6, $debugmessages);
145         // Now, with valid user data.
146         $user->username = 'johndoe321';
147         $user->auth = 'shibboleth';
148         $user->country = 'AU';
149         $user->lang = 'en';
150         $user->theme = 'clean';
151         $user->timezone = 'Australia/Perth';
152         $user->url = 'www.moodle.org';
153         user_update_user($user, true, false);
154         $this->assertDebuggingNotCalled();
155     }
157     /**
158      * Test create_users.
159      */
160     public function test_create_users() {
161         global $DB;
163         $this->resetAfterTest();
165         $user = array(
166             'username' => 'usernametest1',
167             'password' => 'Moodle2012!',
168             'idnumber' => 'idnumbertest1',
169             'firstname' => 'First Name User Test 1',
170             'lastname' => 'Last Name User Test 1',
171             'middlename' => 'Middle Name User Test 1',
172             'lastnamephonetic' => '最後のお名前のテスト一号',
173             'firstnamephonetic' => 'お名前のテスト一号',
174             'alternatename' => 'Alternate Name User Test 1',
175             'email' => 'usertest1@example.com',
176             'description' => 'This is a description for user 1',
177             'city' => 'Perth',
178             'country' => 'AU'
179             );
181         // Create user and capture event.
182         $sink = $this->redirectEvents();
183         $user['id'] = user_create_user($user);
184         $events = $sink->get_events();
185         $sink->close();
186         $event = array_pop($events);
188         // Test user info in DB.
189         $dbuser = $DB->get_record('user', array('id' => $user['id']));
190         $this->assertEquals($dbuser->username, $user['username']);
191         $this->assertEquals($dbuser->idnumber, $user['idnumber']);
192         $this->assertEquals($dbuser->firstname, $user['firstname']);
193         $this->assertEquals($dbuser->lastname, $user['lastname']);
194         $this->assertEquals($dbuser->email, $user['email']);
195         $this->assertEquals($dbuser->description, $user['description']);
196         $this->assertEquals($dbuser->city, $user['city']);
197         $this->assertEquals($dbuser->country, $user['country']);
199         // Test event.
200         $this->assertInstanceOf('\core\event\user_created', $event);
201         $this->assertEquals($user['id'], $event->objectid);
202         $this->assertEquals('user_created', $event->get_legacy_eventname());
203         $this->assertEquals(context_user::instance($user['id']), $event->get_context());
204         $this->assertEventLegacyData($dbuser, $event);
205         $expectedlogdata = array(SITEID, 'user', 'add', '/view.php?id='.$event->objectid, fullname($dbuser));
206         $this->assertEventLegacyLogData($expectedlogdata, $event);
208         // Verify event is not triggred by user_create_user when needed.
209         $user = array('username' => 'usernametest2'); // Create another user.
210         $sink = $this->redirectEvents();
211         user_create_user($user, true, false);
212         $events = $sink->get_events();
213         $sink->close();
214         $this->assertCount(0, $events);
216         // Test user data validation, first some invalid data.
217         $user['username'] = 'johndoe123';
218         $user['auth'] = 'shibolth';
219         $user['country'] = 'WW';
220         $user['lang'] = 'xy';
221         $user['theme'] = 'somewrongthemename';
222         $user['timezone'] = 'Paris';
223         $user['url'] = 'wwww.somewrong@#$url.com.aus';
224         $debugmessages = $this->getDebuggingMessages();
225         $user['id'] = user_create_user($user, true, false);
226         $this->assertDebuggingCalledCount(6, $debugmessages);
227         $dbuser = $DB->get_record('user', array('id' => $user['id']));
228         $this->assertEquals($dbuser->country, 0);
229         $this->assertEquals($dbuser->lang, 'en');
230         $this->assertEquals($dbuser->timezone, 'Australia/Perth');
232         // Now, with valid user data.
233         $user['username'] = 'johndoe321';
234         $user['auth'] = 'shibboleth';
235         $user['country'] = 'AU';
236         $user['lang'] = 'en';
237         $user['theme'] = 'clean';
238         $user['timezone'] = 'Australia/Perth';
239         $user['url'] = 'www.moodle.org';
240         user_create_user($user, true, false);
241         $this->assertDebuggingNotCalled();
242     }
244     /**
245      * Test function user_count_login_failures().
246      */
247     public function test_user_count_login_failures() {
248         $this->resetAfterTest();
249         $user = $this->getDataGenerator()->create_user();
250         $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
251         for ($i = 0; $i < 10; $i++) {
252             login_attempt_failed($user);
253         }
254         $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
255         $count = user_count_login_failures($user); // Reset count.
256         $this->assertEquals(10, $count);
257         $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
259         for ($i = 0; $i < 10; $i++) {
260             login_attempt_failed($user);
261         }
262         $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
263         $count = user_count_login_failures($user, false); // Do not reset count.
264         $this->assertEquals(10, $count);
265         $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
266     }
268     /**
269      * Test function user_add_password_history().
270      */
271     public function test_user_add_password_history() {
272         global $DB;
274         $this->resetAfterTest();
276         $user1 = $this->getDataGenerator()->create_user();
277         $user2 = $this->getDataGenerator()->create_user();
278         $user3 = $this->getDataGenerator()->create_user();
279         $DB->delete_records('user_password_history', array());
281         set_config('passwordreuselimit', 0);
283         user_add_password_history($user1->id, 'pokus');
284         $this->assertEquals(0, $DB->count_records('user_password_history'));
286         // Test adding and discarding of old.
288         set_config('passwordreuselimit', 3);
290         user_add_password_history($user1->id, 'pokus');
291         $this->assertEquals(1, $DB->count_records('user_password_history'));
292         $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
294         user_add_password_history($user1->id, 'pokus2');
295         user_add_password_history($user1->id, 'pokus3');
296         user_add_password_history($user1->id, 'pokus4');
297         $this->assertEquals(3, $DB->count_records('user_password_history'));
298         $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id)));
300         user_add_password_history($user2->id, 'pokus1');
301         $this->assertEquals(4, $DB->count_records('user_password_history'));
302         $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id)));
303         $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user2->id)));
305         user_add_password_history($user2->id, 'pokus2');
306         user_add_password_history($user2->id, 'pokus3');
307         $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id)));
309         $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
310         user_add_password_history($user2->id, 'pokus4');
311         $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id)));
312         $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
314         $removed = array_shift($ids);
315         $added = array_pop($newids);
316         $this->assertSame($ids, $newids);
317         $this->assertGreaterThan($removed, $added);
319         // Test disabling prevents changes.
321         set_config('passwordreuselimit', 0);
323         $this->assertEquals(6, $DB->count_records('user_password_history'));
325         $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
326         user_add_password_history($user2->id, 'pokus5');
327         user_add_password_history($user3->id, 'pokus1');
328         $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
329         $this->assertSame($ids, $newids);
330         $this->assertEquals(6, $DB->count_records('user_password_history'));
332         set_config('passwordreuselimit', -1);
334         $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
335         user_add_password_history($user2->id, 'pokus6');
336         user_add_password_history($user3->id, 'pokus6');
337         $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
338         $this->assertSame($ids, $newids);
339         $this->assertEquals(6, $DB->count_records('user_password_history'));
340     }
342     /**
343      * Test function user_add_password_history().
344      */
345     public function test_user_is_previously_used_password() {
346         global $DB;
348         $this->resetAfterTest();
350         $user1 = $this->getDataGenerator()->create_user();
351         $user2 = $this->getDataGenerator()->create_user();
352         $DB->delete_records('user_password_history', array());
354         set_config('passwordreuselimit', 0);
356         user_add_password_history($user1->id, 'pokus');
357         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus'));
359         set_config('passwordreuselimit', 3);
361         user_add_password_history($user2->id, 'pokus1');
362         user_add_password_history($user2->id, 'pokus2');
364         user_add_password_history($user1->id, 'pokus1');
365         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
366         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
367         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
368         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
370         user_add_password_history($user1->id, 'pokus2');
371         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
372         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
373         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
374         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
376         user_add_password_history($user1->id, 'pokus3');
377         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
378         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
379         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
380         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
382         user_add_password_history($user1->id, 'pokus4');
383         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
384         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
385         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
386         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
388         set_config('passwordreuselimit', 2);
390         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
391         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
392         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
393         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
395         set_config('passwordreuselimit', 3);
397         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
398         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
399         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
400         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
402         set_config('passwordreuselimit', 0);
404         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
405         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
406         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
407         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
408     }
410     /**
411      * Test that password history is deleted together with user.
412      */
413     public function test_delete_of_hashes_on_user_delete() {
414         global $DB;
416         $this->resetAfterTest();
418         $user1 = $this->getDataGenerator()->create_user();
419         $user2 = $this->getDataGenerator()->create_user();
420         $DB->delete_records('user_password_history', array());
422         set_config('passwordreuselimit', 3);
424         user_add_password_history($user1->id, 'pokus');
425         user_add_password_history($user2->id, 'pokus1');
426         user_add_password_history($user2->id, 'pokus2');
428         $this->assertEquals(3, $DB->count_records('user_password_history'));
429         $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
430         $this->assertEquals(2, $DB->count_records('user_password_history', array('userid' => $user2->id)));
432         delete_user($user2);
433         $this->assertEquals(1, $DB->count_records('user_password_history'));
434         $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
435         $this->assertEquals(0, $DB->count_records('user_password_history', array('userid' => $user2->id)));
436     }
438     /**
439      * Test user_list_view function
440      */
441     public function test_user_list_view() {
443         $this->resetAfterTest();
445         // Course without sections.
446         $course = $this->getDataGenerator()->create_course();
447         $context = context_course::instance($course->id);
449         $this->setAdminUser();
451         // Redirect events to the sink, so we can recover them later.
452         $sink = $this->redirectEvents();
454         user_list_view($course, $context);
455         $events = $sink->get_events();
456         $this->assertCount(1, $events);
457         $event = reset($events);
459         // Check the event details are correct.
460         $this->assertInstanceOf('\core\event\user_list_viewed', $event);
461         $this->assertEquals($context, $event->get_context());
462         $this->assertEquals($course->shortname, $event->other['courseshortname']);
463         $this->assertEquals($course->fullname, $event->other['coursefullname']);
465     }
467     /**
468      * Test setting the user menu avatar size.
469      */
470     public function test_user_menu_custom_avatar_size() {
471         global $PAGE;
472         $this->resetAfterTest(true);
474         $testsize = 100;
476         $user = $this->getDataGenerator()->create_user();
477         $opts = user_get_user_navigation_info($user, $PAGE, array('avatarsize' => $testsize));
478         $avatarhtml = $opts->metadata['useravatar'];
480         $matches = [];
481         preg_match('/(?:.*width=")(\d*)(?:" height=")(\d*)(?:".*\/>)/', $avatarhtml, $matches);
482         $this->assertCount(3, $matches);
484         $this->assertEquals(intval($matches[1]), $testsize);
485         $this->assertEquals(intval($matches[2]), $testsize);
486     }
488     /**
489      * Test user_can_view_profile
490      */
491     public function test_user_can_view_profile() {
492         global $DB, $CFG;
494         $this->resetAfterTest();
496         // Create five users.
497         $user1 = $this->getDataGenerator()->create_user();
498         $user2 = $this->getDataGenerator()->create_user();
499         $user3 = $this->getDataGenerator()->create_user();
500         $user4 = $this->getDataGenerator()->create_user();
501         $user5 = $this->getDataGenerator()->create_user();
502         $user6 = $this->getDataGenerator()->create_user(array('deleted' => 1));
503         $user7 = $this->getDataGenerator()->create_user();
505         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
506         // Add the course creator role to the course contact and assign a user to that role.
507         $CFG->coursecontact = '2';
508         $coursecreatorrole = $DB->get_record('role', array('shortname' => 'coursecreator'));
509         $this->getDataGenerator()->role_assign($coursecreatorrole->id, $user7->id);
511          // Create two courses.
512         $course1 = $this->getDataGenerator()->create_course();
513         $course2 = $this->getDataGenerator()->create_course();
514         $coursecontext = context_course::instance($course2->id);
515         // Prepare another course with separate groups and groupmodeforce set to true.
516         $record = new stdClass();
517         $record->groupmode = 1;
518         $record->groupmodeforce = 1;
519         $course3 = $this->getDataGenerator()->create_course($record);
520         // Enrol users 1 and 2 in first course.
521         $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
522         $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
523         // Enrol users 2 and 3 in second course.
524         $this->getDataGenerator()->enrol_user($user2->id, $course2->id);
525         $this->getDataGenerator()->enrol_user($user3->id, $course2->id);
526         // Enrol users 1, 4, and 5 into course 3.
527         $this->getDataGenerator()->enrol_user($user1->id, $course3->id);
528         $this->getDataGenerator()->enrol_user($user4->id, $course3->id);
529         $this->getDataGenerator()->enrol_user($user5->id, $course3->id);
531         // Remove capability moodle/user:viewdetails in course 2.
532         assign_capability('moodle/user:viewdetails', CAP_PROHIBIT, $studentrole->id, $coursecontext);
533         $coursecontext->mark_dirty();
534         // Set current user to user 1.
535         $this->setUser($user1);
536         // User 1 can see User 1's profile.
537         $this->assertTrue(user_can_view_profile($user1));
539         $tempcfg = $CFG->forceloginforprofiles;
540         $CFG->forceloginforprofiles = 0;
541         // Not forced to log in to view profiles, should be able to see all profiles besides user 6.
542         $users = array($user1, $user2, $user3, $user4, $user5, $user7);
543         foreach ($users as $user) {
544             $this->assertTrue(user_can_view_profile($user));
545         }
546         // Restore setting.
547         $CFG->forceloginforprofiles = $tempcfg;
549         // User 1 can not see user 6 as they have been deleted.
550         $this->assertFalse(user_can_view_profile($user6));
551         // User 1 can see User 7 as they are a course contact.
552         $this->assertTrue(user_can_view_profile($user7));
553         // User 1 is in a course with user 2 and has the right capability - return true.
554         $this->assertTrue(user_can_view_profile($user2));
555         // User 1 is not in a course with user 3 - return false.
556         $this->assertFalse(user_can_view_profile($user3));
558         // Set current user to user 2.
559         $this->setUser($user2);
560         // User 2 is in a course with user 3 but does not have the right capability - return false.
561         $this->assertFalse(user_can_view_profile($user3));
563         // Set user 1 in one group and users 4 and 5 in another group.
564         $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
565         $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
566         groups_add_member($group1->id, $user1->id);
567         groups_add_member($group2->id, $user4->id);
568         groups_add_member($group2->id, $user5->id);
569         $this->setUser($user1);
570         // Check that user 1 can not see user 4.
571         $this->assertFalse(user_can_view_profile($user4));
572         // Check that user 5 can see user 4.
573         $this->setUser($user5);
574         $this->assertTrue(user_can_view_profile($user4));
576         $CFG->coursecontact = null;
577     }