Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is 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   * Privacy provider tests.
  19   *
  20   * @package    mod_lti
  21   * @copyright  2018 Mark Nelson <markn@moodle.com>
  22   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  namespace mod_lti\privacy;
  25  
  26  use core_privacy\local\metadata\collection;
  27  use core_privacy\local\request\approved_contextlist;
  28  use core_privacy\local\request\approved_userlist;
  29  use mod_lti\privacy\provider;
  30  
  31  defined('MOODLE_INTERNAL') || die();
  32  
  33  /**
  34   * Privacy provider tests class.
  35   *
  36   * @package    mod_lti
  37   * @copyright  2018 Mark Nelson <markn@moodle.com>
  38   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class provider_test extends \core_privacy\tests\provider_testcase {
  41  
  42      /**
  43       * Test for provider::get_metadata().
  44       */
  45      public function test_get_metadata() {
  46          $collection = new collection('mod_lti');
  47          $newcollection = provider::get_metadata($collection);
  48          $itemcollection = $newcollection->get_collection();
  49          $this->assertCount(4, $itemcollection);
  50  
  51          $ltiproviderexternal = array_shift($itemcollection);
  52          $this->assertEquals('lti_provider', $ltiproviderexternal->get_name());
  53  
  54          $ltisubmissiontable = array_shift($itemcollection);
  55          $this->assertEquals('lti_submission', $ltisubmissiontable->get_name());
  56  
  57          $ltitoolproxies = array_shift($itemcollection);
  58          $this->assertEquals('lti_tool_proxies', $ltitoolproxies->get_name());
  59  
  60          $ltitypestable = array_shift($itemcollection);
  61          $this->assertEquals('lti_types', $ltitypestable->get_name());
  62  
  63          $privacyfields = $ltisubmissiontable->get_privacy_fields();
  64          $this->assertArrayHasKey('userid', $privacyfields);
  65          $this->assertArrayHasKey('datesubmitted', $privacyfields);
  66          $this->assertArrayHasKey('dateupdated', $privacyfields);
  67          $this->assertArrayHasKey('gradepercent', $privacyfields);
  68          $this->assertArrayHasKey('originalgrade', $privacyfields);
  69          $this->assertEquals('privacy:metadata:lti_submission', $ltisubmissiontable->get_summary());
  70  
  71          $privacyfields = $ltitoolproxies->get_privacy_fields();
  72          $this->assertArrayHasKey('name', $privacyfields);
  73          $this->assertArrayHasKey('createdby', $privacyfields);
  74          $this->assertArrayHasKey('timecreated', $privacyfields);
  75          $this->assertArrayHasKey('timemodified', $privacyfields);
  76          $this->assertEquals('privacy:metadata:lti_tool_proxies', $ltitoolproxies->get_summary());
  77  
  78          $privacyfields = $ltitypestable->get_privacy_fields();
  79          $this->assertArrayHasKey('name', $privacyfields);
  80          $this->assertArrayHasKey('createdby', $privacyfields);
  81          $this->assertArrayHasKey('timecreated', $privacyfields);
  82          $this->assertArrayHasKey('timemodified', $privacyfields);
  83          $this->assertEquals('privacy:metadata:lti_types', $ltitypestable->get_summary());
  84      }
  85  
  86      /**
  87       * Test for provider::get_contexts_for_userid().
  88       */
  89      public function test_get_contexts_for_userid() {
  90          $this->resetAfterTest();
  91  
  92          $course = $this->getDataGenerator()->create_course();
  93  
  94          // The LTI activity the user will have submitted something for.
  95          $lti = $this->getDataGenerator()->create_module('lti', array('course' => $course->id));
  96  
  97          // Another LTI activity that has no user activity.
  98          $this->getDataGenerator()->create_module('lti', array('course' => $course->id));
  99  
 100          // Create a user which will make a submission.
 101          $user = $this->getDataGenerator()->create_user();
 102  
 103          $this->create_lti_submission($lti->id, $user->id);
 104  
 105          // Check the contexts supplied are correct.
 106          $contextlist = provider::get_contexts_for_userid($user->id);
 107          $this->assertCount(2, $contextlist);
 108  
 109          $contextformodule = $contextlist->current();
 110          $cmcontext = \context_module::instance($lti->cmid);
 111          $this->assertEquals($cmcontext->id, $contextformodule->id);
 112  
 113          $contextlist->next();
 114          $contextforsystem = $contextlist->current();
 115          $this->assertEquals(SYSCONTEXTID, $contextforsystem->id);
 116      }
 117  
 118      /**
 119       * Test for provider::test_get_users_in_context()
 120       */
 121      public function test_get_users_in_context() {
 122          $this->resetAfterTest();
 123  
 124          $course = $this->getDataGenerator()->create_course();
 125          $component = 'mod_lti';
 126  
 127          // The LTI activity the user will have submitted something for.
 128          $lti1 = $this->getDataGenerator()->create_module('lti', array('course' => $course->id));
 129  
 130          // Another LTI activity that has no user activity.
 131          $lti2 = $this->getDataGenerator()->create_module('lti', array('course' => $course->id));
 132  
 133          // Create user which will make a submission each.
 134          $user1 = $this->getDataGenerator()->create_user();
 135          $user2 = $this->getDataGenerator()->create_user();
 136  
 137          $this->create_lti_submission($lti1->id, $user1->id);
 138          $this->create_lti_submission($lti1->id, $user2->id);
 139  
 140          $context = \context_module::instance($lti1->cmid);
 141          $userlist = new \core_privacy\local\request\userlist($context, $component);
 142          provider::get_users_in_context($userlist);
 143  
 144          $this->assertCount(2, $userlist);
 145          $expected = [$user1->id, $user2->id];
 146          $actual = $userlist->get_userids();
 147          sort($expected);
 148          sort($actual);
 149  
 150          $this->assertEquals($expected, $actual);
 151  
 152          $context = \context_module::instance($lti2->cmid);
 153          $userlist = new \core_privacy\local\request\userlist($context, $component);
 154          provider::get_users_in_context($userlist);
 155  
 156          $this->assertEmpty($userlist);
 157      }
 158  
 159      /**
 160       * Test for provider::export_user_data().
 161       */
 162      public function test_export_for_context_submissions() {
 163          $this->resetAfterTest();
 164  
 165          $course = $this->getDataGenerator()->create_course();
 166  
 167          $lti = $this->getDataGenerator()->create_module('lti', array('course' => $course->id));
 168  
 169          // Create users which will make submissions.
 170          $user1 = $this->getDataGenerator()->create_user();
 171          $user2 = $this->getDataGenerator()->create_user();
 172  
 173          $this->create_lti_submission($lti->id, $user1->id);
 174          $this->create_lti_submission($lti->id, $user1->id);
 175          $this->create_lti_submission($lti->id, $user2->id);
 176  
 177          // Export all of the data for the context for user 1.
 178          $cmcontext = \context_module::instance($lti->cmid);
 179          $this->export_context_data_for_user($user1->id, $cmcontext, 'mod_lti');
 180          $writer = \core_privacy\local\request\writer::with_context($cmcontext);
 181  
 182          $this->assertTrue($writer->has_any_data());
 183  
 184          $data = $writer->get_data();
 185          $this->assertCount(2, $data->submissions);
 186      }
 187  
 188      /**
 189       * Test for provider::export_user_data().
 190       */
 191      public function test_export_for_context_tool_types() {
 192          $this->resetAfterTest();
 193  
 194          $course1 = $this->getDataGenerator()->create_course();
 195          $course2 = $this->getDataGenerator()->create_course();
 196  
 197          // Create a user which will make a tool type.
 198          $user = $this->getDataGenerator()->create_user();
 199          $this->setUser($user);
 200  
 201          // Create a user that will not make a tool type.
 202          $this->getDataGenerator()->create_user();
 203  
 204          $type = new \stdClass();
 205          $type->baseurl = 'http://moodle.org';
 206          $type->course = $course1->id;
 207          lti_add_type($type, new \stdClass());
 208  
 209          $type = new \stdClass();
 210          $type->baseurl = 'http://moodle.org';
 211          $type->course = $course1->id;
 212          lti_add_type($type, new \stdClass());
 213  
 214          $type = new \stdClass();
 215          $type->baseurl = 'http://moodle.org';
 216          $type->course = $course2->id;
 217          lti_add_type($type, new \stdClass());
 218  
 219          // Export all of the data for the context.
 220          $coursecontext = \context_course::instance($course1->id);
 221          $this->export_context_data_for_user($user->id, $coursecontext, 'mod_lti');
 222          $writer = \core_privacy\local\request\writer::with_context($coursecontext);
 223  
 224          $this->assertTrue($writer->has_any_data());
 225  
 226          $data = $writer->get_data();
 227          $this->assertCount(2, $data->lti_types);
 228  
 229          $coursecontext = \context_course::instance($course2->id);
 230          $this->export_context_data_for_user($user->id, $coursecontext, 'mod_lti');
 231          $writer = \core_privacy\local\request\writer::with_context($coursecontext);
 232  
 233          $this->assertTrue($writer->has_any_data());
 234  
 235          $data = $writer->get_data();
 236          $this->assertCount(1, $data->lti_types);
 237      }
 238  
 239      /**
 240       * Test for provider::export_user_data().
 241       */
 242      public function test_export_for_context_tool_proxies() {
 243          $this->resetAfterTest();
 244  
 245          // Create a user that will not make a tool proxy.
 246          $user = $this->getDataGenerator()->create_user();
 247          $this->setUser($user);
 248  
 249          $toolproxy = new \stdClass();
 250          $toolproxy->createdby = $user;
 251          lti_add_tool_proxy($toolproxy);
 252  
 253          // Export all of the data for the context.
 254          $systemcontext = \context_system::instance();
 255          $this->export_context_data_for_user($user->id, $systemcontext, 'mod_lti');
 256          $writer = \core_privacy\local\request\writer::with_context($systemcontext);
 257  
 258          $this->assertTrue($writer->has_any_data());
 259  
 260          $data = $writer->get_data();
 261          $this->assertCount(1, $data->lti_tool_proxies);
 262      }
 263  
 264      /**
 265       * Test for provider::delete_data_for_all_users_in_context().
 266       */
 267      public function test_delete_data_for_all_users_in_context() {
 268          global $DB;
 269  
 270          $this->resetAfterTest();
 271  
 272          $course = $this->getDataGenerator()->create_course();
 273  
 274          $lti = $this->getDataGenerator()->create_module('lti', array('course' => $course->id));
 275  
 276          // Create users that will make submissions.
 277          $user1 = $this->getDataGenerator()->create_user();
 278          $user2 = $this->getDataGenerator()->create_user();
 279  
 280          $this->create_lti_submission($lti->id, $user1->id);
 281          $this->create_lti_submission($lti->id, $user2->id);
 282  
 283          // Before deletion, we should have 2 responses.
 284          $count = $DB->count_records('lti_submission', ['ltiid' => $lti->id]);
 285          $this->assertEquals(2, $count);
 286  
 287          // Delete data based on context.
 288          $cmcontext = \context_module::instance($lti->cmid);
 289          provider::delete_data_for_all_users_in_context($cmcontext);
 290  
 291          // After deletion, the lti submissions for that lti activity should have been deleted.
 292          $count = $DB->count_records('lti_submission', ['ltiid' => $lti->id]);
 293          $this->assertEquals(0, $count);
 294      }
 295  
 296      /**
 297       * Test for provider::delete_data_for_user().
 298       */
 299      public function test_delete_data_for_user() {
 300          global $DB;
 301  
 302          $this->resetAfterTest();
 303  
 304          $course = $this->getDataGenerator()->create_course();
 305  
 306          $lti = $this->getDataGenerator()->create_module('lti', array('course' => $course->id));
 307  
 308          // Create users that will make submissions.
 309          $user1 = $this->getDataGenerator()->create_user();
 310          $user2 = $this->getDataGenerator()->create_user();
 311  
 312          $this->create_lti_submission($lti->id, $user1->id);
 313          $this->create_lti_submission($lti->id, $user2->id);
 314  
 315          // Before deletion we should have 2 responses.
 316          $count = $DB->count_records('lti_submission', ['ltiid' => $lti->id]);
 317          $this->assertEquals(2, $count);
 318  
 319          $context = \context_module::instance($lti->cmid);
 320          $contextlist = new approved_contextlist($user1, 'lti',
 321              [\context_system::instance()->id, $context->id]);
 322          provider::delete_data_for_user($contextlist);
 323  
 324          // After deletion the lti submission for the first user should have been deleted.
 325          $count = $DB->count_records('lti_submission', ['ltiid' => $lti->id, 'userid' => $user1->id]);
 326          $this->assertEquals(0, $count);
 327  
 328          // Check the submission for the other user is still there.
 329          $ltisubmission = $DB->get_records('lti_submission');
 330          $this->assertCount(1, $ltisubmission);
 331          $lastsubmission = reset($ltisubmission);
 332          $this->assertEquals($user2->id, $lastsubmission->userid);
 333      }
 334  
 335      /**
 336       * Test for provider::delete_data_for_users().
 337       */
 338      public function test_delete_data_for_users() {
 339          global $DB;
 340          $component = 'mod_lti';
 341  
 342          $this->resetAfterTest();
 343  
 344          $course = $this->getDataGenerator()->create_course();
 345  
 346          $lti = $this->getDataGenerator()->create_module('lti', array('course' => $course->id));
 347  
 348          // Create users that will make submissions.
 349          $user1 = $this->getDataGenerator()->create_user();
 350          $user2 = $this->getDataGenerator()->create_user();
 351          $user3 = $this->getDataGenerator()->create_user();
 352  
 353          $this->create_lti_submission($lti->id, $user1->id);
 354          $this->create_lti_submission($lti->id, $user2->id);
 355          $this->create_lti_submission($lti->id, $user3->id);
 356  
 357          // Before deletion we should have 2 responses.
 358          $count = $DB->count_records('lti_submission', ['ltiid' => $lti->id]);
 359          $this->assertEquals(3, $count);
 360  
 361          $context = \context_module::instance($lti->cmid);
 362          $approveduserids = [$user1->id, $user2->id];
 363          $approvedlist = new approved_userlist($context, $component, $approveduserids);
 364          provider::delete_data_for_users($approvedlist);
 365  
 366          // After deletion the lti submission for the first two users should have been deleted.
 367          list($insql, $inparams) = $DB->get_in_or_equal($approveduserids, SQL_PARAMS_NAMED);
 368          $sql = "ltiid = :ltiid AND userid {$insql}";
 369          $params = array_merge($inparams, ['ltiid' => $lti->id]);
 370          $count = $DB->count_records_select('lti_submission', $sql, $params);
 371          $this->assertEquals(0, $count);
 372  
 373          // Check the submission for the third user is still there.
 374          $ltisubmission = $DB->get_records('lti_submission');
 375          $this->assertCount(1, $ltisubmission);
 376          $lastsubmission = reset($ltisubmission);
 377          $this->assertEquals($user3->id, $lastsubmission->userid);
 378      }
 379  
 380      /**
 381       * Mimicks the creation of an LTI submission.
 382       *
 383       * There is no API we can use to insert an LTI submission, so we
 384       * will simply insert directly into the database.
 385       *
 386       * @param int $ltiid
 387       * @param int $userid
 388       */
 389      protected function create_lti_submission(int $ltiid, int $userid) {
 390          global $DB;
 391  
 392          $ltisubmissiondata = [
 393              'ltiid' => $ltiid,
 394              'userid' => $userid,
 395              'datesubmitted' => time(),
 396              'dateupdated' => time(),
 397              'gradepercent' => 65,
 398              'originalgrade' => 70,
 399              'launchid' => 3,
 400              'state' => 1
 401          ];
 402  
 403          $DB->insert_record('lti_submission', $ltisubmissiondata);
 404      }
 405  }