Proyectos de Subversion Moodle

Rev

Rev 11 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
namespace core;
18
 
19
/**
20
 * Test core_user class.
21
 *
22
 * @covers \core_user
23
 * @package    core
24
 * @copyright  2013 Rajesh Taneja <rajesh@moodle.com>
25
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26
 */
1441 ariadna 27
final class user_test extends \advanced_testcase {
1 efrain 28
 
29
    /**
30
     * Setup test data.
31
     */
32
    protected function setUp(): void {
1441 ariadna 33
        parent::setUp();
1 efrain 34
        $this->resetAfterTest(true);
35
    }
36
 
11 efrain 37
    public function test_get_user(): void {
1 efrain 38
        global $CFG;
39
 
40
 
41
        // Create user and try fetach it with api.
42
        $user = $this->getDataGenerator()->create_user();
43
        $this->assertEquals($user, \core_user::get_user($user->id, '*', MUST_EXIST));
44
 
45
        // Test noreply user.
46
        $CFG->noreplyuserid = null;
47
        $noreplyuser = \core_user::get_noreply_user();
48
        $this->assertEquals(1, $noreplyuser->emailstop);
49
        $this->assertFalse(\core_user::is_real_user($noreplyuser->id));
50
        $this->assertEquals($CFG->noreplyaddress, $noreplyuser->email);
51
        $this->assertEquals(get_string('noreplyname'), $noreplyuser->firstname);
52
 
53
        // Set user as noreply user and make sure noreply propery is set.
54
        \core_user::reset_internal_users();
55
        $CFG->noreplyuserid = $user->id;
56
        $noreplyuser = \core_user::get_noreply_user();
57
        $this->assertEquals(1, $noreplyuser->emailstop);
58
        $this->assertTrue(\core_user::is_real_user($noreplyuser->id));
59
 
60
        // Test support user.
61
        \core_user::reset_internal_users();
62
        $CFG->supportemail = null;
63
        $CFG->noreplyuserid = null;
64
        $supportuser = \core_user::get_support_user();
65
        $adminuser = get_admin();
66
        $this->assertEquals($adminuser, $supportuser);
67
        $this->assertTrue(\core_user::is_real_user($supportuser->id));
68
 
69
        // When supportemail is set.
70
        \core_user::reset_internal_users();
71
        $CFG->supportemail = 'test@example.com';
72
        $supportuser = \core_user::get_support_user();
73
        $this->assertEquals(\core_user::SUPPORT_USER, $supportuser->id);
74
        $this->assertFalse(\core_user::is_real_user($supportuser->id));
75
 
76
        // Set user as support user and make sure noreply propery is set.
77
        \core_user::reset_internal_users();
78
        $CFG->supportuserid = $user->id;
79
        $supportuser = \core_user::get_support_user();
80
        $this->assertEquals($user, $supportuser);
81
        $this->assertTrue(\core_user::is_real_user($supportuser->id));
82
    }
83
 
84
    /**
85
     * Test get_user_by_username method.
86
     */
11 efrain 87
    public function test_get_user_by_username(): void {
1 efrain 88
        $record = array();
89
        $record['username'] = 'johndoe';
90
        $record['email'] = 'johndoe@example.com';
91
        $record['timecreated'] = time();
92
 
93
        // Create a default user for the test.
94
        $userexpected = $this->getDataGenerator()->create_user($record);
95
 
96
        // Assert that the returned user is the espected one.
97
        $this->assertEquals($userexpected, \core_user::get_user_by_username('johndoe'));
98
 
99
        // Assert that a subset of fields is correctly returned.
100
        $this->assertEquals((object) $record, \core_user::get_user_by_username('johndoe', 'username,email,timecreated'));
101
 
102
        // Assert that a user with a different mnethostid will no be returned.
103
        $this->assertFalse(\core_user::get_user_by_username('johndoe', 'username,email,timecreated', 2));
104
 
105
        // Create a new user from a different host.
106
        $record['mnethostid'] = 2;
107
        $userexpected2 = $this->getDataGenerator()->create_user($record);
108
 
109
        // Assert that the new user is returned when specified the correct mnethostid.
110
        $this->assertEquals($userexpected2, \core_user::get_user_by_username('johndoe', '*', 2));
111
 
112
        // Assert that a user not in the db return false.
113
        $this->assertFalse(\core_user::get_user_by_username('janedoe'));
114
    }
115
 
11 efrain 116
    public function test_search(): void {
1 efrain 117
        global $DB;
118
 
119
        self::init_search_tests();
120
 
121
        // Set up three courses for test.
122
        $generator = $this->getDataGenerator();
123
        $course1 = $generator->create_course();
124
        $course2 = $generator->create_course();
125
        $course3 = $generator->create_course();
126
 
127
        // Manager user in system level.
128
        $manager = $generator->create_user(['firstname' => 'Manager', 'lastname' => 'Person',
129
                'email' => 'x@x.x']);
130
        $systemcontext = \context_system::instance();
131
        $generator->role_assign($DB->get_field('role', 'id', ['shortname' => 'manager']),
132
                $manager->id, $systemcontext->id);
133
 
134
        // Teachers in one and two courses.
135
        $teacher1 = $generator->create_user(['firstname' => 'Alberto', 'lastname' => 'Unwin',
136
                'email' => 'a.unwin@x.x']);
137
        $generator->enrol_user($teacher1->id, $course1->id, 'teacher');
138
        $teacher2and3 = $generator->create_user(['firstname' => 'Alexandra', 'lastname' => 'Penguin',
139
                'email' => 'sillypenguin@x.x']);
140
        $generator->enrol_user($teacher2and3->id, $course2->id, 'teacher');
141
        $generator->enrol_user($teacher2and3->id, $course3->id, 'teacher');
142
 
143
        // Students in each course and some on multiple courses.
144
        $student1 = $generator->create_user(['firstname' => 'Amanda', 'lastname' => 'Hodder',
145
                'email' => 'hodder_a@x.x']);
146
        $generator->enrol_user($student1->id, $course1->id, 'student');
147
        $student2 = $generator->create_user(['firstname' => 'Audrey', 'lastname' => 'Methuen',
148
                'email' => 'audrey@x.x']);
149
        $generator->enrol_user($student2->id, $course2->id, 'student');
150
        $student3 = $generator->create_user(['firstname' => 'Austin', 'lastname' => 'Bloomsbury',
151
                'email' => 'a.bloomsbury@x.x']);
152
        $generator->enrol_user($student3->id, $course3->id, 'student');
153
        $student1and2 = $generator->create_user(['firstname' => 'Augustus', 'lastname' => 'Random',
154
                'email' => 'random@x.x']);
155
        $generator->enrol_user($student1and2->id, $course1->id, 'student');
156
        $generator->enrol_user($student1and2->id, $course2->id, 'student');
157
        $studentall = $generator->create_user(['firstname' => 'Amelia', 'lastname' => 'House',
158
                'email' => 'house@x.x']);
159
        $generator->enrol_user($studentall->id, $course1->id, 'student');
160
        $generator->enrol_user($studentall->id, $course2->id, 'student');
161
        $generator->enrol_user($studentall->id, $course3->id, 'student');
162
 
163
        // Special mixed user (name does not begin with A) is a teacher in one course and student
164
        // in another.
165
        $mixed = $generator->create_user(['firstname' => 'Xavier', 'lastname' => 'Harper',
166
                'email' => 'xh1248@x.x']);
167
        $generator->enrol_user($mixed->id, $course1->id, 'student');
168
        $generator->enrol_user($mixed->id, $course3->id, 'teacher');
169
 
170
        // As admin user, try searching for somebody at system level by first name, checking the
171
        // results.
172
        $this->setAdminUser();
173
        $result = \core_user::search('Amelia');
174
        $this->assertCount(1, $result);
175
 
176
        // Check some basic fields, and test other fields are present.
177
        $this->assertEquals($studentall->id, $result[0]->id);
178
        $this->assertEquals('Amelia', $result[0]->firstname);
179
        $this->assertEquals('House', $result[0]->lastname);
180
        $this->assertEquals('house@x.x', $result[0]->email);
181
        $this->assertEquals(0, $result[0]->deleted);
182
        $this->assertObjectHasProperty('firstnamephonetic', $result[0]);
183
        $this->assertObjectHasProperty('lastnamephonetic', $result[0]);
184
        $this->assertObjectHasProperty('middlename', $result[0]);
185
        $this->assertObjectHasProperty('alternatename', $result[0]);
186
        $this->assertObjectHasProperty('imagealt', $result[0]);
187
        $this->assertObjectHasProperty('username', $result[0]);
188
 
189
        // Now search by lastname, both names, and partials, case-insensitive.
190
        $this->assertEquals($result, \core_user::search('House'));
191
        $this->assertEquals($result, \core_user::search('Amelia house'));
192
        $this->assertEquals($result, \core_user::search('amelI'));
193
        $this->assertEquals($result, \core_user::search('hoUs'));
194
        $this->assertEquals($result, \core_user::search('Amelia H'));
195
 
196
        // Admin user can also search by email (full or partial).
197
        $this->assertEquals($result, \core_user::search('house@x.x'));
198
        $this->assertEquals($result, \core_user::search('hOuse@'));
199
 
200
        // What if we just search for A? (They all begin with A except the manager.)
201
        $result = \core_user::search('a');
202
        $this->assertCount(7, $result);
203
 
204
        // Au gets us Audrey, Austin, and Augustus - in alphabetical order by surname.
205
        $result = \core_user::search('au');
206
        $this->assertCount(3, $result);
207
        $this->assertEquals('Austin', $result[0]->firstname);
208
        $this->assertEquals('Audrey', $result[1]->firstname);
209
        $this->assertEquals('Augustus', $result[2]->firstname);
210
 
211
        // But if we search within course 2 we'll get Audrey and Augustus first.
212
        $course2context = \context_course::instance($course2->id);
213
        $result = \core_user::search('au', $course2context);
214
        $this->assertCount(3, $result);
215
        $this->assertEquals('Audrey', $result[0]->firstname);
216
        $this->assertEquals('Augustus', $result[1]->firstname);
217
        $this->assertEquals('Austin', $result[2]->firstname);
218
 
219
        // Try doing a few searches as manager - we should get the same results and can still
220
        // search by email too.
221
        $this->setUser($manager);
222
        $result = \core_user::search('a');
223
        $this->assertCount(7, $result);
224
        $result = \core_user::search('au', $course2context);
225
        $this->assertCount(3, $result);
226
        $result = \core_user::search('house@x.x');
227
        $this->assertCount(1, $result);
228
 
229
        // Teacher 1. No site-level permission so can't see users outside the enrolled course.
230
        $this->setUser($teacher1);
231
        $result = \core_user::search('au');
232
        $this->assertCount(1, $result);
233
        $this->assertEquals('Augustus', $result[0]->firstname);
234
 
235
        // Can still search by email for that user.
236
        $result = \core_user::search('random@x.x');
237
        $this->assertCount(1, $result);
238
 
239
        // Search everyone - teacher can only see four users (including themself).
240
        $result = \core_user::search('a');
241
        $this->assertCount(4, $result);
242
 
243
        // Search within course 2 - you get the same four users (which doesn't include
244
        // everyone on that course) but the two on course 2 should be first.
245
        $result = \core_user::search('a', $course2context);
246
        $this->assertCount(4, $result);
247
        $this->assertEquals('Amelia', $result[0]->firstname);
248
        $this->assertEquals('Augustus', $result[1]->firstname);
249
 
250
        // Other teacher.
251
        $this->setUser($teacher2and3);
252
        $result = \core_user::search('au');
253
        $this->assertCount(3, $result);
254
 
255
        $result = \core_user::search('a');
256
        $this->assertCount(5, $result);
257
 
258
        // Student can only see users on course 3.
259
        $this->setUser($student3);
260
        $result = \core_user::search('a');
261
        $this->assertCount(3, $result);
262
 
263
        $result = \core_user::search('au');
264
        $this->assertCount(1, $result);
265
        $this->assertEquals('Austin', $result[0]->firstname);
266
 
267
        // Student cannot search by email.
268
        $result = \core_user::search('a.bloomsbury@x.x');
269
        $this->assertCount(0, $result);
270
 
271
        // Student on all courses can see all the A users.
272
        $this->setUser($studentall);
273
        $result = \core_user::search('a');
274
        $this->assertCount(7, $result);
275
 
276
        // Mixed user can see users on courses 1 and 3.
277
        $this->setUser($mixed);
278
        $result = \core_user::search('a');
279
        $this->assertCount(6, $result);
280
 
281
        // Mixed user can search by email for students on course 3 but not on course 1.
282
        $result = \core_user::search('hodder_a@x.x');
283
        $this->assertCount(0, $result);
284
        $result = \core_user::search('house@x.x');
285
        $this->assertCount(1, $result);
286
    }
287
 
288
    /**
289
     * The search function had a bug where it failed if you have no identify fields (or only custom
290
     * ones).
291
     */
292
    public function test_search_no_identity_fields(): void {
293
        self::init_search_tests();
294
 
295
        // Set no user identity fields.
296
        set_config('showuseridentity', '');
297
 
298
        // Set up course for test with teacher in.
299
        $generator = $this->getDataGenerator();
300
        $course = $generator->create_course();
301
        $teacher = $generator->create_user(['firstname' => 'Alberto', 'lastname' => 'Unwin',
302
            'email' => 'a.unwin@x.x']);
303
        $generator->enrol_user($teacher->id, $course->id, 'teacher');
304
 
305
        // Admin user has site-wide permissions, this uses one variant of the query.
306
        $this->setAdminUser();
307
        $result = \core_user::search('Al');
308
        $this->assertCount(1, $result);
309
        $this->assertEquals('Alberto', $result[0]->firstname);
310
 
311
        // Teacher has course-wide permissions, this uses another variant.
312
        $this->setUser($teacher);
313
        $result = \core_user::search('Al');
314
        $this->assertCount(1, $result);
315
        $this->assertEquals('Alberto', $result[0]->firstname);
316
    }
317
 
318
    /**
319
     * Tests the search() function with limits on the number to return.
320
     */
11 efrain 321
    public function test_search_with_count(): void {
1 efrain 322
        self::init_search_tests();
323
        $generator = $this->getDataGenerator();
324
        $course = $generator->create_course();
325
 
326
        // Check default limit (30).
327
        for ($i = 0; $i < 31; $i++) {
328
            $student = $generator->create_user(['firstname' => 'Guy', 'lastname' => 'Xxx' . $i,
329
                    'email' => 'xxx@x.x']);
330
            $generator->enrol_user($student->id, $course->id, 'student');
331
        }
332
        $this->setAdminUser();
333
        $result = \core_user::search('Guy');
334
        $this->assertCount(30, $result);
335
 
336
        // Check a small limit.
337
        $result = \core_user::search('Guy', null, 10);
338
        $this->assertCount(10, $result);
339
 
340
        // Check no limit.
341
        $result = \core_user::search('Guy', null, 0);
342
        $this->assertCount(31, $result);
343
    }
344
 
345
    /**
346
     * When course is in separate groups mode and user is a student, they can't see people who
347
     * are not in the same group. This is checked by the user profile permission thing and not
348
     * currently by the original query.
349
     */
11 efrain 350
    public function test_search_group_permissions(): void {
1 efrain 351
        global $DB;
352
 
353
        self::init_search_tests();
354
 
355
        // Create one user to do the searching.
356
        $generator = $this->getDataGenerator();
357
        $course = $generator->create_course(['groupmode' => SEPARATEGROUPS]);
358
        $searcher = $generator->create_user(['firstname' => 'Searchy', 'lastname' => 'Sam',
359
                'email' => 'xxx@x.x']);
360
        $generator->enrol_user($searcher->id, $course->id, 'student');
361
        $group = $generator->create_group(['courseid' => $course->id]);
362
        groups_add_member($group, $searcher);
363
 
364
        // Create a large number of people so that we have to make multiple database reads.
365
        $targets = [];
366
        for ($i = 0; $i < 50; $i++) {
367
            $student = $generator->create_user(['firstname' => 'Guy', 'lastname' => 'Xxx' . $i,
368
                    'email' => 'xxx@x.x']);
369
            $generator->enrol_user($student->id, $course->id, 'student');
370
            $targets[] = $student;
371
        }
372
 
373
        // The first and last people are in the same group.
374
        groups_add_member($group, $targets[0]);
375
        groups_add_member($group, $targets[49]);
376
 
377
        // As searcher, we only find the 2 in the same group.
378
        $this->setUser($searcher);
379
        $result = \core_user::search('Guy');
380
        $this->assertCount(2, $result);
381
 
382
        // If we change the course to visible groups though, we get the max number.
383
        $DB->set_field('course', 'groupmode', VISIBLEGROUPS, ['id' => $course->id]);
384
        $result = \core_user::search('Guy');
385
        $this->assertCount(30, $result);
386
    }
387
 
388
    /**
389
     * When course is in separate groups mode and user is a student, they can't see people who
390
     * are not in the same group. This is checked by the user profile permission thing and not
391
     * currently by the original query.
392
     */
11 efrain 393
    public function test_search_deleted_users(): void {
1 efrain 394
        self::init_search_tests();
395
 
396
        // Create one user to do the searching.
397
        $generator = $this->getDataGenerator();
398
        $course = $generator->create_course();
399
        $searcher = $generator->create_user(['firstname' => 'Searchy', 'lastname' => 'Sam',
400
                'email' => 'xxx@x.x']);
401
        $generator->enrol_user($searcher->id, $course->id, 'student');
402
 
403
        // Create another two users to search for.
404
        $student1 = $generator->create_user(['firstname' => 'Amelia', 'lastname' => 'Aardvark']);
405
        $student2 = $generator->create_user(['firstname' => 'Amelia', 'lastname' => 'Beetle']);
406
        $generator->enrol_user($student1->id, $course->id, 'student');
407
        $generator->enrol_user($student2->id, $course->id, 'student');
408
 
409
        // As searcher, we find both users.
410
        $this->setUser($searcher);
411
        $result = \core_user::search('Amelia');
412
        $this->assertCount(2, $result);
413
 
414
        // What if one is deleted?
415
        delete_user($student1);
416
        $result = \core_user::search('Amelia');
417
        $this->assertCount(1, $result);
418
        $this->assertEquals('Beetle', $result[0]->lastname);
419
 
420
        // Delete the other, for good measure.
421
        delete_user($student2);
422
        $result = \core_user::search('Amelia');
423
        $this->assertCount(0, $result);
424
    }
425
 
426
    /**
427
     * Carries out standard setup for the search test functions.
428
     */
429
    protected static function init_search_tests() {
430
        global $DB;
431
 
432
        // For all existing users, set their name and email to something stupid so we don't
433
        // accidentally find one, confusing the test counts.
434
        $DB->set_field('user', 'firstname', 'Zaphod');
435
        $DB->set_field('user', 'lastname', 'Beeblebrox');
436
        $DB->set_field('user', 'email', 'zaphod@beeblebrox.example.org');
437
 
438
        // This is the default value, but let's set it just to be certain in case it changes later.
439
        // It affects what fields admin (and other users with the viewuseridentity permission) can
440
        // search in addition to the name.
441
        set_config('showuseridentity', 'email');
442
    }
443
 
444
    /**
445
     * Test require_active_user
446
     */
11 efrain 447
    public function test_require_active_user(): void {
1 efrain 448
        global $DB;
449
 
450
        // Create a default user for the test.
451
        $userexpected = $this->getDataGenerator()->create_user();
452
 
453
        // Simple case, all good.
454
        \core_user::require_active_user($userexpected, true, true);
455
 
456
        // Set user not confirmed.
457
        $DB->set_field('user', 'confirmed', 0, array('id' => $userexpected->id));
458
        try {
459
            \core_user::require_active_user($userexpected);
460
        } catch (\moodle_exception $e) {
461
            $this->assertEquals('usernotconfirmed', $e->errorcode);
462
        }
463
        $DB->set_field('user', 'confirmed', 1, array('id' => $userexpected->id));
464
 
465
        // Set nologin auth method.
466
        $DB->set_field('user', 'auth', 'nologin', array('id' => $userexpected->id));
467
        try {
468
            \core_user::require_active_user($userexpected, false, true);
469
        } catch (\moodle_exception $e) {
470
            $this->assertEquals('suspended', $e->errorcode);
471
        }
472
        // Check no exceptions are thrown if we don't specify to check suspended.
473
        \core_user::require_active_user($userexpected);
474
        $DB->set_field('user', 'auth', 'manual', array('id' => $userexpected->id));
475
 
476
        // Set user suspended.
477
        $DB->set_field('user', 'suspended', 1, array('id' => $userexpected->id));
478
        try {
479
            \core_user::require_active_user($userexpected, true);
480
        } catch (\moodle_exception $e) {
481
            $this->assertEquals('suspended', $e->errorcode);
482
        }
483
        // Check no exceptions are thrown if we don't specify to check suspended.
484
        \core_user::require_active_user($userexpected);
485
 
486
        // Delete user.
487
        delete_user($userexpected);
488
        try {
489
            \core_user::require_active_user($userexpected);
490
        } catch (\moodle_exception $e) {
491
            $this->assertEquals('userdeleted', $e->errorcode);
492
        }
493
 
494
        // Use a not real user.
495
        $noreplyuser = \core_user::get_noreply_user();
496
        try {
497
            \core_user::require_active_user($noreplyuser, true);
498
        } catch (\moodle_exception $e) {
499
            $this->assertEquals('invaliduser', $e->errorcode);
500
        }
501
 
502
        // Get the guest user.
503
        $guestuser = $DB->get_record('user', array('username' => 'guest'));
504
        try {
505
            \core_user::require_active_user($guestuser, true);
506
        } catch (\moodle_exception $e) {
507
            $this->assertEquals('guestsarenotallowed', $e->errorcode);
508
        }
509
 
510
    }
511
 
512
    /**
513
     * Test get_property_definition() method.
514
     */
11 efrain 515
    public function test_get_property_definition(): void {
1 efrain 516
        // Try to get a existing property.
517
        $properties = \core_user::get_property_definition('id');
518
        $this->assertEquals($properties['type'], PARAM_INT);
519
        $properties = \core_user::get_property_definition('username');
520
        $this->assertEquals($properties['type'], PARAM_USERNAME);
521
 
522
        // Invalid property.
523
        try {
524
            \core_user::get_property_definition('fullname');
525
        } catch (\coding_exception $e) {
526
            $this->assertMatchesRegularExpression('/Invalid property requested./', $e->getMessage());
527
        }
528
 
529
        // Empty parameter.
530
        try {
531
            \core_user::get_property_definition('');
532
        } catch (\coding_exception $e) {
533
            $this->assertMatchesRegularExpression('/Invalid property requested./', $e->getMessage());
534
        }
535
    }
536
 
537
    /**
538
     * Test validate() method.
539
     */
11 efrain 540
    public function test_validate(): void {
1 efrain 541
 
542
        // Create user with just with username and firstname.
543
        $record = array('username' => 's10', 'firstname' => 'Bebe Stevens');
544
        $validation = \core_user::validate((object)$record);
545
 
546
        // Validate the user, should return true as the user data is correct.
547
        $this->assertTrue($validation);
548
 
549
        // Create user with incorrect data (invalid country and theme).
550
        $record = array('username' => 's1', 'firstname' => 'Eric Cartman', 'country' => 'UU', 'theme' => 'beise');
551
 
552
        // Should return an array with 2 errors.
553
        $validation = \core_user::validate((object)$record);
554
        $this->assertArrayHasKey('country', $validation);
555
        $this->assertArrayHasKey('theme', $validation);
556
        $this->assertCount(2, $validation);
557
 
558
        // Create user with malicious data (xss).
559
        $record = array('username' => 's3', 'firstname' => 'Kyle<script>alert(1);<script> Broflovski');
560
 
561
        // Should return an array with 1 error.
562
        $validation = \core_user::validate((object)$record);
563
        $this->assertCount(1, $validation);
564
        $this->assertArrayHasKey('firstname', $validation);
565
    }
566
 
567
    /**
568
     * Test clean_data() method.
569
     */
11 efrain 570
    public function test_clean_data(): void {
1 efrain 571
        $this->resetAfterTest(false);
572
 
573
        $user = new \stdClass();
574
        $user->firstname = 'John <script>alert(1)</script> Doe';
575
        $user->username = 'john%#&~%*_doe';
576
        $user->email = ' john@testing.com ';
577
        $user->deleted = 'no';
578
        $user->description = '<b>A description <script>alert(123);</script>about myself.</b>';
579
        $usercleaned = \core_user::clean_data($user);
580
 
581
        // Expected results.
582
        $this->assertEquals('John alert(1) Doe', $usercleaned->firstname);
583
        $this->assertEquals('john@testing.com', $usercleaned->email);
584
        $this->assertEquals(0, $usercleaned->deleted);
585
        $this->assertEquals('<b>A description <script>alert(123);</script>about myself.</b>', $user->description);
586
        $this->assertEquals('john_doe', $user->username);
587
 
588
        // Try to clean an invalid property (userfullname).
589
        $user->userfullname = 'John Doe';
590
        \core_user::clean_data($user);
591
        $this->assertDebuggingCalled("The property 'userfullname' could not be cleaned.");
592
    }
593
 
594
    /**
595
     * Test clean_field() method.
596
     */
11 efrain 597
    public function test_clean_field(): void {
1 efrain 598
 
599
        // Create a 'malicious' user object/
600
        $user = new \stdClass();
601
        $user->firstname = 'John <script>alert(1)</script> Doe';
602
        $user->username = 'john%#&~%*_doe';
603
        $user->email = ' john@testing.com ';
604
        $user->deleted = 'no';
605
        $user->description = '<b>A description <script>alert(123);</script>about myself.</b>';
606
        $user->userfullname = 'John Doe';
607
 
608
        // Expected results.
609
        $this->assertEquals('John alert(1) Doe', \core_user::clean_field($user->firstname, 'firstname'));
610
        $this->assertEquals('john_doe', \core_user::clean_field($user->username, 'username'));
611
        $this->assertEquals('john@testing.com', \core_user::clean_field($user->email, 'email'));
612
        $this->assertEquals(0, \core_user::clean_field($user->deleted, 'deleted'));
613
        $this->assertEquals('<b>A description <script>alert(123);</script>about myself.</b>', \core_user::clean_field($user->description, 'description'));
614
 
615
        // Try to clean an invalid property (fullname).
616
        \core_user::clean_field($user->userfullname, 'fullname');
617
        $this->assertDebuggingCalled("The property 'fullname' could not be cleaned.");
618
    }
619
 
620
    /**
621
     * Test get_property_type() method.
622
     */
11 efrain 623
    public function test_get_property_type(): void {
1 efrain 624
 
625
        // Fetch valid properties and verify if the type is correct.
626
        $type = \core_user::get_property_type('username');
627
        $this->assertEquals(PARAM_USERNAME, $type);
628
        $type = \core_user::get_property_type('email');
629
        $this->assertEquals(PARAM_RAW_TRIMMED, $type);
630
        $type = \core_user::get_property_type('timezone');
631
        $this->assertEquals(PARAM_TIMEZONE, $type);
632
 
633
        // Try to fetch type of a non-existent properties.
634
        $nonexistingproperty = 'userfullname';
635
        $this->expectException('coding_exception');
636
        $this->expectExceptionMessage('Invalid property requested: ' . $nonexistingproperty);
637
        \core_user::get_property_type($nonexistingproperty);
638
        $nonexistingproperty = 'mobilenumber';
639
        $this->expectExceptionMessage('Invalid property requested: ' . $nonexistingproperty);
640
        \core_user::get_property_type($nonexistingproperty);
641
    }
642
 
643
    /**
644
     * Test get_property_null() method.
645
     */
11 efrain 646
    public function test_get_property_null(): void {
1 efrain 647
        // Fetch valid properties and verify if it is NULL_ALLOWED or NULL_NOT_ALLOWED.
648
        $property = \core_user::get_property_null('username');
649
        $this->assertEquals(NULL_NOT_ALLOWED, $property);
650
        $property = \core_user::get_property_null('password');
651
        $this->assertEquals(NULL_NOT_ALLOWED, $property);
652
        $property = \core_user::get_property_null('imagealt');
653
        $this->assertEquals(NULL_ALLOWED, $property);
654
        $property = \core_user::get_property_null('middlename');
655
        $this->assertEquals(NULL_ALLOWED, $property);
656
 
657
        // Try to fetch type of a non-existent properties.
658
        $nonexistingproperty = 'lastnamefonetic';
659
        $this->expectException('coding_exception');
660
        $this->expectExceptionMessage('Invalid property requested: ' . $nonexistingproperty);
661
        \core_user::get_property_null($nonexistingproperty);
662
        $nonexistingproperty = 'midlename';
663
        $this->expectExceptionMessage('Invalid property requested: ' . $nonexistingproperty);
664
        \core_user::get_property_null($nonexistingproperty);
665
    }
666
 
667
    /**
668
     * Test get_property_choices() method.
669
     */
11 efrain 670
    public function test_get_property_choices(): void {
1 efrain 671
 
672
        // Test against country property choices.
673
        $choices = \core_user::get_property_choices('country');
674
        $this->assertArrayHasKey('AU', $choices);
675
        $this->assertArrayHasKey('BR', $choices);
676
        $this->assertArrayNotHasKey('WW', $choices);
677
        $this->assertArrayNotHasKey('TX', $choices);
678
 
679
        // Test against lang property choices.
680
        $choices = \core_user::get_property_choices('lang');
681
        $this->assertArrayHasKey('en', $choices);
682
        $this->assertArrayNotHasKey('ww', $choices);
683
        $this->assertArrayNotHasKey('yy', $choices);
684
 
685
        // Test against theme property choices.
686
        $choices = \core_user::get_property_choices('theme');
687
        $this->assertArrayHasKey('boost', $choices);
688
        $this->assertArrayHasKey('classic', $choices);
689
        $this->assertArrayNotHasKey('unknowntheme', $choices);
690
        $this->assertArrayNotHasKey('wrongtheme', $choices);
691
 
692
        // Try to fetch type of a non-existent properties.
693
        $nonexistingproperty = 'language';
694
        $this->expectException('coding_exception');
695
        $this->expectExceptionMessage('Invalid property requested: ' . $nonexistingproperty);
696
        \core_user::get_property_null($nonexistingproperty);
697
        $nonexistingproperty = 'coutries';
698
        $this->expectExceptionMessage('Invalid property requested: ' . $nonexistingproperty);
699
        \core_user::get_property_null($nonexistingproperty);
700
    }
701
 
702
    /**
703
     * Test get_property_default().
704
     */
11 efrain 705
    public function test_get_property_default(): void {
1 efrain 706
        global $CFG;
707
        $this->resetAfterTest();
708
 
709
        $country = \core_user::get_property_default('country');
710
        $this->assertEquals($CFG->country, $country);
711
        set_config('country', 'AU');
712
        \core_user::reset_caches();
713
        $country = \core_user::get_property_default('country');
714
        $this->assertEquals($CFG->country, $country);
715
 
716
        $lang = \core_user::get_property_default('lang');
717
        $this->assertEquals($CFG->lang, $lang);
718
        set_config('lang', 'en');
719
        $lang = \core_user::get_property_default('lang');
720
        $this->assertEquals($CFG->lang, $lang);
721
 
722
        $this->setTimezone('Europe/London', 'Pacific/Auckland');
723
        \core_user::reset_caches();
724
        $timezone = \core_user::get_property_default('timezone');
725
        $this->assertEquals('Europe/London', $timezone);
726
        $this->setTimezone('99', 'Pacific/Auckland');
727
        \core_user::reset_caches();
728
        $timezone = \core_user::get_property_default('timezone');
729
        $this->assertEquals('Pacific/Auckland', $timezone);
730
 
731
        $this->expectException(\coding_exception::class);
732
        $this->expectExceptionMessage('Invalid property requested, or the property does not has a default value.');
733
        \core_user::get_property_default('firstname');
734
    }
735
 
736
    /**
737
     * Ensure that the noreply user is not cached.
738
     */
11 efrain 739
    public function test_get_noreply_user(): void {
1 efrain 740
        global $CFG;
741
 
742
        // Create a new fake language 'xx' with the 'noreplyname'.
743
        $langfolder = $CFG->dataroot . '/lang/xx';
744
        check_dir_exists($langfolder);
745
        $langconfig = "<?php\n\defined('MOODLE_INTERNAL') || die();";
746
        file_put_contents($langfolder . '/langconfig.php', $langconfig);
747
        $langconfig = "<?php\n\$string['noreplyname'] = 'XXX';";
748
        file_put_contents($langfolder . '/moodle.php', $langconfig);
749
 
750
        $CFG->lang='en';
751
        $enuser = \core_user::get_noreply_user();
752
 
753
        $CFG->lang='xx';
754
        $xxuser = \core_user::get_noreply_user();
755
 
756
        $this->assertNotEquals($enuser, $xxuser);
757
    }
758
 
759
    /**
760
     * Test is_real_user method.
761
     */
11 efrain 762
    public function test_is_real_user(): void {
1 efrain 763
        global $CFG, $USER;
764
 
765
        // Real users are real users.
766
        $auser = $this->getDataGenerator()->create_user();
767
        $guest = guest_user();
768
        $this->assertTrue(\core_user::is_real_user($auser->id));
769
        $this->assertTrue(\core_user::is_real_user($auser->id, true));
770
        $this->assertTrue(\core_user::is_real_user($guest->id));
771
        $this->assertTrue(\core_user::is_real_user($guest->id, true));
772
 
773
        // Non-logged in users are not real users.
774
        $this->assertSame(0, $USER->id, 'The non-logged in user should have an ID of 0.');
775
        $this->assertFalse(\core_user::is_real_user($USER->id));
776
        $this->assertFalse(\core_user::is_real_user($USER->id, true));
777
 
778
        // Other types of logged in users are real users.
779
        $this->setAdminUser();
780
        $this->assertTrue(\core_user::is_real_user($USER->id));
781
        $this->assertTrue(\core_user::is_real_user($USER->id, true));
782
        $this->setGuestUser();
783
        $this->assertTrue(\core_user::is_real_user($USER->id));
784
        $this->assertTrue(\core_user::is_real_user($USER->id, true));
785
        $this->setUser($auser);
786
        $this->assertTrue(\core_user::is_real_user($USER->id));
787
        $this->assertTrue(\core_user::is_real_user($USER->id, true));
788
 
789
        // Fake accounts are not real users.
790
        $CFG->noreplyuserid = null;
791
        $this->assertFalse(\core_user::is_real_user(\core_user::get_noreply_user()->id));
792
        $this->assertFalse(\core_user::is_real_user(\core_user::get_noreply_user()->id, true));
793
        $CFG->supportuserid = null;
794
        $CFG->supportemail = 'test@example.com';
795
        $this->assertFalse(\core_user::is_real_user(\core_user::get_support_user()->id));
796
        $this->assertFalse(\core_user::is_real_user(\core_user::get_support_user()->id, true));
797
    }
798
 
799
    /**
800
     * Tests for the {@see \core_user::awaiting_action()} method.
801
     */
11 efrain 802
    public function test_awaiting_action(): void {
1 efrain 803
        global $CFG, $DB, $USER;
804
 
805
        $guest = \core_user::get_user($CFG->siteguest);
806
        $student = $this->getDataGenerator()->create_user();
807
        $teacher = $this->getDataGenerator()->create_user();
808
        $manager = $this->getDataGenerator()->create_user();
809
        $admin = get_admin();
810
 
811
        $this->getDataGenerator()->role_assign($DB->get_field('role', 'id', ['shortname' => 'manager']),
812
            $manager->id, \context_system::instance()->id);
813
 
814
        // Scenario: Guests required to agree to site policy.
815
        $this->assertFalse(\core_user::awaiting_action($guest));
816
 
817
        $CFG->sitepolicyguest = 'https://example.com';
818
        $this->assertTrue(\core_user::awaiting_action($guest));
819
 
820
        $guest->policyagreed = 1;
821
        $this->assertFalse(\core_user::awaiting_action($guest));
822
 
823
        // Scenario: Student required to fill their profile.
824
        $this->assertFalse(\core_user::awaiting_action($student));
825
 
826
        $student->firstname = '';
827
        $this->assertTrue(\core_user::awaiting_action($student));
828
 
829
        $student->firstname = 'Alice';
830
        $this->assertFalse(\core_user::awaiting_action($student));
831
 
832
        // Scenario: Teacher force to change their password.
833
        $this->assertFalse(\core_user::awaiting_action($teacher));
834
 
835
        set_user_preference('auth_forcepasswordchange', 1, $teacher);
836
        $this->assertTrue(\core_user::awaiting_action($teacher));
837
 
838
        unset_user_preference('auth_forcepasswordchange', $teacher);
839
        $this->assertFalse(\core_user::awaiting_action($teacher));
840
 
841
        // Scenario: Admins do not need to agree to the policy but others do.
842
        $this->assertFalse(\core_user::awaiting_action($admin));
843
        $this->assertFalse(\core_user::awaiting_action($manager));
844
        $CFG->sitepolicy = 'https://example.com';
845
        $this->assertFalse(\core_user::awaiting_action($admin));
846
        $this->assertTrue(\core_user::awaiting_action($manager));
847
    }
848
 
849
    /**
850
     * Test for function to get user details.
851
     *
852
     * @covers \core_user::get_fullname
853
     */
11 efrain 854
    public function test_display_name(): void {
1 efrain 855
        $this->resetAfterTest();
856
 
857
        $user = $this->getDataGenerator()->create_user(['firstname' => 'John', 'lastname' => 'Doe']);
858
        $context = \context_system::instance();
859
 
860
        // Show real name as the force names config are not set.
861
        $this->assertEquals('John Doe', \core_user::get_fullname($user, $context));
862
 
863
        // With override, still show real name.
864
        $options = ['override' => true];
865
        $this->assertEquals('John Doe', \core_user::get_fullname($user, $context, $options));
866
 
867
        // Set the force names config.
868
        set_config('forcefirstname', 'Bruce');
869
        set_config('forcelastname', 'Simpson');
870
 
871
        // Show forced names.
872
        $this->assertEquals('Bruce Simpson', \core_user::get_fullname($user, $context));
873
 
874
        // With override, show real name.
875
        $options = ['override' => true];
876
        $this->assertEquals('John Doe', \core_user::get_fullname($user, $context, $options));
877
    }
878
 
879
    /**
1441 ariadna 880
     * Test retrieving dummy user fullname
881
     *
882
     * @covers \core_user::get_dummy_fullname
883
     */
884
    public function test_get_dummy_fullname(): void {
885
        $context = \context_system::instance();
886
 
887
        // Show real name as the force names config are not set.
888
        $this->assertEquals('firstname lastname', \core_user::get_dummy_fullname($context));
889
 
890
        // With override, still show real name.
891
        $options = ['override' => true];
892
        $this->assertEquals('firstname lastname', \core_user::get_dummy_fullname($context, $options));
893
 
894
        // Set the alternative names config.
895
        set_config('alternativefullnameformat', 'alternatename lastname firstname');
896
 
897
        // Show default name format.
898
        $this->assertEquals('firstname lastname', \core_user::get_dummy_fullname($context));
899
 
900
        // With override, show alternative name format.
901
        $options = ['override' => true];
902
        $this->assertEquals('alternatename lastname firstname', \core_user::get_dummy_fullname($context, $options));
903
    }
904
 
905
    /**
1 efrain 906
     * Test for function to get user details.
907
     *
908
     * @covers \core_user::get_profile_url
909
     */
11 efrain 910
    public function test_display_profile_url(): void {
1 efrain 911
        $this->resetAfterTest();
912
 
913
        $user = $this->getDataGenerator()->create_user(['firstname' => 'John', 'lastname' => 'Doe']);
914
 
915
        // Display profile url at site context.
916
        $this->assertEquals("https://www.example.com/moodle/user/profile.php?id={$user->id}",
917
            \core_user::get_profile_url($user)->out());
918
 
919
        // Display profile url at course context.
920
        $course = $this->getDataGenerator()->create_course();
921
        $coursecontext = \context_course::instance($course->id);
11 efrain 922
        $this->assertEquals("https://www.example.com/moodle/user/view.php?id={$user->id}&amp;course={$course->id}",
1 efrain 923
            \core_user::get_profile_url($user, $coursecontext));
924
 
925
        // Throw error if userid is invalid.
926
        unset($user->id);
927
        $this->expectException(\coding_exception::class);
928
        $this->expectExceptionMessage('User id is required when displaying profile url.');
929
        \core_user::get_profile_url($user, $coursecontext);
930
    }
931
 
932
    /**
933
     * Test for function to get user details.
934
     *
935
     * @covers \core_user::get_profile_picture
936
     */
11 efrain 937
    public function test_display_profile_picture(): void {
1 efrain 938
        global $OUTPUT, $CFG;
939
        $this->resetAfterTest();
940
 
941
        $user1 = $this->getDataGenerator()->create_user(['firstname' => 'John', 'lastname' => 'Doe']);
942
        $user2 = $this->getDataGenerator()->create_user(['picture' => 1]);
943
 
944
        // Display profile picture.
945
        $context = \context_system::instance();
946
        // No image, show initials.
947
        $this->assertStringContainsString(
948
            "<span class=\"userinitials size-35\" title=\"John Doe\" aria-label=\"John Doe\" role=\"img\">JD</span></a>",
949
            $OUTPUT->render(\core_user::get_profile_picture($user1, $context)));
950
        // With Image.
951
        $expectedimagesrc = $CFG->wwwroot . '/pluginfile.php/' . \context_user::instance($user2->id)->id .
952
            '/user/icon/boost/f2?rev=1';
953
        $this->assertStringContainsString($expectedimagesrc,
954
            $OUTPUT->render(\core_user::get_profile_picture($user2, $context)));
955
 
956
        // Display profile picture with options.
957
        $options = ['size' => 50, 'includefullname' => true];
958
        $this->assertStringContainsString(
959
            "<span class=\"userinitials size-50\" title=\"John Doe\" aria-label=\"John Doe\" role=\"img\">JD</span>John Doe</a>",
960
            $OUTPUT->render(\core_user::get_profile_picture($user1, $context, $options)));
961
 
962
        // Display profile picture with options, no link.
963
        $options = ['link' => false];
964
        $this->assertEquals(
965
            "<span class=\"userinitials size-35\" title=\"John Doe\" aria-label=\"John Doe\" role=\"img\">JD</span>",
966
            $OUTPUT->render(\core_user::get_profile_picture($user1, $context, $options)));
967
    }
968
 
969
    /**
970
     * Test that user with Letter avatar respect language preference.
971
     *
972
     * @param array $userdata
973
     * @param string $fullnameconfig
974
     * @param string $expected
975
     * @return void
976
     * @covers       \core_user::get_initials
977
     * @dataProvider user_name_provider
978
     */
979
    public function test_get_initials(array $userdata, string $fullnameconfig, string $expected): void {
980
        $this->resetAfterTest();
981
        // Create a user.
982
        $page = new \moodle_page();
983
        $page->set_url('/user/profile.php');
984
        $page->set_context(\context_system::instance());
985
        $renderer = $page->get_renderer('core');
986
        $user1 =
987
            $this->getDataGenerator()->create_user(
988
                array_merge(
989
                    ['picture' => 0, 'email' => 'user1@example.com'],
990
                    $userdata
991
                )
992
            );
993
        set_config('fullnamedisplay', $fullnameconfig);
994
        $initials = \core_user::get_initials($user1);
995
        $this->assertEquals($expected, $initials);
996
    }
997
 
998
    /**
999
     * Provider of user configuration for testing initials rendering
1000
     *
1001
     * @return array[]
1002
     */
1003
    public static function user_name_provider(): array {
1004
        return [
1005
            'simple user' => [
1441 ariadna 1006
                'userdata' => ['firstname' => 'first', 'lastname' => 'last'],
1007
                'fullnameconfig' => 'language',
1 efrain 1008
                'expected' => 'fl',
1009
            ],
1010
            'simple user with lastname firstname in language settings' => [
1441 ariadna 1011
                'userdata' => ['firstname' => 'first', 'lastname' => 'last'],
1012
                'fullnameconfig' => 'lastname firstname',
1 efrain 1013
                'expected' => 'lf',
1014
            ],
1015
            'simple user with no surname' => [
1441 ariadna 1016
                'userdata' => ['firstname' => '', 'lastname' => 'L'],
1017
                'fullnameconfig' => 'language',
1 efrain 1018
                'expected' => 'L',
1019
            ],
1020
            'simple user with a middle name' => [
1441 ariadna 1021
                'userdata' => ['firstname' => 'f', 'lastname' => 'l', 'middlename' => 'm'],
1022
                'fullnameconfig' => 'middlename lastname',
1 efrain 1023
                'expected' => 'ml',
1024
            ],
1025
            'user with a middle name & fullnamedisplay contains 3 names' => [
1441 ariadna 1026
                'userdata' => ['firstname' => 'first', 'lastname' => 'last', 'middlename' => 'middle'],
1027
                'fullnameconfig' => 'firstname middlename lastname',
1 efrain 1028
                'expected' => 'fl',
1029
            ],
1030
            'simple user with a namefield consisting of one element' => [
1441 ariadna 1031
                'userdata' => ['firstname' => 'first', 'lastname' => 'last'],
1032
                'fullnameconfig' => 'lastname',
1 efrain 1033
                'expected' => 'l',
1034
            ],
1035
        ];
1036
    }
1037
}