Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 400 and 402] [Versions 400 and 403]

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