Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]
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 core_contentbank_testcase 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; 210 211 $this->resetAfterTest(); 212 213 // Create users. 214 $managerroleid = $DB->get_field('role', 'id', ['shortname' => 'manager']); 215 $manager = $this->getDataGenerator()->create_user(); 216 $this->getDataGenerator()->role_assign($managerroleid, $manager->id); 217 218 // Create a category and a course. 219 $coursecat = $this->getDataGenerator()->create_category(); 220 $course = $this->getDataGenerator()->create_course(); 221 $existingcontexts = []; 222 $existingcontexts['system'] = \context_system::instance(); 223 $existingcontexts['category'] = \context_coursecat::instance($coursecat->id); 224 $existingcontexts['course'] = \context_course::instance($course->id); 225 226 if (empty($where)) { 227 $contextid = 0; 228 } else { 229 $contextid = $existingcontexts[$where]->id; 230 } 231 232 // Add some content to the content bank. 233 $generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank'); 234 foreach ($contexts as $context) { 235 $contextinstance = $existingcontexts[$context]; 236 $records = $generator->generate_contentbank_data('contenttype_h5p', 3, 237 $manager->id, $contextinstance, false); 238 } 239 240 // Search for some content. 241 $cb = new contentbank(); 242 $contents = $cb->search_contents($search, $contextid, $contenttypes); 243 244 $this->assertCount($expectedresult, $contents); 245 if (!empty($contents) && !empty($search)) { 246 foreach ($contents as $content) { 247 $this->assertStringContainsString($search, $content->get_name()); 248 } 249 } 250 } 251 252 /** 253 * Data provider for test_search_contents(). 254 * 255 * @return array 256 */ 257 public function search_contents_provider(): array { 258 259 return [ 260 'Search all content in all contexts' => [ 261 null, 262 '', 263 9, 264 ['system', 'category', 'course'] 265 ], 266 'Search in all contexts for existing string in all contents' => [ 267 'content', 268 '', 269 9, 270 ['system', 'category', 'course'] 271 ], 272 'Search in all contexts for unexisting string in all contents' => [ 273 'chocolate', 274 '', 275 0, 276 ['system', 'category', 'course'] 277 ], 278 'Search in all contexts for existing string in some contents' => [ 279 '1', 280 '', 281 3, 282 ['system', 'category', 'course'] 283 ], 284 'Search in all contexts for existing string in some contents (create only 1 context)' => [ 285 '1', 286 '', 287 1, 288 ['system'] 289 ], 290 'Search in system context for existing string in all contents' => [ 291 'content', 292 'system', 293 3, 294 ['system', 'category', 'course'] 295 ], 296 'Search in category context for unexisting string in all contents' => [ 297 'chocolate', 298 'category', 299 0, 300 ['system', 'category', 'course'] 301 ], 302 'Search in course context for existing string in some contents' => [ 303 '1', 304 'course', 305 1, 306 ['system', 'category', 'course'] 307 ], 308 'Search in system context' => [ 309 null, 310 'system', 311 3, 312 ['system', 'category', 'course'] 313 ], 314 'Search in course context with existing content' => [ 315 null, 316 'course', 317 3, 318 ['system', 'category', 'course'] 319 ], 320 'Search in course context without existing content' => [ 321 null, 322 'course', 323 0, 324 ['system', 'category'] 325 ], 326 'Search in an empty contentbank' => [ 327 null, 328 '', 329 0, 330 [] 331 ], 332 'Search in a context in an empty contentbank' => [ 333 null, 334 'system', 335 0, 336 [] 337 ], 338 'Search for a string in an empty contentbank' => [ 339 'content', 340 '', 341 0, 342 [] 343 ], 344 'Search with unexisting content-type' => [ 345 null, 346 'course', 347 0, 348 ['system', 'category', 'course'], 349 ['contenttype_unexisting'], 350 ], 351 ]; 352 } 353 354 /** 355 * Test create_content_from_file function. 356 * 357 * @covers ::create_content_from_file 358 */ 359 public function test_create_content_from_file() { 360 global $USER; 361 362 $this->resetAfterTest(); 363 $this->setAdminUser(); 364 $systemcontext = \context_system::instance(); 365 $name = 'dummy_h5p.h5p'; 366 367 // Create a dummy H5P file. 368 $dummyh5p = array( 369 'contextid' => $systemcontext->id, 370 'component' => 'contentbank', 371 'filearea' => 'public', 372 'itemid' => 1, 373 'filepath' => '/', 374 'filename' => $name, 375 'userid' => $USER->id 376 ); 377 $fs = get_file_storage(); 378 $dummyh5pfile = $fs->create_file_from_string($dummyh5p, 'Dummy H5Pcontent'); 379 380 $cb = new contentbank(); 381 $content = $cb->create_content_from_file($systemcontext, $USER->id, $dummyh5pfile); 382 383 $this->assertEquals('contenttype_h5p', $content->get_content_type()); 384 $this->assertInstanceOf('\\contenttype_h5p\\content', $content); 385 $this->assertEquals($name, $content->get_name()); 386 } 387 388 /** 389 * Test the behaviour of delete_contents(). 390 * 391 * @covers ::delete_contents 392 */ 393 public function test_delete_contents() { 394 global $DB; 395 396 $this->resetAfterTest(); 397 $cb = new \core_contentbank\contentbank(); 398 399 // Create a category and two courses. 400 $systemcontext = context_system::instance(); 401 $coursecat = $this->getDataGenerator()->create_category(); 402 $coursecatcontext = context_coursecat::instance($coursecat->id); 403 $course1 = $this->getDataGenerator()->create_course(); 404 $course1context = context_course::instance($course1->id); 405 $course2 = $this->getDataGenerator()->create_course(); 406 $course2context = context_course::instance($course2->id); 407 408 // Add some content to the content bank. 409 $generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank'); 410 $systemcontent = $generator->generate_contentbank_data(null, 3, 0, $systemcontext); 411 $categorycontent = $generator->generate_contentbank_data(null, 3, 0, $coursecatcontext); 412 $course1content = $generator->generate_contentbank_data(null, 3, 0, $course1context); 413 $course2content = $generator->generate_contentbank_data(null, 3, 0, $course2context); 414 415 // Check the content has been created as expected. 416 $this->assertEquals(12, $DB->count_records('contentbank_content')); 417 418 // Check the system content is deleted as expected and the rest of the content is not. 419 $this->assertTrue($cb->delete_contents($systemcontext)); 420 $this->assertEquals(0, $DB->count_records('contentbank_content', ['contextid' => $systemcontext->id])); 421 // And the rest of the context content exists. 422 $this->assertEquals(9, $DB->count_records('contentbank_content')); 423 424 // Check the course category content is deleted as expected and the rest of the content is not. 425 $this->assertTrue($cb->delete_contents($coursecatcontext)); 426 $this->assertEquals(0, $DB->count_records('contentbank_content', ['contextid' => $coursecatcontext->id])); 427 // And the rest of the context content exists. 428 $this->assertEquals(6, $DB->count_records('contentbank_content')); 429 430 // Check the course content is deleted as expected and the rest of the content is not. 431 $this->assertTrue($cb->delete_contents($course1context)); 432 $this->assertEquals(0, $DB->count_records('contentbank_content', ['contextid' => $course1context->id])); 433 // And the rest of the context content exists. 434 $this->assertEquals(3, $DB->count_records('contentbank_content')); 435 } 436 437 /** 438 * Test the behaviour of delete_contents() for empty content bank. 439 * 440 * @covers ::delete_contents 441 */ 442 public function test_delete_contents_for_empty_contentbank() { 443 444 $this->resetAfterTest(); 445 $cb = new \core_contentbank\contentbank(); 446 447 // Create a category and two courses. 448 $systemcontext = \context_system::instance(); 449 $coursecat = $this->getDataGenerator()->create_category(); 450 $coursecatcontext = \context_coursecat::instance($coursecat->id); 451 $course = $this->getDataGenerator()->create_course(); 452 $coursecontext = \context_course::instance($course->id); 453 454 // Check there's no error when trying to delete content from an empty content bank. 455 $this->assertTrue($cb->delete_contents($systemcontext)); 456 $this->assertTrue($cb->delete_contents($coursecatcontext)); 457 $this->assertTrue($cb->delete_contents($coursecontext)); 458 } 459 460 /** 461 * Test the behaviour of move_contents(). 462 * 463 * @covers ::move_contents 464 */ 465 public function test_move_contents() { 466 global $DB; 467 468 $this->resetAfterTest(); 469 $cb = new \core_contentbank\contentbank(); 470 471 // Create a category and two courses. 472 $course1 = $this->getDataGenerator()->create_course(); 473 $course1context = context_course::instance($course1->id); 474 $course2 = $this->getDataGenerator()->create_course(); 475 $course2context = context_course::instance($course2->id); 476 477 // Add some content to the content bank. 478 $generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank'); 479 $course1content = $generator->generate_contentbank_data(null, 3, 0, $course1context); 480 $course2content = $generator->generate_contentbank_data(null, 3, 0, $course2context); 481 482 // Check the content has been created as expected. 483 $this->assertEquals(6, $DB->count_records('contentbank_content')); 484 $this->assertEquals(3, $DB->count_records('contentbank_content', ['contextid' => $course1context->id])); 485 486 // Check the content is moved to another context as expected and the rest of the content is not. 487 $this->assertTrue($cb->move_contents($course1context, $course2context)); 488 $this->assertEquals(6, $DB->count_records('contentbank_content')); 489 $this->assertEquals(0, $DB->count_records('contentbank_content', ['contextid' => $course1context->id])); 490 $this->assertEquals(6, $DB->count_records('contentbank_content', ['contextid' => $course2context->id])); 491 } 492 493 /** 494 * Test the behaviour of move_contents() for empty content bank. 495 * 496 * @covers ::move_contents 497 */ 498 public function test_move_contents_for_empty_contentbank() { 499 500 $this->resetAfterTest(); 501 $cb = new \core_contentbank\contentbank(); 502 503 // Create a category and two courses. 504 $systemcontext = \context_system::instance(); 505 $course = $this->getDataGenerator()->create_course(); 506 $coursecontext = \context_course::instance($course->id); 507 508 // Check there's no error when trying to move content context from an empty content bank. 509 $this->assertTrue($cb->delete_contents($systemcontext, $coursecontext)); 510 } 511 512 /** 513 * Data provider for get_contenttypes_with_capability_feature. 514 * 515 * @return array 516 */ 517 public function get_contenttypes_with_capability_feature_provider(): array { 518 return [ 519 'no-contenttypes_enabled' => [ 520 'contenttypesenabled' => [], 521 'contenttypescanfeature' => [], 522 ], 523 'contenttype_enabled_noeditable' => [ 524 'contenttypesenabled' => ['testable'], 525 'contenttypescanfeature' => [], 526 ], 527 'contenttype_enabled_editable' => [ 528 'contenttypesenabled' => ['testable'], 529 'contenttypescanfeature' => ['testable'], 530 ], 531 'no-contenttype_enabled_editable' => [ 532 'contenttypesenabled' => [], 533 'contenttypescanfeature' => ['testable'], 534 ], 535 ]; 536 } 537 538 /** 539 * Tests for get_contenttypes_with_capability_feature() function. 540 * 541 * @dataProvider get_contenttypes_with_capability_feature_provider 542 * @param array $contenttypesenabled Content types enabled. 543 * @param array $contenttypescanfeature Content types the user has the permission to use the feature. 544 * 545 * @covers ::get_contenttypes_with_capability_feature 546 */ 547 public function test_get_contenttypes_with_capability_feature(array $contenttypesenabled, array $contenttypescanfeature): void { 548 $this->resetAfterTest(); 549 550 $cb = new contentbank(); 551 552 $plugins = []; 553 554 // Content types not enabled where the user has permission to use a feature. 555 if (empty($contenttypesenabled) && !empty($contenttypescanfeature)) { 556 $enabled = false; 557 558 // Mock core_plugin_manager class and the method get_plugins_of_type. 559 $pluginmanager = $this->getMockBuilder(\core_plugin_manager::class) 560 ->disableOriginalConstructor() 561 ->setMethods(['get_plugins_of_type']) 562 ->getMock(); 563 564 // Replace protected singletoninstance reference (core_plugin_manager property) with mock object. 565 $ref = new \ReflectionProperty(\core_plugin_manager::class, 'singletoninstance'); 566 $ref->setAccessible(true); 567 $ref->setValue(null, $pluginmanager); 568 569 // Return values of get_plugins_of_type method. 570 foreach ($contenttypescanfeature as $contenttypepluginname) { 571 $contenttypeplugin = new \stdClass(); 572 $contenttypeplugin->name = $contenttypepluginname; 573 $contenttypeplugin->type = 'contenttype'; 574 // Add the feature to the fake content type. 575 $classname = "\\contenttype_$contenttypepluginname\\contenttype"; 576 $classname::$featurestotest = ['test2']; 577 $plugins[] = $contenttypeplugin; 578 } 579 580 // Set expectations and return values. 581 $pluginmanager->expects($this->once()) 582 ->method('get_plugins_of_type') 583 ->with('contenttype') 584 ->willReturn($plugins); 585 } else { 586 $enabled = true; 587 // Get access to private property enabledcontenttypes. 588 $rc = new \ReflectionClass(\core_contentbank\contentbank::class); 589 $rcp = $rc->getProperty('enabledcontenttypes'); 590 $rcp->setAccessible(true); 591 592 foreach ($contenttypesenabled as $contenttypename) { 593 $plugins["\\contenttype_$contenttypename\\contenttype"] = $contenttypename; 594 // Add to the testable contenttype the feature to test. 595 if (in_array($contenttypename, $contenttypescanfeature)) { 596 $classname = "\\contenttype_$contenttypename\\contenttype"; 597 $classname::$featurestotest = ['test2']; 598 } 599 } 600 // Set as enabled content types only those in the test. 601 $rcp->setValue($cb, $plugins); 602 } 603 604 $actual = $cb->get_contenttypes_with_capability_feature('test2', null, $enabled); 605 $this->assertEquals($contenttypescanfeature, array_values($actual)); 606 } 607 608 /** 609 * Test the behaviour of get_content_from_id() 610 * 611 * @covers ::get_content_from_id 612 */ 613 public function test_get_content_from_id() { 614 615 $this->resetAfterTest(); 616 $cb = new \core_contentbank\contentbank(); 617 618 // Create a category and two courses. 619 $systemcontext = context_system::instance(); 620 621 // Add some content to the content bank. 622 $generator = $this->getDataGenerator()->get_plugin_generator('core_contentbank'); 623 $contents = $generator->generate_contentbank_data(null, 3, 0, $systemcontext); 624 $content = reset($contents); 625 626 // Get the content instance form id. 627 $newinstance = $cb->get_content_from_id($content->get_id()); 628 $this->assertEquals($content->get_id(), $newinstance->get_id()); 629 630 // Now produce and exception with an innexistent id. 631 $this->expectException(Exception::class); 632 $cb->get_content_from_id(0); 633 } 634 635 /** 636 * Test the behaviour of is_context_allowed(). 637 * 638 * @dataProvider context_provider 639 * @param \Closure $getcontext Get the context to check. 640 * @param bool $expectedresult Expected result. 641 * 642 * @covers ::is_context_allowed 643 */ 644 public function test_is_context_allowed(\Closure $getcontext, bool $expectedresult): void { 645 $this->resetAfterTest(); 646 647 $cb = new contentbank(); 648 $context = $getcontext(); 649 $this->assertEquals($expectedresult, $cb->is_context_allowed($context)); 650 } 651 652 /** 653 * Data provider for test_is_context_allowed(). 654 * 655 * @return array 656 */ 657 public function context_provider(): array { 658 659 return [ 660 'System context' => [ 661 function (): \context { 662 return \context_system::instance(); 663 }, 664 true, 665 ], 666 'User context' => [ 667 function (): \context { 668 $user = $this->getDataGenerator()->create_user(); 669 return \context_user::instance($user->id); 670 }, 671 false, 672 ], 673 'Course category context' => [ 674 function (): \context { 675 $coursecat = $this->getDataGenerator()->create_category(); 676 return \context_coursecat::instance($coursecat->id); 677 }, 678 true, 679 ], 680 'Course context' => [ 681 function (): \context { 682 $course = $this->getDataGenerator()->create_course(); 683 return \context_course::instance($course->id); 684 }, 685 true, 686 ], 687 'Module context' => [ 688 function (): \context { 689 $course = $this->getDataGenerator()->create_course(); 690 $module = $this->getDataGenerator()->create_module('page', ['course' => $course->id]); 691 return \context_module::instance($module->cmid); 692 }, 693 false, 694 ], 695 'Block context' => [ 696 function (): \context { 697 $course = $this->getDataGenerator()->create_course(); 698 $coursecontext = context_course::instance($course->id); 699 $block = $this->getDataGenerator()->create_block('online_users', ['parentcontextid' => $coursecontext->id]); 700 return \context_block::instance($block->id); 701 }, 702 false, 703 ], 704 ]; 705 } 706 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body