faa852e64b6d52be81d85b3f88fd9e7099ba11a5
[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_update_user.
42      */
43     public function test_user_update_user() {
44         global $DB;
46         $this->resetAfterTest();
48         // Create user and modify user profile.
49         $user = $this->getDataGenerator()->create_user();
50         $user->firstname = 'Test';
51         $user->password = 'M00dLe@T';
53         // Update user and capture event.
54         $sink = $this->redirectEvents();
55         user_update_user($user);
56         $events = $sink->get_events();
57         $sink->close();
58         $event = array_pop($events);
60         // Test updated value.
61         $dbuser = $DB->get_record('user', array('id' => $user->id));
62         $this->assertSame($user->firstname, $dbuser->firstname);
63         $this->assertNotSame('M00dLe@T', $dbuser->password);
65         // Test event.
66         $this->assertInstanceOf('\core\event\user_updated', $event);
67         $this->assertSame($user->id, $event->objectid);
68         $this->assertSame('user_updated', $event->get_legacy_eventname());
69         $this->assertEventLegacyData($dbuser, $event);
70         $this->assertEquals(context_user::instance($user->id), $event->get_context());
71         $expectedlogdata = array(SITEID, 'user', 'update', 'view.php?id='.$user->id, '');
72         $this->assertEventLegacyLogData($expectedlogdata, $event);
74         // Update user with no password update.
75         $password = $user->password = hash_internal_user_password('M00dLe@T');
76         user_update_user($user, false);
77         $dbuser = $DB->get_record('user', array('id' => $user->id));
78         $this->assertSame($password, $dbuser->password);
80         // Verify event is not triggred by user_update_user when needed.
81         $sink = $this->redirectEvents();
82         user_update_user($user, false, false);
83         $events = $sink->get_events();
84         $sink->close();
85         $this->assertCount(0, $events);
87         // With password, there should be 1 event.
88         $sink = $this->redirectEvents();
89         user_update_user($user, true, false);
90         $events = $sink->get_events();
91         $sink->close();
92         $this->assertCount(1, $events);
93         $event = array_pop($events);
94         $this->assertInstanceOf('\core\event\user_password_updated', $event);
95     }
97     /**
98      * Test create_users.
99      */
100     public function test_create_users() {
101         global $DB;
103         $this->resetAfterTest();
105         $user = array(
106             'username' => 'usernametest1',
107             'password' => 'Moodle2012!',
108             'idnumber' => 'idnumbertest1',
109             'firstname' => 'First Name User Test 1',
110             'lastname' => 'Last Name User Test 1',
111             'middlename' => 'Middle Name User Test 1',
112             'lastnamephonetic' => '最後のお名前のテスト一号',
113             'firstnamephonetic' => 'お名前のテスト一号',
114             'alternatename' => 'Alternate Name User Test 1',
115             'email' => 'usertest1@example.com',
116             'description' => 'This is a description for user 1',
117             'city' => 'Perth',
118             'country' => 'au'
119             );
121         // Create user and capture event.
122         $sink = $this->redirectEvents();
123         $user['id'] = user_create_user($user);
124         $events = $sink->get_events();
125         $sink->close();
126         $event = array_pop($events);
128         // Test user info in DB.
129         $dbuser = $DB->get_record('user', array('id' => $user['id']));
130         $this->assertEquals($dbuser->username, $user['username']);
131         $this->assertEquals($dbuser->idnumber, $user['idnumber']);
132         $this->assertEquals($dbuser->firstname, $user['firstname']);
133         $this->assertEquals($dbuser->lastname, $user['lastname']);
134         $this->assertEquals($dbuser->email, $user['email']);
135         $this->assertEquals($dbuser->description, $user['description']);
136         $this->assertEquals($dbuser->city, $user['city']);
137         $this->assertEquals($dbuser->country, $user['country']);
139         // Test event.
140         $this->assertInstanceOf('\core\event\user_created', $event);
141         $this->assertEquals($user['id'], $event->objectid);
142         $this->assertEquals('user_created', $event->get_legacy_eventname());
143         $this->assertEquals(context_user::instance($user['id']), $event->get_context());
144         $this->assertEventLegacyData($dbuser, $event);
145         $expectedlogdata = array(SITEID, 'user', 'add', '/view.php?id='.$event->objectid, fullname($dbuser));
146         $this->assertEventLegacyLogData($expectedlogdata, $event);
148         // Verify event is not triggred by user_create_user when needed.
149         $user = array('username' => 'usernametest2'); // Create another user.
150         $sink = $this->redirectEvents();
151         user_create_user($user, true, false);
152         $events = $sink->get_events();
153         $sink->close();
154         $this->assertCount(0, $events);
155     }
157     /**
158      * Test function user_count_login_failures().
159      */
160     public function test_user_count_login_failures() {
161         $this->resetAfterTest();
162         $user = $this->getDataGenerator()->create_user();
163         $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
164         for ($i = 0; $i < 10; $i++) {
165             login_attempt_failed($user);
166         }
167         $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
168         $count = user_count_login_failures($user); // Reset count.
169         $this->assertEquals(10, $count);
170         $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
172         for ($i = 0; $i < 10; $i++) {
173             login_attempt_failed($user);
174         }
175         $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
176         $count = user_count_login_failures($user, false); // Do not reset count.
177         $this->assertEquals(10, $count);
178         $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
179     }
181     /**
182      * Test function user_add_password_history().
183      */
184     public function test_user_add_password_history() {
185         global $DB;
187         $this->resetAfterTest();
189         $user1 = $this->getDataGenerator()->create_user();
190         $user2 = $this->getDataGenerator()->create_user();
191         $user3 = $this->getDataGenerator()->create_user();
192         $DB->delete_records('user_password_history', array());
194         set_config('passwordreuselimit', 0);
196         user_add_password_history($user1->id, 'pokus');
197         $this->assertEquals(0, $DB->count_records('user_password_history'));
199         // Test adding and discarding of old.
201         set_config('passwordreuselimit', 3);
203         user_add_password_history($user1->id, 'pokus');
204         $this->assertEquals(1, $DB->count_records('user_password_history'));
205         $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
207         user_add_password_history($user1->id, 'pokus2');
208         user_add_password_history($user1->id, 'pokus3');
209         user_add_password_history($user1->id, 'pokus4');
210         $this->assertEquals(3, $DB->count_records('user_password_history'));
211         $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id)));
213         user_add_password_history($user2->id, 'pokus1');
214         $this->assertEquals(4, $DB->count_records('user_password_history'));
215         $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id)));
216         $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user2->id)));
218         user_add_password_history($user2->id, 'pokus2');
219         user_add_password_history($user2->id, 'pokus3');
220         $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id)));
222         $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
223         user_add_password_history($user2->id, 'pokus4');
224         $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id)));
225         $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
227         $removed = array_shift($ids);
228         $added = array_pop($newids);
229         $this->assertSame($ids, $newids);
230         $this->assertGreaterThan($removed, $added);
232         // Test disabling prevents changes.
234         set_config('passwordreuselimit', 0);
236         $this->assertEquals(6, $DB->count_records('user_password_history'));
238         $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
239         user_add_password_history($user2->id, 'pokus5');
240         user_add_password_history($user3->id, 'pokus1');
241         $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
242         $this->assertSame($ids, $newids);
243         $this->assertEquals(6, $DB->count_records('user_password_history'));
245         set_config('passwordreuselimit', -1);
247         $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
248         user_add_password_history($user2->id, 'pokus6');
249         user_add_password_history($user3->id, 'pokus6');
250         $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
251         $this->assertSame($ids, $newids);
252         $this->assertEquals(6, $DB->count_records('user_password_history'));
253     }
255     /**
256      * Test function user_add_password_history().
257      */
258     public function test_user_is_previously_used_password() {
259         global $DB;
261         $this->resetAfterTest();
263         $user1 = $this->getDataGenerator()->create_user();
264         $user2 = $this->getDataGenerator()->create_user();
265         $DB->delete_records('user_password_history', array());
267         set_config('passwordreuselimit', 0);
269         user_add_password_history($user1->id, 'pokus');
270         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus'));
272         set_config('passwordreuselimit', 3);
274         user_add_password_history($user2->id, 'pokus1');
275         user_add_password_history($user2->id, 'pokus2');
277         user_add_password_history($user1->id, 'pokus1');
278         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
279         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
280         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
281         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
283         user_add_password_history($user1->id, 'pokus2');
284         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
285         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
286         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
287         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
289         user_add_password_history($user1->id, 'pokus3');
290         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
291         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
292         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
293         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
295         user_add_password_history($user1->id, 'pokus4');
296         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
297         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
298         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
299         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
301         set_config('passwordreuselimit', 2);
303         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
304         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
305         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
306         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
308         set_config('passwordreuselimit', 3);
310         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
311         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
312         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
313         $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
315         set_config('passwordreuselimit', 0);
317         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
318         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
319         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
320         $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
321     }
323     /**
324      * Test that password history is deleted together with user.
325      */
326     public function test_delete_of_hashes_on_user_delete() {
327         global $DB;
329         $this->resetAfterTest();
331         $user1 = $this->getDataGenerator()->create_user();
332         $user2 = $this->getDataGenerator()->create_user();
333         $DB->delete_records('user_password_history', array());
335         set_config('passwordreuselimit', 3);
337         user_add_password_history($user1->id, 'pokus');
338         user_add_password_history($user2->id, 'pokus1');
339         user_add_password_history($user2->id, 'pokus2');
341         $this->assertEquals(3, $DB->count_records('user_password_history'));
342         $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
343         $this->assertEquals(2, $DB->count_records('user_password_history', array('userid' => $user2->id)));
345         delete_user($user2);
346         $this->assertEquals(1, $DB->count_records('user_password_history'));
347         $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
348         $this->assertEquals(0, $DB->count_records('user_password_history', array('userid' => $user2->id)));
349     }
351     /**
352      * Test user_list_view function
353      */
354     public function test_user_list_view() {
356         $this->resetAfterTest();
358         // Course without sections.
359         $course = $this->getDataGenerator()->create_course();
360         $context = context_course::instance($course->id);
362         $this->setAdminUser();
364         // Redirect events to the sink, so we can recover them later.
365         $sink = $this->redirectEvents();
367         user_list_view($course, $context);
368         $events = $sink->get_events();
369         $this->assertCount(1, $events);
370         $event = reset($events);
372         // Check the event details are correct.
373         $this->assertInstanceOf('\core\event\user_list_viewed', $event);
374         $this->assertEquals($context, $event->get_context());
375         $this->assertEquals($course->shortname, $event->other['courseshortname']);
376         $this->assertEquals($course->fullname, $event->other['coursefullname']);
378     }
380     /**
381      * Test setting the user menu avatar size.
382      */
383     public function test_user_menu_custom_avatar_size() {
384         global $PAGE;
385         $this->resetAfterTest(true);
387         $testsize = 100;
389         $user = $this->getDataGenerator()->create_user();
390         $opts = user_get_user_navigation_info($user, $PAGE, array('avatarsize' => $testsize));
391         $avatarhtml = $opts->metadata['useravatar'];
393         $matches = [];
394         preg_match('/(?:.*width=")(\d*)(?:" height=")(\d*)(?:".*\/>)/', $avatarhtml, $matches);
395         $this->assertCount(3, $matches);
397         $this->assertEquals(intval($matches[1]), $testsize);
398         $this->assertEquals(intval($matches[2]), $testsize);
399     }
401     /**
402      * Test user_can_view_profile
403      */
404     public function test_user_can_view_profile() {
405         global $DB, $CFG;
407         $this->resetAfterTest();
409         // Create five users.
410         $user1 = $this->getDataGenerator()->create_user();
411         $user2 = $this->getDataGenerator()->create_user();
412         $user3 = $this->getDataGenerator()->create_user();
413         $user4 = $this->getDataGenerator()->create_user();
414         $user5 = $this->getDataGenerator()->create_user();
415         $user6 = $this->getDataGenerator()->create_user(array('deleted' => 1));
416         $user7 = $this->getDataGenerator()->create_user();
418         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
419         // Add the course creator role to the course contact and assign a user to that role.
420         $CFG->coursecontact = '2';
421         $coursecreatorrole = $DB->get_record('role', array('shortname' => 'coursecreator'));
422         $this->getDataGenerator()->role_assign($coursecreatorrole->id, $user7->id);
424          // Create two courses.
425         $course1 = $this->getDataGenerator()->create_course();
426         $course2 = $this->getDataGenerator()->create_course();
427         $coursecontext = context_course::instance($course2->id);
428         // Prepare another course with separate groups and groupmodeforce set to true.
429         $record = new stdClass();
430         $record->groupmode = 1;
431         $record->groupmodeforce = 1;
432         $course3 = $this->getDataGenerator()->create_course($record);
433         // Enrol users 1 and 2 in first course.
434         $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
435         $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
436         // Enrol users 2 and 3 in second course.
437         $this->getDataGenerator()->enrol_user($user2->id, $course2->id);
438         $this->getDataGenerator()->enrol_user($user3->id, $course2->id);
439         // Enrol users 1, 4, and 5 into course 3.
440         $this->getDataGenerator()->enrol_user($user1->id, $course3->id);
441         $this->getDataGenerator()->enrol_user($user4->id, $course3->id);
442         $this->getDataGenerator()->enrol_user($user5->id, $course3->id);
444         // Remove capability moodle/user:viewdetails in course 2.
445         assign_capability('moodle/user:viewdetails', CAP_PROHIBIT, $studentrole->id, $coursecontext);
446         $coursecontext->mark_dirty();
447         // Set current user to user 1.
448         $this->setUser($user1);
449         // User 1 can see User 1's profile.
450         $this->assertTrue(user_can_view_profile($user1));
452         $tempcfg = $CFG->forceloginforprofiles;
453         $CFG->forceloginforprofiles = 0;
454         // Not forced to log in to view profiles, should be able to see all profiles besides user 6.
455         $users = array($user1, $user2, $user3, $user4, $user5, $user7);
456         foreach ($users as $user) {
457             $this->assertTrue(user_can_view_profile($user));
458         }
459         // Restore setting.
460         $CFG->forceloginforprofiles = $tempcfg;
462         // User 1 can not see user 6 as they have been deleted.
463         $this->assertFalse(user_can_view_profile($user6));
464         // User 1 can see User 7 as they are a course contact.
465         $this->assertTrue(user_can_view_profile($user7));
466         // User 1 is in a course with user 2 and has the right capability - return true.
467         $this->assertTrue(user_can_view_profile($user2));
468         // User 1 is not in a course with user 3 - return false.
469         $this->assertFalse(user_can_view_profile($user3));
471         // Set current user to user 2.
472         $this->setUser($user2);
473         // User 2 is in a course with user 3 but does not have the right capability - return false.
474         $this->assertFalse(user_can_view_profile($user3));
476         // Set user 1 in one group and users 4 and 5 in another group.
477         $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
478         $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
479         groups_add_member($group1->id, $user1->id);
480         groups_add_member($group2->id, $user4->id);
481         groups_add_member($group2->id, $user5->id);
482         $this->setUser($user1);
483         // Check that user 1 can not see user 4.
484         $this->assertFalse(user_can_view_profile($user4));
485         // Check that user 5 can see user 4.
486         $this->setUser($user5);
487         $this->assertTrue(user_can_view_profile($user4));
489         $CFG->coursecontact = null;
490     }