MDL-52781 core_user: improve core_user::fill_properties_cache()
[moodle.git] / user / tests / userlib_test.php
CommitLineData
bb78e249
RT
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/>.
16
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 */
25
26defined('MOODLE_INTERNAL') || die();
27
28global $CFG;
29require_once($CFG->dirroot.'/user/lib.php');
30
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 */
39class core_userliblib_testcase extends advanced_testcase {
40 /**
41 * Test user_update_user.
42 */
43 public function test_user_update_user() {
44 global $DB;
45
46 $this->resetAfterTest();
47
48 // Create user and modify user profile.
49 $user = $this->getDataGenerator()->create_user();
50 $user->firstname = 'Test';
51 $user->password = 'M00dLe@T';
52
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);
59
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);
64
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);
73
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);
2b55cb1b
RT
79
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);
86
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);
bb78e249
RT
95 }
96
97 /**
98 * Test create_users.
99 */
100 public function test_create_users() {
101 global $DB;
102
103 $this->resetAfterTest();
104
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',
0fe86bbd 115 'email' => 'usertest1@example.com',
bb78e249
RT
116 'description' => 'This is a description for user 1',
117 'city' => 'Perth',
118 'country' => 'au'
119 );
120
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);
127
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']);
138
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);
2b55cb1b
RT
147
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);
bb78e249 155 }
52dc1de7
AA
156
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));
171
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 }
1d658535
PS
180
181 /**
182 * Test function user_add_password_history().
183 */
184 public function test_user_add_password_history() {
185 global $DB;
186
187 $this->resetAfterTest();
188
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());
193
194 set_config('passwordreuselimit', 0);
195
196 user_add_password_history($user1->id, 'pokus');
197 $this->assertEquals(0, $DB->count_records('user_password_history'));
198
199 // Test adding and discarding of old.
200
201 set_config('passwordreuselimit', 3);
202
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)));
206
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)));
212
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)));
217
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)));
221
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'));
226
227 $removed = array_shift($ids);
228 $added = array_pop($newids);
229 $this->assertSame($ids, $newids);
230 $this->assertGreaterThan($removed, $added);
231
232 // Test disabling prevents changes.
233
234 set_config('passwordreuselimit', 0);
235
236 $this->assertEquals(6, $DB->count_records('user_password_history'));
237
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'));
244
245 set_config('passwordreuselimit', -1);
246
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 }
254
255 /**
256 * Test function user_add_password_history().
257 */
258 public function test_user_is_previously_used_password() {
259 global $DB;
260
261 $this->resetAfterTest();
262
263 $user1 = $this->getDataGenerator()->create_user();
264 $user2 = $this->getDataGenerator()->create_user();
265 $DB->delete_records('user_password_history', array());
266
267 set_config('passwordreuselimit', 0);
268
269 user_add_password_history($user1->id, 'pokus');
270 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus'));
271
272 set_config('passwordreuselimit', 3);
273
274 user_add_password_history($user2->id, 'pokus1');
275 user_add_password_history($user2->id, 'pokus2');
276
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'));
282
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'));
288
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'));
294
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'));
300
301 set_config('passwordreuselimit', 2);
302
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'));
307
308 set_config('passwordreuselimit', 3);
309
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'));
314
315 set_config('passwordreuselimit', 0);
316
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 }
322
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;
328
329 $this->resetAfterTest();
330
331 $user1 = $this->getDataGenerator()->create_user();
332 $user2 = $this->getDataGenerator()->create_user();
333 $DB->delete_records('user_password_history', array());
334
335 set_config('passwordreuselimit', 3);
336
337 user_add_password_history($user1->id, 'pokus');
338 user_add_password_history($user2->id, 'pokus1');
339 user_add_password_history($user2->id, 'pokus2');
340
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)));
344
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 }
826d572d
JL
350
351 /**
352 * Test user_list_view function
353 */
354 public function test_user_list_view() {
355
356 $this->resetAfterTest();
357
358 // Course without sections.
359 $course = $this->getDataGenerator()->create_course();
360 $context = context_course::instance($course->id);
361
362 $this->setAdminUser();
363
364 // Redirect events to the sink, so we can recover them later.
365 $sink = $this->redirectEvents();
366
367 user_list_view($course, $context);
368 $events = $sink->get_events();
369 $this->assertCount(1, $events);
370 $event = reset($events);
371
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']);
377
378 }
379
9dcd5035
DB
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);
386
387 $testsize = 100;
388
389 $user = $this->getDataGenerator()->create_user();
390 $opts = user_get_user_navigation_info($user, $PAGE, array('avatarsize' => $testsize));
391 $avatarhtml = $opts->metadata['useravatar'];
392
393 $matches = [];
394 preg_match('/(?:.*width=")(\d*)(?:" height=")(\d*)(?:".*\/>)/', $avatarhtml, $matches);
395 $this->assertCount(3, $matches);
396
397 $this->assertEquals(intval($matches[1]), $testsize);
398 $this->assertEquals(intval($matches[2]), $testsize);
399 }
400
82e4d438
AG
401 /**
402 * Test user_can_view_profile
403 */
404 public function test_user_can_view_profile() {
405 global $DB, $CFG;
406
407 $this->resetAfterTest();
408
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();
417
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);
423
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);
443
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));
451
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;
461
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));
470
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));
475
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));
488
489 $CFG->coursecontact = null;
490 }
bb78e249 491}