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.
   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace enrol_lti\local\ltiadvantage\repository;
  18  use enrol_lti\local\ltiadvantage\entity\resource_link;
  19  use enrol_lti\local\ltiadvantage\entity\application_registration;
  20  
  21  /**
  22   * Tests for resource_link_repository objects.
  23   *
  24   * @package enrol_lti
  25   * @copyright 2021 Jake Dallimore <jrhdallimore@gmail.com>
  26   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  27   * @coversDefaultClass \enrol_lti\local\ltiadvantage\repository\resource_link_repository
  28   */
  29  class resource_link_repository_test extends \advanced_testcase {
  30      /**
  31       * Helper to generate a new resource_link instance.
  32       *
  33       * @param string $id the id to use for this the resource link.
  34       * @return resource_link the resource_link instance.
  35       */
  36      protected function generate_resource_link($id = 'res-link-1'): resource_link {
  37          $registration = application_registration::create(
  38              'Test',
  39              'a2c94a2c94',
  40              new \moodle_url('http://lms.example.org'),
  41              'clientid_123',
  42              new \moodle_url('https://example.org/authrequesturl'),
  43              new \moodle_url('https://example.org/jwksurl'),
  44              new \moodle_url('https://example.org/accesstokenurl')
  45          );
  46          $registrationrepo = new application_registration_repository();
  47          $createdregistration = $registrationrepo->save($registration);
  48  
  49          $deployment = $createdregistration->add_tool_deployment('Deployment 1', 'DeployID123');
  50          $deploymentrepo = new deployment_repository();
  51          $saveddeployment = $deploymentrepo->save($deployment);
  52  
  53          $contextrepo = new context_repository();
  54          $context = $saveddeployment->add_context(
  55              'CTX123',
  56              ['http://purl.imsglobal.org/vocab/lis/v2/course#CourseSection']
  57          );
  58          $savedcontext = $contextrepo->save($context);
  59  
  60          $resourcelink = $saveddeployment->add_resource_link($id, $savedcontext->get_id());
  61          $resourcelink->add_grade_service(
  62              new \moodle_url('https://lms.example.com/context/24/lineitems'),
  63              new \moodle_url('https://lms.example.com/context/24/lineitem/3'),
  64              [
  65                  'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem',
  66                  'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly',
  67                  'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly',
  68                  'https://purl.imsglobal.org/spec/lti-ags/scope/score'
  69              ]
  70          );
  71          $resourcelink->add_names_and_roles_service(
  72              new \moodle_url('https://lms.example.com/context/24/memberships'),
  73              [1.0, 2.0]
  74          );
  75  
  76          return $resourcelink;
  77      }
  78  
  79      /**
  80       * Helper to assert that all the key elements of two resource_links (i.e. excluding id) are equal.
  81       *
  82       * @param resource_link $expected the resource_link whose values are deemed correct.
  83       * @param resource_link $check the resource_link to check.
  84       */
  85      protected function assert_same_resourcelink_values(resource_link $expected, resource_link $check): void {
  86          $this->assertEquals($expected->get_resourcelinkid(), $check->get_resourcelinkid());
  87          $this->assertEquals($expected->get_deploymentid(), $check->get_deploymentid());
  88          $this->assertEquals($expected->get_contextid(), $check->get_contextid());
  89          $this->assertEquals($expected->get_grade_service(), $check->get_grade_service());
  90          $this->assertEquals($expected->get_names_and_roles_service(), $check->get_names_and_roles_service());
  91      }
  92  
  93      /**
  94       * Helper to assert that all the key elements of a resource_link are present in the DB.
  95       *
  96       * @param resource_link $expected the resource_link whose values are deemed correct.
  97       */
  98      protected function assert_resourcelink_db_values(resource_link $expected) {
  99          global $DB;
 100          $checkrecord = $DB->get_record('enrol_lti_resource_link', ['id' => $expected->get_id()]);
 101          $gradeservice = $expected->get_grade_service();
 102          $this->assertEquals($expected->get_id(), $checkrecord->id);
 103          $this->assertEquals($expected->get_deploymentid(), $checkrecord->ltideploymentid);
 104          $this->assertEquals($expected->get_resourcelinkid(), $checkrecord->resourcelinkid);
 105          $this->assertEquals($expected->get_contextid(), $checkrecord->lticontextid);
 106          $this->assertEquals($gradeservice ? $gradeservice->get_lineitemsurl() : null, $checkrecord->lineitemsservice);
 107          $this->assertEquals($gradeservice ? $gradeservice->get_lineitemurl() : null, $checkrecord->lineitemservice);
 108          $this->assertEquals($gradeservice ? json_encode($gradeservice->get_lineitemscope()) : null,
 109              $checkrecord->lineitemscope);
 110          $this->assertEquals($gradeservice ? $gradeservice->get_resultscope() : null, $checkrecord->resultscope);
 111          $this->assertEquals($gradeservice ? $gradeservice->get_scorescope() : null, $checkrecord->scorescope);
 112          $this->assertNotEmpty($checkrecord->timecreated);
 113          $this->assertNotEmpty($checkrecord->timemodified);
 114      }
 115  
 116      /**
 117       * Tests adding a resource_link to the store.
 118       *
 119       * @covers ::save
 120       */
 121      public function test_save_new() {
 122          $this->resetAfterTest();
 123          $resourcelink = $this->generate_resource_link();
 124          $repository = new resource_link_repository();
 125          $savedresourcelink = $repository->save($resourcelink);
 126  
 127          $this->assertIsInt($savedresourcelink->get_id());
 128          $this->assert_same_resourcelink_values($resourcelink, $savedresourcelink);
 129          $this->assert_resourcelink_db_values($savedresourcelink);
 130      }
 131  
 132      /**
 133       * Test that we cannot add two resource_links with the same resourcelinkid for a given deploymentid.
 134       *
 135       * @covers ::save
 136       */
 137      public function test_add_uniqueness_constraints() {
 138          $this->resetAfterTest();
 139          $reslink1 = $this->generate_resource_link();
 140          $reslink2 = clone $reslink1;
 141          $repository = new resource_link_repository();
 142          $createdresource1 = $repository->save($reslink1);
 143          $this->assertIsInt($createdresource1->get_id());
 144          $this->expectException(\dml_exception::class);
 145          $repository->save($reslink2);
 146      }
 147  
 148      /**
 149       * Test fetching an object from the store.
 150       *
 151       * @covers ::find
 152       */
 153      public function test_find() {
 154          $this->resetAfterTest();
 155          $resourcelink = $this->generate_resource_link();
 156          $repository = new resource_link_repository();
 157          $newreslink = $repository->save($resourcelink);
 158  
 159          $locatedreslink = $repository->find($newreslink->get_id());
 160          $this->assertEquals($newreslink, $locatedreslink);
 161          $repository->delete($locatedreslink->get_id());
 162          $this->assertEmpty($repository->find($locatedreslink->get_id()));
 163      }
 164  
 165      /**
 166       * Test finding a collection of resource links by resource.
 167       *
 168       * @covers ::find_by_resource
 169       */
 170      public function test_find_by_resource() {
 171          $this->resetAfterTest();
 172          $resourcelink = $this->generate_resource_link();
 173          $repository = new resource_link_repository();
 174          $newreslink = $repository->save($resourcelink);
 175          $resourcelink2 = resource_link::create('another-res-link-1', $newreslink->get_deploymentid(),
 176              $newreslink->get_resourceid());
 177          $newreslink2 = $repository->save($resourcelink2);
 178          $resourcelink3 = resource_link::create('another-res-link-2', $newreslink->get_deploymentid(),
 179              $newreslink->get_resourceid() + 1);
 180          $newreslink3 = $repository->save($resourcelink3);
 181  
 182          $locatedreslinks = $repository->find_by_resource($newreslink->get_resourceid());
 183          $this->assertCount(2, $locatedreslinks);
 184          usort($locatedreslinks, function($a, $b) {
 185              return strcmp($b->get_resourcelinkid(), $a->get_resourcelinkid());
 186          });
 187          $this->assertEquals([$newreslink, $newreslink2], $locatedreslinks);
 188          $locatedreslinks = $repository->find_by_resource($newreslink->get_resourceid() + 1);
 189          $this->assertCount(1, $locatedreslinks);
 190          $this->assertEquals([$newreslink3], $locatedreslinks);
 191          $this->assertEmpty($repository->find_by_resource(0));
 192      }
 193  
 194      /**
 195       * Test finding a collection of resource links by resource and user.
 196       *
 197       * @covers ::find_by_resource_and_user
 198       */
 199      public function test_find_by_resource_and_user() {
 200          global $CFG;
 201          $this->resetAfterTest();
 202          $resourcelink = $this->generate_resource_link();
 203          $repository = new resource_link_repository();
 204          $newreslink = $repository->save($resourcelink);
 205          $resourcelink2 = resource_link::create('another-res-link-1', $newreslink->get_deploymentid(),
 206              $newreslink->get_resourceid());
 207          $newreslink2 = $repository->save($resourcelink2);
 208          $resourcelink3 = resource_link::create('another-res-link-2', $newreslink->get_deploymentid(),
 209              $newreslink->get_resourceid());
 210          $newreslink3 = $repository->save($resourcelink3);
 211          $user1 = $this->getDataGenerator()->create_user();
 212          $user2 = $this->getDataGenerator()->create_user();
 213  
 214          $userrepo = new user_repository();
 215          $user = $newreslink->add_user(
 216              $user1->id,
 217              'platform-user-id-123',
 218              $CFG->lang,
 219              'Sydney',
 220              'AU',
 221              'Test university',
 222              '99'
 223          );
 224          $createduser = $userrepo->save($user);
 225          $createduser->set_resourcelinkid($newreslink2->get_id());
 226          $userrepo->save($createduser);
 227  
 228          $user2 = $newreslink3->add_user(
 229              $user2->id,
 230              'platform-user-id-777',
 231              $CFG->lang,
 232              'Melbourne',
 233              'AU',
 234              'Test university',
 235              '99'
 236          );
 237          $createduser2 = $userrepo->save($user2);
 238  
 239          $locatedreslinks = $repository->find_by_resource_and_user($newreslink->get_resourceid(),
 240              $createduser->get_id());
 241          $this->assertCount(2, $locatedreslinks);
 242          usort($locatedreslinks, function($a, $b) {
 243              return strcmp($b->get_resourcelinkid(), $a->get_resourcelinkid());
 244          });
 245          $this->assertEquals([$newreslink, $newreslink2], $locatedreslinks);
 246          $locatedreslinks = $repository->find_by_resource_and_user($newreslink->get_resourceid(),
 247              $createduser2->get_id());
 248          $this->assertCount(1, $locatedreslinks);
 249          $this->assertEquals([$newreslink3], $locatedreslinks);
 250          $this->assertEmpty($repository->find_by_resource_and_user($newreslink->get_resourceid(), 0));
 251      }
 252  
 253      /**
 254       * Test deletion from the store.
 255       *
 256       * @covers ::delete
 257       */
 258      public function test_delete() {
 259          global $CFG;
 260          $this->resetAfterTest();
 261          $resourcelink = $this->generate_resource_link();
 262          $repository = new resource_link_repository();
 263          $newreslink = $repository->save($resourcelink);
 264          $this->assertTrue($repository->exists($newreslink->get_id()));
 265  
 266          // Also create a user from this resource link so we get some test user_resource_link mappings.
 267          $user = $newreslink->add_user(
 268              2,
 269              'source-id-123',
 270              $CFG->lang,
 271              'Perth',
 272              'AU',
 273              'An Example Institution',
 274              '99',
 275              2,
 276          );
 277          $userrepo = new user_repository();
 278          $userrepo->save($user);
 279          global $DB;
 280          $this->assertTrue($DB->record_exists('enrol_lti_user_resource_link',
 281              ['resourcelinkid' => $newreslink->get_id()]));
 282  
 283          $repository->delete($newreslink->get_id());
 284          $this->assertFalse($repository->exists($newreslink->get_id()));
 285          $this->assertEmpty($repository->find($newreslink->get_id()));
 286          $this->assertFalse($DB->record_exists('enrol_lti_user_resource_link',
 287              ['resourcelinkid' => $newreslink->get_id()]));
 288  
 289          $this->assertNull($repository->delete($newreslink->get_id()));
 290      }
 291  
 292      /**
 293       * Test deleting a group of resource links by resource.
 294       *
 295       * @covers ::delete_by_resource
 296       */
 297      public function test_delete_by_resource() {
 298          global $CFG;
 299          $this->resetAfterTest();
 300          $resourcelink = $this->generate_resource_link();
 301          $repository = new resource_link_repository();
 302          $newreslink = $repository->save($resourcelink);
 303  
 304          // Also create a user from this resource link so we get some test user_resource_link mappings.
 305          $user = $newreslink->add_user(
 306              2,
 307              'source-id-123',
 308              $CFG->lang,
 309              'Perth',
 310              'AU',
 311              'An Example Institution',
 312              '99',
 313              2,
 314          );
 315          $userrepo = new user_repository();
 316          $userrepo->save($user);
 317          global $DB;
 318          $this->assertTrue($DB->record_exists('enrol_lti_user_resource_link',
 319              ['resourcelinkid' => $newreslink->get_id()]));
 320  
 321          // Create a resource link under the same deployment for another resource.
 322          $resourcelink2 = resource_link::create('another-res-link-1', $newreslink->get_deploymentid(),
 323              $newreslink->get_resourceid() + 1);
 324          $newreslink2 = $repository->save($resourcelink2);
 325  
 326          $repository->delete_by_resource($newreslink->get_resourceid());
 327          $this->assertFalse($repository->exists($newreslink->get_id()));
 328          $this->assertEmpty($repository->find($newreslink->get_id()));
 329          $this->assertFalse($DB->record_exists('enrol_lti_user_resource_link',
 330              ['resourcelinkid' => $newreslink->get_id()]));
 331          $this->assertTrue($repository->exists($newreslink2->get_id()));
 332          $this->assertInstanceOf(resource_link::class, $repository->find($newreslink2->get_id()));
 333  
 334          $this->assertNull($repository->delete_by_deployment($newreslink->get_deploymentid()));
 335      }
 336  
 337      /**
 338       * Test deleting a resource links by their deployment container.
 339       *
 340       * @covers ::delete_by_deployment
 341       */
 342      public function test_delete_by_deployment() {
 343          global $CFG;
 344          $this->resetAfterTest();
 345          $resourcelink = $this->generate_resource_link();
 346          $repository = new resource_link_repository();
 347          $newreslink = $repository->save($resourcelink);
 348          $this->assertTrue($repository->exists($newreslink->get_id()));
 349  
 350          // Also create a user from this resource link so we get some test user_resource_link mappings.
 351          $user = $newreslink->add_user(
 352              2,
 353              'source-id-123',
 354              $CFG->lang,
 355              'Perth',
 356              'AU',
 357              'An Example Institution',
 358              '99',
 359              2,
 360          );
 361          $userrepo = new user_repository();
 362          $userrepo->save($user);
 363          global $DB;
 364          $this->assertTrue($DB->record_exists('enrol_lti_user_resource_link',
 365              ['resourcelinkid' => $newreslink->get_id()]));
 366  
 367          $repository->delete_by_deployment($newreslink->get_deploymentid());
 368          $this->assertFalse($repository->exists($newreslink->get_id()));
 369          $this->assertEmpty($repository->find($newreslink->get_id()));
 370          $this->assertFalse($DB->record_exists('enrol_lti_user_resource_link',
 371              ['resourcelinkid' => $newreslink->get_id()]));
 372  
 373          $this->assertNull($repository->delete_by_deployment($newreslink->get_deploymentid()));
 374      }
 375  
 376      /**
 377       * Test checking existence in the store.
 378       *
 379       * @covers ::exists
 380       */
 381      public function test_exists() {
 382          $this->resetAfterTest();
 383          $resourcelink = $this->generate_resource_link();
 384          $repository = new resource_link_repository();
 385          $newreslink = $repository->save($resourcelink);
 386          $this->assertTrue($repository->exists($newreslink->get_id()));
 387          $repository->delete($newreslink->get_id());
 388          $this->assertFalse($repository->exists($newreslink->get_id()));
 389      }
 390  
 391      /**
 392       * Test update of an existing resource_link.
 393       *
 394       * @covers ::save
 395       */
 396      public function test_save_existing() {
 397          $this->resetAfterTest();
 398          $resourcelink = $this->generate_resource_link();
 399          $repository = new resource_link_repository();
 400          $newreslink = $repository->save($resourcelink);
 401          $newreslink->add_grade_service(
 402              new \moodle_url('https://lms.example.org/context/lineitems')
 403          );
 404  
 405          $updatedreslink = $repository->save($newreslink);
 406          $this->assertEquals($newreslink, $updatedreslink);
 407      }
 408  
 409      /**
 410       * Test update with a stale object which is no longer present in the store.
 411       *
 412       * @covers ::save
 413       */
 414      public function test_update_stale() {
 415          $this->resetAfterTest();
 416          $resourcelink = $this->generate_resource_link();
 417          $repository = new resource_link_repository();
 418          $newreslink = $repository->save($resourcelink);
 419          $repository->delete($newreslink->get_id());
 420  
 421          $newreslink->add_grade_service(
 422              new \moodle_url('https://lms.example.org/context/lineitems')
 423          );
 424          $this->expectException(\coding_exception::class);
 425          $repository->save($newreslink);
 426      }
 427  }