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