See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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 /** 18 * Test for various bits of datalib.php. 19 * 20 * @package core 21 * @category phpunit 22 * @copyright 2012 The Open University 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 29 /** 30 * Test for various bits of datalib.php. 31 * 32 * @package core 33 * @category phpunit 34 * @copyright 2012 The Open University 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 */ 37 class core_datalib_testcase extends advanced_testcase { 38 protected function normalise_sql($sort) { 39 return preg_replace('~\s+~', ' ', $sort); 40 } 41 42 protected function assert_same_sql($expected, $actual) { 43 $this->assertSame($this->normalise_sql($expected), $this->normalise_sql($actual)); 44 } 45 46 /** 47 * Do a test of the user search SQL with database users. 48 */ 49 public function test_users_search_sql() { 50 global $DB; 51 $this->resetAfterTest(); 52 53 // Set up test users. 54 $user1 = array( 55 'username' => 'usernametest1', 56 'idnumber' => 'idnumbertest1', 57 'firstname' => 'First Name User Test 1', 58 'lastname' => 'Last Name User Test 1', 59 'email' => 'usertest1@example.com', 60 'address' => '2 Test Street Perth 6000 WA', 61 'phone1' => '01010101010', 62 'phone2' => '02020203', 63 'icq' => 'testuser1', 64 'skype' => 'testuser1', 65 'yahoo' => 'testuser1', 66 'aim' => 'testuser1', 67 'msn' => 'testuser1', 68 'department' => 'Department of user 1', 69 'institution' => 'Institution of user 1', 70 'description' => 'This is a description for user 1', 71 'descriptionformat' => FORMAT_MOODLE, 72 'city' => 'Perth', 73 'url' => 'http://moodle.org', 74 'country' => 'AU' 75 ); 76 $user1 = self::getDataGenerator()->create_user($user1); 77 $user2 = array( 78 'username' => 'usernametest2', 79 'idnumber' => 'idnumbertest2', 80 'firstname' => 'First Name User Test 2', 81 'lastname' => 'Last Name User Test 2', 82 'email' => 'usertest2@example.com', 83 'address' => '222 Test Street Perth 6000 WA', 84 'phone1' => '01010101010', 85 'phone2' => '02020203', 86 'icq' => 'testuser1', 87 'skype' => 'testuser1', 88 'yahoo' => 'testuser1', 89 'aim' => 'testuser1', 90 'msn' => 'testuser1', 91 'department' => 'Department of user 2', 92 'institution' => 'Institution of user 2', 93 'description' => 'This is a description for user 2', 94 'descriptionformat' => FORMAT_MOODLE, 95 'city' => 'Perth', 96 'url' => 'http://moodle.org', 97 'country' => 'AU' 98 ); 99 $user2 = self::getDataGenerator()->create_user($user2); 100 101 // Search by name (anywhere in text). 102 list($sql, $params) = users_search_sql('User Test 2', ''); 103 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 104 $this->assertFalse(array_key_exists($user1->id, $results)); 105 $this->assertTrue(array_key_exists($user2->id, $results)); 106 107 // Search by (most of) full name. 108 list($sql, $params) = users_search_sql('First Name User Test 2 Last Name User', ''); 109 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 110 $this->assertFalse(array_key_exists($user1->id, $results)); 111 $this->assertTrue(array_key_exists($user2->id, $results)); 112 113 // Search by name (start of text) valid or not. 114 list($sql, $params) = users_search_sql('User Test 2', '', false); 115 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 116 $this->assertEquals(0, count($results)); 117 list($sql, $params) = users_search_sql('First Name User Test 2', '', false); 118 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 119 $this->assertFalse(array_key_exists($user1->id, $results)); 120 $this->assertTrue(array_key_exists($user2->id, $results)); 121 122 // Search by extra fields included or not (address). 123 list($sql, $params) = users_search_sql('Test Street', '', true); 124 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 125 $this->assertCount(0, $results); 126 list($sql, $params) = users_search_sql('Test Street', '', true, array('address')); 127 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 128 $this->assertCount(2, $results); 129 130 // Exclude user. 131 list($sql, $params) = users_search_sql('User Test', '', true, array(), array($user1->id)); 132 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 133 $this->assertFalse(array_key_exists($user1->id, $results)); 134 $this->assertTrue(array_key_exists($user2->id, $results)); 135 136 // Include only user. 137 list($sql, $params) = users_search_sql('User Test', '', true, array(), array(), array($user1->id)); 138 $results = $DB->get_records_sql("SELECT id FROM {user} WHERE $sql ORDER BY username", $params); 139 $this->assertTrue(array_key_exists($user1->id, $results)); 140 $this->assertFalse(array_key_exists($user2->id, $results)); 141 142 // Join with another table and use different prefix. 143 set_user_preference('amphibian', 'frog', $user1); 144 set_user_preference('amphibian', 'salamander', $user2); 145 list($sql, $params) = users_search_sql('User Test 1', 'qq'); 146 $results = $DB->get_records_sql(" 147 SELECT up.id, up.value 148 FROM {user} qq 149 JOIN {user_preferences} up ON up.userid = qq.id 150 WHERE up.name = :prefname 151 AND $sql", array_merge(array('prefname' => 'amphibian'), $params)); 152 $this->assertEquals(1, count($results)); 153 foreach ($results as $record) { 154 $this->assertSame('frog', $record->value); 155 } 156 } 157 158 public function test_users_order_by_sql_simple() { 159 list($sort, $params) = users_order_by_sql(); 160 $this->assert_same_sql('lastname, firstname, id', $sort); 161 $this->assertEquals(array(), $params); 162 } 163 164 public function test_users_order_by_sql_table_prefix() { 165 list($sort, $params) = users_order_by_sql('u'); 166 $this->assert_same_sql('u.lastname, u.firstname, u.id', $sort); 167 $this->assertEquals(array(), $params); 168 } 169 170 public function test_users_order_by_sql_search_no_extra_fields() { 171 global $CFG, $DB; 172 $this->resetAfterTest(true); 173 174 $CFG->showuseridentity = ''; 175 176 list($sort, $params) = users_order_by_sql('', 'search', context_system::instance()); 177 $this->assert_same_sql('CASE WHEN 178 ' . $DB->sql_fullname() . ' = :usersortexact1 OR 179 LOWER(firstname) = LOWER(:usersortexact2) OR 180 LOWER(lastname) = LOWER(:usersortexact3) 181 THEN 0 ELSE 1 END, lastname, firstname, id', $sort); 182 $this->assertEquals(array('usersortexact1' => 'search', 'usersortexact2' => 'search', 183 'usersortexact3' => 'search'), $params); 184 } 185 186 public function test_users_order_by_sql_search_with_extra_fields_and_prefix() { 187 global $CFG, $DB; 188 $this->resetAfterTest(); 189 190 $CFG->showuseridentity = 'email,idnumber'; 191 $this->setAdminUser(); 192 193 list($sort, $params) = users_order_by_sql('u', 'search', context_system::instance()); 194 $this->assert_same_sql('CASE WHEN 195 ' . $DB->sql_fullname('u.firstname', 'u.lastname') . ' = :usersortexact1 OR 196 LOWER(u.firstname) = LOWER(:usersortexact2) OR 197 LOWER(u.lastname) = LOWER(:usersortexact3) OR 198 LOWER(u.email) = LOWER(:usersortexact4) OR 199 LOWER(u.idnumber) = LOWER(:usersortexact5) 200 THEN 0 ELSE 1 END, u.lastname, u.firstname, u.id', $sort); 201 $this->assertEquals(array('usersortexact1' => 'search', 'usersortexact2' => 'search', 202 'usersortexact3' => 'search', 'usersortexact4' => 'search', 'usersortexact5' => 'search'), $params); 203 } 204 205 public function test_get_admin() { 206 global $CFG, $DB; 207 $this->resetAfterTest(); 208 209 $this->assertSame('2', $CFG->siteadmins); // Admin always has id 2 in new installs. 210 $defaultadmin = get_admin(); 211 $this->assertEquals($defaultadmin->id, 2); 212 213 unset_config('siteadmins'); 214 $this->assertFalse(get_admin()); 215 216 set_config('siteadmins', -1); 217 $this->assertFalse(get_admin()); 218 219 $user1 = $this->getDataGenerator()->create_user(); 220 $user2 = $this->getDataGenerator()->create_user(); 221 222 set_config('siteadmins', $user1->id.','.$user2->id); 223 $admin = get_admin(); 224 $this->assertEquals($user1->id, $admin->id); 225 226 set_config('siteadmins', '-1,'.$user2->id.','.$user1->id); 227 $admin = get_admin(); 228 $this->assertEquals($user2->id, $admin->id); 229 230 $odlread = $DB->perf_get_reads(); 231 get_admin(); // No DB queries on repeated call expected. 232 get_admin(); 233 get_admin(); 234 $this->assertEquals($odlread, $DB->perf_get_reads()); 235 } 236 237 public function test_get_admins() { 238 global $CFG, $DB; 239 $this->resetAfterTest(); 240 241 $this->assertSame('2', $CFG->siteadmins); // Admin always has id 2 in new installs. 242 243 $user1 = $this->getDataGenerator()->create_user(); 244 $user2 = $this->getDataGenerator()->create_user(); 245 $user3 = $this->getDataGenerator()->create_user(); 246 $user4 = $this->getDataGenerator()->create_user(); 247 248 $admins = get_admins(); 249 $this->assertCount(1, $admins); 250 $admin = reset($admins); 251 $this->assertTrue(isset($admins[$admin->id])); 252 $this->assertEquals(2, $admin->id); 253 254 unset_config('siteadmins'); 255 $this->assertSame(array(), get_admins()); 256 257 set_config('siteadmins', -1); 258 $this->assertSame(array(), get_admins()); 259 260 set_config('siteadmins', '-1,'.$user2->id.','.$user1->id.','.$user3->id); 261 $this->assertEquals(array($user2->id=>$user2, $user1->id=>$user1, $user3->id=>$user3), get_admins()); 262 263 $odlread = $DB->perf_get_reads(); 264 get_admins(); // This should make just one query. 265 $this->assertEquals($odlread+1, $DB->perf_get_reads()); 266 } 267 268 public function test_get_course() { 269 global $DB, $PAGE, $SITE; 270 $this->resetAfterTest(); 271 272 // First test course will be current course ($COURSE). 273 $course1obj = $this->getDataGenerator()->create_course(array('shortname' => 'FROGS')); 274 $PAGE->set_course($course1obj); 275 276 // Second test course is not current course. 277 $course2obj = $this->getDataGenerator()->create_course(array('shortname' => 'ZOMBIES')); 278 279 // Check it does not make any queries when requesting the $COURSE/$SITE. 280 $before = $DB->perf_get_queries(); 281 $result = get_course($course1obj->id); 282 $this->assertEquals($before, $DB->perf_get_queries()); 283 $this->assertSame('FROGS', $result->shortname); 284 $result = get_course($SITE->id); 285 $this->assertEquals($before, $DB->perf_get_queries()); 286 287 // Check it makes 1 query to request other courses. 288 $result = get_course($course2obj->id); 289 $this->assertSame('ZOMBIES', $result->shortname); 290 $this->assertEquals($before + 1, $DB->perf_get_queries()); 291 } 292 293 /** 294 * Test that specifying fields when calling get_courses always returns required fields "id, category, visible" 295 */ 296 public function test_get_courses_with_fields(): void { 297 $this->resetAfterTest(); 298 299 $category = $this->getDataGenerator()->create_category(); 300 $course = $this->getDataGenerator()->create_course(['category' => $category->id]); 301 302 // Specify "id" only. 303 $courses = get_courses($category->id, 'c.sortorder', 'c.id'); 304 $this->assertCount(1, $courses); 305 $this->assertEquals((object) [ 306 'id' => $course->id, 307 'category' => $course->category, 308 'visible' => $course->visible, 309 ], reset($courses)); 310 311 // Specify some optional fields. 312 $courses = get_courses($category->id, 'c.sortorder', 'c.id, c.shortname, c.fullname'); 313 $this->assertCount(1, $courses); 314 $this->assertEquals((object) [ 315 'id' => $course->id, 316 'category' => $course->category, 317 'visible' => $course->visible, 318 'shortname' => $course->shortname, 319 'fullname' => $course->fullname, 320 ], reset($courses)); 321 } 322 323 public function test_increment_revision_number() { 324 global $DB; 325 $this->resetAfterTest(); 326 327 // Use one of the fields that are used with increment_revision_number(). 328 $course1 = $this->getDataGenerator()->create_course(); 329 $course2 = $this->getDataGenerator()->create_course(); 330 $DB->set_field('course', 'cacherev', 1, array()); 331 332 $record1 = $DB->get_record('course', array('id'=>$course1->id)); 333 $record2 = $DB->get_record('course', array('id'=>$course2->id)); 334 $this->assertEquals(1, $record1->cacherev); 335 $this->assertEquals(1, $record2->cacherev); 336 337 // Incrementing some lower value. 338 $this->setCurrentTimeStart(); 339 increment_revision_number('course', 'cacherev', 'id = :id', array('id'=>$course1->id)); 340 $record1 = $DB->get_record('course', array('id'=>$course1->id)); 341 $record2 = $DB->get_record('course', array('id'=>$course2->id)); 342 $this->assertTimeCurrent($record1->cacherev); 343 $this->assertEquals(1, $record2->cacherev); 344 345 // Incrementing in the same second. 346 $rev1 = $DB->get_field('course', 'cacherev', array('id'=>$course1->id)); 347 $now = time(); 348 $DB->set_field('course', 'cacherev', $now, array('id'=>$course1->id)); 349 increment_revision_number('course', 'cacherev', 'id = :id', array('id'=>$course1->id)); 350 $rev2 = $DB->get_field('course', 'cacherev', array('id'=>$course1->id)); 351 $this->assertGreaterThan($rev1, $rev2); 352 increment_revision_number('course', 'cacherev', 'id = :id', array('id'=>$course1->id)); 353 $rev3 = $DB->get_field('course', 'cacherev', array('id'=>$course1->id)); 354 $this->assertGreaterThan($rev2, $rev3); 355 $this->assertGreaterThan($now+1, $rev3); 356 increment_revision_number('course', 'cacherev', 'id = :id', array('id'=>$course1->id)); 357 $rev4 = $DB->get_field('course', 'cacherev', array('id'=>$course1->id)); 358 $this->assertGreaterThan($rev3, $rev4); 359 $this->assertGreaterThan($now+2, $rev4); 360 361 // Recovering from runaway revision. 362 $DB->set_field('course', 'cacherev', time()+60*60*60, array('id'=>$course2->id)); 363 $record2 = $DB->get_record('course', array('id'=>$course2->id)); 364 $this->assertGreaterThan(time(), $record2->cacherev); 365 $this->setCurrentTimeStart(); 366 increment_revision_number('course', 'cacherev', 'id = :id', array('id'=>$course2->id)); 367 $record2b = $DB->get_record('course', array('id'=>$course2->id)); 368 $this->assertTimeCurrent($record2b->cacherev); 369 370 // Update all revisions. 371 $DB->set_field('course', 'cacherev', 1, array()); 372 $this->setCurrentTimeStart(); 373 increment_revision_number('course', 'cacherev', ''); 374 $record1 = $DB->get_record('course', array('id'=>$course1->id)); 375 $record2 = $DB->get_record('course', array('id'=>$course2->id)); 376 $this->assertTimeCurrent($record1->cacherev); 377 $this->assertEquals($record1->cacherev, $record2->cacherev); 378 } 379 380 public function test_get_coursemodule_from_id() { 381 global $CFG; 382 383 $this->resetAfterTest(); 384 $this->setAdminUser(); // Some generators have bogus access control. 385 386 $this->assertFileExists("$CFG->dirroot/mod/folder/lib.php"); 387 $this->assertFileExists("$CFG->dirroot/mod/glossary/lib.php"); 388 389 $course1 = $this->getDataGenerator()->create_course(); 390 $course2 = $this->getDataGenerator()->create_course(); 391 392 $folder1a = $this->getDataGenerator()->create_module('folder', array('course' => $course1, 'section' => 3)); 393 $folder1b = $this->getDataGenerator()->create_module('folder', array('course' => $course1)); 394 $glossary1 = $this->getDataGenerator()->create_module('glossary', array('course' => $course1)); 395 396 $folder2 = $this->getDataGenerator()->create_module('folder', array('course' => $course2)); 397 398 $cm = get_coursemodule_from_id('folder', $folder1a->cmid); 399 $this->assertInstanceOf('stdClass', $cm); 400 $this->assertSame('folder', $cm->modname); 401 $this->assertSame($folder1a->id, $cm->instance); 402 $this->assertSame($folder1a->course, $cm->course); 403 $this->assertObjectNotHasAttribute('sectionnum', $cm); 404 405 $this->assertEquals($cm, get_coursemodule_from_id('', $folder1a->cmid)); 406 $this->assertEquals($cm, get_coursemodule_from_id('folder', $folder1a->cmid, $course1->id)); 407 $this->assertEquals($cm, get_coursemodule_from_id('folder', $folder1a->cmid, 0)); 408 $this->assertFalse(get_coursemodule_from_id('folder', $folder1a->cmid, -10)); 409 410 $cm2 = get_coursemodule_from_id('folder', $folder1a->cmid, 0, true); 411 $this->assertEquals(3, $cm2->sectionnum); 412 unset($cm2->sectionnum); 413 $this->assertEquals($cm, $cm2); 414 415 $this->assertFalse(get_coursemodule_from_id('folder', -11)); 416 417 try { 418 get_coursemodule_from_id('folder', -11, 0, false, MUST_EXIST); 419 $this->fail('dml_missing_record_exception expected'); 420 } catch (moodle_exception $e) { 421 $this->assertInstanceOf('dml_missing_record_exception', $e); 422 } 423 424 try { 425 get_coursemodule_from_id('', -11, 0, false, MUST_EXIST); 426 $this->fail('dml_missing_record_exception expected'); 427 } catch (moodle_exception $e) { 428 $this->assertInstanceOf('dml_missing_record_exception', $e); 429 } 430 431 try { 432 get_coursemodule_from_id('a b', $folder1a->cmid, 0, false, MUST_EXIST); 433 $this->fail('coding_exception expected'); 434 } catch (moodle_exception $e) { 435 $this->assertInstanceOf('coding_exception', $e); 436 } 437 438 try { 439 get_coursemodule_from_id('abc', $folder1a->cmid, 0, false, MUST_EXIST); 440 $this->fail('dml_read_exception expected'); 441 } catch (moodle_exception $e) { 442 $this->assertInstanceOf('dml_read_exception', $e); 443 } 444 } 445 446 public function test_get_coursemodule_from_instance() { 447 global $CFG; 448 449 $this->resetAfterTest(); 450 $this->setAdminUser(); // Some generators have bogus access control. 451 452 $this->assertFileExists("$CFG->dirroot/mod/folder/lib.php"); 453 $this->assertFileExists("$CFG->dirroot/mod/glossary/lib.php"); 454 455 $course1 = $this->getDataGenerator()->create_course(); 456 $course2 = $this->getDataGenerator()->create_course(); 457 458 $folder1a = $this->getDataGenerator()->create_module('folder', array('course' => $course1, 'section' => 3)); 459 $folder1b = $this->getDataGenerator()->create_module('folder', array('course' => $course1)); 460 461 $folder2 = $this->getDataGenerator()->create_module('folder', array('course' => $course2)); 462 463 $cm = get_coursemodule_from_instance('folder', $folder1a->id); 464 $this->assertInstanceOf('stdClass', $cm); 465 $this->assertSame('folder', $cm->modname); 466 $this->assertSame($folder1a->id, $cm->instance); 467 $this->assertSame($folder1a->course, $cm->course); 468 $this->assertObjectNotHasAttribute('sectionnum', $cm); 469 470 $this->assertEquals($cm, get_coursemodule_from_instance('folder', $folder1a->id, $course1->id)); 471 $this->assertEquals($cm, get_coursemodule_from_instance('folder', $folder1a->id, 0)); 472 $this->assertFalse(get_coursemodule_from_instance('folder', $folder1a->id, -10)); 473 474 $cm2 = get_coursemodule_from_instance('folder', $folder1a->id, 0, true); 475 $this->assertEquals(3, $cm2->sectionnum); 476 unset($cm2->sectionnum); 477 $this->assertEquals($cm, $cm2); 478 479 $this->assertFalse(get_coursemodule_from_instance('folder', -11)); 480 481 try { 482 get_coursemodule_from_instance('folder', -11, 0, false, MUST_EXIST); 483 $this->fail('dml_missing_record_exception expected'); 484 } catch (moodle_exception $e) { 485 $this->assertInstanceOf('dml_missing_record_exception', $e); 486 } 487 488 try { 489 get_coursemodule_from_instance('a b', $folder1a->cmid, 0, false, MUST_EXIST); 490 $this->fail('coding_exception expected'); 491 } catch (moodle_exception $e) { 492 $this->assertInstanceOf('coding_exception', $e); 493 } 494 495 try { 496 get_coursemodule_from_instance('', $folder1a->cmid, 0, false, MUST_EXIST); 497 $this->fail('coding_exception expected'); 498 } catch (moodle_exception $e) { 499 $this->assertInstanceOf('coding_exception', $e); 500 } 501 502 try { 503 get_coursemodule_from_instance('abc', $folder1a->cmid, 0, false, MUST_EXIST); 504 $this->fail('dml_read_exception expected'); 505 } catch (moodle_exception $e) { 506 $this->assertInstanceOf('dml_read_exception', $e); 507 } 508 } 509 510 public function test_get_coursemodules_in_course() { 511 global $CFG; 512 513 $this->resetAfterTest(); 514 $this->setAdminUser(); // Some generators have bogus access control. 515 516 $this->assertFileExists("$CFG->dirroot/mod/folder/lib.php"); 517 $this->assertFileExists("$CFG->dirroot/mod/glossary/lib.php"); 518 $this->assertFileExists("$CFG->dirroot/mod/label/lib.php"); 519 520 $course1 = $this->getDataGenerator()->create_course(); 521 $course2 = $this->getDataGenerator()->create_course(); 522 523 $folder1a = $this->getDataGenerator()->create_module('folder', array('course' => $course1, 'section' => 3)); 524 $folder1b = $this->getDataGenerator()->create_module('folder', array('course' => $course1)); 525 $glossary1 = $this->getDataGenerator()->create_module('glossary', array('course' => $course1)); 526 527 $folder2 = $this->getDataGenerator()->create_module('folder', array('course' => $course2)); 528 $glossary2a = $this->getDataGenerator()->create_module('glossary', array('course' => $course2)); 529 $glossary2b = $this->getDataGenerator()->create_module('glossary', array('course' => $course2)); 530 531 $modules = get_coursemodules_in_course('folder', $course1->id); 532 $this->assertCount(2, $modules); 533 534 $cm = $modules[$folder1a->cmid]; 535 $this->assertSame('folder', $cm->modname); 536 $this->assertSame($folder1a->id, $cm->instance); 537 $this->assertSame($folder1a->course, $cm->course); 538 $this->assertObjectNotHasAttribute('sectionnum', $cm); 539 $this->assertObjectNotHasAttribute('revision', $cm); 540 $this->assertObjectNotHasAttribute('display', $cm); 541 542 $cm = $modules[$folder1b->cmid]; 543 $this->assertSame('folder', $cm->modname); 544 $this->assertSame($folder1b->id, $cm->instance); 545 $this->assertSame($folder1b->course, $cm->course); 546 $this->assertObjectNotHasAttribute('sectionnum', $cm); 547 $this->assertObjectNotHasAttribute('revision', $cm); 548 $this->assertObjectNotHasAttribute('display', $cm); 549 550 $modules = get_coursemodules_in_course('folder', $course1->id, 'revision, display'); 551 $this->assertCount(2, $modules); 552 553 $cm = $modules[$folder1a->cmid]; 554 $this->assertSame('folder', $cm->modname); 555 $this->assertSame($folder1a->id, $cm->instance); 556 $this->assertSame($folder1a->course, $cm->course); 557 $this->assertObjectNotHasAttribute('sectionnum', $cm); 558 $this->assertObjectHasAttribute('revision', $cm); 559 $this->assertObjectHasAttribute('display', $cm); 560 561 $modules = get_coursemodules_in_course('label', $course1->id); 562 $this->assertCount(0, $modules); 563 564 try { 565 get_coursemodules_in_course('a b', $course1->id); 566 $this->fail('coding_exception expected'); 567 } catch (moodle_exception $e) { 568 $this->assertInstanceOf('coding_exception', $e); 569 } 570 571 try { 572 get_coursemodules_in_course('abc', $course1->id); 573 $this->fail('dml_read_exception expected'); 574 } catch (moodle_exception $e) { 575 $this->assertInstanceOf('dml_read_exception', $e); 576 } 577 } 578 579 public function test_get_all_instances_in_courses() { 580 global $CFG; 581 582 $this->resetAfterTest(); 583 $this->setAdminUser(); // Some generators have bogus access control. 584 585 $this->assertFileExists("$CFG->dirroot/mod/folder/lib.php"); 586 $this->assertFileExists("$CFG->dirroot/mod/glossary/lib.php"); 587 588 $course1 = $this->getDataGenerator()->create_course(); 589 $course2 = $this->getDataGenerator()->create_course(); 590 $course3 = $this->getDataGenerator()->create_course(); 591 592 $folder1a = $this->getDataGenerator()->create_module('folder', array('course' => $course1, 'section' => 3)); 593 $folder1b = $this->getDataGenerator()->create_module('folder', array('course' => $course1)); 594 $glossary1 = $this->getDataGenerator()->create_module('glossary', array('course' => $course1)); 595 596 $folder2 = $this->getDataGenerator()->create_module('folder', array('course' => $course2)); 597 $glossary2a = $this->getDataGenerator()->create_module('glossary', array('course' => $course2)); 598 $glossary2b = $this->getDataGenerator()->create_module('glossary', array('course' => $course2)); 599 600 $folder3 = $this->getDataGenerator()->create_module('folder', array('course' => $course3)); 601 602 $modules = get_all_instances_in_courses('folder', array($course1->id => $course1, $course2->id => $course2)); 603 $this->assertCount(3, $modules); 604 605 foreach ($modules as $cm) { 606 if ($folder1a->cmid == $cm->coursemodule) { 607 $folder = $folder1a; 608 } else if ($folder1b->cmid == $cm->coursemodule) { 609 $folder = $folder1b; 610 } else if ($folder2->cmid == $cm->coursemodule) { 611 $folder = $folder2; 612 } else { 613 $this->fail('Unexpected cm'. $cm->coursemodule); 614 } 615 $this->assertSame($folder->name, $cm->name); 616 $this->assertSame($folder->course, $cm->course); 617 } 618 619 try { 620 get_all_instances_in_courses('a b', array($course1->id => $course1, $course2->id => $course2)); 621 $this->fail('coding_exception expected'); 622 } catch (moodle_exception $e) { 623 $this->assertInstanceOf('coding_exception', $e); 624 } 625 626 try { 627 get_all_instances_in_courses('', array($course1->id => $course1, $course2->id => $course2)); 628 $this->fail('coding_exception expected'); 629 } catch (moodle_exception $e) { 630 $this->assertInstanceOf('coding_exception', $e); 631 } 632 } 633 634 public function test_get_all_instances_in_course() { 635 global $CFG; 636 637 $this->resetAfterTest(); 638 $this->setAdminUser(); // Some generators have bogus access control. 639 640 $this->assertFileExists("$CFG->dirroot/mod/folder/lib.php"); 641 $this->assertFileExists("$CFG->dirroot/mod/glossary/lib.php"); 642 643 $course1 = $this->getDataGenerator()->create_course(); 644 $course2 = $this->getDataGenerator()->create_course(); 645 $course3 = $this->getDataGenerator()->create_course(); 646 647 $folder1a = $this->getDataGenerator()->create_module('folder', array('course' => $course1, 'section' => 3)); 648 $folder1b = $this->getDataGenerator()->create_module('folder', array('course' => $course1)); 649 $glossary1 = $this->getDataGenerator()->create_module('glossary', array('course' => $course1)); 650 651 $folder2 = $this->getDataGenerator()->create_module('folder', array('course' => $course2)); 652 $glossary2a = $this->getDataGenerator()->create_module('glossary', array('course' => $course2)); 653 $glossary2b = $this->getDataGenerator()->create_module('glossary', array('course' => $course2)); 654 655 $folder3 = $this->getDataGenerator()->create_module('folder', array('course' => $course3)); 656 657 $modules = get_all_instances_in_course('folder', $course1); 658 $this->assertCount(2, $modules); 659 660 foreach ($modules as $cm) { 661 if ($folder1a->cmid == $cm->coursemodule) { 662 $folder = $folder1a; 663 } else if ($folder1b->cmid == $cm->coursemodule) { 664 $folder = $folder1b; 665 } else { 666 $this->fail('Unexpected cm'. $cm->coursemodule); 667 } 668 $this->assertSame($folder->name, $cm->name); 669 $this->assertSame($folder->course, $cm->course); 670 } 671 672 try { 673 get_all_instances_in_course('a b', $course1); 674 $this->fail('coding_exception expected'); 675 } catch (moodle_exception $e) { 676 $this->assertInstanceOf('coding_exception', $e); 677 } 678 679 try { 680 get_all_instances_in_course('', $course1); 681 $this->fail('coding_exception expected'); 682 } catch (moodle_exception $e) { 683 $this->assertInstanceOf('coding_exception', $e); 684 } 685 } 686 687 /** 688 * Tests the get_users_listing function. 689 */ 690 public function test_get_users_listing(): void { 691 global $DB; 692 693 $this->resetAfterTest(); 694 695 $generator = $this->getDataGenerator(); 696 697 // Set up profile field. 698 $generator->create_custom_profile_field(['datatype' => 'text', 699 'shortname' => 'specialid', 'name' => 'Special user id']); 700 701 // Set up the show user identity option. 702 set_config('showuseridentity', 'department'); 703 704 // Get all the existing user ids (we're going to remove these from test results). 705 $existingids = array_fill_keys($DB->get_fieldset_select('user', 'id', '1 = 1'), true); 706 707 // Create some test user accounts. 708 $userids = []; 709 foreach (['a', 'b', 'c', 'd'] as $key) { 710 $record = [ 711 'username' => 'user_' . $key, 712 'firstname' => $key . '_first', 713 'lastname' => 'last_' . $key, 714 'department' => 'department_' . $key, 715 'lastaccess' => ord($key) 716 ]; 717 $user = $generator->create_user($record); 718 $userids[] = $user->id; 719 } 720 721 // Check default result with no parameters. 722 $results = get_users_listing(); 723 $results = array_diff_key($results, $existingids); 724 725 // It should return all the results in order. 726 $this->assertEquals($userids, array_keys($results)); 727 728 // Results should have some general fields and name fields, check some samples. 729 $this->assertEquals('user_a', $results[$userids[0]]->username); 730 $this->assertEquals('user_a@example.com', $results[$userids[0]]->email); 731 $this->assertEquals(1, $results[$userids[0]]->confirmed); 732 $this->assertEquals('a_first', $results[$userids[0]]->firstname); 733 $this->assertObjectHasAttribute('firstnamephonetic', $results[$userids[0]]); 734 735 // Should not have department because no context specified. 736 $this->assertObjectNotHasAttribute('department', $results[$userids[0]]); 737 738 // Check sorting. 739 $results = get_users_listing('username', 'DESC'); 740 $results = array_diff_key($results, $existingids); 741 $this->assertEquals([$userids[3], $userids[2], $userids[1], $userids[0]], array_keys($results)); 742 743 // Check default fallback sort field works as expected. 744 $results = get_users_listing('blah2', 'ASC'); 745 $results = array_diff_key($results, $existingids); 746 $this->assertEquals([$userids[0], $userids[1], $userids[2], $userids[3]], array_keys($results)); 747 748 // Check default fallback sort direction works as expected. 749 $results = get_users_listing('lastaccess', 'blah2'); 750 $results = array_diff_key($results, $existingids); 751 $this->assertEquals([$userids[0], $userids[1], $userids[2], $userids[3]], array_keys($results)); 752 753 // Add the options to showuseridentity and check it returns those fields but only if you 754 // specify a context AND have permissions. 755 $results = get_users_listing('lastaccess', 'asc', 0, 0, '', '', '', '', null, 756 \context_system::instance()); 757 $this->assertObjectNotHasAttribute('department', $results[$userids[0]]); 758 $this->setAdminUser(); 759 $results = get_users_listing('lastaccess', 'asc', 0, 0, '', '', '', '', null, 760 \context_system::instance()); 761 $this->assertEquals('department_a', $results[$userids[0]]->department); 762 763 // Check search (full name, email, username). 764 $results = get_users_listing('lastaccess', 'asc', 0, 0, 'b_first last_b'); 765 $this->assertEquals([$userids[1]], array_keys($results)); 766 $results = get_users_listing('lastaccess', 'asc', 0, 0, 'c@example'); 767 $this->assertEquals([$userids[2]], array_keys($results)); 768 $results = get_users_listing('lastaccess', 'asc', 0, 0, 'user_d'); 769 $this->assertEquals([$userids[3]], array_keys($results)); 770 771 // Check first and last initial restriction (all the test ones have same last initial). 772 $results = get_users_listing('lastaccess', 'asc', 0, 0, '', 'C'); 773 $this->assertEquals([$userids[2]], array_keys($results)); 774 $results = get_users_listing('lastaccess', 'asc', 0, 0, '', '', 'L'); 775 $results = array_diff_key($results, $existingids); 776 $this->assertEquals($userids, array_keys($results)); 777 778 // Check the extra where clause, either with the 'u.' prefix or not. 779 $results = get_users_listing('lastaccess', 'asc', 0, 0, '', '', '', 'id IN (:x,:y)', 780 ['x' => $userids[1], 'y' => $userids[3]]); 781 $results = array_diff_key($results, $existingids); 782 $this->assertEquals([$userids[1], $userids[3]], array_keys($results)); 783 $results = get_users_listing('lastaccess', 'asc', 0, 0, '', '', '', 'id IN (:x,:y)', 784 ['x' => $userids[1], 'y' => $userids[3]]); 785 $results = array_diff_key($results, $existingids); 786 $this->assertEquals([$userids[1], $userids[3]], array_keys($results)); 787 } 788 789 /** 790 * Data provider for test_get_safe_orderby(). 791 * 792 * @return array 793 */ 794 public function get_safe_orderby_provider(): array { 795 $orderbymap = [ 796 'courseid' => 'c.id', 797 'somecustomvalue' => 'c.startdate, c.shortname', 798 'default' => 'c.fullname', 799 ]; 800 $orderbymapnodefault = [ 801 'courseid' => 'c.id', 802 'somecustomvalue' => 'c.startdate, c.shortname', 803 ]; 804 805 return [ 806 'Valid option, no direction specified' => [ 807 $orderbymap, 808 'somecustomvalue', 809 '', 810 ' ORDER BY c.startdate, c.shortname', 811 ], 812 'Valid option, valid direction specified' => [ 813 $orderbymap, 814 'courseid', 815 'DESC', 816 ' ORDER BY c.id DESC', 817 ], 818 'Valid option, valid lowercase direction specified' => [ 819 $orderbymap, 820 'courseid', 821 'asc', 822 ' ORDER BY c.id ASC', 823 ], 824 'Valid option, invalid direction specified' => [ 825 $orderbymap, 826 'courseid', 827 'BOOP', 828 ' ORDER BY c.id', 829 ], 830 'Valid option, invalid lowercase direction specified' => [ 831 $orderbymap, 832 'courseid', 833 'boop', 834 ' ORDER BY c.id', 835 ], 836 'Invalid option default fallback, with valid direction' => [ 837 $orderbymap, 838 'thisdoesnotexist', 839 'ASC', 840 ' ORDER BY c.fullname ASC', 841 ], 842 'Invalid option default fallback, with invalid direction' => [ 843 $orderbymap, 844 'thisdoesnotexist', 845 'BOOP', 846 ' ORDER BY c.fullname', 847 ], 848 'Invalid option without default, with valid direction' => [ 849 $orderbymapnodefault, 850 'thisdoesnotexist', 851 'ASC', 852 '', 853 ], 854 'Invalid option without default, with invalid direction' => [ 855 $orderbymapnodefault, 856 'thisdoesnotexist', 857 'NOPE', 858 '', 859 ], 860 ]; 861 } 862 863 /** 864 * Tests the get_safe_orderby function. 865 * 866 * @dataProvider get_safe_orderby_provider 867 * @param array $orderbymap The ORDER BY parameter mapping array. 868 * @param string $orderbykey The string key being provided, to check against the map. 869 * @param string $direction The optional direction to order by. 870 * @param string $expected The expected string output of the method. 871 */ 872 public function test_get_safe_orderby(array $orderbymap, string $orderbykey, string $direction, string $expected): void { 873 $actual = get_safe_orderby($orderbymap, $orderbykey, $direction); 874 $this->assertEquals($expected, $actual); 875 } 876 877 /** 878 * Data provider for test_get_safe_orderby_multiple(). 879 * 880 * @return array 881 */ 882 public function get_safe_orderby_multiple_provider(): array { 883 $orderbymap = [ 884 'courseid' => 'c.id', 885 'firstname' => 'u.firstname', 886 'default' => 'c.startdate', 887 ]; 888 $orderbymapnodefault = [ 889 'courseid' => 'c.id', 890 'firstname' => 'u.firstname', 891 ]; 892 893 return [ 894 'Valid options, no directions specified' => [ 895 $orderbymap, 896 ['courseid', 'firstname'], 897 [], 898 ' ORDER BY c.id, u.firstname', 899 ], 900 'Valid options, some direction specified' => [ 901 $orderbymap, 902 ['courseid', 'firstname'], 903 ['DESC'], 904 ' ORDER BY c.id DESC, u.firstname', 905 ], 906 'Valid options, all directions specified' => [ 907 $orderbymap, 908 ['courseid', 'firstname'], 909 ['ASC', 'desc'], 910 ' ORDER BY c.id ASC, u.firstname DESC', 911 ], 912 'Valid options, valid and invalid directions specified' => [ 913 $orderbymap, 914 ['courseid', 'firstname'], 915 ['BOOP', 'DESC'], 916 ' ORDER BY c.id, u.firstname DESC', 917 ], 918 'Valid options, all invalid directions specified' => [ 919 $orderbymap, 920 ['courseid', 'firstname'], 921 ['BOOP', 'SNOOT'], 922 ' ORDER BY c.id, u.firstname', 923 ], 924 'Valid and invalid option default fallback, with valid directions' => [ 925 $orderbymap, 926 ['thisdoesnotexist', 'courseid'], 927 ['asc', 'DESC'], 928 ' ORDER BY c.startdate ASC, c.id DESC', 929 ], 930 'Valid and invalid option default fallback, with invalid direction' => [ 931 $orderbymap, 932 ['courseid', 'thisdoesnotexist'], 933 ['BOOP', 'SNOOT'], 934 ' ORDER BY c.id, c.startdate', 935 ], 936 'Valid and invalid option without default, with valid direction' => [ 937 $orderbymapnodefault, 938 ['thisdoesnotexist', 'courseid'], 939 ['ASC', 'DESC'], 940 ' ORDER BY c.id DESC', 941 ], 942 'Valid and invalid option without default, with invalid direction' => [ 943 $orderbymapnodefault, 944 ['thisdoesnotexist', 'courseid'], 945 ['BOOP', 'SNOOT'], 946 ' ORDER BY c.id', 947 ], 948 'Invalid option only without default, with valid direction' => [ 949 $orderbymapnodefault, 950 ['thisdoesnotexist'], 951 ['ASC'], 952 '', 953 ], 954 'Invalid option only without default, with invalid direction' => [ 955 $orderbymapnodefault, 956 ['thisdoesnotexist'], 957 ['BOOP'], 958 '', 959 ], 960 'Single valid option, direction specified' => [ 961 $orderbymap, 962 ['firstname'], 963 ['ASC'], 964 ' ORDER BY u.firstname ASC', 965 ], 966 'Single valid option, direction not specified' => [ 967 $orderbymap, 968 ['firstname'], 969 [], 970 ' ORDER BY u.firstname', 971 ], 972 ]; 973 } 974 975 /** 976 * Tests the get_safe_orderby_multiple function. 977 * 978 * @dataProvider get_safe_orderby_multiple_provider 979 * @param array $orderbymap The ORDER BY parameter mapping array. 980 * @param array $orderbykeys The array of string keys being provided, to check against the map. 981 * @param array $directions The optional directions to order by. 982 * @param string $expected The expected string output of the method. 983 */ 984 public function test_get_safe_orderby_multiple(array $orderbymap, array $orderbykeys, array $directions, 985 string $expected): void { 986 $actual = get_safe_orderby_multiple($orderbymap, $orderbykeys, $directions); 987 $this->assertEquals($expected, $actual); 988 } 989 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body