Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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

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