Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402] [Versions 402 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 core_group;
  18  
  19  use core_external\external_api;
  20  use core_group_external;
  21  use externallib_advanced_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 . '/group/externallib.php');
  29  require_once($CFG->dirroot . '/group/lib.php');
  30  
  31  /**
  32   * Group external PHPunit tests
  33   *
  34   * @package    core_group
  35   * @category   external
  36   * @copyright  2012 Jerome Mouneyrac
  37   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   * @since Moodle 2.4
  39   */
  40  class externallib_test extends externallib_advanced_testcase {
  41  
  42      /**
  43       * Test create_groups
  44       */
  45      public function test_create_groups() {
  46          global $DB;
  47  
  48          $this->resetAfterTest(true);
  49  
  50          $course  = self::getDataGenerator()->create_course();
  51  
  52          $group1 = array();
  53          $group1['courseid'] = $course->id;
  54          $group1['name'] = 'Group Test 1';
  55          $group1['description'] = 'Group Test 1 description';
  56          $group1['descriptionformat'] = FORMAT_MOODLE;
  57          $group1['enrolmentkey'] = 'Test group enrol secret phrase';
  58          $group1['idnumber'] = 'TEST1';
  59          $group2 = array();
  60          $group2['courseid'] = $course->id;
  61          $group2['name'] = 'Group Test 2';
  62          $group2['description'] = 'Group Test 2 description';
  63          $group2['visibility'] = GROUPS_VISIBILITY_MEMBERS;
  64          $group2['participation'] = false;
  65          $group3 = array();
  66          $group3['courseid'] = $course->id;
  67          $group3['name'] = 'Group Test 3';
  68          $group3['description'] = 'Group Test 3 description';
  69          $group3['idnumber'] = 'TEST1';
  70          $group4 = array();
  71          $group4['courseid'] = $course->id;
  72          $group4['name'] = 'Group Test 4';
  73          $group4['description'] = 'Group Test 4 description';
  74  
  75          // Set the required capabilities by the external function
  76          $context = \context_course::instance($course->id);
  77          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
  78          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
  79  
  80          // Call the external function.
  81          $groups = core_group_external::create_groups(array($group1, $group2));
  82  
  83          // We need to execute the return values cleaning process to simulate the web service server.
  84          $groups = external_api::clean_returnvalue(core_group_external::create_groups_returns(), $groups);
  85  
  86          // Checks against DB values
  87          $this->assertEquals(2, count($groups));
  88          foreach ($groups as $group) {
  89              $dbgroup = $DB->get_record('groups', array('id' => $group['id']), '*', MUST_EXIST);
  90              switch ($dbgroup->name) {
  91                  case $group1['name']:
  92                      $groupdescription = $group1['description'];
  93                      $groupcourseid = $group1['courseid'];
  94                      $this->assertEquals($dbgroup->descriptionformat, $group1['descriptionformat']);
  95                      $this->assertEquals($dbgroup->enrolmentkey, $group1['enrolmentkey']);
  96                      $this->assertEquals($dbgroup->idnumber, $group1['idnumber']);
  97                      // The visibility and participation attributes were not specified, so should match the default values.
  98                      $groupvisibility = GROUPS_VISIBILITY_ALL;
  99                      $groupparticipation = true;
 100                      break;
 101                  case $group2['name']:
 102                      $groupdescription = $group2['description'];
 103                      $groupcourseid = $group2['courseid'];
 104                      $groupvisibility = $group2['visibility'];
 105                      $groupparticipation = $group2['participation'];
 106                      break;
 107                  default:
 108                      throw new \moodle_exception('unknowgroupname');
 109                      break;
 110              }
 111              $this->assertEquals($dbgroup->description, $groupdescription);
 112              $this->assertEquals($dbgroup->courseid, $groupcourseid);
 113              $this->assertEquals($dbgroup->visibility, $groupvisibility);
 114              $this->assertEquals($dbgroup->participation, $groupparticipation);
 115          }
 116  
 117          try {
 118              $froups = core_group_external::create_groups(array($group3));
 119              $this->fail('Exception expected due to already existing idnumber.');
 120          } catch (\moodle_exception $e) {
 121              $this->assertInstanceOf('moodle_exception', $e);
 122              $this->assertEquals(get_string('idnumbertaken', 'error'), $e->getMessage());
 123          }
 124  
 125          // Call without required capability
 126          $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid);
 127  
 128          $this->expectException(\required_capability_exception::class);
 129          $froups = core_group_external::create_groups(array($group4));
 130      }
 131  
 132      /**
 133       * Test that creating a group with an invalid visibility value throws an exception.
 134       *
 135       * @covers \core_group_external::create_groups
 136       * @return void
 137       */
 138      public function test_create_group_invalid_visibility(): void {
 139          $this->resetAfterTest(true);
 140  
 141          $course = self::getDataGenerator()->create_course();
 142  
 143          $group1 = array();
 144          $group1['courseid'] = $course->id;
 145          $group1['name'] = 'Group Test 1';
 146          $group1['description'] = 'Group Test 1 description';
 147          $group1['visibility'] = 1000;
 148  
 149          // Set the required capabilities by the external function.
 150          $context = \context_course::instance($course->id);
 151          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 152          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 153  
 154          // Call the external function.
 155          $this->expectException('invalid_parameter_exception');
 156          core_group_external::create_groups([$group1]);
 157      }
 158  
 159      /**
 160       * Test update_groups
 161       */
 162      public function test_update_groups() {
 163          global $DB;
 164  
 165          $this->resetAfterTest(true);
 166  
 167          $course = self::getDataGenerator()->create_course();
 168  
 169          $group1data = array();
 170          $group1data['courseid'] = $course->id;
 171          $group1data['name'] = 'Group Test 1';
 172          $group1data['description'] = 'Group Test 1 description';
 173          $group1data['descriptionformat'] = FORMAT_MOODLE;
 174          $group1data['enrolmentkey'] = 'Test group enrol secret phrase';
 175          $group1data['idnumber'] = 'TEST1';
 176          $group2data = array();
 177          $group2data['courseid'] = $course->id;
 178          $group2data['name'] = 'Group Test 2';
 179          $group2data['description'] = 'Group Test 2 description';
 180          $group2data['idnumber'] = 'TEST2';
 181  
 182          // Set the required capabilities by the external function.
 183          $context = \context_course::instance($course->id);
 184          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 185          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 186  
 187          // Create the test groups.
 188          $group1 = self::getDataGenerator()->create_group($group1data);
 189          $group2 = self::getDataGenerator()->create_group($group2data);
 190  
 191          $group1data['id'] = $group1->id;
 192          unset($group1data['courseid']);
 193          $group2data['id'] = $group2->id;
 194          unset($group2data['courseid']);
 195  
 196          // No exceptions should be triggered.
 197          $group1data['idnumber'] = 'CHANGED';
 198          core_group_external::update_groups(array($group1data));
 199          $group2data['description'] = 'Group Test 2 description CHANGED';
 200          $group2data['visibility'] = GROUPS_VISIBILITY_MEMBERS;
 201          core_group_external::update_groups(array($group2data));
 202  
 203          foreach ([$group1, $group2] as $group) {
 204              $dbgroup = $DB->get_record('groups', array('id' => $group->id), '*', MUST_EXIST);
 205              switch ($dbgroup->name) {
 206                  case $group1data['name']:
 207                      $this->assertEquals($dbgroup->idnumber, $group1data['idnumber']);
 208                      $groupdescription = $group1data['description'];
 209                      // Visibility was not specified, so should match the default value.
 210                      $groupvisibility = GROUPS_VISIBILITY_ALL;
 211                      break;
 212                  case $group2data['name']:
 213                      $this->assertEquals($dbgroup->idnumber, $group2data['idnumber']);
 214                      $groupdescription = $group2data['description'];
 215                      $groupvisibility = $group2data['visibility'];
 216                      break;
 217                  default:
 218                      throw new \moodle_exception('unknowngroupname');
 219                      break;
 220              }
 221              $this->assertEquals($dbgroup->description, $groupdescription);
 222              $this->assertEquals($dbgroup->visibility, $groupvisibility);
 223          }
 224  
 225          // Taken idnumber exception.
 226          $group1data['idnumber'] = 'TEST2';
 227          try {
 228              $groups = core_group_external::update_groups(array($group1data));
 229              $this->fail('Exception expected due to already existing idnumber.');
 230          } catch (\moodle_exception $e) {
 231              $this->assertInstanceOf('moodle_exception', $e);
 232              $this->assertEquals(get_string('idnumbertaken', 'error'), $e->getMessage());
 233          }
 234  
 235          // Call without required capability.
 236          $group1data['idnumber'] = 'TEST1';
 237          $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid);
 238  
 239          $this->expectException(\required_capability_exception::class);
 240          $groups = core_group_external::update_groups(array($group1data));
 241      }
 242  
 243      /**
 244       * Test an exception is thrown when an invalid visibility value is passed in an update.
 245       *
 246       * @covers \core_group_external::update_groups
 247       * @return void
 248       */
 249      public function test_update_groups_invalid_visibility(): void {
 250          $this->resetAfterTest(true);
 251  
 252          $course = self::getDataGenerator()->create_course();
 253  
 254          $group1data = array();
 255          $group1data['courseid'] = $course->id;
 256          $group1data['name'] = 'Group Test 1';
 257  
 258          // Set the required capabilities by the external function.
 259          $context = \context_course::instance($course->id);
 260          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 261          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 262  
 263          // Create the test group.
 264          $group1 = self::getDataGenerator()->create_group($group1data);
 265  
 266          $group1data['id'] = $group1->id;
 267          unset($group1data['courseid']);
 268          $group1data['visibility'] = 1000;
 269  
 270          $this->expectException('invalid_parameter_exception');
 271          core_group_external::update_groups(array($group1data));
 272      }
 273  
 274      /**
 275       * Attempting to change the visibility of a group with members should throw an exception.
 276       *
 277       * @covers \core_group_external::update_groups
 278       * @return void
 279       */
 280      public function test_update_groups_visibility_with_members(): void {
 281          $this->resetAfterTest(true);
 282  
 283          $course = self::getDataGenerator()->create_course();
 284  
 285          $group1data = array();
 286          $group1data['courseid'] = $course->id;
 287          $group1data['name'] = 'Group Test 1';
 288  
 289          // Set the required capabilities by the external function.
 290          $context = \context_course::instance($course->id);
 291          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 292          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 293  
 294          // Create the test group and add a member.
 295          $group1 = self::getDataGenerator()->create_group($group1data);
 296          $user1 = self::getDataGenerator()->create_and_enrol($course);
 297          self::getDataGenerator()->create_group_member(['userid' => $user1->id, 'groupid' => $group1->id]);
 298  
 299          $group1data['id'] = $group1->id;
 300          unset($group1data['courseid']);
 301          $group1data['visibility'] = GROUPS_VISIBILITY_MEMBERS;
 302  
 303          $this->expectExceptionMessage('The visibility of this group cannot be changed as it currently has members.');
 304          core_group_external::update_groups(array($group1data));
 305      }
 306  
 307      /**
 308       * Attempting to change the participation field of a group with members should throw an exception.
 309       *
 310       * @covers \core_group_external::update_groups
 311       * @return void
 312       */
 313      public function test_update_groups_participation_with_members(): void {
 314          $this->resetAfterTest(true);
 315  
 316          $course = self::getDataGenerator()->create_course();
 317  
 318          $group1data = array();
 319          $group1data['courseid'] = $course->id;
 320          $group1data['name'] = 'Group Test 1';
 321  
 322          // Set the required capabilities by the external function.
 323          $context = \context_course::instance($course->id);
 324          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 325          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 326  
 327          // Create the test group and add a member.
 328          $group1 = self::getDataGenerator()->create_group($group1data);
 329          $user1 = self::getDataGenerator()->create_and_enrol($course);
 330          self::getDataGenerator()->create_group_member(['userid' => $user1->id, 'groupid' => $group1->id]);
 331  
 332          $group1data['id'] = $group1->id;
 333          unset($group1data['courseid']);
 334          $group1data['participation'] = false;
 335  
 336          $this->expectExceptionMessage('The participation mode of this group cannot be changed as it currently has members.');
 337          core_group_external::update_groups(array($group1data));
 338      }
 339  
 340      /**
 341       * Test get_groups
 342       */
 343      public function test_get_groups() {
 344          global $DB;
 345  
 346          $this->resetAfterTest(true);
 347  
 348          $course = self::getDataGenerator()->create_course();
 349          $group1data = array();
 350          $group1data['courseid'] = $course->id;
 351          $group1data['name'] = 'Group Test 1';
 352          $group1data['description'] = 'Group Test 1 description';
 353          $group1data['descriptionformat'] = FORMAT_MOODLE;
 354          $group1data['enrolmentkey'] = 'Test group enrol secret phrase';
 355          $group1data['idnumber'] = 'TEST1';
 356          $group2data = array();
 357          $group2data['courseid'] = $course->id;
 358          $group2data['name'] = 'Group Test 2';
 359          $group2data['description'] = 'Group Test 2 description';
 360          $group2data['visibility'] = GROUPS_VISIBILITY_MEMBERS;
 361          $group2data['participation'] = false;
 362          $group1 = self::getDataGenerator()->create_group($group1data);
 363          $group2 = self::getDataGenerator()->create_group($group2data);
 364  
 365          // Set the required capabilities by the external function
 366          $context = \context_course::instance($course->id);
 367          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 368          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 369  
 370          // Call the external function.
 371          $groups = core_group_external::get_groups(array($group1->id, $group2->id));
 372  
 373          // We need to execute the return values cleaning process to simulate the web service server.
 374          $groups = external_api::clean_returnvalue(core_group_external::get_groups_returns(), $groups);
 375  
 376          // Checks against DB values
 377          $this->assertEquals(2, count($groups));
 378          foreach ($groups as $group) {
 379              $dbgroup = $DB->get_record('groups', array('id' => $group['id']), '*', MUST_EXIST);
 380              switch ($dbgroup->name) {
 381                  case $group1->name:
 382                      $groupdescription = $group1->description;
 383                      $groupcourseid = $group1->courseid;
 384                      // The visibility and participation attributes were not specified, so should match the default values.
 385                      $groupvisibility = GROUPS_VISIBILITY_ALL;
 386                      $groupparticipation = true;
 387                      $this->assertEquals($dbgroup->descriptionformat, $group1->descriptionformat);
 388                      $this->assertEquals($dbgroup->enrolmentkey, $group1->enrolmentkey);
 389                      $this->assertEquals($dbgroup->idnumber, $group1->idnumber);
 390                      break;
 391                  case $group2->name:
 392                      $groupdescription = $group2->description;
 393                      $groupcourseid = $group2->courseid;
 394                      $groupvisibility = $group2->visibility;
 395                      $groupparticipation = $group2->participation;
 396                      break;
 397                  default:
 398                      throw new \moodle_exception('unknowgroupname');
 399                      break;
 400              }
 401              $this->assertEquals($dbgroup->description, $groupdescription);
 402              $this->assertEquals($dbgroup->courseid, $groupcourseid);
 403              $this->assertEquals($dbgroup->visibility, $groupvisibility);
 404              $this->assertEquals($dbgroup->participation, $groupparticipation);
 405          }
 406  
 407          // Call without required capability
 408          $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid);
 409  
 410          $this->expectException(\required_capability_exception::class);
 411          $groups = core_group_external::get_groups(array($group1->id, $group2->id));
 412      }
 413  
 414      /**
 415       * Test delete_groups
 416       */
 417      public function test_delete_groups() {
 418          global $DB;
 419  
 420          $this->resetAfterTest(true);
 421  
 422          $course = self::getDataGenerator()->create_course();
 423          $group1data = array();
 424          $group1data['courseid'] = $course->id;
 425          $group1data['name'] = 'Group Test 1';
 426          $group1data['description'] = 'Group Test 1 description';
 427          $group1data['descriptionformat'] = FORMAT_MOODLE;
 428          $group1data['enrolmentkey'] = 'Test group enrol secret phrase';
 429          $group2data = array();
 430          $group2data['courseid'] = $course->id;
 431          $group2data['name'] = 'Group Test 2';
 432          $group2data['description'] = 'Group Test 2 description';
 433          $group3data['courseid'] = $course->id;
 434          $group3data['name'] = 'Group Test 3';
 435          $group3data['description'] = 'Group Test 3 description';
 436          $group1 = self::getDataGenerator()->create_group($group1data);
 437          $group2 = self::getDataGenerator()->create_group($group2data);
 438          $group3 = self::getDataGenerator()->create_group($group3data);
 439  
 440          // Set the required capabilities by the external function
 441          $context = \context_course::instance($course->id);
 442          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 443          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 444  
 445          // Checks against DB values
 446          $groupstotal = $DB->count_records('groups', array());
 447          $this->assertEquals(3, $groupstotal);
 448  
 449          // Call the external function.
 450          core_group_external::delete_groups(array($group1->id, $group2->id));
 451  
 452          // Checks against DB values
 453          $groupstotal = $DB->count_records('groups', array());
 454          $this->assertEquals(1, $groupstotal);
 455  
 456          // Call without required capability
 457          $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid);
 458  
 459          $this->expectException(\required_capability_exception::class);
 460          $froups = core_group_external::delete_groups(array($group3->id));
 461      }
 462  
 463      /**
 464       * Test create and update groupings.
 465       * @return void
 466       */
 467      public function test_create_update_groupings() {
 468          global $DB;
 469  
 470          $this->resetAfterTest(true);
 471  
 472          $this->setAdminUser();
 473  
 474          $course = self::getDataGenerator()->create_course();
 475  
 476          $grouping1data = array();
 477          $grouping1data['courseid'] = $course->id;
 478          $grouping1data['name'] = 'Grouping 1 Test';
 479          $grouping1data['description'] = 'Grouping 1 Test description';
 480          $grouping1data['descriptionformat'] = FORMAT_MOODLE;
 481          $grouping1data['idnumber'] = 'TEST';
 482  
 483          $grouping1 = self::getDataGenerator()->create_grouping($grouping1data);
 484  
 485          $grouping1data['name'] = 'Another group';
 486  
 487          try {
 488              $groupings = core_group_external::create_groupings(array($grouping1data));
 489              $this->fail('Exception expected due to already existing idnumber.');
 490          } catch (\moodle_exception $e) {
 491              $this->assertInstanceOf('moodle_exception', $e);
 492              $this->assertEquals(get_string('idnumbertaken', 'error'), $e->getMessage());
 493          }
 494  
 495          // No exception should be triggered.
 496          $grouping1data['id'] = $grouping1->id;
 497          $grouping1data['idnumber'] = 'CHANGED';
 498          unset($grouping1data['courseid']);
 499          core_group_external::update_groupings(array($grouping1data));
 500  
 501          $grouping2data = array();
 502          $grouping2data['courseid'] = $course->id;
 503          $grouping2data['name'] = 'Grouping 2 Test';
 504          $grouping2data['description'] = 'Grouping 2 Test description';
 505          $grouping2data['descriptionformat'] = FORMAT_MOODLE;
 506          $grouping2data['idnumber'] = 'TEST';
 507  
 508          $grouping2 = self::getDataGenerator()->create_grouping($grouping2data);
 509  
 510          $grouping2data['id'] = $grouping2->id;
 511          $grouping2data['idnumber'] = 'CHANGED';
 512          unset($grouping2data['courseid']);
 513          try {
 514              $groupings = core_group_external::update_groupings(array($grouping2data));
 515              $this->fail('Exception expected due to already existing idnumber.');
 516          } catch (\moodle_exception $e) {
 517              $this->assertInstanceOf('moodle_exception', $e);
 518              $this->assertEquals(get_string('idnumbertaken', 'error'), $e->getMessage());
 519          }
 520      }
 521  
 522      /**
 523       * Test get_groupings
 524       */
 525      public function test_get_groupings() {
 526          global $DB;
 527  
 528          $this->resetAfterTest(true);
 529  
 530          $course = self::getDataGenerator()->create_course();
 531  
 532          $groupingdata = array();
 533          $groupingdata['courseid'] = $course->id;
 534          $groupingdata['name'] = 'Grouping Test';
 535          $groupingdata['description'] = 'Grouping Test description';
 536          $groupingdata['descriptionformat'] = FORMAT_MOODLE;
 537  
 538          $grouping = self::getDataGenerator()->create_grouping($groupingdata);
 539  
 540          // Set the required capabilities by the external function.
 541          $context = \context_course::instance($course->id);
 542          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 543          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 544  
 545          // Call the external function without specifying the optional parameter.
 546          $groupings = core_group_external::get_groupings(array($grouping->id));
 547          // We need to execute the return values cleaning process to simulate the web service server.
 548          $groupings = external_api::clean_returnvalue(core_group_external::get_groupings_returns(), $groupings);
 549  
 550          $this->assertEquals(1, count($groupings));
 551  
 552          $group1data = array();
 553          $group1data['courseid'] = $course->id;
 554          $group1data['name'] = 'Group Test 1';
 555          $group1data['description'] = 'Group Test 1 description';
 556          $group1data['descriptionformat'] = FORMAT_MOODLE;
 557          $group2data = array();
 558          $group2data['courseid'] = $course->id;
 559          $group2data['name'] = 'Group Test 2';
 560          $group2data['description'] = 'Group Test 2 description';
 561          $group2data['descriptionformat'] = FORMAT_MOODLE;
 562  
 563          $group1 = self::getDataGenerator()->create_group($group1data);
 564          $group2 = self::getDataGenerator()->create_group($group2data);
 565  
 566          groups_assign_grouping($grouping->id, $group1->id);
 567          groups_assign_grouping($grouping->id, $group2->id);
 568  
 569          // Call the external function specifying that groups are returned.
 570          $groupings = core_group_external::get_groupings(array($grouping->id), true);
 571          // We need to execute the return values cleaning process to simulate the web service server.
 572          $groupings = external_api::clean_returnvalue(core_group_external::get_groupings_returns(), $groupings);
 573          $this->assertEquals(1, count($groupings));
 574          $this->assertEquals(2, count($groupings[0]['groups']));
 575          foreach ($groupings[0]['groups'] as $group) {
 576              $dbgroup = $DB->get_record('groups', array('id' => $group['id']), '*', MUST_EXIST);
 577              $dbgroupinggroups = $DB->get_record('groupings_groups',
 578                                                  array('groupingid' => $groupings[0]['id'],
 579                                                        'groupid' => $group['id']),
 580                                                  '*', MUST_EXIST);
 581              switch ($dbgroup->name) {
 582                  case $group1->name:
 583                      $groupdescription = $group1->description;
 584                      $groupcourseid = $group1->courseid;
 585                      break;
 586                  case $group2->name:
 587                      $groupdescription = $group2->description;
 588                      $groupcourseid = $group2->courseid;
 589                      break;
 590                  default:
 591                      throw new \moodle_exception('unknowgroupname');
 592                      break;
 593              }
 594              $this->assertEquals($dbgroup->description, $groupdescription);
 595              $this->assertEquals($dbgroup->courseid, $groupcourseid);
 596          }
 597      }
 598  
 599      /**
 600       * Test delete_groupings.
 601       */
 602      public function test_delete_groupings() {
 603          global $DB;
 604  
 605          $this->resetAfterTest(true);
 606  
 607          $course = self::getDataGenerator()->create_course();
 608  
 609          $groupingdata1 = array();
 610          $groupingdata1['courseid'] = $course->id;
 611          $groupingdata1['name'] = 'Grouping Test';
 612          $groupingdata1['description'] = 'Grouping Test description';
 613          $groupingdata1['descriptionformat'] = FORMAT_MOODLE;
 614          $groupingdata2 = array();
 615          $groupingdata2['courseid'] = $course->id;
 616          $groupingdata2['name'] = 'Grouping Test';
 617          $groupingdata2['description'] = 'Grouping Test description';
 618          $groupingdata2['descriptionformat'] = FORMAT_MOODLE;
 619          $groupingdata3 = array();
 620          $groupingdata3['courseid'] = $course->id;
 621          $groupingdata3['name'] = 'Grouping Test';
 622          $groupingdata3['description'] = 'Grouping Test description';
 623          $groupingdata3['descriptionformat'] = FORMAT_MOODLE;
 624  
 625          $grouping1 = self::getDataGenerator()->create_grouping($groupingdata1);
 626          $grouping2 = self::getDataGenerator()->create_grouping($groupingdata2);
 627          $grouping3 = self::getDataGenerator()->create_grouping($groupingdata3);
 628  
 629          // Set the required capabilities by the external function.
 630          $context = \context_course::instance($course->id);
 631          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 632          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 633  
 634          // Checks against DB values.
 635          $groupingstotal = $DB->count_records('groupings', array());
 636          $this->assertEquals(3, $groupingstotal);
 637  
 638          // Call the external function.
 639          core_group_external::delete_groupings(array($grouping1->id, $grouping2->id));
 640  
 641          // Checks against DB values.
 642          $groupingstotal = $DB->count_records('groupings', array());
 643          $this->assertEquals(1, $groupingstotal);
 644  
 645          // Call without required capability.
 646          $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid);
 647  
 648          $this->expectException(\required_capability_exception::class);
 649          core_group_external::delete_groupings(array($grouping3->id));
 650      }
 651  
 652      /**
 653       * Test get_groups
 654       */
 655      public function test_get_course_user_groups() {
 656          global $DB;
 657  
 658          $this->resetAfterTest(true);
 659  
 660          $student1 = self::getDataGenerator()->create_user();
 661          $student2 = self::getDataGenerator()->create_user();
 662          $teacher = self::getDataGenerator()->create_user();
 663  
 664          $course = self::getDataGenerator()->create_course();
 665          $anothercourse = self::getDataGenerator()->create_course();
 666          $emptycourse = self::getDataGenerator()->create_course();
 667  
 668          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 669          $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
 670          $this->getDataGenerator()->enrol_user($student1->id, $anothercourse->id, $studentrole->id);
 671          $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
 672  
 673          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
 674          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
 675          $this->getDataGenerator()->enrol_user($teacher->id, $emptycourse->id, $teacherrole->id);
 676  
 677          $group1data = array();
 678          $group1data['courseid'] = $course->id;
 679          $group1data['name'] = 'Group Test 1';
 680          $group1data['description'] = 'Group Test 1 description';
 681          $group1data['idnumber'] = 'TEST1';
 682          $group2data = array();
 683          $group2data['courseid'] = $course->id;
 684          $group2data['name'] = 'Group Test 2';
 685          $group2data['description'] = 'Group Test 2 description';
 686          $group3data = array();
 687          $group3data['courseid'] = $anothercourse->id;
 688          $group3data['name'] = 'Group Test 3';
 689          $group3data['description'] = 'Group Test 3 description';
 690          $group3data['idnumber'] = 'TEST3';
 691          $group1 = self::getDataGenerator()->create_group($group1data);
 692          $group2 = self::getDataGenerator()->create_group($group2data);
 693          $group3 = self::getDataGenerator()->create_group($group3data);
 694  
 695          groups_add_member($group1->id, $student1->id);
 696          groups_add_member($group1->id, $student2->id);
 697          groups_add_member($group2->id, $student1->id);
 698          groups_add_member($group3->id, $student1->id);
 699  
 700          // Create a grouping.
 701          $groupingdata = array();
 702          $groupingdata['courseid'] = $course->id;
 703          $groupingdata['name'] = 'Grouping Test';
 704          $groupingdata['description'] = 'Grouping Test description';
 705          $groupingdata['descriptionformat'] = FORMAT_MOODLE;
 706  
 707          $grouping = self::getDataGenerator()->create_grouping($groupingdata);
 708          // Grouping only containing group1.
 709          groups_assign_grouping($grouping->id, $group1->id);
 710  
 711          $this->setUser($student1);
 712  
 713          $groups = core_group_external::get_course_user_groups($course->id, $student1->id);
 714          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 715          // Check that I see my groups.
 716          $this->assertCount(2, $groups['groups']);
 717          $this->assertEquals($course->id, $groups['groups'][0]['courseid']);
 718          $this->assertEquals($course->id, $groups['groups'][1]['courseid']);
 719  
 720          // Check that I only see my groups inside the given grouping.
 721          $groups = core_group_external::get_course_user_groups($course->id, $student1->id, $grouping->id);
 722          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 723          // Check that I see my groups in the grouping.
 724          $this->assertCount(1, $groups['groups']);
 725          $this->assertEquals($group1->id, $groups['groups'][0]['id']);
 726  
 727  
 728          // Check optional parameters (all student 1 courses and current user).
 729          $groups = core_group_external::get_course_user_groups();
 730          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 731          // Check that I see my groups in all my courses.
 732          $this->assertCount(3, $groups['groups']);
 733  
 734          $this->setUser($student2);
 735          $groups = core_group_external::get_course_user_groups($course->id, $student2->id);
 736          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 737          // Check that I see my groups.
 738          $this->assertCount(1, $groups['groups']);
 739  
 740          $this->assertEquals($group1data['name'], $groups['groups'][0]['name']);
 741          $this->assertEquals($group1data['description'], $groups['groups'][0]['description']);
 742          $this->assertEquals($group1data['idnumber'], $groups['groups'][0]['idnumber']);
 743  
 744          $this->setUser($teacher);
 745          $groups = core_group_external::get_course_user_groups($course->id, $student1->id);
 746          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 747          // Check that a teacher can see student groups in given course.
 748          $this->assertCount(2, $groups['groups']);
 749  
 750          $groups = core_group_external::get_course_user_groups($course->id, $student2->id);
 751          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 752          // Check that a teacher can see student groups in given course.
 753          $this->assertCount(1, $groups['groups']);
 754  
 755          $groups = core_group_external::get_course_user_groups(0, $student1->id);
 756          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 757          // Check that a teacher can see student groups in all the user courses if the teacher is enrolled in the course.
 758          $this->assertCount(2, $groups['groups']); // Teacher only see groups in first course.
 759          $this->assertCount(1, $groups['warnings']); // Enrolment warnings.
 760          $this->assertEquals('1', $groups['warnings'][0]['warningcode']);
 761  
 762          // Enrol teacher in second course.
 763          $this->getDataGenerator()->enrol_user($teacher->id, $anothercourse->id, $teacherrole->id);
 764          $groups = core_group_external::get_course_user_groups(0, $student1->id);
 765          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 766          // Check that a teacher can see student groups in all the user courses if the teacher is enrolled in the course.
 767          $this->assertCount(3, $groups['groups']);
 768  
 769          // Check permissions.
 770          $this->setUser($student1);
 771  
 772          // Student can's see other students group.
 773          $groups = core_group_external::get_course_user_groups($course->id, $student2->id);
 774          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 775          $this->assertCount(1, $groups['warnings']);
 776          $this->assertEquals('cannotmanagegroups', $groups['warnings'][0]['warningcode']);
 777  
 778          // Not enrolled course.
 779          $groups = core_group_external::get_course_user_groups($emptycourse->id, $student2->id);
 780          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 781          $this->assertCount(1, $groups['warnings']);
 782          $this->assertEquals('1', $groups['warnings'][0]['warningcode']);
 783  
 784          $this->setUser($teacher);
 785          // Check user checking not enrolled in given course.
 786          $groups = core_group_external::get_course_user_groups($emptycourse->id, $student1->id);
 787          $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups);
 788          $this->assertCount(1, $groups['warnings']);
 789          $this->assertEquals('notenrolled', $groups['warnings'][0]['warningcode']);
 790      }
 791  
 792      /**
 793       * Test get_activity_allowed_groups
 794       */
 795      public function test_get_activity_allowed_groups() {
 796          global $DB;
 797  
 798          $this->resetAfterTest(true);
 799  
 800          $generator = self::getDataGenerator();
 801  
 802          $student = $generator->create_user();
 803          $otherstudent = $generator->create_user();
 804          $teacher = $generator->create_user();
 805          $course = $generator->create_course();
 806          $othercourse = $generator->create_course();
 807  
 808          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 809          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
 810          $generator->enrol_user($student->id, $course->id, $studentrole->id);
 811          $generator->enrol_user($otherstudent->id, $othercourse->id, $studentrole->id);
 812          $generator->enrol_user($teacher->id, $course->id, $teacherrole->id);
 813  
 814          $forum1 = $generator->create_module("forum", array('course' => $course->id), array('groupmode' => VISIBLEGROUPS));
 815          $forum2 = $generator->create_module("forum", array('course' => $othercourse->id));
 816          $forum3 = $generator->create_module("forum", array('course' => $course->id), array('visible' => 0));
 817  
 818          // Request data for tests.
 819          $cm1 = get_coursemodule_from_instance("forum", $forum1->id);
 820          $cm2 = get_coursemodule_from_instance("forum", $forum2->id);
 821          $cm3 = get_coursemodule_from_instance("forum", $forum3->id);
 822  
 823          $group1data = array();
 824          $group1data['courseid'] = $course->id;
 825          $group1data['name'] = 'Group Test 1';
 826          $group1data['description'] = 'Group Test 1 description';
 827          $group1data['idnumber'] = 'TEST1';
 828          $group2data = array();
 829          $group2data['courseid'] = $course->id;
 830          $group2data['name'] = 'Group Test 2';
 831          $group2data['description'] = 'Group Test 2 description';
 832          $group2data['idnumber'] = 'TEST2';
 833          $group1 = $generator->create_group($group1data);
 834          $group2 = $generator->create_group($group2data);
 835  
 836          groups_add_member($group1->id, $student->id);
 837          groups_add_member($group2->id, $student->id);
 838  
 839          $this->setUser($student);
 840  
 841          // First try possible errors.
 842          try {
 843              $data = core_group_external::get_activity_allowed_groups($cm2->id);
 844          } catch (\moodle_exception $e) {
 845              $this->assertEquals('requireloginerror', $e->errorcode);
 846          }
 847  
 848          try {
 849              $data = core_group_external::get_activity_allowed_groups($cm3->id);
 850          } catch (\moodle_exception $e) {
 851              $this->assertEquals('requireloginerror', $e->errorcode);
 852          }
 853  
 854          // Retrieve my groups.
 855          $groups = core_group_external::get_activity_allowed_groups($cm1->id);
 856          $groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups);
 857          $this->assertCount(2, $groups['groups']);
 858          $this->assertFalse($groups['canaccessallgroups']);
 859  
 860          foreach ($groups['groups'] as $group) {
 861              if ($group['name'] == $group1data['name']) {
 862                  $this->assertEquals($group1data['description'], $group['description']);
 863                  $this->assertEquals($group1data['idnumber'], $group['idnumber']);
 864              } else {
 865                  $this->assertEquals($group2data['description'], $group['description']);
 866                  $this->assertEquals($group2data['idnumber'], $group['idnumber']);
 867              }
 868          }
 869  
 870          $this->setUser($teacher);
 871          // Retrieve other users groups.
 872          $groups = core_group_external::get_activity_allowed_groups($cm1->id, $student->id);
 873          $groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups);
 874          $this->assertCount(2, $groups['groups']);
 875          // We are checking the $student passed as parameter so this will return false.
 876          $this->assertFalse($groups['canaccessallgroups']);
 877  
 878          // Check warnings. Trying to get groups for a user not enrolled in course.
 879          $groups = core_group_external::get_activity_allowed_groups($cm1->id, $otherstudent->id);
 880          $groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups);
 881          $this->assertCount(1, $groups['warnings']);
 882          $this->assertFalse($groups['canaccessallgroups']);
 883  
 884          // Checking teacher groups.
 885          $groups = core_group_external::get_activity_allowed_groups($cm1->id);
 886          $groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups);
 887          $this->assertCount(2, $groups['groups']);
 888          // Teachers by default can access all groups.
 889          $this->assertTrue($groups['canaccessallgroups']);
 890      }
 891  
 892      /**
 893       * Test get_activity_groupmode
 894       */
 895      public function test_get_activity_groupmode() {
 896          global $DB;
 897  
 898          $this->resetAfterTest(true);
 899  
 900          $generator = self::getDataGenerator();
 901  
 902          $student = $generator->create_user();
 903          $course = $generator->create_course();
 904          $othercourse = $generator->create_course();
 905  
 906          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 907          $generator->enrol_user($student->id, $course->id, $studentrole->id);
 908  
 909          $forum1 = $generator->create_module("forum", array('course' => $course->id), array('groupmode' => VISIBLEGROUPS));
 910          $forum2 = $generator->create_module("forum", array('course' => $othercourse->id));
 911          $forum3 = $generator->create_module("forum", array('course' => $course->id), array('visible' => 0));
 912  
 913          // Request data for tests.
 914          $cm1 = get_coursemodule_from_instance("forum", $forum1->id);
 915          $cm2 = get_coursemodule_from_instance("forum", $forum2->id);
 916          $cm3 = get_coursemodule_from_instance("forum", $forum3->id);
 917  
 918          $this->setUser($student);
 919  
 920          $data = core_group_external::get_activity_groupmode($cm1->id);
 921          $data = external_api::clean_returnvalue(core_group_external::get_activity_groupmode_returns(), $data);
 922          $this->assertEquals(VISIBLEGROUPS, $data['groupmode']);
 923  
 924          try {
 925              $data = core_group_external::get_activity_groupmode($cm2->id);
 926          } catch (\moodle_exception $e) {
 927              $this->assertEquals('requireloginerror', $e->errorcode);
 928          }
 929  
 930          try {
 931              $data = core_group_external::get_activity_groupmode($cm3->id);
 932          } catch (\moodle_exception $e) {
 933              $this->assertEquals('requireloginerror', $e->errorcode);
 934          }
 935  
 936      }
 937  
 938      /**
 939       * Test add_group_members.
 940       */
 941      public function test_add_group_members() {
 942          global $DB;
 943  
 944          $this->resetAfterTest(true);
 945  
 946          $student1 = self::getDataGenerator()->create_user();
 947          $student2 = self::getDataGenerator()->create_user();
 948          $student3 = self::getDataGenerator()->create_user();
 949  
 950          $course = self::getDataGenerator()->create_course();
 951  
 952          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 953          $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
 954          $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
 955          $this->getDataGenerator()->enrol_user($student3->id, $course->id, $studentrole->id);
 956  
 957          $group1data = array();
 958          $group1data['courseid'] = $course->id;
 959          $group1data['name'] = 'Group Test 1';
 960          $group1data['description'] = 'Group Test 1 description';
 961          $group1data['idnumber'] = 'TEST1';
 962          $group1 = self::getDataGenerator()->create_group($group1data);
 963  
 964          // Checks against DB values.
 965          $memberstotal = $DB->count_records('groups_members', ['groupid' => $group1->id]);
 966          $this->assertEquals(0, $memberstotal);
 967  
 968          // Set the required capabilities by the external function.
 969          $context = \context_course::instance($course->id);
 970          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
 971          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
 972  
 973          core_group_external::add_group_members([
 974              'members' => [
 975                  'groupid' => $group1->id,
 976                  'userid' => $student1->id,
 977              ]
 978          ]);
 979          core_group_external::add_group_members([
 980              'members' => [
 981                  'groupid' => $group1->id,
 982                  'userid' => $student2->id,
 983              ]
 984          ]);
 985          core_group_external::add_group_members([
 986              'members' => [
 987                  'groupid' => $group1->id,
 988                  'userid' => $student3->id,
 989              ]
 990          ]);
 991  
 992          // Checks against DB values.
 993          $memberstotal = $DB->count_records('groups_members', ['groupid' => $group1->id]);
 994          $this->assertEquals(3, $memberstotal);
 995      }
 996  
 997      /**
 998       * Test delete_group_members.
 999       */
