See Release Notes
Long Term Support Release
Differences Between: [Versions 310 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 namespace core_privacy; 18 19 use core_privacy\local\request\writer; 20 use core_privacy\local\request\approved_contextlist; 21 22 defined('MOODLE_INTERNAL') || die(); 23 global $CFG; 24 require_once($CFG->dirroot . '/privacy/tests/fixtures/mock_null_provider.php'); 25 require_once($CFG->dirroot . '/privacy/tests/fixtures/mock_provider.php'); 26 require_once($CFG->dirroot . '/privacy/tests/fixtures/mock_plugin_subplugin_provider.php'); 27 require_once($CFG->dirroot . '/privacy/tests/fixtures/mock_mod_with_user_data_provider.php'); 28 require_once($CFG->dirroot . '/privacy/tests/fixtures/provider_a.php'); 29 require_once($CFG->dirroot . '/privacy/tests/fixtures/provider_throwing_exception.php'); 30 31 /** 32 * Privacy manager unit tests. 33 * 34 * @copyright 2018 Jake Dallimore <jrhdallimore@gmail.com> 35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 36 * @coversDefaultClass \core_privacy\manager 37 */ 38 class manager_test extends \advanced_testcase { 39 /** 40 * Test tearDown. 41 */ 42 public function tearDown(): void { 43 \core_privacy\local\request\writer::reset(); 44 } 45 46 /** 47 * Helper to spoof the results of the internal function get_components_list, allowing mock components to be tested. 48 * 49 * @param array $componentnames and array of component names to include as valid core components. 50 * @return PHPUnit_Framework_MockObject_MockObject 51 */ 52 protected function get_mock_manager_with_core_components($componentnames) { 53 $mock = $this->getMockBuilder(\core_privacy\manager::class) 54 ->onlyMethods(['get_component_list']) 55 ->getMock(); 56 $mock->expects($this->any()) 57 ->method('get_component_list') 58 ->will($this->returnValue($componentnames)); 59 return $mock; 60 } 61 62 /** 63 * Test collection of metadata for components implementing a metadata provider. 64 * 65 * @covers ::get_metadata_for_components 66 */ 67 public function test_get_metadata_for_components() { 68 // Get a mock manager, in which the core components list is mocked to include all mock plugins. 69 // testcomponent is a core provider, testcomponent2 is a null provider, testcomponent3 is subplugin provider (non core). 70 $mockman = $this->get_mock_manager_with_core_components(['mod_testcomponent', 'mod_testcomponent2', 'mod_testcomponent3']); 71 72 // Core providers and shared providers both implement the metadata provider. 73 $collectionarray = $mockman->get_metadata_for_components(); 74 $this->assertArrayHasKey('mod_testcomponent', $collectionarray); 75 $collection = $collectionarray['mod_testcomponent']; 76 $this->assertInstanceOf(\core_privacy\local\metadata\collection::class, $collection); 77 $this->assertArrayHasKey('mod_testcomponent3', $collectionarray); 78 $collection = $collectionarray['mod_testcomponent3']; 79 $this->assertInstanceOf(\core_privacy\local\metadata\collection::class, $collection); 80 81 // Component which implements just the local\metadata\null_provider. Metadata is not provided. 82 $this->assertArrayNotHasKey('mod_testcomponent2', $collectionarray); 83 } 84 85 /** 86 * Test that get_contexts_for_userid() only returns contextlist collections for core providers. 87 * 88 * @covers ::get_contexts_for_userid 89 */ 90 public function test_get_contexts_for_userid() { 91 // Get a mock manager, in which the core components list is mocked to include all mock plugins. 92 // testcomponent is a core provider, testcomponent2 is a null provider, testcomponent3 is subplugin provider (non core). 93 $mockman = $this->get_mock_manager_with_core_components(['mod_testcomponent', 'mod_testcomponent2', 'mod_testcomponent3']); 94 95 // Get the contextlist_collection. 96 $contextlistcollection = $mockman->get_contexts_for_userid(10); 97 $this->assertInstanceOf(\core_privacy\local\request\contextlist_collection::class, $contextlistcollection); 98 99 ob_flush(); 100 101 // Verify we have a contextlist for the component in the collection. 102 $this->assertInstanceOf(\core_privacy\local\request\contextlist::class, 103 $contextlistcollection->get_contextlist_for_component('mod_testcomponent')); 104 105 // Verify we don't have a contextlist for the shared provider in the collection. 106 $this->assertNull($contextlistcollection->get_contextlist_for_component('mod_testcomponent3')); 107 108 // Verify we don't have a contextlist for the component which does not store user data. 109 $this->assertEmpty($contextlistcollection->get_contextlist_for_component('mod_testcomponent2')); 110 } 111 112 /** 113 * Test verifying the output of component_is_compliant. 114 * 115 * @covers ::component_is_compliant 116 */ 117 public function test_component_is_compliant() { 118 // Get a mock manager, in which the core components list is mocked to include all mock plugins. 119 // testcomponent is a core provider, testcomponent2 is a null provider, testcomponent3 is subplugin provider (non core). 120 $mockman = $this->get_mock_manager_with_core_components(['mod_testcomponent', 'mod_testcomponent2', 'mod_testcomponent3']); 121 122 // A core_provider plugin implementing all required interfaces (local\metadata\provider, local\request\plugin_provider). 123 $this->assertTrue($mockman->component_is_compliant('mod_testcomponent')); 124 125 // A component implementing just the \core_privacy\local\metadata\null_provider is compliant. 126 $this->assertTrue($mockman->component_is_compliant('mod_testcomponent2')); 127 128 // A shared provider plugin implementing all required interfaces (local\metadata\provider, local\request\plugin\subplugin_provider) 129 // is compliant. 130 $this->assertTrue($mockman->component_is_compliant('mod_testcomponent3')); 131 132 // A component implementing none of the providers. 133 $this->assertFalse($mockman->component_is_compliant('tool_thisisnotarealtool123')); 134 } 135 136 /** 137 * Provider for component_is_compliant tests. 138 * 139 * @return array 140 */ 141 public function component_is_compliant_provider() { 142 return [ 143 'An empty subsystem' => [ 144 'core_countries', 145 true, 146 ], 147 'A real subsystem' => [ 148 'core_privacy', 149 true, 150 ], 151 ]; 152 } 153 154 /** 155 * Test verifying the output of component_is_compliant with specified 156 * components. 157 * 158 * @dataProvider component_is_compliant_provider 159 * @param string $component 160 * @param boolean $expected 161 * @covers ::component_is_compliant 162 */ 163 public function test_component_is_compliant_examples($component, $expected) { 164 $manager = new \core_privacy\manager(); 165 166 $this->assertEquals($expected, $manager->component_is_compliant($component)); 167 } 168 169 /** 170 * Test verifying only approved contextlists can be used with the export_user_data method. 171 * 172 * @covers ::export_user_data 173 */ 174 public function test_export_user_data() { 175 // Get a mock manager, in which the core components list is mocked to include all mock plugins. 176 // testcomponent is a core provider, testcomponent2 is a null provider, testcomponent3 is subplugin provider (non core). 177 $mockman = $this->get_mock_manager_with_core_components(['mod_testcomponent', 'mod_testcomponent2', 'mod_testcomponent3', 'mod_testcomponent4']); 178 179 // Get the non-approved contextlists. 180 $contextlistcollection = $mockman->get_contexts_for_userid(10); 181 182 // Create an approved contextlist. 183 $approvedcontextlistcollection = new \core_privacy\local\request\contextlist_collection(10); 184 foreach ($contextlistcollection->get_contextlists() as $contextlist) { 185 $approvedcontextlist = new approved_contextlist(new \stdClass(), $contextlist->get_component(), 186 $contextlist->get_contextids()); 187 $approvedcontextlistcollection->add_contextlist($approvedcontextlist); 188 } 189 // Verify the mocked return from the writer, meaning the manager method exited normally. 190 $this->assertEquals('mock_path', $mockman->export_user_data($approvedcontextlistcollection)); 191 192 // Verify that a user preference was exported for 'mod_testcomponent4'. 193 $prefs = writer::with_context(\context_system::instance())->get_user_preferences('mod_testcomponent4'); 194 $this->assertNotEmpty($prefs); 195 $this->assertNotEmpty($prefs->mykey); 196 $this->assertEquals('myvalue', $prefs->mykey->value); 197 $this->assertEquals('mydescription', $prefs->mykey->description); 198 199 // Verify an exception is thrown if trying to pass in a collection of non-approved_contextlist items. 200 $this->expectException(\moodle_exception::class); 201 $mockman->export_user_data($contextlistcollection); 202 } 203 204 /** 205 * Test verifying only approved contextlists can be used with the delete_data_for_user method. 206 * 207 * @covers ::delete_data_for_user 208 */ 209 public function test_delete_data_for_user() { 210 $this->resetAfterTest(); 211 // Get a mock manager, in which the core components list is mocked to include all mock plugins. 212 // testcomponent is a core provider, testcomponent2 is a null provider, testcomponent3 is subplugin provider (non core). 213 $mockman = $this->get_mock_manager_with_core_components(['mod_testcomponent', 'mod_testcomponent2', 'mod_testcomponent3']); 214 215 // Get the non-approved contextlists. 216 $user = \core_user::get_user_by_username('admin'); 217 $contextlistcollection = $mockman->get_contexts_for_userid($user->id); 218 219 // Create an approved contextlist. 220 $approvedcontextlistcollection = new \core_privacy\local\request\contextlist_collection($user->id); 221 foreach ($contextlistcollection->get_contextlists() as $contextlist) { 222 $approvedcontextlist = new approved_contextlist($user, $contextlist->get_component(), 223 $contextlist->get_contextids()); 224 $approvedcontextlistcollection->add_contextlist($approvedcontextlist); 225 } 226 227 // Verify null, as the method has no return type and exits normally. Mainly checking we don't see any exception. 228 $this->assertNull($mockman->delete_data_for_user($approvedcontextlistcollection)); 229 230 // Verify an exception is thrown if trying to pass in a collection of non-approved_contextlist items. 231 $this->expectException(\moodle_exception::class); 232 $mockman->delete_data_for_user($contextlistcollection); 233 } 234 235 /** 236 * Ensure that all installed plugins can provide metadata. 237 * 238 * This really just checks that all providers can be safely autoloaded. 239 * 240 * @covers ::get_metadata_for_components 241 */ 242 public function test_installed_plugins() { 243 $manager = new \core_privacy\manager(); 244 $metadata = $manager->get_metadata_for_components(); 245 $this->assertNotEmpty($metadata); 246 } 247 248 /** 249 * Test that the reason for the null provider is returned. 250 * 251 * @covers ::get_null_provider_reason 252 */ 253 public function test_get_null_provider_reason() { 254 $manager = new \core_privacy\manager(); 255 // Null providers return the reason string. 256 $this->assertEquals('testcomponent2 null provider reason', $manager->get_null_provider_reason('mod_testcomponent2')); 257 // Throw an exception if the wrong type of provider is given. 258 $this->expectException(\coding_exception::class); 259 $string = $manager->get_null_provider_reason('mod_testcomponent'); 260 } 261 262 /** 263 * Test that manager::plugintype_class_callback() can be executed. 264 * 265 * @covers ::plugintype_class_callback 266 */ 267 public function test_plugintype_class_callback() { 268 \core_privacy\manager::plugintype_class_callback('doesnotexist', 'unusable', 'foo', ['bar']); 269 } 270 271 /** 272 * Test that manager::component_class_callback() can be executed. 273 * 274 * @covers ::component_class_callback 275 */ 276 public function test_component_class_callback() { 277 \core_privacy\manager::component_class_callback('foo_bar', 'unusable', 'foo', ['bar']); 278 } 279 280 /** 281 * Test the manager::is_empty_subsystem function. 282 * 283 * @dataProvider is_empty_subsystem_provider 284 * @param string $component 285 * @param bool $expected 286 * @covers ::is_empty_subsystem 287 */ 288 public function test_is_empty_subsystem($component, $expected) { 289 $this->assertEquals($expected, \core_privacy\manager::is_empty_subsystem($component)); 290 } 291 292 /** 293 * Test cases for the is_empty_subsystem function. 294 * 295 * @return array 296 */ 297 public function is_empty_subsystem_provider() { 298 return [ 299 'A subsystem which has no directory' => [ 300 'core_langconfig', 301 true, 302 ], 303 'A subsystem with a directory' => [ 304 'core_portfolio', 305 false, 306 ], 307 'A plugin' => [ 308 'mod_forum', 309 false, 310 ], 311 'A plugintype' => [ 312 'mod', 313 false, 314 ], 315 'An unprefixed subsystem with no directory' => [ 316 'langconfig', 317 false, 318 ], 319 ]; 320 } 321 322 /** 323 * Test that get_contexts_for_userid() with a failing item. 324 * 325 * @covers ::get_contexts_for_userid 326 */ 327 public function test_get_contexts_for_userid_with_failing() { 328 // Get a mock manager, in which the core components list is mocked to include all mock plugins. 329 // testcomponent is a core provider, testcomponent2 isa null provider, testcomponent3 is subplugin provider (non core). 330 $mockman = $this->get_mock_manager_with_core_components(['mod_component_broken', 'mod_component_a']); 331 332 $observer = $this->getMockBuilder(\core_privacy\manager_observer::class) 333 ->onlyMethods(['handle_component_failure']) 334 ->getMock(); 335 $mockman->set_observer($observer); 336 337 $observer->expects($this->once()) 338 ->method('handle_component_failure') 339 ->with( 340 $this->isInstanceOf(\coding_exception::class), 341 $this->identicalTo('mod_component_broken'), 342 $this->identicalTo(\core_privacy\local\request\core_user_data_provider::class), 343 $this->identicalTo('get_contexts_for_userid'), 344 $this->anything() 345 ); 346 347 // Get the contextlist_collection. 348 $contextlistcollection = $mockman->get_contexts_for_userid(10); 349 $this->assertDebuggingCalled(); 350 $this->assertInstanceOf(\core_privacy\local\request\contextlist_collection::class, $contextlistcollection); 351 $this->assertCount(1, $contextlistcollection); 352 353 // The component which completed shoudl have returned a contextlist. 354 $this->assertInstanceOf(\core_privacy\local\request\contextlist::class, 355 $contextlistcollection->get_contextlist_for_component('mod_component_a')); 356 $this->assertEmpty($contextlistcollection->get_contextlist_for_component('mod_component_broken')); 357 } 358 359 /** 360 * Test that export_user_data() with a failing item. 361 * 362 * @covers ::export_user_data 363 */ 364 public function test_export_user_data_with_failing() { 365 $user = \core_user::get_user_by_username('admin'); 366 $mockman = $this->get_mock_manager_with_core_components(['mod_component_broken', 'mod_component_a']); 367 $context = \context_system::instance(); 368 $contextid = $context->id; 369 370 $observer = $this->getMockBuilder(\core_privacy\manager_observer::class) 371 ->onlyMethods(['handle_component_failure']) 372 ->getMock(); 373 $mockman->set_observer($observer); 374 375 $observer->expects($this->once()) 376 ->method('handle_component_failure') 377 ->with( 378 $this->isInstanceOf(\coding_exception::class), 379 $this->identicalTo('mod_component_broken'), 380 $this->identicalTo(\core_privacy\local\request\core_user_data_provider::class), 381 $this->identicalTo('export_user_data'), 382 $this->anything() 383 ); 384 385 $collection = new \core_privacy\local\request\contextlist_collection(10); 386 $collection->add_contextlist(new approved_contextlist($user, 'mod_component_broken', [$contextid])); 387 $collection->add_contextlist(new approved_contextlist($user, 'mod_component_a', [$contextid])); 388 389 // Get the contextlist_collection. 390 $mockman->export_user_data($collection); 391 $this->assertDebuggingCalled(); 392 } 393 394 /** 395 * Test that delete_data_for_user() with a failing item. 396 * 397 * @covers ::delete_data_for_user 398 */ 399 public function test_delete_data_for_user_with_failing() { 400 $user = \core_user::get_user_by_username('admin'); 401 $mockman = $this->get_mock_manager_with_core_components(['mod_component_broken', 'mod_component_a']); 402 $context = \context_system::instance(); 403 $contextid = $context->id; 404 405 $observer = $this->getMockBuilder(\core_privacy\manager_observer::class) 406 ->onlyMethods(['handle_component_failure']) 407 ->getMock(); 408 $mockman->set_observer($observer); 409 410 $observer->expects($this->once()) 411 ->method('handle_component_failure') 412 ->with( 413 $this->isInstanceOf(\coding_exception::class), 414 $this->identicalTo('mod_component_broken'), 415 $this->identicalTo(\core_privacy\local\request\core_user_data_provider::class), 416 $this->identicalTo('delete_data_for_user'), 417 $this->anything() 418 ); 419 420 $collection = new \core_privacy\local\request\contextlist_collection(10); 421 $collection->add_contextlist(new approved_contextlist($user, 'mod_component_broken', [$contextid])); 422 $collection->add_contextlist(new approved_contextlist($user, 'mod_component_a', [$contextid])); 423 424 // Get the contextlist_collection. 425 $mockman->delete_data_for_user($collection); 426 $this->assertDebuggingCalled(); 427 } 428 429 /** 430 * Test that delete_data_for_all_users_in_context() with a failing item. 431 * 432 * @covers ::delete_data_for_all_users_in_context 433 */ 434 public function test_delete_data_for_all_users_in_context_with_failing() { 435 $user = \core_user::get_user_by_username('admin'); 436 $mockman = $this->get_mock_manager_with_core_components(['mod_component_broken', 'mod_component_a']); 437 $context = \context_system::instance(); 438 439 $observer = $this->getMockBuilder(\core_privacy\manager_observer::class) 440 ->onlyMethods(['handle_component_failure']) 441 ->getMock(); 442 $mockman->set_observer($observer); 443 444 $observer->expects($this->once()) 445 ->method('handle_component_failure') 446 ->with( 447 $this->isInstanceOf(\coding_exception::class), 448 $this->identicalTo('mod_component_broken'), 449 $this->identicalTo(\core_privacy\local\request\core_user_data_provider::class), 450 $this->identicalTo('delete_data_for_all_users_in_context'), 451 $this->anything() 452 ); 453 454 // Get the contextlist_collection. 455 $mockman->delete_data_for_all_users_in_context($context); 456 $this->assertDebuggingCalled(); 457 } 458 459 /** 460 * Test that get_metadata_for_components() with a failing item. 461 * 462 * @covers ::get_metadata_for_components 463 */ 464 public function test_get_metadata_for_components_with_failing() { 465 $user = \core_user::get_user_by_username('admin'); 466 $mockman = $this->get_mock_manager_with_core_components(['mod_component_broken', 'mod_component_a']); 467 $context = \context_system::instance(); 468 469 $observer = $this->getMockBuilder(\core_privacy\manager_observer::class) 470 ->onlyMethods(['handle_component_failure']) 471 ->getMock(); 472 $mockman->set_observer($observer); 473 474 $observer->expects($this->once()) 475 ->method('handle_component_failure') 476 ->with( 477 $this->isInstanceOf(\coding_exception::class), 478 $this->identicalTo('mod_component_broken'), 479 $this->identicalTo(\core_privacy\local\metadata\provider::class), 480 $this->identicalTo('get_metadata'), 481 $this->anything() 482 ); 483 484 // Get the contextlist_collection. 485 $metadata = $mockman->get_metadata_for_components(); 486 $this->assertDebuggingCalled(); 487 488 $this->assertIsArray($metadata); 489 $this->assertCount(1, $metadata); 490 } 491 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body