See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401]
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 extensions manager. 19 * 20 * @package core_contentbank 21 * @category test 22 * @copyright 2020 Amaia Anabitarte <amaia@moodle.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 namespace core_contentbank; 27 28 defined('MOODLE_INTERNAL') || die(); 29 30 use advanced_testcase; 31 use context_block; 32 use context_course; 33 use context_coursecat; 34 use context_module; 35 use context_system; 36 use context_user; 37 use Exception; 38 39 global $CFG; 40 require_once($CFG->dirroot . '/contentbank/tests/fixtures/testable_contenttype.php'); 41 require_once($CFG->dirroot . '/contentbank/tests/fixtures/testable_content.php'); 42 43 /** 44 * Test for extensions manager. 45 * 46 * @package core_contentbank 47 * @category test 48 * @copyright 2020 Amaia Anabitarte <amaia@moodle.com> 49 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 50 * @coversDefaultClass \core_contentbank\contentbank 51 */ 52 class contentbank_test extends advanced_testcase { 53 54 /** 55 * Setup to ensure that fixtures are loaded. 56 */ 57 public static function setupBeforeClass(): void { 58 global $CFG; 59 60 require_once($CFG->dirroot . '/contentbank/tests/fixtures/testable_contenttype.php'); 61 } 62 63 /** 64 * Data provider for test_get_extension_supporter. 65 * 66 * @return array 67 */ 68 public function get_extension_provider() { 69 return [ 70 'H5P file' => ['something.h5p', '.h5p'], 71 'PDF file' => ['something.pdf', '.pdf'] 72 ]; 73 } 74 75 /** 76 * Tests for get_extension() function. 77 * 78 * @dataProvider get_extension_provider 79 * @param string $filename The filename given 80 * @param string $expected The extension of the file 81 * 82 * @covers ::get_extension 83 */ 84 public function test_get_extension(string $filename, string $expected) { 85 $this->resetAfterTest(); 86 87 $cb = new contentbank(); 88 89 $extension = $cb->get_extension($filename); 90 $this->assertEquals($expected, $extension); 91 } 92 93 /** 94 * Data provider for test_load_context_supported_extensions. 95 * 96 * @return array 97 */ 98 public function get_extension_supporters_provider() { 99 return [ 100 'H5P first' => [['.h5p' => ['h5p', 'testable']], '.h5p', 'h5p'], 101 'Testable first (but upload not implemented)' => [['.h5p' => ['testable', 'h5p']], '.h5p', 'h5p'], 102 ]; 103 } 104 105 /** 106 * Tests for get_extension_supporter() function with admin permissions. 107 * 108 * @dataProvider get_extension_supporters_provider 109 * @param array $supporters The content type plugin supporters for each extension 110 * @param string $extension The extension of the file given 111 * @param string $expected The supporter contenttype of the file 112 * 113 * @covers ::load_context_supported_extensions 114 */ 115 public function test_get_extension_supporter_for_admins(array $supporters, string $extension, string $expected) { 116 $this->resetAfterTest(); 117 118 $cb = new contentbank(); 119 120 $systemcontext = context_system::instance(); 121 122 // All contexts allowed for admins. 123 $this->setAdminUser(); 124 $contextsupporters = $cb->load_context_supported_extensions($systemcontext); 125 $this->assertArrayHasKey($extension, $contextsupporters); 126 $this->assertEquals($expected, $contextsupporters[$extension]); 127 } 128 129 /** 130 * Tests for get_extension_supporter() function with user default permissions. 131 * 132 * @dataProvider get_extension_supporters_provider 133 * @param array $supporters The content type plugin supporters for each extension 134 * @param string $extension The extension of the file given 135 * @param string $expected The supporter contenttype of the file 136 * 137 * @covers ::load_context_supported_extensions 138 */ 139 public function test_get_extension_supporter_for_users(array $supporters, string $extension, string $expected) { 140 $this->resetAfterTest(); 141 142 $cb = new contentbank(); 143 $systemcontext = context_system::instance(); 144 145 // Set a user with no permissions. 146 $user = $this->getDataGenerator()->create_user(); 147 $this->setUser($user); 148 149 // Users with no capabilities can't upload content. 150 $contextsupporters = $cb->load_context_supported_extensions($systemcontext); 151 $this->assertEquals([], $contextsupporters); 152 } 153 154 /** 155 * Tests for get_extension_supporter() function with teacher defaul permissions. 156 * 157 * @dataProvider get_extension_supporters_provider 158 * @param array $supporters The content type plugin supporters for each extension 159 * @param string $extension The extension of the file given 160 * @param string $expected The supporter contenttype of the file 161 * 162 * @covers ::load_context_supported_extensions 163 */ 164 public function test_get_extension_supporter_for_teachers(array $supporters, string $extension, string $expected) { 165 $this->resetAfterTest(); 166 167 $cb = new contentbank(); 168 169 $course = $this->getDataGenerator()->create_course(); 170 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher'); 171 $this->setUser($teacher); 172 $coursecontext = context_course::instance($course->id); 173 174 // Teachers has permission in their context to upload supported by H5P content type. 175 $contextsupporters = $cb->load_context_supported_extensions($coursecontext); 176 $this->assertArrayHasKey($extension, $contextsupporters); 177 $this->assertEquals($expected, $contextsupporters[$extension]); 178 } 179 180 /** 181 * Tests for get_extension_supporter() function. 182 * 183 * @dataProvider get_extension_supporters_provider 184 * @param array $supporters The content type plugin supporters for each extension 185 * @param string $extension The extension of the file given 186 * @param string $expected The supporter contenttype of the file 187 * 188 * @covers ::get_extension_supporter 189 */ 190 public function test_get_extension_supporter(array $supporters, string $extension, string $expected) { 191 $this->resetAfterTest(); 192 193 $cb = new contentbank(); 194 $systemcontext = context_system::instance(); 195 $this->setAdminUser(); 196 197 $supporter = $cb->get_extension_supporter($extension, $systemcontext); 198 $this->assertEquals($expected, $supporter); 199 } 200 201 /** 202 * Test the behaviour of search_contents(). 203 * 204 * @dataProvider search_contents_provider 205 * @param string $search String to search. 206 * @param string $where Context where to search. 207 * @param int $expectedresult Expected result. 208 * @param array $contexts List of contexts where to create content. 209 */ 210 public function test_search_contents(?string $search, string $where, int $expectedresult, array $contexts = [], 211 array $contenttypes = null): void { 212 global $DB, $CFG; 213 214 $this->resetAfterTest(); 215 $this->setAdminUser(); 216 217 // Create users. 218 $managerroleid = $DB->get_field('role', 'id', ['shortname' => 'manager']); 219 $manager = $this->getDataGenerator()->create_user(); 220 $this->getDataGenerator()->role_assign($managerroleid, $manager->id); 221 222 // Create a category and a course. 223 $coursecat = $this->getDataGenerator()->create_category(); 224 $course = $this->getDataGenerator()->create_course(); 225 $existingcontexts = []; 226 $existingcontexts['system'] = \context_system::instance(); 227 $existingcontexts['category'] = \context_coursecat::instance($coursecat->id); 228 $existingcontexts['course'] = \context_course::instance($course->id); 229 230 if (empty($where)) { 231 $contextid = 0; 232 } else { 233 $contextid = $existingcontexts[$where]->id; 234 } 235 236 // Add some content to the content bank. 237 $filepath = $CFG->dirroot . '/h5p/tests/fixtures/filltheblanks.h5p'; 238 $generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank'); 239 foreach ($contexts as $context) { 240 $contextinstance = $existingcontexts[$context]; 241 $records = $generator->generate_contentbank_data('contenttype_h5p', 3, 242 $manager->id, $contextinstance, false, $filepath); 243 } 244 245 // Search for some content. 246 $cb = new contentbank(); 247 $contents = $cb->search_contents($search, $contextid, $contenttypes); 248 249 $this->assertCount($expectedresult, $contents); 250 if (!empty($contents) && !empty($search)) { 251 foreach ($contents as $content) { 252 $this->assertStringContainsString($search, $content->get_name()); 253 } 254 } 255 } 256 257 /** 258 * Data provider for test_search_contents(). 259 * 260 * @return array 261 */ 262 public function search_contents_provider(): array { 263 264 return [ 265 'Search all content in all contexts' => [ 266 null, 267 '', 268 9, 269 ['system', 'category', 'course'] 270 ], 271 'Search in all contexts for existing string in all contents' => [ 272 'content', 273 '', 274 9, 275 ['system', 'category', 'course'] 276 ], 277 'Search in all contexts for unexisting string in all contents' => [ 278 'chocolate', 279 '', 280 0, 281 ['system', 'category', 'course'] 282 ], 283 'Search in all contexts for existing string in some contents' => [ 284 '1', 285 '', 286 3, 287 ['system', 'category', 'course'] 288 ], 289 'Search in all contexts for existing string in some contents (create only 1 context)' => [ 290 '1', 291 '', 292 1, 293 ['system'] 294 ], 295 'Search in system context for existing string in all contents' => [ 296 'content', 297 'system', 298 3, 299 ['system', 'category', 'course'] 300 ], 301 'Search in category context for unexisting string in all contents' => [ 302 'chocolate', 303 'category', 304 0, 305 ['system', 'category', 'course'] 306 ], 307 'Search in course context for existing string in some contents' => [ 308 '1', 309 'course', 310 1, 311 ['system', 'category', 'course'] 312 ], 313 'Search in system context' => [ 314 null, 315 'system', 316 3, 317 ['system', 'category', 'course'] 318 ], 319 'Search in course context with existing content' => [ 320 null, 321 'course', 322 3, 323 ['system', 'category', 'course'] 324 ], 325 'Search in course context without existing content' => [ 326 null, 327 'course', 328 0, 329 ['system', 'category'] 330 ], 331 'Search in an empty contentbank' => [ 332 null, 333 '', 334 0, 335 [] 336 ], 337 'Search in a context in an empty contentbank' => [ 338 null, 339 'system', 340 0, 341 [] 342 ], 343 'Search for a string in an empty contentbank' => [ 344 'content', 345 '', 346 0, 347 [] 348 ], 349 'Search with unexisting content-type' => [ 350 null, 351 'course', 352 0, 353 ['system', 'category', 'course'], 354 ['contenttype_unexisting'], 355 ], 356 ]; 357 } 358 359 /** 360 * Test create_content_from_file function. 361 * 362 * @covers ::create_content_from_file 363 */ 364 public function test_create_content_from_file() { 365 global $USER, $CFG; 366 367 $this->resetAfterTest(); 368 $this->setAdminUser(); 369 $systemcontext = \context_system::instance(); 370 $name = 'greeting-card.h5p'; 371 372 // Create a dummy H5P file. 373 $dummyh5p = array( 374 'contextid' => $systemcontext->id, 375 'component' => 'contentbank', 376 'filearea' => 'public', 377 'itemid' => 1, 378 'filepath' => '/', 379 'filename' => $name, 380 'userid' => $USER->id 381 ); 382 $path = $CFG->dirroot . '/h5p/tests/fixtures/' . $name; 383 $dummyh5pfile = \core_h5p\helper::create_fake_stored_file_from_path($path); 384 385 $cb = new contentbank(); 386 $content = $cb->create_content_from_file($systemcontext, $USER->id, $dummyh5pfile); 387 388 $this->assertEquals('contenttype_h5p', $content->get_content_type()); 389 $this->assertInstanceOf('\\contenttype_h5p\\content', $content); 390 $this->assertEquals($name, $content->get_name()); 391 } 392 393 /** 394 * Test the behaviour of delete_contents(). 395 * 396 * @covers ::delete_contents 397 */ 398 public function test_delete_contents() { 399 global $DB; 400 401 $this->resetAfterTest(); 402 $cb = new \core_contentbank\contentbank(); 403 404 // Create a category and two courses. 405 $systemcontext = context_system::instance(); 406 $coursecat = $this->getDataGenerator()->create_category(); 407 $coursecatcontext = context_coursecat::instance($coursecat->id); 408 $course1 = $this->getDataGenerator()->create_course(); 409 $course1context = context_course::instance($course1->id); 410 $course2 = $this->getDataGenerator()->create_course(); 411 $course2context = context_course::instance($course2->id); 412 413 // Add some content to the content bank. 414 $generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank'); 415 $systemcontent = $generator->generate_contentbank_data(null, 3, 0, $systemcontext); 416 $categorycontent = $generator->generate_contentbank_data(null, 3, 0, $coursecatcontext); 417 $course1content = $generator->generate_contentbank_data(null, 3, 0, $course1context); 418 $course2content = $generator->generate_contentbank_data(null, 3, 0, $course2context); 419 420 // Check the content has been created as expected. 421 $this->assertEquals(12, $DB->count_records('contentbank_content')); 422 423 // Check the system content is deleted as expected and the rest of the content is not. 424 $this->assertTrue($cb->delete_contents($systemcontext)); 425 $this->assertEquals(0, $DB->count_records('contentbank_content', ['contextid' => $systemcontext->id])); 426 // And the rest of the context content exists. 427 $this->assertEquals(9, $DB->count_records('contentbank_content')); 428 429 // Check the course category content is deleted as expected and the rest of the content is not. 430 $this->assertTrue($cb->delete_contents($coursecatcontext)); 431 $this->assertEquals(0, $DB->count_records('contentbank_content', ['contextid' => $coursecatcontext->id])); 432 // And the rest of the context content exists. 433 $this->assertEquals(6, $DB->count_records('contentbank_content')); 434 435 // Check the course content is deleted as expected and the rest of the content is not. 436 $this->assertTrue($cb->delete_contents($course1context)); 437 $this->assertEquals(0, $DB->count_records('contentbank_content', ['contextid' => $course1context->id])); 438 // And the rest of the context content exists. 439 $this->assertEquals(3, $DB->count_records('contentbank_content')); 440 } 441 442 /** 443 * Test the behaviour of delete_contents() for empty content bank. 444 * 445 * @covers ::delete_contents 446 */ 447 public function test_delete_contents_for_empty_contentbank() { 448 449 $this->resetAfterTest(); 450 $cb = new \core_contentbank\contentbank(); 451 452 // Create a category and two courses. 453 $systemcontext = \context_system::instance(); 454 $coursecat = $this->getDataGenerator()->create_category(); 455 $coursecatcontext = \context_coursecat::instance($coursecat->id); 456 $course = $this->getDataGenerator()->create_course(); 457 $coursecontext = \context_course::instance($course->id); 458 459 // Check there's no error when trying to delete content from an empty content bank. 460 $this->assertTrue($cb->delete_contents($systemcontext)); 461 $this->assertTrue($cb->delete_contents($coursecatcontext)); 462 $this->assertTrue($cb->delete_contents($coursecontext)); 463 } 464 465 /** 466 * Test the behaviour of move_contents(). 467 * 468 * @covers ::move_contents 469 */ 470 public function test_move_contents() { 471 global $DB; 472 473 $this->resetAfterTest(); 474 $cb = new \core_contentbank\contentbank(); 475 476 // Create a category and two courses. 477 $course1 = $this->getDataGenerator()->create_course(); 478 $course1context = context_course::instance($course1->id); 479 $course2 = $this->getDataGenerator()->create_course(); 480 $course2context = context_course::instance($course2->id); 481 482 // Add some content to the content bank. 483 $generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank'); 484 $course1content = $generator->generate_contentbank_data(null, 3, 0, $course1context); 485 $course2content = $generator->generate_contentbank_data(null, 3, 0, $course2context); 486 487 // Check the content has been created as expected. 488 $this->assertEquals(6, $DB->count_records('contentbank_content')); 489 $this->assertEquals(3, $DB->count_records('contentbank_content', ['contextid' => $course1context->id])); 490 491 // Check the content is moved to another context as expected and the rest of the content is not. 492 $this->assertTrue($cb->move_contents($course1context, $course2context)); 493 $this->assertEquals(6, $DB->count_records('contentbank_content')); 494 $this->assertEquals(0, $DB->count_records('contentbank_content', ['contextid' => $course1context->id])); 495 $this->assertEquals(6, $DB->count_records('contentbank_content', ['contextid' => $course2context->id])); 496 } 497 498 /** 499 * Test the behaviour of move_contents() for empty content bank. 500 * 501 * @covers ::move_contents 502 */ 503 public function test_move_contents_for_empty_contentbank() { 504 505 $this->resetAfterTest(); 506 $cb = new \core_contentbank\contentbank(); 507 508 // Create a category and two courses. 509 $systemcontext = \context_system::instance(); 510 $course = $this->getDataGenerator()->create_course(); 511 $coursecontext = \context_course::instance($course->id); 512 513 // Check there's no error when trying to move content context from an empty content bank. 514 $this->assertTrue($cb->delete_contents($systemcontext, $coursecontext)); 515 } 516 517 /** 518 * Data provider for get_contenttypes_with_capability_feature. 519 * 520 * @return array 521 */ 522 public function get_contenttypes_with_capability_feature_provider(): array { 523 return [ 524 'no-contenttypes_enabled' => [ 525 'contenttypesenabled' => [], 526 'contenttypescanfeature' => [], 527 ], 528 'contenttype_enabled_noeditable' => [ 529 'contenttypesenabled' => ['testable'], 530 'contenttypescanfeature' => [], 531 ], 532 'contenttype_enabled_editable' => [ 533 'contenttypesenabled' => ['testable'], 534 'contenttypescanfeature' => ['testable'], 535 ], 536 'no-contenttype_enabled_editable' => [ 537 'contenttypesenabled' => [], 538 'contenttypescanfeature' => ['testable'], 539 ], 540 ]; 541 } 542 543 /** 544 * Tests for get_contenttypes_with_capability_feature() function. 545 * 546 * @dataProvider get_contenttypes_with_capability_feature_provider 547 * @param array $contenttypesenabled Content types enabled. 548 * @param array $contenttypescanfeature Content types the user has the permission to use the feature. 549 * 550 * @covers ::get_contenttypes_with_capability_feature 551 */ 552 public function test_get_contenttypes_with_capability_feature(array $contenttypesenabled, array $contenttypescanfeature): void { 553 $this->resetAfterTest(); 554 555 $cb = new contentbank(); 556 557 $plugins = []; 558 559 // Content types not enabled where the user has permission to use a feature. 560 if (empty($contenttypesenabled) && !empty($contenttypescanfeature)) { 561 $enabled = false; 562 563 // Mock core_plugin_manager class and the method get_plugins_of_type. 564 $pluginmanager = $this->getMockBuilder(\core_plugin_manager::class) 565 ->disableOriginalConstructor() 566 ->onlyMethods(['get_plugins_of_type']) 567 ->getMock(); 568 569 // Replace protected singletoninstance reference (core_plugin_manager property) with mock object. 570 $ref = new \ReflectionProperty(\core_plugin_manager::class, 'singletoninstance'); 571 $ref->setAccessible(true); 572 $ref->setValue(null, $pluginmanager); 573 574 // Return values of get_plugins_of_type method. 575 foreach ($contenttypescanfeature as $contenttypepluginname) { 576 $contenttypeplugin = new \stdClass(); 577 $contenttypeplugin->name = $contenttypepluginname; 578 $contenttypeplugin->type = 'contenttype'; 579 // Add the feature to the fake content type. 580 $classname = "\\contenttype_$contenttypepluginname\\contenttype"; 581 $classname::$featurestotest = ['test2']; 582 $plugins[] = $contenttypeplugin; 583 } 584 585 // Set expectations and return values. 586 $pluginmanager->expects($this->once()) 587 ->method('get_plugins_of_type') 588 ->with('contenttype') 589 ->willReturn($plugins); 590 } else { 591 $enabled = true; 592 // Get access to private property enabledcontenttypes. 593 $rc = new \ReflectionClass(\core_contentbank\contentbank::class); 594 $rcp = $rc->getProperty('enabledcontenttypes'); 595 $rcp->setAccessible(true); 596 597 foreach ($contenttypesenabled as $contenttypename) { 598 $plugins["\\contenttype_$contenttypename\\contenttype"] = $contenttypename; 599 // Add to the testable contenttype the feature to test. 600 if (in_array($contenttypename, $contenttypescanfeature)) { 601 $classname = "\\contenttype_$contenttypename\\contenttype"; 602 $classname::$featurestotest = ['test2']; 603 } 604 } 605 // Set as enabled content types only those in the test. 606 $rcp->setValue($cb, $plugins); 607 } 608 609 $actual = $cb->get_contenttypes_with_capability_feature('test2', null, $enabled); 610 $this->assertEquals($contenttypescanfeature, array_values($actual)); 611 } 612 613 /** 614 * Test the behaviour of get_content_from_id() 615 * 616 * @covers ::get_content_from_id 617 */ 618 public function test_get_content_from_id() { 619 620 $this->resetAfterTest(); 621 $cb = new \core_contentbank\contentbank(); 622 623 // Create a category and two courses. 624 $systemcontext = context_system::instance(); 625 626 // Add some content to the content bank. 627 $generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank'); 628 $contents = $generator->generate_contentbank_data(null, 3, 0, $systemcontext); 629 $content = reset($contents); 630 631 // Get the content instance form id. 632 $newinstance = $cb->get_content_from_id($content->get_id()); 633 $this->assertEquals($content->get_id(), $newinstance->get_id()); 634 635 // Now produce and exception with an innexistent id. 636 $this->expectException(Exception::class); 637 $cb->get_content_from_id(0); 638 } 639 640 /** 641 * Test the behaviour of is_context_allowed(). 642 * 643 * @covers ::is_context_allowed 644 */ 645 public function test_is_context_allowed(): void { 646 $this->resetAfterTest(); 647 648 $cb = new contentbank(); 649 650 // System context. 651 $this->assertTrue($cb->is_context_allowed(context_system::instance())); 652 653 // User context. 654 $user = $this->getDataGenerator()->create_user(); 655 $this->assertFalse($cb->is_context_allowed(context_user::instance($user->id))); 656 657 // Category context. 658 $category = $this->getDataGenerator()->create_category(); 659 $this->assertTrue($cb->is_context_allowed(context_coursecat::instance($category->id))); 660 661 // Course context. 662 $course = $this->getDataGenerator()->create_course(['category' => $category->id]); 663 $coursecontext = context_course::instance($course->id); 664 $this->assertTrue($cb->is_context_allowed($coursecontext)); 665 666 // Module context. 667 $module = $this->getDataGenerator()->create_module('page', ['course' => $course->id]); 668 $this->assertFalse($cb->is_context_allowed(context_module::instance($module->cmid))); 669 670 // Block context. 671 $block = $this->getDataGenerator()->create_block('online_users', ['parentcontextid' => $coursecontext->id]); 672 $this->assertFalse($cb->is_context_allowed(context_block::instance($block->id))); 673 } 674 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body