MDL-54042 user: Fix debugging in core_user_get_users_by_field
[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);
7a067206
SL
95
96 // Test user data validation.
97 $user->username = 'johndoe123';
98 $user->auth = 'shibolth';
99 $user->country = 'WW';
100 $user->lang = 'xy';
101 $user->theme = 'somewrongthemename';
102 $user->timezone = 'Paris';
103 $user->url = 'wwww.somewrong@#$url.com.aus';
104 $debugmessages = $this->getDebuggingMessages();
105 user_update_user($user, true, false);
106 $this->assertDebuggingCalledCount(6, $debugmessages);
107
108 // Now, with valid user data.
109 $user->username = 'johndoe321';
110 $user->auth = 'shibboleth';
111 $user->country = 'AU';
112 $user->lang = 'en';
113 $user->theme = 'clean';
114 $user->timezone = 'Australia/Perth';
115 $user->url = 'www.moodle.org';
116 user_update_user($user, true, false);
117 $this->assertDebuggingNotCalled();
bb78e249
RT
118 }
119
120 /**
121 * Test create_users.
122 */
123 public function test_create_users() {
124 global $DB;
125
126 $this->resetAfterTest();
127
128 $user = array(
129 'username' => 'usernametest1',
130 'password' => 'Moodle2012!',
131 'idnumber' => 'idnumbertest1',
132 'firstname' => 'First Name User Test 1',
133 'lastname' => 'Last Name User Test 1',
134 'middlename' => 'Middle Name User Test 1',
135 'lastnamephonetic' => '最後のお名前のテスト一号',
136 'firstnamephonetic' => 'お名前のテスト一号',
137 'alternatename' => 'Alternate Name User Test 1',
0fe86bbd 138 'email' => 'usertest1@example.com',
bb78e249
RT
139 'description' => 'This is a description for user 1',
140 'city' => 'Perth',
7a067206 141 'country' => 'AU'
bb78e249
RT
142 );
143
144 // Create user and capture event.
145 $sink = $this->redirectEvents();
146 $user['id'] = user_create_user($user);
147 $events = $sink->get_events();
148 $sink->close();
149 $event = array_pop($events);
150
151 // Test user info in DB.
152 $dbuser = $DB->get_record('user', array('id' => $user['id']));
153 $this->assertEquals($dbuser->username, $user['username']);
154 $this->assertEquals($dbuser->idnumber, $user['idnumber']);
155 $this->assertEquals($dbuser->firstname, $user['firstname']);
156 $this->assertEquals($dbuser->lastname, $user['lastname']);
157 $this->assertEquals($dbuser->email, $user['email']);
158 $this->assertEquals($dbuser->description, $user['description']);
159 $this->assertEquals($dbuser->city, $user['city']);
160 $this->assertEquals($dbuser->country, $user['country']);
161
162 // Test event.
163 $this->assertInstanceOf('\core\event\user_created', $event);
164 $this->assertEquals($user['id'], $event->objectid);
165 $this->assertEquals('user_created', $event->get_legacy_eventname());
166 $this->assertEquals(context_user::instance($user['id']), $event->get_context());
167 $this->assertEventLegacyData($dbuser, $event);
168 $expectedlogdata = array(SITEID, 'user', 'add', '/view.php?id='.$event->objectid, fullname($dbuser));
169 $this->assertEventLegacyLogData($expectedlogdata, $event);
2b55cb1b
RT
170
171 // Verify event is not triggred by user_create_user when needed.
172 $user = array('username' => 'usernametest2'); // Create another user.
173 $sink = $this->redirectEvents();
174 user_create_user($user, true, false);
175 $events = $sink->get_events();
176 $sink->close();
177 $this->assertCount(0, $events);
7a067206
SL
178
179 // Test user data validation, first some invalid data.
180 $user['username'] = 'johndoe123';
181 $user['auth'] = 'shibolth';
182 $user['country'] = 'WW';
183 $user['lang'] = 'xy';
184 $user['theme'] = 'somewrongthemename';
185 $user['timezone'] = 'Paris';
186 $user['url'] = 'wwww.somewrong@#$url.com.aus';
187 $debugmessages = $this->getDebuggingMessages();
188 $user['id'] = user_create_user($user, true, false);
189 $this->assertDebuggingCalledCount(6, $debugmessages);
190 $dbuser = $DB->get_record('user', array('id' => $user['id']));
191 $this->assertEquals($dbuser->country, 0);
192 $this->assertEquals($dbuser->lang, 'en');
193 $this->assertEquals($dbuser->timezone, 'Australia/Perth');
194
195 // Now, with valid user data.
196 $user['username'] = 'johndoe321';
197 $user['auth'] = 'shibboleth';
198 $user['country'] = 'AU';
199 $user['lang'] = 'en';
200 $user['theme'] = 'clean';
201 $user['timezone'] = 'Australia/Perth';
202 $user['url'] = 'www.moodle.org';
203 user_create_user($user, true, false);
204 $this->assertDebuggingNotCalled();
bb78e249 205 }
52dc1de7
AA
206
207 /**
208 * Test function user_count_login_failures().
209 */
210 public function test_user_count_login_failures() {
211 $this->resetAfterTest();
212 $user = $this->getDataGenerator()->create_user();
213 $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
214 for ($i = 0; $i < 10; $i++) {
215 login_attempt_failed($user);
216 }
217 $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
218 $count = user_count_login_failures($user); // Reset count.
219 $this->assertEquals(10, $count);
220 $this->assertEquals(0, get_user_preferences('login_failed_count_since_success', 0, $user));
221
222 for ($i = 0; $i < 10; $i++) {
223 login_attempt_failed($user);
224 }
225 $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
226 $count = user_count_login_failures($user, false); // Do not reset count.
227 $this->assertEquals(10, $count);
228 $this->assertEquals(10, get_user_preferences('login_failed_count_since_success', 0, $user));
229 }
1d658535
PS
230
231 /**
232 * Test function user_add_password_history().
233 */
234 public function test_user_add_password_history() {
235 global $DB;
236
237 $this->resetAfterTest();
238
239 $user1 = $this->getDataGenerator()->create_user();
240 $user2 = $this->getDataGenerator()->create_user();
241 $user3 = $this->getDataGenerator()->create_user();
242 $DB->delete_records('user_password_history', array());
243
244 set_config('passwordreuselimit', 0);
245
246 user_add_password_history($user1->id, 'pokus');
247 $this->assertEquals(0, $DB->count_records('user_password_history'));
248
249 // Test adding and discarding of old.
250
251 set_config('passwordreuselimit', 3);
252
253 user_add_password_history($user1->id, 'pokus');
254 $this->assertEquals(1, $DB->count_records('user_password_history'));
255 $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
256
257 user_add_password_history($user1->id, 'pokus2');
258 user_add_password_history($user1->id, 'pokus3');
259 user_add_password_history($user1->id, 'pokus4');
260 $this->assertEquals(3, $DB->count_records('user_password_history'));
261 $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id)));
262
263 user_add_password_history($user2->id, 'pokus1');
264 $this->assertEquals(4, $DB->count_records('user_password_history'));
265 $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user1->id)));
266 $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user2->id)));
267
268 user_add_password_history($user2->id, 'pokus2');
269 user_add_password_history($user2->id, 'pokus3');
270 $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id)));
271
272 $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
273 user_add_password_history($user2->id, 'pokus4');
274 $this->assertEquals(3, $DB->count_records('user_password_history', array('userid' => $user2->id)));
275 $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
276
277 $removed = array_shift($ids);
278 $added = array_pop($newids);
279 $this->assertSame($ids, $newids);
280 $this->assertGreaterThan($removed, $added);
281
282 // Test disabling prevents changes.
283
284 set_config('passwordreuselimit', 0);
285
286 $this->assertEquals(6, $DB->count_records('user_password_history'));
287
288 $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
289 user_add_password_history($user2->id, 'pokus5');
290 user_add_password_history($user3->id, 'pokus1');
291 $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
292 $this->assertSame($ids, $newids);
293 $this->assertEquals(6, $DB->count_records('user_password_history'));
294
295 set_config('passwordreuselimit', -1);
296
297 $ids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
298 user_add_password_history($user2->id, 'pokus6');
299 user_add_password_history($user3->id, 'pokus6');
300 $newids = array_keys($DB->get_records('user_password_history', array('userid' => $user2->id), 'timecreated ASC, id ASC'));
301 $this->assertSame($ids, $newids);
302 $this->assertEquals(6, $DB->count_records('user_password_history'));
303 }
304
305 /**
306 * Test function user_add_password_history().
307 */
308 public function test_user_is_previously_used_password() {
309 global $DB;
310
311 $this->resetAfterTest();
312
313 $user1 = $this->getDataGenerator()->create_user();
314 $user2 = $this->getDataGenerator()->create_user();
315 $DB->delete_records('user_password_history', array());
316
317 set_config('passwordreuselimit', 0);
318
319 user_add_password_history($user1->id, 'pokus');
320 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus'));
321
322 set_config('passwordreuselimit', 3);
323
324 user_add_password_history($user2->id, 'pokus1');
325 user_add_password_history($user2->id, 'pokus2');
326
327 user_add_password_history($user1->id, 'pokus1');
328 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
329 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
330 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
331 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
332
333 user_add_password_history($user1->id, 'pokus2');
334 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
335 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
336 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
337 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
338
339 user_add_password_history($user1->id, 'pokus3');
340 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus1'));
341 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
342 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
343 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
344
345 user_add_password_history($user1->id, 'pokus4');
346 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
347 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus2'));
348 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
349 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
350
351 set_config('passwordreuselimit', 2);
352
353 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
354 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
355 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
356 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
357
358 set_config('passwordreuselimit', 3);
359
360 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
361 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
362 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus3'));
363 $this->assertTrue(user_is_previously_used_password($user1->id, 'pokus4'));
364
365 set_config('passwordreuselimit', 0);
366
367 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus1'));
368 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus2'));
369 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus3'));
370 $this->assertFalse(user_is_previously_used_password($user1->id, 'pokus4'));
371 }
372
373 /**
374 * Test that password history is deleted together with user.
375 */
376 public function test_delete_of_hashes_on_user_delete() {
377 global $DB;
378
379 $this->resetAfterTest();
380
381 $user1 = $this->getDataGenerator()->create_user();
382 $user2 = $this->getDataGenerator()->create_user();
383 $DB->delete_records('user_password_history', array());
384
385 set_config('passwordreuselimit', 3);
386
387 user_add_password_history($user1->id, 'pokus');
388 user_add_password_history($user2->id, 'pokus1');
389 user_add_password_history($user2->id, 'pokus2');
390
391 $this->assertEquals(3, $DB->count_records('user_password_history'));
392 $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
393 $this->assertEquals(2, $DB->count_records('user_password_history', array('userid' => $user2->id)));
394
395 delete_user($user2);
396 $this->assertEquals(1, $DB->count_records('user_password_history'));
397 $this->assertEquals(1, $DB->count_records('user_password_history', array('userid' => $user1->id)));
398 $this->assertEquals(0, $DB->count_records('user_password_history', array('userid' => $user2->id)));
399 }
826d572d
JL
400
401 /**
402 * Test user_list_view function
403 */
404 public function test_user_list_view() {
405
406 $this->resetAfterTest();
407
408 // Course without sections.
409 $course = $this->getDataGenerator()->create_course();
410 $context = context_course::instance($course->id);
411
412 $this->setAdminUser();
413
414 // Redirect events to the sink, so we can recover them later.
415 $sink = $this->redirectEvents();
416
417 user_list_view($course, $context);
418 $events = $sink->get_events();
419 $this->assertCount(1, $events);
420 $event = reset($events);
421
422 // Check the event details are correct.
423 $this->assertInstanceOf('\core\event\user_list_viewed', $event);
424 $this->assertEquals($context, $event->get_context());
425 $this->assertEquals($course->shortname, $event->other['courseshortname']);
426 $this->assertEquals($course->fullname, $event->other['coursefullname']);
427
428 }
429
9dcd5035
DB
430 /**
431 * Test setting the user menu avatar size.
432 */
433 public function test_user_menu_custom_avatar_size() {
434 global $PAGE;
435 $this->resetAfterTest(true);
436
437 $testsize = 100;
438
439 $user = $this->getDataGenerator()->create_user();
440 $opts = user_get_user_navigation_info($user, $PAGE, array('avatarsize' => $testsize));
441 $avatarhtml = $opts->metadata['useravatar'];
442
443 $matches = [];
444 preg_match('/(?:.*width=")(\d*)(?:" height=")(\d*)(?:".*\/>)/', $avatarhtml, $matches);
445 $this->assertCount(3, $matches);
446
447 $this->assertEquals(intval($matches[1]), $testsize);
448 $this->assertEquals(intval($matches[2]), $testsize);
449 }
450
82e4d438
AG
451 /**
452 * Test user_can_view_profile
453 */
454 public function test_user_can_view_profile() {
455 global $DB, $CFG;
456
457 $this->resetAfterTest();
458
459 // Create five users.
460 $user1 = $this->getDataGenerator()->create_user();
461 $user2 = $this->getDataGenerator()->create_user();
462 $user3 = $this->getDataGenerator()->create_user();
463 $user4 = $this->getDataGenerator()->create_user();
464 $user5 = $this->getDataGenerator()->create_user();
465 $user6 = $this->getDataGenerator()->create_user(array('deleted' => 1));
466 $user7 = $this->getDataGenerator()->create_user();
467
468 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
469 // Add the course creator role to the course contact and assign a user to that role.
470 $CFG->coursecontact = '2';
471 $coursecreatorrole = $DB->get_record('role', array('shortname' => 'coursecreator'));
472 $this->getDataGenerator()->role_assign($coursecreatorrole->id, $user7->id);
473
474 // Create two courses.
475 $course1 = $this->getDataGenerator()->create_course();
476 $course2 = $this->getDataGenerator()->create_course();
477 $coursecontext = context_course::instance($course2->id);
478 // Prepare another course with separate groups and groupmodeforce set to true.
479 $record = new stdClass();
480 $record->groupmode = 1;
481 $record->groupmodeforce = 1;
482 $course3 = $this->getDataGenerator()->create_course($record);
483 // Enrol users 1 and 2 in first course.
484 $this->getDataGenerator()->enrol_user($user1->id, $course1->id);
485 $this->getDataGenerator()->enrol_user($user2->id, $course1->id);
486 // Enrol users 2 and 3 in second course.
487 $this->getDataGenerator()->enrol_user($user2->id, $course2->id);
488 $this->getDataGenerator()->enrol_user($user3->id, $course2->id);
489 // Enrol users 1, 4, and 5 into course 3.
490 $this->getDataGenerator()->enrol_user($user1->id, $course3->id);
491 $this->getDataGenerator()->enrol_user($user4->id, $course3->id);
492 $this->getDataGenerator()->enrol_user($user5->id, $course3->id);
493
494 // Remove capability moodle/user:viewdetails in course 2.
495 assign_capability('moodle/user:viewdetails', CAP_PROHIBIT, $studentrole->id, $coursecontext);
496 $coursecontext->mark_dirty();
497 // Set current user to user 1.
498 $this->setUser($user1);
499 // User 1 can see User 1's profile.
500 $this->assertTrue(user_can_view_profile($user1));
501
502 $tempcfg = $CFG->forceloginforprofiles;
503 $CFG->forceloginforprofiles = 0;
504 // Not forced to log in to view profiles, should be able to see all profiles besides user 6.
505 $users = array($user1, $user2, $user3, $user4, $user5, $user7);
506 foreach ($users as $user) {
507 $this->assertTrue(user_can_view_profile($user));
508 }
509 // Restore setting.
510 $CFG->forceloginforprofiles = $tempcfg;
511
512 // User 1 can not see user 6 as they have been deleted.
513 $this->assertFalse(user_can_view_profile($user6));
514 // User 1 can see User 7 as they are a course contact.
515 $this->assertTrue(user_can_view_profile($user7));
516 // User 1 is in a course with user 2 and has the right capability - return true.
517 $this->assertTrue(user_can_view_profile($user2));
518 // User 1 is not in a course with user 3 - return false.
519 $this->assertFalse(user_can_view_profile($user3));
520
521 // Set current user to user 2.
522 $this->setUser($user2);
523 // User 2 is in a course with user 3 but does not have the right capability - return false.
524 $this->assertFalse(user_can_view_profile($user3));
525
526 // Set user 1 in one group and users 4 and 5 in another group.
527 $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
528 $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course3->id));
529 groups_add_member($group1->id, $user1->id);
530 groups_add_member($group2->id, $user4->id);
531 groups_add_member($group2->id, $user5->id);
532 $this->setUser($user1);
533 // Check that user 1 can not see user 4.
534 $this->assertFalse(user_can_view_profile($user4));
535 // Check that user 5 can see user 4.
536 $this->setUser($user5);
537 $this->assertTrue(user_can_view_profile($user4));
538
539 $CFG->coursecontact = null;
540 }
bb78e249 541}