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