See Release Notes
Long Term Support Release
Differences Between: [Versions 400 and 401] [Versions 401 and 403]
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 declare(strict_types=1); 18 19 namespace core_user\reportbuilder\datasource; 20 21 use core_collator; 22 use core_reportbuilder_testcase; 23 use core_reportbuilder_generator; 24 use core_reportbuilder\local\filters\boolean_select; 25 use core_reportbuilder\local\filters\date; 26 use core_reportbuilder\local\filters\select; 27 use core_reportbuilder\local\filters\tags; 28 use core_reportbuilder\local\filters\text; 29 use core_reportbuilder\local\filters\user as user_filter; 30 31 defined('MOODLE_INTERNAL') || die(); 32 33 global $CFG; 34 require_once("{$CFG->dirroot}/reportbuilder/tests/helpers.php"); 35 36 /** 37 * Unit tests for users datasource 38 * 39 * @package core_user 40 * @covers \core_user\reportbuilder\datasource\users 41 * @copyright 2022 Paul Holden <paulh@moodle.com> 42 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 43 */ 44 class users_test extends core_reportbuilder_testcase { 45 46 /** 47 * Test default datasource 48 */ 49 public function test_datasource_default(): void { 50 $this->resetAfterTest(); 51 52 $user2 = $this->getDataGenerator()->create_user(['firstname' => 'Charles']); 53 $user3 = $this->getDataGenerator()->create_user(['firstname' => 'Brian']); 54 55 /** @var core_reportbuilder_generator $generator */ 56 $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); 57 $report = $generator->create_report(['name' => 'Users', 'source' => users::class, 'default' => 1]); 58 59 $content = $this->get_custom_report_content($report->get('id')); 60 $this->assertCount(3, $content); 61 62 // Default columns are fullname, username, email. Results are sorted by the fullname. 63 [$adminrow, $userrow1, $userrow2] = array_map('array_values', $content); 64 65 $this->assertEquals(['Admin User', 'admin', 'admin@example.com'], $adminrow); 66 $this->assertEquals([fullname($user3), $user3->username, $user3->email], $userrow1); 67 $this->assertEquals([fullname($user2), $user2->username, $user2->email], $userrow2); 68 } 69 70 /** 71 * Test datasource columns that aren't added by default 72 */ 73 public function test_datasource_non_default_columns(): void { 74 $this->resetAfterTest(); 75 76 $user = $this->getDataGenerator()->create_user([ 77 'firstname' => 'Zoe', 78 'idnumber' => 'U0001', 79 'city' => 'London', 80 'country' => 'GB', 81 'interests' => ['Horses'], 82 ]); 83 84 /** @var core_reportbuilder_generator $generator */ 85 $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); 86 $report = $generator->create_report(['name' => 'Users', 'source' => users::class, 'default' => 0]); 87 88 // User. 89 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullnamewithlink']); 90 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullnamewithpicture']); 91 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:fullnamewithpicturelink']); 92 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:picture']); 93 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:firstname']); 94 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastname']); 95 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:city']); 96 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:country']); 97 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:description']); 98 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:firstnamephonetic']); 99 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastnamephonetic']); 100 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:middlename']); 101 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:alternatename']); 102 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:idnumber']); 103 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:institution']); 104 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:department']); 105 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:phone1']); 106 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:phone2']); 107 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:address']); 108 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:lastaccess']); 109 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:suspended']); 110 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:confirmed']); 111 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:moodlenetprofile']); 112 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:timecreated']); 113 114 // Tags. 115 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'tag:name']); 116 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'tag:namewithlink']); 117 118 $content = $this->get_custom_report_content($report->get('id')); 119 $this->assertCount(2, $content); 120 121 // Consistent order by firstname, just in case. 122 core_collator::asort_array_of_arrays_by_key($content, 'c4_firstname'); 123 $content = array_values($content); 124 125 [$adminrow, $userrow] = array_map('array_values', $content); 126 127 $this->assertStringContainsString('Admin User', $adminrow[0]); 128 $this->assertStringContainsString('Admin User', $adminrow[1]); 129 $this->assertStringContainsString('Admin User', $adminrow[2]); 130 $this->assertNotEmpty($adminrow[3]); 131 $this->assertEquals('Admin', $adminrow[4]); 132 $this->assertEquals('User', $adminrow[5]); 133 134 $this->assertStringContainsString(fullname($user), $userrow[0]); 135 $this->assertStringContainsString(fullname($user), $userrow[1]); 136 $this->assertStringContainsString(fullname($user), $userrow[2]); 137 $this->assertNotEmpty($userrow[3]); 138 $this->assertEquals($user->firstname, $userrow[4]); 139 $this->assertEquals($user->lastname, $userrow[5]); 140 $this->assertEquals($user->city, $userrow[6]); 141 $this->assertEquals('United Kingdom', $userrow[7]); 142 $this->assertEquals($user->description, $userrow[8]); 143 $this->assertEquals($user->firstnamephonetic, $userrow[9]); 144 $this->assertEquals($user->lastnamephonetic, $userrow[10]); 145 $this->assertEquals($user->middlename, $userrow[11]); 146 $this->assertEquals($user->alternatename, $userrow[12]); 147 $this->assertEquals($user->idnumber, $userrow[13]); 148 $this->assertEquals($user->institution, $userrow[14]); 149 $this->assertEquals($user->department, $userrow[15]); 150 $this->assertEquals($user->phone1, $userrow[16]); 151 $this->assertEquals($user->phone2, $userrow[17]); 152 $this->assertEquals($user->address, $userrow[18]); 153 $this->assertEmpty($userrow[19]); 154 $this->assertEquals('No', $userrow[20]); 155 $this->assertEquals('Yes', $userrow[21]); 156 $this->assertEquals($user->moodlenetprofile, $userrow[22]); 157 $this->assertNotEmpty($userrow[23]); 158 $this->assertEquals('Horses', $userrow[24]); 159 $this->assertStringContainsString('Horses', $userrow[25]); 160 } 161 162 /** 163 * Data provider for {@see test_datasource_filters} 164 * 165 * @return array[] 166 */ 167 public function datasource_filters_provider(): array { 168 return [ 169 // User. 170 'Filter user' => ['user:userselect', [ 171 'user:userselect_operator' => user_filter::USER_SELECT, 172 'user:userselect_value' => [-1], 173 ], false], 174 'Filter fullname' => ['user:fullname', [ 175 'user:fullname_operator' => text::CONTAINS, 176 'user:fullname_value' => 'Zoe', 177 ], true], 178 'Filter fullname (no match)' => ['user:fullname', [ 179 'user:fullname_operator' => text::CONTAINS, 180 'user:fullname_value' => 'Alfie', 181 ], false], 182 'Filter firstname' => ['user:firstname', [ 183 'user:firstname_operator' => text::IS_EQUAL_TO, 184 'user:firstname_value' => 'Zoe', 185 ], true], 186 'Filter firstname (no match)' => ['user:firstname', [ 187 'user:firstname_operator' => text::IS_EQUAL_TO, 188 'user:firstname_value' => 'Alfie', 189 ], false], 190 'Filter middlename' => ['user:middlename', [ 191 'user:middlename_operator' => text::IS_EQUAL_TO, 192 'user:middlename_value' => 'Zebediah', 193 ], true], 194 'Filter middlename (no match)' => ['user:middlename', [ 195 'user:middlename_operator' => text::IS_EQUAL_TO, 196 'user:middlename_value' => 'Aardvark', 197 ], false], 198 'Filter lastname' => ['user:lastname', [ 199 'user:lastname_operator' => text::IS_EQUAL_TO, 200 'user:lastname_value' => 'Zebra', 201 ], true], 202 'Filter lastname (no match)' => ['user:lastname', [ 203 'user:lastname_operator' => text::IS_EQUAL_TO, 204 'user:lastname_value' => 'Aardvark', 205 ], false], 206 'Filter firstnamephonetic' => ['user:firstnamephonetic', [ 207 'user:firstnamephonetic_operator' => text::IS_EQUAL_TO, 208 'user:firstnamephonetic_value' => 'Eoz', 209 ], true], 210 'Filter firstnamephonetic (no match)' => ['user:firstnamephonetic', [ 211 'user:firstnamephonetic_operator' => text::IS_EQUAL_TO, 212 'user:firstnamephonetic_value' => 'Alfie', 213 ], false], 214 'Filter lastnamephonetic' => ['user:lastnamephonetic', [ 215 'user:lastnamephonetic_operator' => text::IS_EQUAL_TO, 216 'user:lastnamephonetic_value' => 'Arbez', 217 ], true], 218 'Filter lastnamephonetic (no match)' => ['user:lastnamephonetic', [ 219 'user:lastnamephonetic_operator' => text::IS_EQUAL_TO, 220 'user:lastnamephonetic_value' => 'Aardvark', 221 ], false], 222 'Filter alternatename' => ['user:alternatename', [ 223 'user:alternatename_operator' => text::IS_EQUAL_TO, 224 'user:alternatename_value' => 'Zee', 225 ], true], 226 'Filter alternatename (no match)' => ['user:alternatename', [ 227 'user:alternatename_operator' => text::IS_EQUAL_TO, 228 'user:alternatename_value' => 'Aardvark', 229 ], false], 230 'Filter email' => ['user:email', [ 231 'user:email_operator' => text::CONTAINS, 232 'user:email_value' => 'zoe1', 233 ], true], 234 'Filter email (no match)' => ['user:email', [ 235 'user:email_operator' => text::CONTAINS, 236 'user:email_value' => 'alfie1', 237 ], false], 238 'Filter phone1' => ['user:phone1', [ 239 'user:phone1_operator' => text::IS_EQUAL_TO, 240 'user:phone1_value' => '111', 241 ], true], 242 'Filter phone1 (no match)' => ['user:phone1', [ 243 'user:phone1_operator' => text::IS_EQUAL_TO, 244 'user:phone1_value' => '119', 245 ], false], 246 'Filter phone2' => ['user:phone2', [ 247 'user:phone2_operator' => text::IS_EQUAL_TO, 248 'user:phone2_value' => '222', 249 ], true], 250 'Filter phone2 (no match)' => ['user:phone2', [ 251 'user:phone2_operator' => text::IS_EQUAL_TO, 252 'user:phone2_value' => '229', 253 ], false], 254 'Filter address' => ['user:address', [ 255 'user:address_operator' => text::IS_EQUAL_TO, 256 'user:address_value' => 'Big Farm', 257 ], true], 258 'Filter address (no match)' => ['user:address', [ 259 'user:address_operator' => text::IS_EQUAL_TO, 260 'user:address_value' => 'Small Farm', 261 ], false], 262 263 'Filter city' => ['user:city', [ 264 'user:city_operator' => text::IS_EQUAL_TO, 265 'user:city_value' => 'Barcelona', 266 ], true], 267 'Filter city (no match)' => ['user:city', [ 268 'user:city_operator' => text::IS_EQUAL_TO, 269 'user:city_value' => 'Perth', 270 ], false], 271 'Filter country' => ['user:country', [ 272 'user:country_operator' => select::EQUAL_TO, 273 'user:country_value' => 'ES', 274 ], true], 275 'Filter country (no match)' => ['user:country', [ 276 'user:country_operator' => select::EQUAL_TO, 277 'user:country_value' => 'AU', 278 ], false], 279 'Filter description' => ['user:description', [ 280 'user:description_operator' => text::CONTAINS, 281 'user:description_value' => 'Hello there', 282 ], true], 283 'Filter description (no match)' => ['user:description', [ 284 'user:description_operator' => text::CONTAINS, 285 'user:description_value' => 'Goodbye', 286 ], false], 287 'Filter auth' => ['user:auth', [ 288 'user:auth_operator' => select::EQUAL_TO, 289 'user:auth_value' => 'manual', 290 ], true], 291 'Filter auth (no match)' => ['user:auth', [ 292 'user:auth_operator' => select::EQUAL_TO, 293 'user:auth_value' => 'ldap', 294 ], false], 295 'Filter username' => ['user:username', [ 296 'user:username_operator' => text::IS_EQUAL_TO, 297 'user:username_value' => 'zoe1', 298 ], true], 299 'Filter username (no match)' => ['user:username', [ 300 'user:username_operator' => text::IS_EQUAL_TO, 301 'user:username_value' => 'alfie1', 302 ], false], 303 'Filter idnumber' => ['user:idnumber', [ 304 'user:idnumber_operator' => text::IS_EQUAL_TO, 305 'user:idnumber_value' => 'Z0001', 306 ], true], 307 'Filter idnumber (no match)' => ['user:idnumber', [ 308 'user:idnumber_operator' => text::IS_EQUAL_TO, 309 'user:idnumber_value' => 'A0001', 310 ], false], 311 'Filter institution' => ['user:institution', [ 312 'user:institution_operator' => text::IS_EQUAL_TO, 313 'user:institution_value' => 'Farm', 314 ], true], 315 'Filter institution (no match)' => ['user:institution', [ 316 'user:institution_operator' => text::IS_EQUAL_TO, 317 'user:institution_value' => 'University', 318 ], false], 319 'Filter department' => ['user:department', [ 320 'user:department_operator' => text::IS_EQUAL_TO, 321 'user:department_value' => 'Stable', 322 ], true], 323 'Filter department (no match)' => ['user:department', [ 324 'user:department_operator' => text::IS_EQUAL_TO, 325 'user:department_value' => 'Office', 326 ], false], 327 'Filter moodlenetprofile' => ['user:moodlenetprofile', [ 328 'user:moodlenetprofile_operator' => text::IS_EQUAL_TO, 329 'user:moodlenetprofile_value' => '@zoe1@example.com', 330 ], true], 331 'Filter moodlenetprofile (no match)' => ['user:moodlenetprofile', [ 332 'user:moodlenetprofile_operator' => text::IS_EQUAL_TO, 333 'user:moodlenetprofile_value' => '@alfie1@example.com', 334 ], false], 335 'Filter suspended' => ['user:suspended', [ 336 'user:suspended_operator' => boolean_select::NOT_CHECKED, 337 ], true], 338 'Filter suspended (no match)' => ['user:suspended', [ 339 'user:suspended_operator' => boolean_select::CHECKED, 340 ], false], 341 'Filter confirmed' => ['user:confirmed', [ 342 'user:confirmed_operator' => boolean_select::CHECKED, 343 ], true], 344 'Filter confirmed (no match)' => ['user:confirmed', [ 345 'user:confirmed_operator' => boolean_select::NOT_CHECKED, 346 ], false], 347 'Filter timecreated' => ['user:timecreated', [ 348 'user:timecreated_operator' => date::DATE_RANGE, 349 'user:timecreated_from' => 1622502000, 350 ], true], 351 'Filter timecreated (no match)' => ['user:timecreated', [ 352 'user:timecreated_operator' => date::DATE_RANGE, 353 'user:timecreated_from' => 1619823600, 354 'user:timecreated_to' => 1622502000, 355 ], false], 356 'Filter lastaccess' => ['user:lastaccess', [ 357 'user:lastaccess_operator' => date::DATE_EMPTY, 358 ], true], 359 'Filter lastaccess (no match)' => ['user:lastaccess', [ 360 'user:lastaccess_operator' => date::DATE_RANGE, 361 'user:lastaccess_from' => 1619823600, 362 'user:lastaccess_to' => 1622502000, 363 ], false], 364 365 // Tags. 366 'Filter tag name' => ['tag:name', [ 367 'tag:name_operator' => tags::EQUAL_TO, 368 'tag:name_value' => [-1], 369 ], false], 370 'Filter tag name not empty' => ['tag:name', [ 371 'tag:name_operator' => tags::NOT_EMPTY, 372 ], true], 373 ]; 374 } 375 376 /** 377 * Test datasource filters 378 * 379 * @param string $filtername 380 * @param array $filtervalues 381 * @param bool $expectmatch 382 * 383 * @dataProvider datasource_filters_provider 384 */ 385 public function test_datasource_filters(string $filtername, array $filtervalues, bool $expectmatch): void { 386 $this->resetAfterTest(); 387 388 $user = $this->getDataGenerator()->create_user([ 389 'username' => 'zoe1', 390 'email' => 'zoe1@example.com', 391 'firstname' => 'Zoe', 392 'middlename' => 'Zebediah', 393 'lastname' => 'Zebra', 394 'firstnamephonetic' => 'Eoz', 395 'lastnamephonetic' => 'Arbez', 396 'alternatename' => 'Zee', 397 'idnumber' => 'Z0001', 398 'institution' => 'Farm', 399 'department' => 'Stable', 400 'phone1' => '111', 401 'phone2' => '222', 402 'address' => 'Big Farm', 403 'city' => 'Barcelona', 404 'country' => 'ES', 405 'description' => 'Hello there', 406 'moodlenetprofile' => '@zoe1@example.com', 407 'interests' => ['Horses'], 408 ]); 409 410 /** @var core_reportbuilder_generator $generator */ 411 $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); 412 413 // Create report containing single column, and given filter. 414 $report = $generator->create_report(['name' => 'Tasks', 'source' => users::class, 'default' => 0]); 415 $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'user:username']); 416 417 // Add filter, set it's values. 418 $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]); 419 $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues); 420 421 if ($expectmatch) { 422 $this->assertNotEmpty($content); 423 424 // Merge report usernames into easily traversable array. 425 $usernames = array_merge(...array_map('array_values', $content)); 426 $this->assertContains($user->username, $usernames); 427 } else { 428 $this->assertEmpty($content); 429 } 430 } 431 432 /** 433 * Stress test datasource 434 * 435 * In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php 436 */ 437 public function test_stress_datasource(): void { 438 if (!PHPUNIT_LONGTEST) { 439 $this->markTestSkipped('PHPUNIT_LONGTEST is not defined'); 440 } 441 442 $this->resetAfterTest(); 443 444 $user = $this->getDataGenerator()->create_user(); 445 446 $this->datasource_stress_test_columns(users::class); 447 $this->datasource_stress_test_columns_aggregation(users::class); 448 $this->datasource_stress_test_conditions(users::class, 'user:username'); 449 } 450 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body