Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.
   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   * External tests.
  19   *
  20   * @package    tool_dataprivacy
  21   * @copyright  2018 Jun Pataleta
  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  
  28  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  29  
  30  use tool_dataprivacy\api;
  31  use tool_dataprivacy\context_instance;
  32  use tool_dataprivacy\external;
  33  
  34  /**
  35   * External testcase.
  36   *
  37   * @package    tool_dataprivacy
  38   * @copyright  2018 Jun Pataleta
  39   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  40   */
  41  class tool_dataprivacy_external_testcase extends externallib_advanced_testcase {
  42  
  43      /**
  44       * Test for external::approve_data_request() with the user not logged in.
  45       */
  46      public function test_approve_data_request_not_logged_in() {
  47          $this->resetAfterTest();
  48  
  49          $generator = new testing_data_generator();
  50          $requester = $generator->create_user();
  51          $comment = 'sample comment';
  52  
  53          // Test data request creation.
  54          $this->setUser($requester);
  55          $datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
  56  
  57          // Log out the user and set force login to true.
  58          $this->setUser();
  59  
  60          $this->expectException(require_login_exception::class);
  61          external::approve_data_request($datarequest->get('id'));
  62      }
  63  
  64      /**
  65       * Test for external::approve_data_request() with the user not having a DPO role.
  66       */
  67      public function test_approve_data_request_not_dpo() {
  68          $this->resetAfterTest();
  69  
  70          $generator = new testing_data_generator();
  71          $requester = $generator->create_user();
  72          $comment = 'sample comment';
  73  
  74          // Test data request creation.
  75          $this->setUser($requester);
  76          $datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
  77  
  78          // Login as the requester.
  79          $this->setUser($requester);
  80          $this->expectException(required_capability_exception::class);
  81          external::approve_data_request($datarequest->get('id'));
  82      }
  83  
  84      /**
  85       * Test for external::approve_data_request() for request that's not ready for approval
  86       */
  87      public function test_approve_data_request_not_waiting_for_approval() {
  88          $this->resetAfterTest();
  89  
  90          $generator = new testing_data_generator();
  91          $requester = $generator->create_user();
  92          $comment = 'sample comment';
  93  
  94          // Test data request creation.
  95          $this->setUser($requester);
  96          $datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
  97          $datarequest->set('status', api::DATAREQUEST_STATUS_CANCELLED)->save();
  98  
  99          // Admin as DPO. (The default when no one's assigned as a DPO in the site).
 100          $this->setAdminUser();
 101          $this->expectException(moodle_exception::class);
 102          external::approve_data_request($datarequest->get('id'));
 103      }
 104  
 105      /**
 106       * Test for external::approve_data_request()
 107       */
 108      public function test_approve_data_request() {
 109          $this->resetAfterTest();
 110  
 111          $generator = new testing_data_generator();
 112          $requester = $generator->create_user();
 113          $comment = 'sample comment';
 114  
 115          // Test data request creation.
 116          $this->setUser($requester);
 117          $datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 118  
 119          // Admin as DPO. (The default when no one's assigned as a DPO in the site).
 120          $this->setAdminUser();
 121          api::update_request_status($datarequest->get('id'), api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 122          $result = external::approve_data_request($datarequest->get('id'));
 123          $return = (object) external_api::clean_returnvalue(external::approve_data_request_returns(), $result);
 124          $this->assertTrue($return->result);
 125          $this->assertEmpty($return->warnings);
 126      }
 127  
 128      /**
 129       * Test for external::approve_data_request() for a non-existent request ID.
 130       */
 131      public function test_approve_data_request_non_existent() {
 132          $this->resetAfterTest();
 133  
 134          // Admin as DPO. (The default when no one's assigned as a DPO in the site).
 135          $this->setAdminUser();
 136  
 137          $result = external::approve_data_request(1);
 138  
 139          $return = (object) external_api::clean_returnvalue(external::approve_data_request_returns(), $result);
 140          $this->assertFalse($return->result);
 141          $this->assertCount(1, $return->warnings);
 142          $warning = reset($return->warnings);
 143          $this->assertEquals('errorrequestnotfound', $warning['warningcode']);
 144      }
 145  
 146      /**
 147       * Test for external::cancel_data_request() of another user.
 148       */
 149      public function test_cancel_data_request_other_user() {
 150          $this->resetAfterTest();
 151  
 152          $generator = new testing_data_generator();
 153          $requester = $generator->create_user();
 154          $otheruser = $generator->create_user();
 155          $comment = 'sample comment';
 156  
 157          // Test data request creation.
 158          $this->setUser($requester);
 159          $datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 160  
 161          // Login as other user.
 162          $this->setUser($otheruser);
 163  
 164          $result = external::cancel_data_request($datarequest->get('id'));
 165          $return = (object) external_api::clean_returnvalue(external::approve_data_request_returns(), $result);
 166          $this->assertFalse($return->result);
 167          $this->assertCount(1, $return->warnings);
 168          $warning = reset($return->warnings);
 169          $this->assertEquals('errorrequestnotfound', $warning['warningcode']);
 170      }
 171  
 172      /**
 173       * Test cancellation of a request where you are the requester of another user's data.
 174       */
 175      public function test_cancel_data_request_other_user_as_requester() {
 176          $this->resetAfterTest();
 177  
 178          $generator = new testing_data_generator();
 179          $requester = $generator->create_user();
 180          $otheruser = $generator->create_user();
 181          $comment = 'sample comment';
 182  
 183          // Assign requester as otheruser'sparent.
 184          $systemcontext = \context_system::instance();
 185          $parentrole = $generator->create_role();
 186          assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentrole, $systemcontext);
 187          role_assign($parentrole, $requester->id, \context_user::instance($otheruser->id));
 188  
 189          // Test data request creation.
 190          $this->setUser($requester);
 191          $datarequest = api::create_data_request($otheruser->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 192  
 193          $result = external::cancel_data_request($datarequest->get('id'));
 194          $return = (object) external_api::clean_returnvalue(external::approve_data_request_returns(), $result);
 195          $this->assertTrue($return->result);
 196          $this->assertEmpty($return->warnings);
 197      }
 198  
 199      /**
 200       * Test cancellation of a request where you are the requester of another user's data.
 201       */
 202      public function test_cancel_data_request_requester_lost_permissions() {
 203          $this->resetAfterTest();
 204  
 205          $generator = new testing_data_generator();
 206          $requester = $generator->create_user();
 207          $otheruser = $generator->create_user();
 208          $comment = 'sample comment';
 209  
 210          // Assign requester as otheruser'sparent.
 211          $systemcontext = \context_system::instance();
 212          $parentrole = $generator->create_role();
 213          assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentrole, $systemcontext);
 214          role_assign($parentrole, $requester->id, \context_user::instance($otheruser->id));
 215  
 216          $this->setUser($requester);
 217          $datarequest = api::create_data_request($otheruser->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 218  
 219          // Unassign the role.
 220          role_unassign($parentrole, $requester->id, \context_user::instance($otheruser->id)->id);
 221  
 222          // This user can no longer make the request.
 223          $this->expectException(required_capability_exception::class);
 224  
 225          $result = external::cancel_data_request($datarequest->get('id'));
 226      }
 227  
 228      /**
 229       * Test cancellation of a request where you are the requester of another user's data.
 230       */
 231      public function test_cancel_data_request_other_user_as_child() {
 232          $this->resetAfterTest();
 233  
 234          $generator = new testing_data_generator();
 235          $requester = $generator->create_user();
 236          $otheruser = $generator->create_user();
 237          $comment = 'sample comment';
 238  
 239          // Assign requester as otheruser'sparent.
 240          $systemcontext = \context_system::instance();
 241          $parentrole = $generator->create_role();
 242          assign_capability('tool/dataprivacy:makedatarequestsforchildren', CAP_ALLOW, $parentrole, $systemcontext);
 243          role_assign($parentrole, $requester->id, \context_user::instance($otheruser->id));
 244  
 245          // Test data request creation.
 246          $this->setUser($otheruser);
 247          $datarequest = api::create_data_request($otheruser->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 248  
 249          $result = external::cancel_data_request($datarequest->get('id'));
 250          $return = (object) external_api::clean_returnvalue(external::approve_data_request_returns(), $result);
 251          $this->assertTrue($return->result);
 252          $this->assertEmpty($return->warnings);
 253      }
 254  
 255      /**
 256       * Test for external::cancel_data_request()
 257       */
 258      public function test_cancel_data_request() {
 259          $this->resetAfterTest();
 260  
 261          $generator = new testing_data_generator();
 262          $requester = $generator->create_user();
 263          $comment = 'sample comment';
 264  
 265          // Test data request creation.
 266          $this->setUser($requester);
 267          $datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 268  
 269          // Test cancellation.
 270          $this->setUser($requester);
 271          $result = external::cancel_data_request($datarequest->get('id'));
 272  
 273          $return = (object) external_api::clean_returnvalue(external::approve_data_request_returns(), $result);
 274          $this->assertTrue($return->result);
 275          $this->assertEmpty($return->warnings);
 276      }
 277  
 278      /**
 279       * Test contact DPO.
 280       */
 281      public function test_contact_dpo() {
 282          $this->resetAfterTest();
 283  
 284          $generator = new testing_data_generator();
 285          $user = $generator->create_user();
 286  
 287          $this->setUser($user);
 288          $message = 'Hello world!';
 289          $result = external::contact_dpo($message);
 290          $return = (object) external_api::clean_returnvalue(external::contact_dpo_returns(), $result);
 291          $this->assertTrue($return->result);
 292          $this->assertEmpty($return->warnings);
 293      }
 294  
 295      /**
 296       * Test contact DPO with message containing invalid input.
 297       */
 298      public function test_contact_dpo_with_nasty_input() {
 299          $this->resetAfterTest();
 300  
 301          $generator = new testing_data_generator();
 302          $user = $generator->create_user();
 303  
 304          $this->setUser($user);
 305          $this->expectException('invalid_parameter_exception');
 306          external::contact_dpo('de<>\\..scription');
 307      }
 308  
 309      /**
 310       * Test for external::deny_data_request() with the user not logged in.
 311       */
 312      public function test_deny_data_request_not_logged_in() {
 313          $this->resetAfterTest();
 314  
 315          $generator = new testing_data_generator();
 316          $requester = $generator->create_user();
 317          $comment = 'sample comment';
 318  
 319          // Test data request creation.
 320          $this->setUser($requester);
 321          $datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 322  
 323          // Log out.
 324          $this->setUser();
 325          $this->expectException(require_login_exception::class);
 326          external::deny_data_request($datarequest->get('id'));
 327      }
 328  
 329      /**
 330       * Test for external::deny_data_request() with the user not having a DPO role.
 331       */
 332      public function test_deny_data_request_not_dpo() {
 333          $this->resetAfterTest();
 334  
 335          $generator = new testing_data_generator();
 336          $requester = $generator->create_user();
 337          $comment = 'sample comment';
 338  
 339          $this->setUser($requester);
 340          $datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 341  
 342          // Login as the requester.
 343          $this->setUser($requester);
 344          $this->expectException(required_capability_exception::class);
 345          external::deny_data_request($datarequest->get('id'));
 346      }
 347  
 348      /**
 349       * Test for external::deny_data_request() for request that's not ready for approval
 350       */
 351      public function test_deny_data_request_not_waiting_for_approval() {
 352          $this->resetAfterTest();
 353  
 354          $generator = new testing_data_generator();
 355          $requester = $generator->create_user();
 356          $comment = 'sample comment';
 357  
 358          $this->setUser($requester);
 359          $datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 360          $datarequest->set('status', api::DATAREQUEST_STATUS_CANCELLED)->save();
 361  
 362          // Admin as DPO. (The default when no one's assigned as a DPO in the site).
 363          $this->setAdminUser();
 364          $this->expectException(moodle_exception::class);
 365          external::deny_data_request($datarequest->get('id'));
 366      }
 367  
 368      /**
 369       * Test for external::deny_data_request()
 370       */
 371      public function test_deny_data_request() {
 372          $this->resetAfterTest();
 373  
 374          $generator = new testing_data_generator();
 375          $requester = $generator->create_user();
 376          $comment = 'sample comment';
 377  
 378          $this->setUser($requester);
 379          $datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 380  
 381          // Admin as DPO. (The default when no one's assigned as a DPO in the site).
 382          $this->setAdminUser();
 383          api::update_request_status($datarequest->get('id'), api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 384          $result = external::approve_data_request($datarequest->get('id'));
 385          $return = (object) external_api::clean_returnvalue(external::deny_data_request_returns(), $result);
 386          $this->assertTrue($return->result);
 387          $this->assertEmpty($return->warnings);
 388      }
 389  
 390      /**
 391       * Test for external::deny_data_request() for a non-existent request ID.
 392       */
 393      public function test_deny_data_request_non_existent() {
 394          $this->resetAfterTest();
 395  
 396          // Admin as DPO. (The default when no one's assigned as a DPO in the site).
 397          $this->setAdminUser();
 398          $result = external::deny_data_request(1);
 399  
 400          $return = (object) external_api::clean_returnvalue(external::deny_data_request_returns(), $result);
 401          $this->assertFalse($return->result);
 402          $this->assertCount(1, $return->warnings);
 403          $warning = reset($return->warnings);
 404          $this->assertEquals('errorrequestnotfound', $warning['warningcode']);
 405      }
 406  
 407      /**
 408       * Test for external::get_data_request() with the user not logged in.
 409       */
 410      public function test_get_data_request_not_logged_in() {
 411          $this->resetAfterTest();
 412  
 413          $generator = new testing_data_generator();
 414          $requester = $generator->create_user();
 415          $comment = 'sample comment';
 416  
 417          $this->setUser($requester);
 418          $datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 419  
 420          $this->setUser();
 421          $this->expectException(require_login_exception::class);
 422          external::get_data_request($datarequest->get('id'));
 423      }
 424  
 425      /**
 426       * Test for external::get_data_request() with the user not having a DPO role.
 427       */
 428      public function test_get_data_request_not_dpo() {
 429          $this->resetAfterTest();
 430  
 431          $generator = new testing_data_generator();
 432          $requester = $generator->create_user();
 433          $otheruser = $generator->create_user();
 434          $comment = 'sample comment';
 435  
 436          $this->setUser($requester);
 437          $datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 438  
 439          // Login as the otheruser.
 440          $this->setUser($otheruser);
 441          $this->expectException(required_capability_exception::class);
 442          external::get_data_request($datarequest->get('id'));
 443      }
 444  
 445      /**
 446       * Test for external::get_data_request()
 447       */
 448      public function test_get_data_request() {
 449          $this->resetAfterTest();
 450  
 451          $generator = new testing_data_generator();
 452          $requester = $generator->create_user();
 453          $comment = 'sample comment';
 454  
 455          $this->setUser($requester);
 456          $datarequest = api::create_data_request($requester->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
 457  
 458          // Admin as DPO. (The default when no one's assigned as a DPO in the site).
 459          $this->setAdminUser();
 460          $result = external::get_data_request($datarequest->get('id'));
 461  
 462          $return = (object) external_api::clean_returnvalue(external::get_data_request_returns(), $result);
 463          $this->assertEquals(api::DATAREQUEST_TYPE_EXPORT, $return->result['type']);
 464          $this->assertEquals('sample comment', $return->result['comments']);
 465          $this->assertEquals($requester->id, $return->result['userid']);
 466          $this->assertEquals($requester->id, $return->result['requestedby']);
 467          $this->assertEmpty($return->warnings);
 468      }
 469  
 470      /**
 471       * Test for external::get_data_request() for a non-existent request ID.
 472       */
 473      public function test_get_data_request_non_existent() {
 474          $this->resetAfterTest();
 475  
 476          // Admin as DPO. (The default when no one's assigned as a DPO in the site).
 477          $this->setAdminUser();
 478          $this->expectException(dml_missing_record_exception::class);
 479          external::get_data_request(1);
 480      }
 481  
 482      /**
 483       * Test for \tool_dataprivacy\external::set_context_defaults()
 484       * when called by a user that doesn't have the manage registry capability.
 485       */
 486      public function test_set_context_defaults_no_capability() {
 487          $this->resetAfterTest();
 488  
 489          $generator = $this->getDataGenerator();
 490          $user = $generator->create_user();
 491          $this->setUser($user);
 492          $this->expectException(required_capability_exception::class);
 493          external::set_context_defaults(CONTEXT_COURSECAT, context_instance::INHERIT, context_instance::INHERIT, '', false);
 494      }
 495  
 496      /**
 497       * Test for \tool_dataprivacy\external::set_context_defaults().
 498       *
 499       * We're just checking the module context level here to test the WS function.
 500       * More testing is done in \tool_dataprivacy_api_testcase::test_set_context_defaults().
 501       *
 502       * @dataProvider get_options_provider
 503       * @param bool $modulelevel Whether defaults are to be applied on the module context level or for an activity only.
 504       * @param bool $override Whether to override instances.
 505       */
 506      public function test_set_context_defaults($modulelevel, $override) {
 507          $this->resetAfterTest();
 508  
 509          $this->setAdminUser();
 510          $generator = $this->getDataGenerator();
 511  
 512          // Generate course cat, course, block, assignment, forum instances.
 513          $coursecat = $generator->create_category();
 514          $course = $generator->create_course(['category' => $coursecat->id]);
 515          $assign = $generator->create_module('assign', ['course' => $course->id]);
 516          list($course, $assigncm) = get_course_and_cm_from_instance($assign->id, 'assign');
 517          $assigncontext = context_module::instance($assigncm->id);
 518  
 519          // Generate purpose and category.
 520          $category1 = api::create_category((object)['name' => 'Test category 1']);
 521          $category2 = api::create_category((object)['name' => 'Test category 2']);
 522          $purpose1 = api::create_purpose((object)[
 523              'name' => 'Test purpose 1', 'retentionperiod' => 'PT1M', 'lawfulbases' => 'gdpr_art_6_1_a'
 524          ]);
 525          $purpose2 = api::create_purpose((object)[
 526              'name' => 'Test purpose 2', 'retentionperiod' => 'PT1M', 'lawfulbases' => 'gdpr_art_6_1_a'
 527          ]);
 528  
 529          // Set a custom purpose and ID for this assignment instance.
 530          $assignctxinstance = api::set_context_instance((object) [
 531              'contextid' => $assigncontext->id,
 532              'purposeid' => $purpose1->get('id'),
 533              'categoryid' => $category1->get('id'),
 534          ]);
 535  
 536          $modulename = $modulelevel ? 'assign' : '';
 537          $categoryid = $category2->get('id');
 538          $purposeid = $purpose2->get('id');
 539          $result = external::set_context_defaults(CONTEXT_MODULE, $categoryid, $purposeid, $modulename, $override);
 540  
 541          // Extract the result.
 542          $return = external_api::clean_returnvalue(external::set_context_defaults_returns(), $result);
 543          $this->assertTrue($return['result']);
 544  
 545          // Check the assignment context instance.
 546          $instanceexists = context_instance::record_exists($assignctxinstance->get('id'));
 547          if ($override) {
 548              // The custom assign instance should have been deleted.
 549              $this->assertFalse($instanceexists);
 550          } else {
 551              // The custom assign instance should still exist.
 552              $this->assertTrue($instanceexists);
 553          }
 554  
 555          // Check the saved defaults.
 556          list($savedpurpose, $savedcategory) = \tool_dataprivacy\data_registry::get_defaults(CONTEXT_MODULE, $modulename);
 557          $this->assertEquals($categoryid, $savedcategory);
 558          $this->assertEquals($purposeid, $savedpurpose);
 559      }
 560  
 561      /**
 562       * Test for \tool_dataprivacy\external::get_category_options()
 563       * when called by a user that doesn't have the manage registry capability.
 564       */
 565      public function test_get_category_options_no_capability() {
 566          $this->resetAfterTest();
 567  
 568          $user = $this->getDataGenerator()->create_user();
 569          $this->setUser($user);
 570  
 571          $this->expectException(required_capability_exception::class);
 572          external::get_category_options(true, true);
 573      }
 574  
 575      /**
 576       * Data provider for \tool_dataprivacy_external_testcase::test_XX_options().
 577       */
 578      public function get_options_provider() {
 579          return [
 580              [false, false],
 581              [false, true],
 582              [true, false],
 583              [true, true],
 584          ];
 585      }
 586  
 587      /**
 588       * Test for \tool_dataprivacy\external::get_category_options().
 589       *
 590       * @dataProvider get_options_provider
 591       * @param bool $includeinherit Whether "Inherit" would be included to the options.
 592       * @param bool $includenotset Whether "Not set" would be included to the options.
 593       */
 594      public function test_get_category_options($includeinherit, $includenotset) {
 595          $this->resetAfterTest();
 596          $this->setAdminUser();
 597  
 598          // Prepare our expected options.
 599          $expectedoptions = [];
 600          if ($includeinherit) {
 601              $expectedoptions[] = [
 602                  'id' => context_instance::INHERIT,
 603                  'name' => get_string('inherit', 'tool_dataprivacy'),
 604              ];
 605          }
 606  
 607          if ($includenotset) {
 608              $expectedoptions[] = [
 609                  'id' => context_instance::NOTSET,
 610                  'name' => get_string('notset', 'tool_dataprivacy'),
 611              ];
 612          }
 613  
 614          for ($i = 1; $i <= 3; $i++) {
 615              $category = api::create_category((object)['name' => 'Category ' . $i]);
 616              $expectedoptions[] = [
 617                  'id' => $category->get('id'),
 618                  'name' => $category->get('name'),
 619              ];
 620          }
 621  
 622          // Call the WS function.
 623          $result = external::get_category_options($includeinherit, $includenotset);
 624  
 625          // Extract the options.
 626          $return = (object) external_api::clean_returnvalue(external::get_category_options_returns(), $result);
 627          $options = $return->options;
 628  
 629          // Make sure everything checks out.
 630          $this->assertCount(count($expectedoptions), $options);
 631          foreach ($options as $option) {
 632              $this->assertContains($option, $expectedoptions);
 633          }
 634      }
 635  
 636      /**
 637       * Test for \tool_dataprivacy\external::get_purpose_options()
 638       * when called by a user that doesn't have the manage registry capability.
 639       */
 640      public function test_get_purpose_options_no_capability() {
 641          $this->resetAfterTest();
 642          $generator = $this->getDataGenerator();
 643          $user = $generator->create_user();
 644          $this->setUser($user);
 645          $this->expectException(required_capability_exception::class);
 646          external::get_category_options(true, true);
 647      }
 648  
 649      /**
 650       * Test for \tool_dataprivacy\external::get_purpose_options().
 651       *
 652       * @dataProvider get_options_provider
 653       * @param bool $includeinherit Whether "Inherit" would be included to the options.
 654       * @param bool $includenotset Whether "Not set" would be included to the options.
 655       */
 656      public function test_get_purpose_options($includeinherit, $includenotset) {
 657          $this->resetAfterTest();
 658          $this->setAdminUser();
 659  
 660          // Prepare our expected options.
 661          $expectedoptions = [];
 662          if ($includeinherit) {
 663              $expectedoptions[] = [
 664                  'id' => context_instance::INHERIT,
 665                  'name' => get_string('inherit', 'tool_dataprivacy'),
 666              ];
 667          }
 668  
 669          if ($includenotset) {
 670              $expectedoptions[] = [
 671                  'id' => context_instance::NOTSET,
 672                  'name' => get_string('notset', 'tool_dataprivacy'),
 673              ];
 674          }
 675  
 676          for ($i = 1; $i <= 3; $i++) {
 677              $purpose = api::create_purpose((object)[
 678                  'name' => 'Purpose ' . $i, 'retentionperiod' => 'PT1M', 'lawfulbases' => 'gdpr_art_6_1_a'
 679              ]);
 680              $expectedoptions[] = [
 681                  'id' => $purpose->get('id'),
 682                  'name' => $purpose->get('name'),
 683              ];
 684          }
 685  
 686          // Call the WS function.
 687          $result = external::get_purpose_options($includeinherit, $includenotset);
 688  
 689          // Extract the options.
 690          $return = (object) external_api::clean_returnvalue(external::get_purpose_options_returns(), $result);
 691          $options = $return->options;
 692  
 693          // Make sure everything checks out.
 694          $this->assertCount(count($expectedoptions), $options);
 695          foreach ($options as $option) {
 696              $this->assertContains($option, $expectedoptions);
 697          }
 698      }
 699  
 700      /**
 701       * Data provider for \tool_dataprivacy_external_testcase::get_activity_options().
 702       */
 703      public function get_activity_options_provider() {
 704          return [
 705              [false, false, true],
 706              [false, true, true],
 707              [true, false, true],
 708              [true, true, true],
 709              [false, false, false],
 710              [false, true, false],
 711              [true, false, false],
 712              [true, true, false],
 713          ];
 714      }
 715  
 716      /**
 717       * Test for \tool_dataprivacy\external::get_activity_options().
 718       *
 719       * @dataProvider get_activity_options_provider
 720       * @param bool $inheritcategory Whether the category would be set to "Inherit".
 721       * @param bool $inheritpurpose Whether the purpose would be set to "Inherit".
 722       * @param bool $nodefaults Whether to fetch only activities that don't have defaults.
 723       */
 724      public function test_get_activity_options($inheritcategory, $inheritpurpose, $nodefaults) {
 725          $this->resetAfterTest();
 726          $this->setAdminUser();
 727  
 728          $category = api::create_category((object)['name' => 'Test category']);
 729          $purpose = api::create_purpose((object)[
 730              'name' => 'Test purpose ', 'retentionperiod' => 'PT1M', 'lawfulbases' => 'gdpr_art_6_1_a'
 731          ]);
 732          $categoryid = $category->get('id');
 733          $purposeid = $purpose->get('id');
 734  
 735          if ($inheritcategory) {
 736              $categoryid = context_instance::INHERIT;
 737          }
 738          if ($inheritpurpose) {
 739              $purposeid = context_instance::INHERIT;
 740          }
 741  
 742          // Set the context default for the assignment module.
 743          api::set_context_defaults(CONTEXT_MODULE, $categoryid, $purposeid, 'assign');
 744  
 745          // Call the WS function.
 746          $result = external::get_activity_options($nodefaults);
 747  
 748          // Extract the options.
 749          $return = (object) external_api::clean_returnvalue(external::get_activity_options_returns(), $result);
 750          $options = $return->options;
 751  
 752          // Make sure the options list is not empty.
 753          $this->assertNotEmpty($options);
 754  
 755          $pluginwithdefaults = [
 756              'name' => 'assign',
 757              'displayname' => get_string('pluginname', 'assign')
 758          ];
 759  
 760          // If we don't want plugins with defaults to be listed or if both of the category and purpose are set to inherit,
 761          // the assign module should be listed.
 762          if (!$nodefaults || ($inheritcategory && $inheritpurpose)) {
 763              $this->assertContains($pluginwithdefaults, $options);
 764          } else {
 765              $this->assertNotContains($pluginwithdefaults, $options);
 766          }
 767      }
 768  
 769      /**
 770       * Test for external::bulk_approve_data_requests().
 771       */
 772      public function test_bulk_approve_data_requests() {
 773          $this->resetAfterTest();
 774  
 775          // Create delete data requests.
 776          $requester1 = $this->getDataGenerator()->create_user();
 777          $this->setUser($requester1->id);
 778          $datarequest1 = api::create_data_request($requester1->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
 779          $requestid1 = $datarequest1->get('id');
 780  
 781          $requester2 = $this->getDataGenerator()->create_user();
 782          $this->setUser($requester2->id);
 783          $datarequest2 = api::create_data_request($requester2->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
 784          $requestid2 = $datarequest2->get('id');
 785  
 786          // Approve the requests.
 787          $this->setAdminUser();
 788          api::update_request_status($requestid1, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 789          api::update_request_status($requestid2, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 790          $result = external::bulk_approve_data_requests([$requestid1, $requestid2]);
 791  
 792          $return = (object) external_api::clean_returnvalue(external::bulk_approve_data_requests_returns(), $result);
 793          $this->assertTrue($return->result);
 794          $this->assertEmpty($return->warnings);
 795      }
 796  
 797      /**
 798       * Test for external::bulk_approve_data_requests() for a non-existent request ID.
 799       */
 800      public function test_bulk_approve_data_requests_non_existent() {
 801          $this->resetAfterTest();
 802  
 803          $this->setAdminUser();
 804  
 805          $result = external::bulk_approve_data_requests([42]);
 806  
 807          $return = (object) external_api::clean_returnvalue(external::bulk_approve_data_requests_returns(), $result);
 808          $this->assertFalse($return->result);
 809          $this->assertCount(1, $return->warnings);
 810          $warning = reset($return->warnings);
 811          $this->assertEquals('errorrequestnotfound', $warning['warningcode']);
 812          $this->assertEquals(42, $warning['item']);
 813      }
 814  
 815      /**
 816       * Test for external::bulk_deny_data_requests() for a user without permission to deny requests.
 817       */
 818      public function test_bulk_approve_data_requests_no_permission() {
 819          $this->resetAfterTest();
 820  
 821          // Create delete data requests.
 822          $requester1 = $this->getDataGenerator()->create_user();
 823          $this->setUser($requester1->id);
 824          $datarequest1 = api::create_data_request($requester1->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
 825          $requestid1 = $datarequest1->get('id');
 826  
 827          $requester2 = $this->getDataGenerator()->create_user();
 828          $this->setUser($requester2->id);
 829          $datarequest2 = api::create_data_request($requester2->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
 830          $requestid2 = $datarequest2->get('id');
 831  
 832          $this->setAdminUser();
 833          api::update_request_status($requestid1, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 834          api::update_request_status($requestid2, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 835  
 836          // Approve the requests.
 837          $uut = $this->getDataGenerator()->create_user();
 838          $this->setUser($uut);
 839  
 840          $this->expectException(required_capability_exception::class);
 841          $result = external::bulk_approve_data_requests([$requestid1, $requestid2]);
 842      }
 843  
 844      /**
 845       * Test for external::bulk_deny_data_requests() for a user without permission to deny requests.
 846       */
 847      public function test_bulk_approve_data_requests_own_request() {
 848          $this->resetAfterTest();
 849  
 850          // Create delete data requests.
 851          $requester1 = $this->getDataGenerator()->create_user();
 852          $this->setUser($requester1->id);
 853          $datarequest1 = api::create_data_request($requester1->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
 854          $requestid1 = $datarequest1->get('id');
 855  
 856          $requester2 = $this->getDataGenerator()->create_user();
 857          $this->setUser($requester2->id);
 858          $datarequest2 = api::create_data_request($requester2->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
 859          $requestid2 = $datarequest2->get('id');
 860  
 861          $this->setAdminUser();
 862          api::update_request_status($requestid1, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 863          api::update_request_status($requestid2, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 864  
 865          // Deny the requests.
 866          $this->setUser($requester1);
 867  
 868          $this->expectException(required_capability_exception::class);
 869          $result = external::bulk_approve_data_requests([$requestid1]);
 870      }
 871  
 872      /**
 873       * Test for external::bulk_deny_data_requests().
 874       */
 875      public function test_bulk_deny_data_requests() {
 876          $this->resetAfterTest();
 877  
 878          // Create delete data requests.
 879          $requester1 = $this->getDataGenerator()->create_user();
 880          $this->setUser($requester1->id);
 881          $datarequest1 = api::create_data_request($requester1->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
 882          $requestid1 = $datarequest1->get('id');
 883  
 884          $requester2 = $this->getDataGenerator()->create_user();
 885          $this->setUser($requester2->id);
 886          $datarequest2 = api::create_data_request($requester2->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
 887          $requestid2 = $datarequest2->get('id');
 888  
 889          // Deny the requests.
 890          $this->setAdminUser();
 891          api::update_request_status($requestid1, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 892          api::update_request_status($requestid2, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 893          $result = external::bulk_deny_data_requests([$requestid1, $requestid2]);
 894  
 895          $return = (object) external_api::clean_returnvalue(external::bulk_approve_data_requests_returns(), $result);
 896          $this->assertTrue($return->result);
 897          $this->assertEmpty($return->warnings);
 898      }
 899  
 900      /**
 901       * Test for external::bulk_deny_data_requests() for a non-existent request ID.
 902       */
 903      public function test_bulk_deny_data_requests_non_existent() {
 904          $this->resetAfterTest();
 905  
 906          $this->setAdminUser();
 907          $result = external::bulk_deny_data_requests([42]);
 908          $return = (object) external_api::clean_returnvalue(external::bulk_approve_data_requests_returns(), $result);
 909  
 910          $this->assertFalse($return->result);
 911          $this->assertCount(1, $return->warnings);
 912          $warning = reset($return->warnings);
 913          $this->assertEquals('errorrequestnotfound', $warning['warningcode']);
 914          $this->assertEquals(42, $warning['item']);
 915      }
 916  
 917      /**
 918       * Test for external::bulk_deny_data_requests() for a user without permission to deny requests.
 919       */
 920      public function test_bulk_deny_data_requests_no_permission() {
 921          $this->resetAfterTest();
 922  
 923          // Create delete data requests.
 924          $requester1 = $this->getDataGenerator()->create_user();
 925          $this->setUser($requester1->id);
 926          $datarequest1 = api::create_data_request($requester1->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
 927          $requestid1 = $datarequest1->get('id');
 928  
 929          $requester2 = $this->getDataGenerator()->create_user();
 930          $this->setUser($requester2->id);
 931          $datarequest2 = api::create_data_request($requester2->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
 932          $requestid2 = $datarequest2->get('id');
 933  
 934          $this->setAdminUser();
 935          api::update_request_status($requestid1, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 936          api::update_request_status($requestid2, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 937  
 938          // Deny the requests.
 939          $uut = $this->getDataGenerator()->create_user();
 940          $this->setUser($uut);
 941  
 942          $this->expectException(required_capability_exception::class);
 943          $result = external::bulk_deny_data_requests([$requestid1, $requestid2]);
 944      }
 945  
 946      /**
 947       * Test for external::bulk_deny_data_requests() for a user cannot approve their own request.
 948       */
 949      public function test_bulk_deny_data_requests_own_request() {
 950          $this->resetAfterTest();
 951  
 952          // Create delete data requests.
 953          $requester1 = $this->getDataGenerator()->create_user();
 954          $this->setUser($requester1->id);
 955          $datarequest1 = api::create_data_request($requester1->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
 956          $requestid1 = $datarequest1->get('id');
 957  
 958          $requester2 = $this->getDataGenerator()->create_user();
 959          $this->setUser($requester2->id);
 960          $datarequest2 = api::create_data_request($requester2->id, api::DATAREQUEST_TYPE_DELETE, 'Example comment');
 961          $requestid2 = $datarequest2->get('id');
 962  
 963          $this->setAdminUser();
 964          api::update_request_status($requestid1, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 965          api::update_request_status($requestid2, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
 966  
 967          // Deny the requests.
 968          $this->setUser($requester1);
 969  
 970          $this->expectException(required_capability_exception::class);
 971          $result = external::bulk_deny_data_requests([$requestid1]);
 972      }
 973  
 974      /**
 975       * Test for external::get_users(), case search using non-identity field without
 976       * facing any permission problem.
 977       *
 978       * @throws coding_exception
 979       * @throws dml_exception
 980       * @throws invalid_parameter_exception
 981       * @throws required_capability_exception
 982       * @throws restricted_context_exception
 983       */
 984      public function test_get_users_using_using_non_identity() {
 985          $this->resetAfterTest();
 986          $context = context_system::instance();
 987          $requester = $this->getDataGenerator()->create_user();
 988          $role = $this->getDataGenerator()->create_role();
 989          role_assign($role, $requester->id, $context);
 990          assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $role, $context);
 991          $this->setUser($requester);
 992  
 993          $this->getDataGenerator()->create_user([
 994              'firstname' => 'First Student'
 995          ]);
 996          $student2 = $this->getDataGenerator()->create_user([
 997              'firstname' => 'Second Student'
 998          ]);
 999  
1000          $results = external::get_users('Second');
1001          $this->assertCount(1, $results);
1002          $this->assertEquals((object)[
1003              'id' => $student2->id,
1004              'fullname' => fullname($student2),
1005              'extrafields' => []
1006          ], $results[$student2->id]);
1007      }
1008  
1009      /**
1010       * Test for external::get_users(), case search using identity field but
1011       * don't have "moodle/site:viewuseridentity" permission.
1012       *
1013       * @throws coding_exception
1014       * @throws dml_exception
1015       * @throws invalid_parameter_exception
1016       * @throws required_capability_exception
1017       * @throws restricted_context_exception
1018       */
1019      public function test_get_users_using_identity_without_permission() {
1020          global $CFG;
1021  
1022          $this->resetAfterTest();
1023          $CFG->showuseridentity = 'institution';
1024  
1025          // Create requester user and assign correct capability.
1026          $context = context_system::instance();
1027          $requester = $this->getDataGenerator()->create_user();
1028          $role = $this->getDataGenerator()->create_role();
1029          role_assign($role, $requester->id, $context);
1030          assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $role, $context);
1031          $this->setUser($requester);
1032  
1033          $this->getDataGenerator()->create_user([
1034              'institution' => 'University1'
1035          ]);
1036  
1037          $results = external::get_users('University1');
1038          $this->assertEmpty($results);
1039      }
1040  
1041      /**
1042       * Test for external::get_users(), case search using disabled identity field
1043       * even they have "moodle/site:viewuseridentity" permission.
1044       *
1045       * @throws coding_exception
1046       * @throws dml_exception
1047       * @throws invalid_parameter_exception
1048       * @throws required_capability_exception
1049       * @throws restricted_context_exception
1050       */
1051      public function test_get_users_using_field_not_in_identity() {
1052          $this->resetAfterTest();
1053  
1054          $context = context_system::instance();
1055          $requester = $this->getDataGenerator()->create_user();
1056          $role = $this->getDataGenerator()->create_role();
1057          role_assign($role, $requester->id, $context);
1058          assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $role, $context);
1059          assign_capability('moodle/site:viewuseridentity', CAP_ALLOW, $role, $context);
1060          $this->setUser($requester);
1061  
1062          $this->getDataGenerator()->create_user([
1063              'institution' => 'University1'
1064          ]);
1065  
1066          $results = external::get_users('University1');
1067          $this->assertEmpty($results);
1068      }
1069  
1070      /**
1071       * Test for external::get_users(), case search using enabled identity field
1072       * with "moodle/site:viewuseridentity" permission.
1073       *
1074       * @throws coding_exception
1075       * @throws dml_exception
1076       * @throws invalid_parameter_exception
1077       * @throws required_capability_exception
1078       * @throws restricted_context_exception
1079       */
1080      public function test_get_users() {
1081          global $CFG;
1082          $this->resetAfterTest();
1083          $CFG->showuseridentity = 'institution';
1084          $context = context_system::instance();
1085          $requester = $this->getDataGenerator()->create_user();
1086          $role = $this->getDataGenerator()->create_role();
1087          role_assign($role, $requester->id, $context);
1088          assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $role, $context);
1089          assign_capability('moodle/site:viewuseridentity', CAP_ALLOW, $role, $context);
1090          $this->setUser($requester);
1091  
1092          $student1 = $this->getDataGenerator()->create_user([
1093              'institution' => 'University1'
1094          ]);
1095          $this->getDataGenerator()->create_user([
1096              'institution' => 'University2'
1097          ]);
1098  
1099          $results = external::get_users('University1');
1100          $this->assertCount(1, $results);
1101          $this->assertEquals((object)[
1102              'id' => $student1->id,
1103              'fullname' => fullname($student1),
1104              'extrafields' => [
1105                  0 => (object)[
1106                      'name' => 'institution',
1107                      'value' => 'University1'
1108                  ]
1109              ]
1110          ], $results[$student1->id]);
1111      }
1112  }