Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace core_completion;
  18  
  19  use core_completion_external;
  20  use externallib_advanced_testcase;
  21  
  22  defined('MOODLE_INTERNAL') || die();
  23  
  24  global $CFG;
  25  
  26  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  27  
  28  /**
  29   * External completion functions unit tests
  30   *
  31   * @package    core_completion
  32   * @category   external
  33   * @copyright  2015 Juan Leyva <juan@moodle.com>
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   * @since      Moodle 2.9
  36   */
  37  class externallib_test extends externallib_advanced_testcase {
  38  
  39      /**
  40       * Test update_activity_completion_status_manually
  41       */
  42      public function test_update_activity_completion_status_manually() {
  43          global $DB, $CFG;
  44  
  45          $this->resetAfterTest(true);
  46  
  47          $CFG->enablecompletion = true;
  48          $user = $this->getDataGenerator()->create_user();
  49          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
  50          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id),
  51                                                               array('completion' => 1));
  52          $cm = get_coursemodule_from_id('data', $data->cmid);
  53  
  54          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
  55          $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
  56  
  57          $this->setUser($user);
  58  
  59          $result = core_completion_external::update_activity_completion_status_manually($data->cmid, true);
  60          // We need to execute the return values cleaning process to simulate the web service server.
  61          $result = \external_api::clean_returnvalue(
  62              core_completion_external::update_activity_completion_status_manually_returns(), $result);
  63  
  64          // Check in DB.
  65          $this->assertEquals(1, $DB->get_field('course_modules_completion', 'completionstate',
  66                              array('coursemoduleid' => $data->cmid)));
  67  
  68          // Check using the API.
  69          $completion = new \completion_info($course);
  70          $completiondata = $completion->get_data($cm);
  71          $this->assertEquals(1, $completiondata->completionstate);
  72          $this->assertTrue($result['status']);
  73  
  74          $result = core_completion_external::update_activity_completion_status_manually($data->cmid, false);
  75          // We need to execute the return values cleaning process to simulate the web service server.
  76          $result = \external_api::clean_returnvalue(
  77              core_completion_external::update_activity_completion_status_manually_returns(), $result);
  78  
  79          $this->assertEquals(0, $DB->get_field('course_modules_completion', 'completionstate',
  80                              array('coursemoduleid' => $data->cmid)));
  81          $completiondata = $completion->get_data($cm);
  82          $this->assertEquals(0, $completiondata->completionstate);
  83          $this->assertTrue($result['status']);
  84      }
  85  
  86      /**
  87       * Test update_activity_completion_status
  88       */
  89      public function test_get_activities_completion_status() {
  90          global $DB, $CFG, $PAGE;
  91  
  92          $this->resetAfterTest(true);
  93  
  94          $CFG->enablecompletion = true;
  95          $student = $this->getDataGenerator()->create_user();
  96          $teacher = $this->getDataGenerator()->create_user();
  97  
  98          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1,
  99                                                                      'groupmode' => SEPARATEGROUPS,
 100                                                                      'groupmodeforce' => 1));
 101          \availability_completion\condition::wipe_static_cache();
 102  
 103          $data = $this->getDataGenerator()->create_module('data',
 104              ['course' => $course->id],
 105              ['completion' => COMPLETION_TRACKING_MANUAL],
 106          );
 107          $forum = $this->getDataGenerator()->create_module('forum',
 108              ['course' => $course->id],
 109              ['completion' => COMPLETION_TRACKING_MANUAL],
 110          );
 111          $forumautocompletion = $this->getDataGenerator()->create_module('forum',
 112              ['course' => $course->id],
 113              ['showdescription' => true, 'completionview' => 1, 'completion' => COMPLETION_TRACKING_AUTOMATIC],
 114          );
 115          $availability = '{"op":"&","c":[{"type":"completion","cm":' . $forum->cmid .',"e":1}],"showc":[true]}';
 116          $assign = $this->getDataGenerator()->create_module('assign',
 117              ['course' => $course->id],
 118              ['availability' => $availability],
 119          );
 120          $page = $this->getDataGenerator()->create_module('page',  array('course' => $course->id),
 121                                                              array('completion' => 1, 'visible' => 0));
 122  
 123          $cmdata = get_coursemodule_from_id('data', $data->cmid);
 124          $cmforum = get_coursemodule_from_id('forum', $forum->cmid);
 125          $cmforumautocompletion = get_coursemodule_from_id('forum', $forumautocompletion->cmid);
 126  
 127          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 128          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
 129          $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id);
 130          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
 131  
 132          $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
 133          $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
 134  
 135          // Teacher and student in different groups initially.
 136          groups_add_member($group1->id, $student->id);
 137          groups_add_member($group2->id, $teacher->id);
 138  
 139          $this->setUser($student);
 140          // Forum complete.
 141          $completion = new \completion_info($course);
 142          $completion->update_state($cmforum, COMPLETION_COMPLETE);
 143  
 144          $result = core_completion_external::get_activities_completion_status($course->id, $student->id);
 145          // We need to execute the return values cleaning process to simulate the web service server.
 146          $result = \external_api::clean_returnvalue(
 147              core_completion_external::get_activities_completion_status_returns(), $result);
 148  
 149          // We added 5 activities, but only 4 with completion enabled and one of those is hidden.
 150          $numberofactivities = 5;
 151          $numberofhidden = 1;
 152          $numberofcompletions = $numberofactivities - $numberofhidden;
 153          $numberofstatusstudent = 3;
 154  
 155          $this->assertCount($numberofstatusstudent, $result['statuses']);
 156  
 157          $activitiesfound = 0;
 158          foreach ($result['statuses'] as $status) {
 159              if ($status['cmid'] == $forum->cmid and $status['modname'] == 'forum' and $status['instance'] == $forum->id) {
 160                  $activitiesfound++;
 161                  $this->assertEquals(COMPLETION_COMPLETE, $status['state']);
 162                  $this->assertEquals(COMPLETION_TRACKING_MANUAL, $status['tracking']);
 163                  $this->assertTrue($status['valueused']);
 164                  $this->assertTrue($status['hascompletion']);
 165                  $this->assertFalse($status['isautomatic']);
 166                  $this->assertTrue($status['istrackeduser']);
 167                  $this->assertTrue($status['uservisible']);
 168                  $details = $status['details'];
 169                  $this->assertCount(0, $details);
 170              } else if ($status['cmid'] == $forumautocompletion->cmid) {
 171                  $activitiesfound++;
 172                  $this->assertEquals(COMPLETION_INCOMPLETE, $status['state']);
 173                  $this->assertEquals(COMPLETION_TRACKING_AUTOMATIC, $status['tracking']);
 174                  $this->assertFalse($status['valueused']);
 175                  $this->assertTrue($status['hascompletion']);
 176                  $this->assertTrue($status['isautomatic']);
 177                  $this->assertTrue($status['istrackeduser']);
 178                  $this->assertTrue($status['uservisible']);
 179                  $details = $status['details'];
 180                  $this->assertCount(1, $details);
 181                  $this->assertEquals('completionview', $details[0]['rulename']);
 182                  $this->assertEquals(0, $details[0]['rulevalue']['status']);
 183  
 184              } else if ($status['cmid'] == $data->cmid and $status['modname'] == 'data' and $status['instance'] == $data->id) {
 185                  $activitiesfound++;
 186                  $this->assertEquals(COMPLETION_INCOMPLETE, $status['state']);
 187                  $this->assertEquals(COMPLETION_TRACKING_MANUAL, $status['tracking']);
 188                  $this->assertFalse($status['valueused']);
 189                  $this->assertFalse($status['valueused']);
 190                  $this->assertTrue($status['hascompletion']);
 191                  $this->assertFalse($status['isautomatic']);
 192                  $this->assertTrue($status['istrackeduser']);
 193                  $this->assertTrue($status['uservisible']);
 194                  $details = $status['details'];
 195                  $this->assertCount(0, $details);
 196              }
 197          }
 198          $this->assertEquals(3, $activitiesfound);
 199  
 200          // Teacher should see students status, they are in different groups but the teacher can access all groups.
 201          $this->setUser($teacher);
 202          $result = core_completion_external::get_activities_completion_status($course->id, $student->id);
 203          // We need to execute the return values cleaning process to simulate the web service server.
 204          $result = \external_api::clean_returnvalue(
 205              core_completion_external::get_activities_completion_status_returns(), $result);
 206  
 207          $this->assertCount($numberofcompletions, $result['statuses']);
 208  
 209          // Override status by teacher.
 210          $completion->update_state($cmforum, COMPLETION_INCOMPLETE, $student->id, true);
 211  
 212          $result = core_completion_external::get_activities_completion_status($course->id, $student->id);
 213          // We need to execute the return values cleaning process to simulate the web service server.
 214          $result = \external_api::clean_returnvalue(
 215              core_completion_external::get_activities_completion_status_returns(), $result);
 216  
 217          // Check forum has been overriden by the teacher.
 218          foreach ($result['statuses'] as $status) {
 219              if ($status['cmid'] == $forum->cmid) {
 220                  $this->assertEquals(COMPLETION_INCOMPLETE, $status['state']);
 221                  $this->assertEquals(COMPLETION_TRACKING_MANUAL, $status['tracking']);
 222                  $this->assertEquals($teacher->id, $status['overrideby']);
 223                  break;
 224              }
 225          }
 226  
 227          // Teacher should see his own completion status.
 228  
 229          // Forum complete for teacher.
 230          $completion = new \completion_info($course);
 231          $completion->update_state($cmforum, COMPLETION_COMPLETE);
 232  
 233          $result = core_completion_external::get_activities_completion_status($course->id, $teacher->id);
 234          // We need to execute the return values cleaning process to simulate the web service server.
 235          $result = \external_api::clean_returnvalue(
 236              core_completion_external::get_activities_completion_status_returns(), $result);
 237  
 238          $this->assertCount($numberofcompletions, $result['statuses']);
 239  
 240          $activitiesfound = 0;
 241          foreach ($result['statuses'] as $status) {
 242              if ($status['cmid'] == $forum->cmid and $status['modname'] == 'forum' and $status['instance'] == $forum->id) {
 243                  $activitiesfound++;
 244                  $this->assertEquals(COMPLETION_COMPLETE, $status['state']);
 245                  $this->assertEquals(COMPLETION_TRACKING_MANUAL, $status['tracking']);
 246              } else if ($status['cmid'] == $forumautocompletion->cmid) {
 247                  $activitiesfound++;
 248                  $this->assertEquals(COMPLETION_INCOMPLETE, $status['state']);
 249                  $this->assertEquals(COMPLETION_TRACKING_AUTOMATIC, $status['tracking']);
 250              } else {
 251                  $activitiesfound++;
 252                  $this->assertEquals(COMPLETION_INCOMPLETE, $status['state']);
 253                  $this->assertEquals(COMPLETION_TRACKING_MANUAL, $status['tracking']);
 254              }
 255          }
 256          $this->assertEquals(4, $activitiesfound);
 257  
 258          // Change teacher role capabilities (disable access all groups).
 259          $context = \context_course::instance($course->id);
 260          assign_capability('moodle/site:accessallgroups', CAP_PROHIBIT, $teacherrole->id, $context);
 261          accesslib_clear_all_caches_for_unit_testing();
 262  
 263          try {
 264              $result = core_completion_external::get_activities_completion_status($course->id, $student->id);
 265              $this->fail('Exception expected due to groups permissions.');
 266          } catch (\moodle_exception $e) {
 267              $this->assertEquals('accessdenied', $e->errorcode);
 268          }
 269  
 270          // Now add the teacher in the same group.
 271          groups_add_member($group1->id, $teacher->id);
 272          $result = core_completion_external::get_activities_completion_status($course->id, $student->id);
 273          // We need to execute the return values cleaning process to simulate the web service server.
 274          $result = \external_api::clean_returnvalue(
 275              core_completion_external::get_activities_completion_status_returns(), $result);
 276          $this->assertCount($numberofcompletions, $result['statuses']);
 277      }
 278  
 279      /**
 280       * Test override_activity_completion_status
 281       */
 282      public function test_override_activity_completion_status() {
 283          global $DB, $CFG;
 284          $this->resetAfterTest(true);
 285  
 286          // Create course with teacher and student enrolled.
 287          $CFG->enablecompletion = true;
 288          $course  = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
 289          $student = $this->getDataGenerator()->create_user();
 290          $teacher = $this->getDataGenerator()->create_user();
 291          $studentrole = $DB->get_record('role', ['shortname' => 'student']);
 292          $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id);
 293          $teacherrole = $DB->get_record('role', ['shortname' => 'teacher']);
 294          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
 295  
 296          // Create 2 activities, one with manual completion (data), one with automatic completion triggered by viewing it (forum).
 297          $data    = $this->getDataGenerator()->create_module('data', ['course' => $course->id], ['completion' => 1]);
 298          $forum   = $this->getDataGenerator()->create_module('forum',  ['course' => $course->id],
 299                                                              ['completion' => 2, 'completionview' => 1]);
 300          $cmdata = get_coursemodule_from_id('data', $data->cmid);
 301          $cmforum = get_coursemodule_from_id('forum', $forum->cmid);
 302  
 303          // Manually complete the data activity as the student.
 304          $this->setUser($student);
 305          $completion = new \completion_info($course);
 306          $completion->update_state($cmdata, COMPLETION_COMPLETE);
 307  
 308          // Test overriding the status of the manual-completion-activity 'incomplete'.
 309          $this->setUser($teacher);
 310          $result = core_completion_external::override_activity_completion_status($student->id, $data->cmid, COMPLETION_INCOMPLETE);
 311          $result = \external_api::clean_returnvalue(core_completion_external::override_activity_completion_status_returns(), $result);
 312          $this->assertEquals($result['state'], COMPLETION_INCOMPLETE);
 313          $completiondata = $completion->get_data($cmdata, false, $student->id);
 314          $this->assertEquals(COMPLETION_INCOMPLETE, $completiondata->completionstate);
 315  
 316          // Test overriding the status of the manual-completion-activity back to 'complete'.
 317          $result = core_completion_external::override_activity_completion_status($student->id, $data->cmid, COMPLETION_COMPLETE);
 318          $result = \external_api::clean_returnvalue(core_completion_external::override_activity_completion_status_returns(), $result);
 319          $this->assertEquals($result['state'], COMPLETION_COMPLETE);
 320          $completiondata = $completion->get_data($cmdata, false, $student->id);
 321          $this->assertEquals(COMPLETION_COMPLETE, $completiondata->completionstate);
 322  
 323          // Test overriding the status of the auto-completion-activity to 'complete'.
 324          $result = core_completion_external::override_activity_completion_status($student->id, $forum->cmid, COMPLETION_COMPLETE);
 325          $result = \external_api::clean_returnvalue(core_completion_external::override_activity_completion_status_returns(), $result);
 326          $this->assertEquals($result['state'], COMPLETION_COMPLETE);
 327          $completionforum = $completion->get_data($cmforum, false, $student->id);
 328          $this->assertEquals(COMPLETION_COMPLETE, $completionforum->completionstate);
 329  
 330          // Test overriding the status of the auto-completion-activity to 'incomplete'.
 331          $result = core_completion_external::override_activity_completion_status($student->id, $forum->cmid, COMPLETION_INCOMPLETE);
 332          $result = \external_api::clean_returnvalue(core_completion_external::override_activity_completion_status_returns(), $result);
 333          $this->assertEquals($result['state'], COMPLETION_INCOMPLETE);
 334          $completionforum = $completion->get_data($cmforum, false, $student->id);
 335          $this->assertEquals(COMPLETION_INCOMPLETE, $completionforum->completionstate);
 336  
 337          // Test overriding the status of the auto-completion-activity to an invalid state.
 338          $this->expectException('moodle_exception');
 339          core_completion_external::override_activity_completion_status($student->id, $forum->cmid, 3);
 340      }
 341  
 342      /**
 343       * Test overriding the activity completion status as a user without the capability to do so.
 344       */
 345      public function test_override_status_user_without_capability() {
 346          global $DB, $CFG;
 347          $this->resetAfterTest(true);
 348  
 349          // Create course with teacher and student enrolled.
 350          $CFG->enablecompletion = true;
 351          $course  = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
 352          $student = $this->getDataGenerator()->create_user();
 353          $teacher = $this->getDataGenerator()->create_user();
 354          $studentrole = $DB->get_record('role', ['shortname' => 'student']);
 355          $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id);
 356          $teacherrole = $DB->get_record('role', ['shortname' => 'teacher']);
 357          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
 358          $coursecontext = \context_course::instance($course->id);
 359  
 360          // Create an activity with automatic completion (a forum).
 361          $forum   = $this->getDataGenerator()->create_module('forum',  ['course' => $course->id],
 362              ['completion' => 2, 'completionview' => 1]);
 363  
 364          // Test overriding the status of the activity for a user without the capability.
 365          $this->setUser($teacher);
 366          assign_capability('moodle/course:overridecompletion', CAP_PREVENT, $teacherrole->id, $coursecontext);
 367          $this->expectException('required_capability_exception');
 368          core_completion_external::override_activity_completion_status($student->id, $forum->cmid, COMPLETION_COMPLETE);
 369      }
 370  
 371      /**
 372       * Test get_course_completion_status
 373       */
 374      public function test_get_course_completion_status() {
 375          global $DB, $CFG, $COMPLETION_CRITERIA_TYPES;
 376          require_once($CFG->dirroot.'/completion/criteria/completion_criteria_self.php');
 377          require_once($CFG->dirroot.'/completion/criteria/completion_criteria_date.php');
 378          require_once($CFG->dirroot.'/completion/criteria/completion_criteria_unenrol.php');
 379          require_once($CFG->dirroot.'/completion/criteria/completion_criteria_activity.php');
 380          require_once($CFG->dirroot.'/completion/criteria/completion_criteria_duration.php');
 381          require_once($CFG->dirroot.'/completion/criteria/completion_criteria_grade.php');
 382          require_once($CFG->dirroot.'/completion/criteria/completion_criteria_role.php');
 383          require_once($CFG->dirroot.'/completion/criteria/completion_criteria_course.php');
 384  
 385          $this->resetAfterTest(true);
 386  
 387          $CFG->enablecompletion = true;
 388          $student = $this->getDataGenerator()->create_user();
 389          $teacher = $this->getDataGenerator()->create_user();
 390  
 391          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1,
 392                                                                      'groupmode' => SEPARATEGROUPS,
 393                                                                      'groupmodeforce' => 1));
 394  
 395          $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id),
 396                                                               array('completion' => 1));
 397          $forum = $this->getDataGenerator()->create_module('forum',  array('course' => $course->id),
 398                                                               array('completion' => 1));
 399          $assign = $this->getDataGenerator()->create_module('assign',  array('course' => $course->id));
 400  
 401          $cmdata = get_coursemodule_from_id('data', $data->cmid);
 402          $cmforum = get_coursemodule_from_id('forum', $forum->cmid);
 403  
 404          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 405          $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
 406          $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id);
 407          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
 408  
 409          $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
 410          $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
 411          // Teacher and student in different groups initially.
 412          groups_add_member($group1->id, $student->id);
 413          groups_add_member($group2->id, $teacher->id);
 414  
 415          // Set completion rules.
 416          $completion = new \completion_info($course);
 417  
 418          // Loop through each criteria type and run its update_config() method.
 419  
 420          $criteriadata = new \stdClass();
 421          $criteriadata->id = $course->id;
 422          $criteriadata->criteria_activity = array();
 423          // Some activities.
 424          $criteriadata->criteria_activity[$cmdata->id] = 1;
 425          $criteriadata->criteria_activity[$cmforum->id] = 1;
 426  
 427          // In a week criteria date value.
 428          $criteriadata->criteria_date_value = time() + WEEKSECS;
 429  
 430          // Self completion.
 431          $criteriadata->criteria_self = 1;
 432  
 433          foreach ($COMPLETION_CRITERIA_TYPES as $type) {
 434              $class = 'completion_criteria_'.$type;
 435              $criterion = new $class();
 436              $criterion->update_config($criteriadata);
 437          }
 438  
 439          // Handle overall aggregation.
 440          $aggdata = array(
 441              'course'        => $course->id,
 442              'criteriatype'  => null
 443          );
 444          $aggregation = new \completion_aggregation($aggdata);
 445          $aggregation->setMethod(COMPLETION_AGGREGATION_ALL);
 446          $aggregation->save();
 447  
 448          $aggdata['criteriatype'] = COMPLETION_CRITERIA_TYPE_ACTIVITY;
 449          $aggregation = new \completion_aggregation($aggdata);
 450          $aggregation->setMethod(COMPLETION_AGGREGATION_ALL);
 451          $aggregation->save();
 452  
 453          $this->setUser($student);
 454  
 455          $result = core_completion_external::get_course_completion_status($course->id, $student->id);
 456          // We need to execute the return values cleaning process to simulate the web service server.
 457          $studentresult = \external_api::clean_returnvalue(
 458              core_completion_external::get_course_completion_status_returns(), $result);
 459  
 460          // 3 different criteria.
 461          $this->assertCount(3, $studentresult['completionstatus']['completions']);
 462  
 463          $this->assertEquals(COMPLETION_AGGREGATION_ALL, $studentresult['completionstatus']['aggregation']);
 464          $this->assertFalse($studentresult['completionstatus']['completed']);
 465  
 466          $this->assertEquals('No', $studentresult['completionstatus']['completions'][0]['status']);
 467          $this->assertEquals('No', $studentresult['completionstatus']['completions'][1]['status']);
 468          $this->assertEquals('No', $studentresult['completionstatus']['completions'][2]['status']);
 469  
 470          // Teacher should see students status, they are in different groups but the teacher can access all groups.
 471          $this->setUser($teacher);
 472          $result = core_completion_external::get_course_completion_status($course->id, $student->id);
 473          // We need to execute the return values cleaning process to simulate the web service server.
 474          $teacherresult = \external_api::clean_returnvalue(
 475              core_completion_external::get_course_completion_status_returns(), $result);
 476  
 477          $this->assertEquals($studentresult, $teacherresult);
 478  
 479          // Change teacher role capabilities (disable access al goups).
 480          $context = \context_course::instance($course->id);
 481          assign_capability('moodle/site:accessallgroups', CAP_PROHIBIT, $teacherrole->id, $context);
 482          accesslib_clear_all_caches_for_unit_testing();
 483  
 484          try {
 485              $result = core_completion_external::get_course_completion_status($course->id, $student->id);
 486              $this->fail('Exception expected due to groups permissions.');
 487          } catch (\moodle_exception $e) {
 488              $this->assertEquals('accessdenied', $e->errorcode);
 489          }
 490  
 491          // Now add the teacher in the same group.
 492          groups_add_member($group1->id, $teacher->id);
 493          $result = core_completion_external::get_course_completion_status($course->id, $student->id);
 494          // We need to execute the return values cleaning process to simulate the web service server.
 495          $teacherresult = \external_api::clean_returnvalue(
 496              core_completion_external::get_course_completion_status_returns(), $result);
 497  
 498          $this->assertEquals($studentresult, $teacherresult);
 499  
 500      }
 501  
 502      /**
 503       * Test mark_course_self_completed
 504       */
 505      public function test_mark_course_self_completed() {
 506          global $DB, $CFG;
 507          require_once($CFG->dirroot.'/completion/criteria/completion_criteria_self.php');
 508  
 509          $this->resetAfterTest(true);
 510  
 511          $CFG->enablecompletion = true;
 512          $student = $this->getDataGenerator()->create_user();
 513          $teacher = $this->getDataGenerator()->create_user();
 514  
 515          $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
 516  
 517          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
 518          $this->getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id);
 519  
 520          // Set completion rules.
 521          $completion = new \completion_info($course);
 522  
 523          $criteriadata = new \stdClass();
 524          $criteriadata->id = $course->id;
 525          $criteriadata->criteria_activity = array();
 526  
 527          // Self completion.
 528          $criteriadata->criteria_self = COMPLETION_CRITERIA_TYPE_SELF;
 529          $class = 'completion_criteria_self';
 530          $criterion = new $class();
 531          $criterion->update_config($criteriadata);
 532  
 533          // Handle overall aggregation.
 534          $aggdata = array(
 535              'course'        => $course->id,
 536              'criteriatype'  => null
 537          );
 538          $aggregation = new \completion_aggregation($aggdata);
 539          $aggregation->setMethod(COMPLETION_AGGREGATION_ALL);
 540          $aggregation->save();
 541  
 542          $this->setUser($student);
 543  
 544          $result = core_completion_external::mark_course_self_completed($course->id);
 545          // We need to execute the return values cleaning process to simulate the web service server.
 546          $result = \external_api::clean_returnvalue(
 547              core_completion_external::mark_course_self_completed_returns(), $result);
 548  
 549          // We expect a valid result.
 550          $this->assertEquals(true, $result['status']);
 551  
 552          $result = core_completion_external::get_course_completion_status($course->id, $student->id);
 553          // We need to execute the return values cleaning process to simulate the web service server.
 554          $result = \external_api::clean_returnvalue(
 555              core_completion_external::get_course_completion_status_returns(), $result);
 556  
 557          // Course must be completed.
 558          $this->assertEquals(COMPLETION_COMPLETE, $result['completionstatus']['completions'][0]['complete']);
 559  
 560          try {
 561              $result = core_completion_external::mark_course_self_completed($course->id);
 562              $this->fail('Exception expected due course already self completed.');
 563          } catch (\moodle_exception $e) {
 564              $this->assertEquals('useralreadymarkedcomplete', $e->errorcode);
 565          }
 566  
 567      }
 568  
 569  }