Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 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 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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  /**
  18   * Tests for the sync_members scheduled task class.
  19   *
  20   * @package enrol_lti
  21   * @copyright 2016 Jun Pataleta <jun@moodle.com>
  22   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  use enrol_lti\data_connector;
  26  use enrol_lti\helper;
  27  use enrol_lti\task\sync_members;
  28  use enrol_lti\tool_provider;
  29  use IMSGlobal\LTI\ToolProvider\Context;
  30  use IMSGlobal\LTI\ToolProvider\ResourceLink;
  31  use IMSGlobal\LTI\ToolProvider\ToolConsumer;
  32  use IMSGlobal\LTI\ToolProvider\User;
  33  
  34  defined('MOODLE_INTERNAL') || die();
  35  
  36  /**
  37   * Tests for the sync_members scheduled task class.
  38   *
  39   * @package enrol_lti
  40   * @copyright 2016 Jun Pataleta <jun@moodle.com>
  41   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  42   */
  43  class sync_members_testcase extends advanced_testcase {
  44      /** @var dummy_sync_members_task $task */
  45      protected $task;
  46  
  47      /** @var  stdClass $tool The published tool. */
  48      protected $tool;
  49  
  50      /** @var User[] $members */
  51      protected $members;
  52  
  53      /** @var  ToolConsumer $consumer */
  54      protected $consumer;
  55  
  56      /** @var  Context $context */
  57      protected $context;
  58  
  59      /** @var  ResourceLink $resourcelink */
  60      protected $resourcelink;
  61  
  62      public function setUp() {
  63          $this->resetAfterTest();
  64  
  65          // Set this user as the admin.
  66          $this->setAdminUser();
  67  
  68          $this->task = new dummy_sync_members_task();
  69  
  70          $generator = $this->getDataGenerator();
  71          $course = $generator->create_course();
  72          $tooldata = [
  73              'courseid' => $course->id,
  74              'membersyncmode' => helper::MEMBER_SYNC_ENROL_AND_UNENROL,
  75              'membersync' => 1,
  76          ];
  77          $tool = $generator->create_lti_tool((object)$tooldata);
  78          $this->tool = helper::get_lti_tool($tool->id);
  79  
  80          $dataconnector = $this->task->get_dataconnector();
  81          $this->consumer = new ToolConsumer('Consumer1Key', $dataconnector);
  82          $this->consumer->name = 'Consumer1';
  83          $this->consumer->secret = 'Consumer1Secret';
  84          $this->consumer->save();
  85  
  86          $toolprovider = new tool_provider($this->tool->id);
  87          $toolprovider->consumer = $this->consumer;
  88          $toolprovider->map_tool_to_consumer();
  89  
  90          $imageurl = $this->getExternalTestFileUrl('test.jpg');
  91          $count = 10;
  92          $this->members = [];
  93          for ($i = 1; $i <= $count; $i++) {
  94              $user = new User();
  95              $user->firstname = 'Firstname' . $i;
  96              $user->lastname = 'Lastname' . $i;
  97              $user->ltiUserId = 'user' . $i;
  98              // Set user image values for some users.
  99              if ($i % 3 == 0) {
 100                  $user->image = $imageurl;
 101              }
 102              $this->members[] = $user;
 103          }
 104  
 105          $this->context = Context::fromConsumer($this->consumer, 'testlticontextid');
 106          $this->context->save();
 107  
 108          $this->resourcelink = ResourceLink::fromContext($this->context, 'testresourcelinkid');
 109          $this->resourcelink->save();
 110      }
 111  
 112      /**
 113       * Test for sync_members::do_context_membership_request().
 114       */
 115      public function test_do_context_membership_request() {
 116          // Suppress output.
 117          ob_start();
 118          $members = $this->task->do_context_membership_request($this->context);
 119          ob_end_clean();
 120          $this->assertFalse($members);
 121      }
 122  
 123      /**
 124       * Test for sync_members::do_resourcelink_membership_request().
 125       */
 126      public function test_do_resourcelink_membership_request() {
 127          $members = $this->task->do_resourcelink_membership_request($this->resourcelink);
 128          $this->assertFalse($members);
 129      }
 130  
 131      /**
 132       * Test for sync_members::execute() when auth_lti is disabled.
 133       */
 134      public function test_execute_authdisabled() {
 135          ob_start();
 136          $this->task->execute();
 137          $output = ob_get_clean();
 138          $message = 'Skipping task - ' . get_string('pluginnotenabled', 'auth', get_string('pluginname', 'auth_lti'));
 139          $this->assertContains($message, $output);
 140      }
 141  
 142      /**
 143       * Test for sync_members::execute() when enrol_lti is disabled.
 144       */
 145      public function test_execute_enroldisabled() {
 146          // Enable auth_lti.
 147          $this->enable_auth();
 148  
 149          ob_start();
 150          $this->task->execute();
 151          $output = ob_get_clean();
 152          $message = 'Skipping task - ' . get_string('enrolisdisabled', 'enrol_lti');
 153          $this->assertContains($message, $output);
 154      }
 155  
 156      /**
 157       * Test for sync_members::execute().
 158       */
 159      public function test_execute() {
 160          // Enable auth_lti.
 161          $this->enable_auth();
 162  
 163          // Enable enrol_lti.
 164          $this->enable_enrol();
 165  
 166          ob_start();
 167          $this->task->execute();
 168          $output = ob_get_clean();
 169  
 170          $membersyncmessage = "Completed - Synced members for tool '{$this->tool->id}' in the course '{$this->tool->courseid}'";
 171          $this->assertContains($membersyncmessage, $output);
 172  
 173          $imagesyncmessage = "Completed - Synced 0 profile images.";
 174          $this->assertContains($imagesyncmessage, $output);
 175      }
 176  
 177      /**
 178       * Test for sync_members::fetch_members_from_consumer() with no resource link nor context associated with the consumer.
 179       */
 180      public function test_fetch_members_from_consumer_noresourcelink_nocontext() {
 181          // Suppress output.
 182          ob_start();
 183          $members = $this->task->fetch_members_from_consumer($this->consumer);
 184          ob_end_clean();
 185          $this->assertFalse($members);
 186      }
 187  
 188      /**
 189       * Test for sync_members::get_name().
 190       */
 191      public function test_get_name() {
 192          $this->assertEquals(get_string('tasksyncmembers', 'enrol_lti'), $this->task->get_name());
 193      }
 194  
 195      /**
 196       * Test for sync_members::should_sync_enrol().
 197       */
 198      public function test_should_sync_enrol() {
 199          $this->assertTrue($this->task->should_sync_enrol(helper::MEMBER_SYNC_ENROL_AND_UNENROL));
 200          $this->assertTrue($this->task->should_sync_enrol(helper::MEMBER_SYNC_ENROL_NEW));
 201          $this->assertFalse($this->task->should_sync_enrol(helper::MEMBER_SYNC_UNENROL_MISSING));
 202      }
 203  
 204      /**
 205       * Test for sync_members::should_sync_unenrol().
 206       */
 207      public function test_should_sync_unenrol() {
 208          $this->assertTrue($this->task->should_sync_unenrol(helper::MEMBER_SYNC_ENROL_AND_UNENROL));
 209          $this->assertFalse($this->task->should_sync_unenrol(helper::MEMBER_SYNC_ENROL_NEW));
 210          $this->assertTrue($this->task->should_sync_unenrol(helper::MEMBER_SYNC_UNENROL_MISSING));
 211      }
 212  
 213      /**
 214       * Test for sync_members::sync_member_information().
 215       */
 216      public function test_sync_member_information() {
 217          list($totalcount, $enrolledcount) = $this->task->sync_member_information($this->tool, $this->consumer, $this->members);
 218          $membercount = count($this->members);
 219          $this->assertCount(10, $this->members);
 220          $this->assertEquals($membercount, $totalcount);
 221          $this->assertEquals($membercount, $enrolledcount);
 222      }
 223  
 224      /**
 225       * Test for sync_members::sync_profile_images().
 226       */
 227      public function test_sync_profile_images() {
 228          $task = $this->task;
 229          list($totalcount, $enrolledcount) = $task->sync_member_information($this->tool, $this->consumer, $this->members);
 230          $membercount = count($this->members);
 231          $this->assertCount(10, $this->members);
 232          $this->assertEquals($membercount, $totalcount);
 233          $this->assertEquals($membercount, $enrolledcount);
 234  
 235          // Suppress output.
 236          ob_start();
 237          $this->assertEquals(3, $task->sync_profile_images());
 238          ob_end_clean();
 239      }
 240  
 241      /**
 242       * Test for sync_members::sync_unenrol().
 243       */
 244      public function test_sync_unenrol() {
 245          $tool = $this->tool;
 246          $task = $this->task;
 247  
 248          $task->sync_member_information($tool, $this->consumer, $this->members);
 249  
 250          // Simulate that the fetched list of current users has been reduced by 3.
 251          $unenrolcount = 3;
 252          for ($i = 0; $i < $unenrolcount; $i++) {
 253              $task->pop_current_users();
 254          }
 255          $this->assertEquals($unenrolcount, $task->sync_unenrol($tool));
 256      }
 257  
 258      /**
 259       * Enable auth_lti plugin.
 260       */
 261      protected function enable_auth() {
 262          $auths = get_enabled_auth_plugins();
 263          if (!in_array('lti', $auths)) {
 264              $auths[] = 'lti';
 265          }
 266          set_config('auth', implode(',', $auths));
 267      }
 268  
 269      /**
 270       * Enable enrol_lti plugin.
 271       */
 272      protected function enable_enrol() {
 273          $enabled = enrol_get_plugins(true);
 274          $enabled['lti'] = true;
 275          $enabled = array_keys($enabled);
 276          set_config('enrol_plugins_enabled', implode(',', $enabled));
 277      }
 278  }
 279  
 280  /**
 281   * Class dummy_sync_members_task.
 282   *
 283   * A class that extends sync_members so that we can expose the protected methods that we would like to test.
 284   *
 285   * @copyright 2016 Jun Pataleta <jun@moodle.com>
 286   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 287   */
 288  class dummy_sync_members_task extends sync_members {
 289      /**
 290       * Exposes/generates the dataconnector property.
 291       *
 292       * @return data_connector
 293       */
 294      public function get_dataconnector() {
 295          if (!$this->dataconnector) {
 296              $this->dataconnector = new data_connector();
 297          }
 298          return $this->dataconnector;
 299      }
 300  
 301      /**
 302       * Helper method that removes an element in the array of current users.
 303       */
 304      public function pop_current_users() {
 305          array_pop($this->currentusers);
 306      }
 307  
 308      /**
 309       * Exposes sync_members::do_context_membership_request()
 310       *
 311       * @param Context $context The context object.
 312       * @param ResourceLink $resourcelink The resource link object.
 313       * @param string $membershipsurltemplate The memberships endpoint URL template.
 314       * @return bool|User[] Array of User objects upon successful membership service request. False, otherwise.
 315       */
 316      public function do_context_membership_request(Context $context, ResourceLink $resourcelink = null,
 317                                                    $membershipsurltemplate = '') {
 318          $members = parent::do_context_membership_request($context, $resourcelink, $membershipsurltemplate);
 319          return $members;
 320      }
 321  
 322  
 323      /**
 324       * Exposes sync_members::do_resourcelink_membership_request()
 325       *
 326       * @param ResourceLink $resourcelink
 327       * @return bool|User[]
 328       */
 329      public function do_resourcelink_membership_request(ResourceLink $resourcelink) {
 330          $members = parent::do_resourcelink_membership_request($resourcelink);
 331          return $members;
 332      }
 333  
 334      /**
 335       * Exposes sync_members::fetch_members_from_consumer()
 336       *
 337       * @param ToolConsumer $consumer
 338       * @return bool|User[]
 339       */
 340      public function fetch_members_from_consumer(ToolConsumer $consumer) {
 341          $members = parent::fetch_members_from_consumer($consumer);
 342          return $members;
 343      }
 344  
 345      /**
 346       * Exposes sync_members::should_sync_unenrol()
 347       *
 348       * @param int $syncmode The tool's membersyncmode.
 349       * @return bool
 350       */
 351      public function should_sync_unenrol($syncmode) {
 352          $shouldsync = parent::should_sync_unenrol($syncmode);
 353          return $shouldsync;
 354      }
 355  
 356      /**
 357       * Exposes sync_members::should_sync_enrol()
 358       *
 359       * @param int $syncmode The tool's membersyncmode.
 360       * @return bool
 361       */
 362      public function should_sync_enrol($syncmode) {
 363          $shouldsync = parent::should_sync_enrol($syncmode);
 364          return $shouldsync;
 365      }
 366  
 367      /**
 368       * Exposes sync_members::sync_member_information()
 369       *
 370       * @param stdClass $tool
 371       * @param ToolConsumer $consumer
 372       * @param User[] $members
 373       * @return array
 374       */
 375      public function sync_member_information(stdClass $tool, ToolConsumer $consumer, $members) {
 376          $result = parent::sync_member_information($tool, $consumer, $members);
 377          return $result;
 378      }
 379  
 380      /**
 381       * Exposes sync_members::sync_profile_images()
 382       *
 383       * @return int
 384       */
 385      public function sync_profile_images() {
 386          $count = parent::sync_profile_images();
 387          return $count;
 388      }
 389  
 390      /**
 391       * Exposes sync_members::sync_unenrol()
 392       *
 393       * @param stdClass $tool
 394       * @return int
 395       */
 396      public function sync_unenrol(stdClass $tool) {
 397          $count = parent::sync_unenrol($tool);
 398          return $count;
 399      }
 400  }