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.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * External tool module external functions tests
  19   *
  20   * @package    mod_lti
  21   * @category   external
  22   * @copyright  2015 Juan Leyva <juan@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   * @since      Moodle 3.0
  25   */
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  global $CFG;
  30  
  31  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  32  require_once($CFG->dirroot . '/mod/lti/lib.php');
  33  
  34  /**
  35   * External tool module external functions tests
  36   *
  37   * @package    mod_lti
  38   * @category   external
  39   * @copyright  2015 Juan Leyva <juan@moodle.com>
  40   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  41   * @since      Moodle 3.0
  42   */
  43  class mod_lti_external_testcase extends externallib_advanced_testcase {
  44  
  45      /**
  46       * Set up for every test
  47       */
  48      public function setUp(): void {
  49          $this->resetAfterTest();
  50      }
  51  
  52      /**
  53       * Sets up some basic test data including course, users, roles, and an lti instance, for use in some tests.
  54       * @return array
  55       */
  56      protected function setup_test_data() {
  57          global $DB;
  58          $this->setAdminUser();
  59  
  60          // Setup test data.
  61          $course = $this->getDataGenerator()->create_course();
  62          $lti = $this->getDataGenerator()->create_module(
  63              'lti',
  64              ['course' => $course->id, 'toolurl' => 'http://localhost/not/real/tool.php']
  65          );
  66          $context = context_module::instance($lti->cmid);
  67          $cm = get_coursemodule_from_instance('lti', $lti->id);
  68  
  69          // Create users.
  70          $student = self::getDataGenerator()->create_user();
  71          $teacher = self::getDataGenerator()->create_user();
  72  
  73          // Users enrolments.
  74          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
  75          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
  76          $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id, 'manual');
  77          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id, 'manual');
  78  
  79          return [
  80              'course' => $course,
  81              'lti' => $lti,
  82              'context' => $context,
  83              'cm' => $cm,
  84              'student' => $student,
  85              'teacher' => $teacher,
  86              'studentrole' => $studentrole,
  87              'teacherrole' => $teacherrole
  88          ];
  89      }
  90  
  91      /**
  92       * Generate a tool type.
  93       *
  94       * @param string $uniqueid Each tool type needs a different base url. Provide a unique string for every tool type created.
  95       * @param int|null $toolproxyid Optional proxy to associate with tool type.
  96       * @return stdClass A tool type.
  97       */
  98      protected function generate_tool_type(string $uniqueid, int $toolproxyid = null) : stdClass {
  99          // Create a tool type.
 100          $type = new stdClass();
 101          $type->state = LTI_TOOL_STATE_CONFIGURED;
 102          $type->name = "Test tool $uniqueid";
 103          $type->description = "Example description $uniqueid";
 104          $type->toolproxyid = $toolproxyid;
 105          $type->baseurl = $this->getExternalTestFileUrl("/test$uniqueid.html");
 106          lti_add_type($type, new stdClass());
 107          return $type;
 108      }
 109  
 110      /**
 111       * Generate a tool proxy.
 112       *
 113       * @param string $uniqueid Each tool proxy needs a different reg url. Provide a unique string for every tool proxy created.
 114       * @return stdClass A tool proxy.
 115       */
 116      protected function generate_tool_proxy(string $uniqueid) : stdClass {
 117          // Create a tool proxy.
 118          $proxy = mod_lti_external::create_tool_proxy("Test proxy $uniqueid",
 119                  $this->getExternalTestFileUrl("/proxy$uniqueid.html"), array(), array());
 120          $proxy = (object)external_api::clean_returnvalue(mod_lti_external::create_tool_proxy_returns(), $proxy);
 121          return $proxy;
 122      }
 123  
 124      /**
 125       * Test get_tool_proxies.
 126       */
 127      public function test_mod_lti_get_tool_proxies() {
 128          // Create two tool proxies. One to associate with tool, and one to leave orphaned.
 129          $this->setAdminUser();
 130          $proxy = $this->generate_tool_proxy("1");
 131          $orphanedproxy = $this->generate_tool_proxy("2");
 132          $this->generate_tool_type("1", $proxy->id); // Associate proxy 1 with tool type.
 133  
 134          // Fetch all proxies.
 135          $proxies = mod_lti_external::get_tool_proxies(false);
 136          $proxies = external_api::clean_returnvalue(mod_lti_external::get_tool_proxies_returns(), $proxies);
 137  
 138          $this->assertCount(2, $proxies);
 139          $this->assertEqualsCanonicalizing([(array) $proxy, (array) $orphanedproxy], $proxies);
 140      }
 141  
 142      /**
 143       * Test get_tool_proxies with orphaned proxies only.
 144       */
 145      public function test_mod_lti_get_orphaned_tool_proxies() {
 146          // Create two tool proxies. One to associate with tool, and one to leave orphaned.
 147          $this->setAdminUser();
 148          $proxy = $this->generate_tool_proxy("1");
 149          $orphanedproxy = $this->generate_tool_proxy("2");
 150          $this->generate_tool_type("1", $proxy->id); // Associate proxy 1 with tool type.
 151  
 152          // Fetch all proxies.
 153          $proxies = mod_lti_external::get_tool_proxies(true);
 154          $proxies = external_api::clean_returnvalue(mod_lti_external::get_tool_proxies_returns(), $proxies);
 155  
 156          $this->assertCount(1, $proxies);
 157          $this->assertEqualsCanonicalizing([(array) $orphanedproxy], $proxies);
 158      }
 159  
 160      /**
 161       * Test get_tool_launch_data.
 162       */
 163      public function test_get_tool_launch_data() {
 164          global $USER;
 165  
 166          [
 167              'course' => $course,
 168              'lti' => $lti
 169          ] = $this->setup_test_data();
 170  
 171          $result = mod_lti_external::get_tool_launch_data($lti->id);
 172          $result = external_api::clean_returnvalue(mod_lti_external::get_tool_launch_data_returns(), $result);
 173  
 174          // Basic test, the function returns what it's expected.
 175          self::assertEquals($lti->toolurl, $result['endpoint']);
 176          self::assertCount(36, $result['parameters']);
 177  
 178          // Check some parameters.
 179          $parameters = array();
 180          foreach ($result['parameters'] as $param) {
 181              $parameters[$param['name']] = $param['value'];
 182          }
 183          self::assertEquals($lti->resourcekey, $parameters['oauth_consumer_key']);
 184          self::assertEquals($course->fullname, $parameters['context_title']);
 185          self::assertEquals($course->shortname, $parameters['context_label']);
 186          self::assertEquals($USER->id, $parameters['user_id']);
 187          self::assertEquals($USER->firstname, $parameters['lis_person_name_given']);
 188          self::assertEquals($USER->lastname, $parameters['lis_person_name_family']);
 189          self::assertEquals(fullname($USER), $parameters['lis_person_name_full']);
 190          self::assertEquals($USER->username, $parameters['ext_user_username']);
 191          self::assertEquals("phpunit", $parameters['tool_consumer_instance_name']);
 192          self::assertEquals("PHPUnit test site", $parameters['tool_consumer_instance_description']);
 193      }
 194  
 195      /**
 196       * Test get_ltis_by_courses.
 197       */
 198      public function test_mod_lti_get_ltis_by_courses() {
 199          [
 200              'course' => $course,
 201              'lti' => $lti,
 202              'student' => $student,
 203              'teacher' => $teacher,
 204              'studentrole' => $studentrole,
 205          ] = $this->setup_test_data();
 206  
 207          // Create additional course.
 208          $course2 = self::getDataGenerator()->create_course();
 209  
 210          // Second lti.
 211          $record = new stdClass();
 212          $record->course = $course2->id;
 213          $lti2 = self::getDataGenerator()->create_module('lti', $record);
 214  
 215          // Execute real Moodle enrolment as we'll call unenrol() method on the instance later.
 216          $enrol = enrol_get_plugin('manual');
 217          $enrolinstances = enrol_get_instances($course2->id, true);
 218          foreach ($enrolinstances as $courseenrolinstance) {
 219              if ($courseenrolinstance->enrol == "manual") {
 220                  $instance2 = $courseenrolinstance;
 221                  break;
 222              }
 223          }
 224          $enrol->enrol_user($instance2, $student->id, $studentrole->id);
 225  
 226          self::setUser($student);
 227  
 228          $returndescription = mod_lti_external::get_ltis_by_courses_returns();
 229  
 230          // Create what we expect to be returned when querying the two courses.
 231          // First for the student user.
 232          $expectedfields = array('id', 'coursemodule', 'course', 'name', 'intro', 'introformat', 'introfiles',
 233              'launchcontainer', 'showtitlelaunch', 'showdescriptionlaunch', 'icon', 'secureicon');
 234  
 235          // Add expected coursemodule and data.
 236          $lti1 = $lti;
 237          $lti1->coursemodule = $lti1->cmid;
 238          $lti1->introformat = 1;
 239          $lti1->section = 0;
 240          $lti1->visible = true;
 241          $lti1->groupmode = 0;
 242          $lti1->groupingid = 0;
 243          $lti1->introfiles = [];
 244  
 245          $lti2->coursemodule = $lti2->cmid;
 246          $lti2->introformat = 1;
 247          $lti2->section = 0;
 248          $lti2->visible = true;
 249          $lti2->groupmode = 0;
 250          $lti2->groupingid = 0;
 251          $lti2->introfiles = [];
 252  
 253          foreach ($expectedfields as $field) {
 254              $expected1[$field] = $lti1->{$field};
 255              $expected2[$field] = $lti2->{$field};
 256          }
 257  
 258          $expectedltis = array($expected2, $expected1);
 259  
 260          // Call the external function passing course ids.
 261          $result = mod_lti_external::get_ltis_by_courses(array($course2->id, $course->id));
 262          $result = external_api::clean_returnvalue($returndescription, $result);
 263  
 264          $this->assertEquals($expectedltis, $result['ltis']);
 265          $this->assertCount(0, $result['warnings']);
 266  
 267          // Call the external function without passing course id.
 268          $result = mod_lti_external::get_ltis_by_courses();
 269          $result = external_api::clean_returnvalue($returndescription, $result);
 270          $this->assertEquals($expectedltis, $result['ltis']);
 271          $this->assertCount(0, $result['warnings']);
 272  
 273          // Unenrol user from second course and alter expected ltis.
 274          $enrol->unenrol_user($instance2, $student->id);
 275          array_shift($expectedltis);
 276  
 277          // Call the external function without passing course id.
 278          $result = mod_lti_external::get_ltis_by_courses();
 279          $result = external_api::clean_returnvalue($returndescription, $result);
 280          $this->assertEquals($expectedltis, $result['ltis']);
 281  
 282          // Call for the second course we unenrolled the user from, expected warning.
 283          $result = mod_lti_external::get_ltis_by_courses(array($course2->id));
 284          $result = external_api::clean_returnvalue($returndescription, $result);
 285          $this->assertCount(1, $result['warnings']);
 286          $this->assertEquals('1', $result['warnings'][0]['warningcode']);
 287          $this->assertEquals($course2->id, $result['warnings'][0]['itemid']);
 288  
 289          // Now, try as a teacher for getting all the additional fields.
 290          self::setUser($teacher);
 291  
 292          $additionalfields = array('timecreated', 'timemodified', 'typeid', 'toolurl', 'securetoolurl',
 293              'instructorchoicesendname', 'instructorchoicesendemailaddr', 'instructorchoiceallowroster',
 294              'instructorchoiceallowsetting', 'instructorcustomparameters', 'instructorchoiceacceptgrades', 'grade',
 295              'resourcekey', 'password', 'debuglaunch', 'servicesalt', 'visible', 'groupmode', 'groupingid');
 296  
 297          foreach ($additionalfields as $field) {
 298              $expectedltis[0][$field] = $lti1->{$field};
 299          }
 300  
 301          $result = mod_lti_external::get_ltis_by_courses();
 302          $result = external_api::clean_returnvalue($returndescription, $result);
 303          $this->assertEquals($expectedltis, $result['ltis']);
 304  
 305          // Admin also should get all the information.
 306          self::setAdminUser();
 307  
 308          $result = mod_lti_external::get_ltis_by_courses(array($course->id));
 309          $result = external_api::clean_returnvalue($returndescription, $result);
 310          $this->assertEquals($expectedltis, $result['ltis']);
 311  
 312          // Now, prohibit capabilities.
 313          $this->setUser($student);
 314          $contextcourse1 = context_course::instance($course->id);
 315          // Prohibit capability = mod:lti:view on Course1 for students.
 316          assign_capability('mod/lti:view', CAP_PROHIBIT, $studentrole->id, $contextcourse1->id);
 317          // Empty all the caches that may be affected by this change.
 318          accesslib_clear_all_caches_for_unit_testing();
 319          course_modinfo::clear_instance_cache();
 320  
 321          $ltis = mod_lti_external::get_ltis_by_courses(array($course->id));
 322          $ltis = external_api::clean_returnvalue(mod_lti_external::get_ltis_by_courses_returns(), $ltis);
 323          $this->assertCount(0, $ltis['ltis']);
 324      }
 325  
 326      /**
 327       * Test view_lti with an invalid instance id.
 328       */
 329      public function test_view_lti_invalid_instanceid() {
 330          $this->expectException(moodle_exception::class);
 331          mod_lti_external::view_lti(0);
 332      }
 333  
 334      /**
 335       * Test view_lti as a user who is not enrolled in the course.
 336       */
 337      public function test_view_lti_no_enrolment() {
 338          [
 339              'lti' => $lti
 340          ] = $this->setup_test_data();
 341  
 342          // Test not-enrolled user.
 343          $usernotenrolled = self::getDataGenerator()->create_user();
 344          $this->setUser($usernotenrolled);
 345  
 346          $this->expectException(moodle_exception::class);
 347          mod_lti_external::view_lti($lti->id);
 348      }
 349  
 350      /**
 351       * Test view_lti for a user without the mod/lti:view capability.
 352       */
 353      public function test_view_lti_no_capability() {
 354          [
 355              'lti' => $lti,
 356              'student' => $student,
 357              'studentrole' => $studentrole,
 358              'context' => $context,
 359          ] = $this->setup_test_data();
 360  
 361          $this->setUser($student);
 362  
 363          // We need a explicit prohibit since this capability is only defined in authenticated user and guest roles.
 364          assign_capability('mod/lti:view', CAP_PROHIBIT, $studentrole->id, $context->id);
 365          // Empty all the caches that may be affected by this change.
 366          accesslib_clear_all_caches_for_unit_testing();
 367          course_modinfo::clear_instance_cache();
 368  
 369          $this->expectException(moodle_exception::class);
 370          mod_lti_external::view_lti($lti->id);
 371      }
 372  
 373      /**
 374       * Test view_lti for a user with the mod/lti:view capability in the course.
 375       */
 376      public function test_view_lti() {
 377          [
 378              'lti' => $lti,
 379              'context' => $context,
 380              'cm' => $cm,
 381              'student' => $student,
 382          ] = $this->setup_test_data();
 383  
 384          // Test user with full capabilities.
 385          $this->setUser($student);
 386  
 387          // Trigger and capture the event.
 388          $sink = $this->redirectEvents();
 389  
 390          $result = mod_lti_external::view_lti($lti->id);
 391          // The value of the result isn't needed but validation is.
 392          external_api::clean_returnvalue(mod_lti_external::view_lti_returns(), $result);
 393  
 394          $events = $sink->get_events();
 395          $this->assertCount(1, $events);
 396          $event = array_shift($events);
 397  
 398          // Checking that the event contains the expected values.
 399          $this->assertInstanceOf('\mod_lti\event\course_module_viewed', $event);
 400          $this->assertEquals($context, $event->get_context());
 401          $moodlelti = new moodle_url('/mod/lti/view.php', array('id' => $cm->id));
 402          $this->assertEquals($moodlelti, $event->get_url());
 403          $this->assertEventContextNotUsed($event);
 404          $this->assertNotEmpty($event->get_name());
 405      }
 406  
 407      /**
 408       * Test create_tool_proxy.
 409       */
 410      public function test_mod_lti_create_tool_proxy() {
 411          $this->setAdminUser();
 412          $capabilities = ['AA', 'BB'];
 413          $proxy = mod_lti_external::create_tool_proxy('Test proxy', $this->getExternalTestFileUrl('/test.html'), $capabilities, []);
 414          $proxy = (object) external_api::clean_returnvalue(mod_lti_external::create_tool_proxy_returns(), $proxy);
 415  
 416          $this->assertEquals('Test proxy', $proxy->name);
 417          $this->assertEquals($this->getExternalTestFileUrl('/test.html'), $proxy->regurl);
 418          $this->assertEquals(LTI_TOOL_PROXY_STATE_PENDING, $proxy->state);
 419          $this->assertEquals(implode("\n", $capabilities), $proxy->capabilityoffered);
 420      }
 421  
 422      /**
 423       * Test create_tool_proxy with a duplicate url.
 424       */
 425      public function test_mod_lti_create_tool_proxy_duplicateurl() {
 426          $this->setAdminUser();
 427          mod_lti_external::create_tool_proxy('Test proxy 1', $this->getExternalTestFileUrl('/test.html'), array(), array());
 428  
 429          $this->expectException(moodle_exception::class);
 430          mod_lti_external::create_tool_proxy('Test proxy 2', $this->getExternalTestFileUrl('/test.html'), array(), array());
 431      }
 432  
 433      /**
 434       * Test create_tool_proxy for a user without the required capability.
 435       */
 436      public function test_mod_lti_create_tool_proxy_without_capability() {
 437          $course = $this->getDataGenerator()->create_course();
 438          $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
 439          $this->setUser($teacher);
 440          $this->expectException(required_capability_exception::class);
 441          mod_lti_external::create_tool_proxy('Test proxy', $this->getExternalTestFileUrl('/test.html'), array(), array());
 442      }
 443  
 444      /**
 445       * Test delete_tool_proxy.
 446       */
 447      public function test_mod_lti_delete_tool_proxy() {
 448          $this->setAdminUser();
 449          $proxy = mod_lti_external::create_tool_proxy('Test proxy', $this->getExternalTestFileUrl('/test.html'), array(), array());
 450          $proxy = (object) external_api::clean_returnvalue(mod_lti_external::create_tool_proxy_returns(), $proxy);
 451          $this->assertNotEmpty(lti_get_tool_proxy($proxy->id));
 452  
 453          $proxy = mod_lti_external::delete_tool_proxy($proxy->id);
 454          $proxy = (object) external_api::clean_returnvalue(mod_lti_external::delete_tool_proxy_returns(), $proxy);
 455  
 456          $this->assertEquals('Test proxy', $proxy->name);
 457          $this->assertEquals($this->getExternalTestFileUrl('/test.html'), $proxy->regurl);
 458          $this->assertEquals(LTI_TOOL_PROXY_STATE_PENDING, $proxy->state);
 459          $this->assertEmpty(lti_get_tool_proxy($proxy->id));
 460      }
 461  
 462      /**
 463       * Test get_tool_proxy_registration_request.
 464       */
 465      public function test_mod_lti_get_tool_proxy_registration_request() {
 466          $this->setAdminUser();
 467          $proxy = mod_lti_external::create_tool_proxy('Test proxy', $this->getExternalTestFileUrl('/test.html'), array(), array());
 468          $proxy = (object) external_api::clean_returnvalue(mod_lti_external::create_tool_proxy_returns(), $proxy);
 469  
 470          $request = mod_lti_external::get_tool_proxy_registration_request($proxy->id);
 471          $request = external_api::clean_returnvalue(mod_lti_external::get_tool_proxy_registration_request_returns(),
 472              $request);
 473  
 474          $this->assertEquals('ToolProxyRegistrationRequest', $request['lti_message_type']);
 475          $this->assertEquals('LTI-2p0', $request['lti_version']);
 476      }
 477  
 478      /**
 479       * Test get_tool_types.
 480       */
 481      public function test_mod_lti_get_tool_types() {
 482          // Create a tool proxy.
 483          $this->setAdminUser();
 484          $proxy = mod_lti_external::create_tool_proxy('Test proxy', $this->getExternalTestFileUrl('/test.html'), array(), array());
 485          $proxy = (object) external_api::clean_returnvalue(mod_lti_external::create_tool_proxy_returns(), $proxy);
 486  
 487          // Create a tool type, associated with that proxy.
 488          $type = new stdClass();
 489          $data = new stdClass();
 490          $type->state = LTI_TOOL_STATE_CONFIGURED;
 491          $type->name = "Test tool";
 492          $type->description = "Example description";
 493          $type->toolproxyid = $proxy->id;
 494          $type->baseurl = $this->getExternalTestFileUrl('/test.html');
 495          lti_add_type($type, $data);
 496  
 497          $types = mod_lti_external::get_tool_types($proxy->id);
 498          $types = external_api::clean_returnvalue(mod_lti_external::get_tool_types_returns(), $types);
 499  
 500          $this->assertCount(1, $types);
 501          $type = $types[0];
 502          $this->assertEquals('Test tool', $type['name']);
 503          $this->assertEquals('Example description', $type['description']);
 504      }
 505  
 506      /**
 507       * Test create_tool_type.
 508       */
 509      public function test_mod_lti_create_tool_type() {
 510          $this->setAdminUser();
 511          $type = mod_lti_external::create_tool_type($this->getExternalTestFileUrl('/ims_cartridge_basic_lti_link.xml'), '', '');
 512          $type = external_api::clean_returnvalue(mod_lti_external::create_tool_type_returns(), $type);
 513  
 514          $this->assertEquals('Example tool', $type['name']);
 515          $this->assertEquals('Example tool description', $type['description']);
 516          $this->assertEquals('https://download.moodle.org/unittest/test.jpg', $type['urls']['icon']);
 517          $typeentry = lti_get_type($type['id']);
 518          $this->assertEquals('http://www.example.com/lti/provider.php', $typeentry->baseurl);
 519          $config = lti_get_type_config($type['id']);
 520          $this->assertTrue(isset($config['sendname']));
 521          $this->assertTrue(isset($config['sendemailaddr']));
 522          $this->assertTrue(isset($config['acceptgrades']));
 523          $this->assertTrue(isset($config['forcessl']));
 524      }
 525  
 526      /**
 527       * Test create_tool_type failure from non existent file.
 528       */
 529      public function test_mod_lti_create_tool_type_nonexistant_file() {
 530          $this->expectException(moodle_exception::class);
 531          mod_lti_external::create_tool_type($this->getExternalTestFileUrl('/doesntexist.xml'), '', '');
 532      }
 533  
 534      /**
 535       * Test create_tool_type failure from xml that is not a cartridge.
 536       */
 537      public function test_mod_lti_create_tool_type_bad_file() {
 538          $this->expectException(moodle_exception::class);
 539          mod_lti_external::create_tool_type($this->getExternalTestFileUrl('/rsstest.xml'), '', '');
 540      }
 541  
 542      /**
 543       * Test create_tool_type as a user without the required capability.
 544       */
 545      public function test_mod_lti_create_tool_type_without_capability() {
 546          $course = $this->getDataGenerator()->create_course();
 547          $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
 548          $this->setUser($teacher);
 549          $this->expectException(required_capability_exception::class);
 550          mod_lti_external::create_tool_type($this->getExternalTestFileUrl('/ims_cartridge_basic_lti_link.xml'), '', '');
 551      }
 552  
 553      /**
 554       * Test update_tool_type.
 555       */
 556      public function test_mod_lti_update_tool_type() {
 557          $this->setAdminUser();
 558          $type = mod_lti_external::create_tool_type($this->getExternalTestFileUrl('/ims_cartridge_basic_lti_link.xml'), '', '');
 559          $type = external_api::clean_returnvalue(mod_lti_external::create_tool_type_returns(), $type);
 560  
 561          $type = mod_lti_external::update_tool_type($type['id'], 'New name', 'New description', LTI_TOOL_STATE_PENDING);
 562          $type = external_api::clean_returnvalue(mod_lti_external::update_tool_type_returns(), $type);
 563  
 564          $this->assertEquals('New name', $type['name']);
 565          $this->assertEquals('New description', $type['description']);
 566          $this->assertEquals('Pending', $type['state']['text']);
 567      }
 568  
 569      /**
 570       * Test delete_tool_type for a user with the required capability.
 571       */
 572      public function test_mod_lti_delete_tool_type() {
 573          $this->setAdminUser();
 574          $type = mod_lti_external::create_tool_type($this->getExternalTestFileUrl('/ims_cartridge_basic_lti_link.xml'), '', '');
 575          $type = external_api::clean_returnvalue(mod_lti_external::create_tool_type_returns(), $type);
 576          $this->assertNotEmpty(lti_get_type($type['id']));
 577  
 578          $type = mod_lti_external::delete_tool_type($type['id']);
 579          $type = external_api::clean_returnvalue(mod_lti_external::delete_tool_type_returns(), $type);
 580          $this->assertEmpty(lti_get_type($type['id']));
 581      }
 582  
 583      /**
 584       * Test delete_tool_type for a user without the required capability.
 585       */
 586      public function test_mod_lti_delete_tool_type_without_capability() {
 587          $this->setAdminUser();
 588          $type = mod_lti_external::create_tool_type($this->getExternalTestFileUrl('/ims_cartridge_basic_lti_link.xml'), '', '');
 589          $type = external_api::clean_returnvalue(mod_lti_external::create_tool_type_returns(), $type);
 590          $this->assertNotEmpty(lti_get_type($type['id']));
 591  
 592          $course = $this->getDataGenerator()->create_course();
 593          $teacher = $this->getDataGenerator()->create_and_enrol($course, 'editingteacher');
 594          $this->setUser($teacher);
 595          $this->expectException(required_capability_exception::class);
 596          mod_lti_external::delete_tool_type($type['id']);
 597      }
 598  
 599      /**
 600       * Test is_cartridge.
 601       */
 602      public function test_mod_lti_is_cartridge() {
 603          $this->setAdminUser();
 604          $result = mod_lti_external::is_cartridge($this->getExternalTestFileUrl('/ims_cartridge_basic_lti_link.xml'));
 605          $result = external_api::clean_returnvalue(mod_lti_external::is_cartridge_returns(), $result);
 606          $this->assertTrue($result['iscartridge']);
 607  
 608          $result = mod_lti_external::is_cartridge($this->getExternalTestFileUrl('/test.html'));
 609          $result = external_api::clean_returnvalue(mod_lti_external::is_cartridge_returns(), $result);
 610          $this->assertFalse($result['iscartridge']);
 611      }
 612  }