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.

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

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