1000      public function test_delete_group_members() {
1001          global $DB;
1002  
1003          $this->resetAfterTest(true);
1004  
1005          $student1 = self::getDataGenerator()->create_user();
1006          $student2 = self::getDataGenerator()->create_user();
1007          $student3 = self::getDataGenerator()->create_user();
1008  
1009          $course = self::getDataGenerator()->create_course();
1010  
1011          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
1012          $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
1013          $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
1014          $this->getDataGenerator()->enrol_user($student3->id, $course->id, $studentrole->id);
1015  
1016          $group1data = array();
1017          $group1data['courseid'] = $course->id;
1018          $group1data['name'] = 'Group Test 1';
1019          $group1data['description'] = 'Group Test 1 description';
1020          $group1data['idnumber'] = 'TEST1';
1021          $group1 = self::getDataGenerator()->create_group($group1data);
1022  
1023          groups_add_member($group1->id, $student1->id);
1024          groups_add_member($group1->id, $student2->id);
1025          groups_add_member($group1->id, $student3->id);
1026  
1027          // Checks against DB values.
1028          $memberstotal = $DB->count_records('groups_members', ['groupid' => $group1->id]);
1029          $this->assertEquals(3, $memberstotal);
1030  
1031          // Set the required capabilities by the external function.
1032          $context = \context_course::instance($course->id);
1033          $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id);
1034          $this->assignUserCapability('moodle/course:view', $context->id, $roleid);
1035  
1036          core_group_external::delete_group_members([
1037              'members' => [
1038                  'groupid' => $group1->id,
1039                  'userid' => $student2->id,
1040              ]
1041          ]);
1042  
1043          // Checks against DB values.
1044          $memberstotal = $DB->count_records('groups_members', ['groupid' => $group1->id]);
1045          $this->assertEquals(2, $memberstotal);
1046      }
1047  }