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 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 gradereport_user;
  18  
  19  use externallib_advanced_testcase;
  20  use gradereport_user_external;
  21  
  22  defined('MOODLE_INTERNAL') || die();
  23  
  24  global $CFG;
  25  
  26  require_once($CFG->dirroot . '/webservice/tests/helpers.php');
  27  require_once($CFG->dirroot . '/grade/report/user/externallib.php');
  28  
  29  /**
  30   * User grade report functions unit tests
  31   *
  32   * @package    gradereport_user
  33   * @category   external
  34   * @copyright  2015 Juan Leyva <juan@moodle.com>
  35   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class externallib_test extends externallib_advanced_testcase {
  38  
  39      /**
  40       * Loads some data to be used by the different tests
  41       * @param  int $s1grade Student 1 grade
  42       * @param  int $s2grade Student 2 grade
  43       * @return array          Course and users instances
  44       */
  45      private function load_data($s1grade, $s2grade) {
  46          global $DB;
  47  
  48          $course = $this->getDataGenerator()->create_course(array('groupmode' => SEPARATEGROUPS, 'groupmodeforce' => 1));
  49  
  50          $studentrole = $DB->get_record('role', array('shortname' => 'student'));
  51          $student1 = $this->getDataGenerator()->create_user(array('idnumber' => 'testidnumber'));
  52          $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
  53  
  54          $student2 = $this->getDataGenerator()->create_user();
  55          $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
  56  
  57          $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
  58          $teacher = $this->getDataGenerator()->create_user();
  59          $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
  60  
  61          $context = \context_course::instance($course->id);
  62          assign_capability('moodle/site:accessallgroups', CAP_PROHIBIT, $teacherrole->id, $context);
  63          accesslib_clear_all_caches_for_unit_testing();
  64  
  65          $group1 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
  66          $group2 = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
  67          groups_add_member($group1->id, $student1->id);
  68          groups_add_member($group1->id, $teacher->id);
  69          groups_add_member($group2->id, $student2->id);
  70  
  71          $assignment = $this->getDataGenerator()->create_module('assign', array('name' => "Test assign", 'course' => $course->id));
  72          $modcontext = get_coursemodule_from_instance('assign', $assignment->id, $course->id);
  73          $assignment->cmidnumber = $modcontext->id;
  74  
  75          $student1grade = array('userid' => $student1->id, 'rawgrade' => $s1grade, 'idnumber' => 'testidnumber1');
  76          $student2grade = array('userid' => $student2->id, 'rawgrade' => $s2grade, 'idnumber' => 'testidnumber2');
  77          $studentgrades = array($student1->id => $student1grade, $student2->id => $student2grade);
  78          assign_grade_item_update($assignment, $studentgrades);
  79  
  80          return array($course, $teacher, $student1, $student2, $assignment);
  81      }
  82  
  83      /**
  84       * Test get_grades_table function case teacher
  85       */
  86      public function test_get_grades_table_teacher() {
  87  
  88          $this->resetAfterTest(true);
  89  
  90          $s1grade = 80;
  91          $s2grade = 60;
  92  
  93          list($course, $teacher, $student1, $student2, $assignment) = $this->load_data($s1grade, $s2grade);
  94  
  95          // A teacher must see all student grades (in their group only).
  96          $this->setUser($teacher);
  97  
  98          $studentgrades = gradereport_user_external::get_grades_table($course->id);
  99          $studentgrades = \external_api::clean_returnvalue(gradereport_user_external::get_grades_table_returns(), $studentgrades);
 100  
 101          // No warnings returned.
 102          $this->assertCount(0, $studentgrades['warnings']);
 103  
 104          // Check that only grades for the student in the teacher group are returned.
 105          $this->assertCount(1, $studentgrades['tables']);
 106  
 107          // Read returned grades.
 108          $studentreturnedgrades = array();
 109          $studentreturnedgrades[$studentgrades['tables'][0]['userid']] =
 110              (int) $studentgrades['tables'][0]['tabledata'][1]['grade']['content'];
 111  
 112          $this->assertEquals($s1grade, $studentreturnedgrades[$student1->id]);
 113      }
 114  
 115      /**
 116       * Test get_grades_table function case student
 117       */
 118      public function test_get_grades_table_student() {
 119          global $CFG, $DB;
 120  
 121          $this->resetAfterTest(true);
 122  
 123          $s1grade = 80;
 124          $s2grade = 60;
 125  
 126          list($course, $teacher, $student1, $student2, $assignment) = $this->load_data($s1grade, $s2grade);
 127  
 128          // A user can see his own grades.
 129          $this->setUser($student1);
 130          $studentgrade = gradereport_user_external::get_grades_table($course->id, $student1->id);
 131          $studentgrade = \external_api::clean_returnvalue(gradereport_user_external::get_grades_table_returns(), $studentgrade);
 132  
 133          // No warnings returned.
 134          $this->assertTrue(count($studentgrade['warnings']) == 0);
 135  
 136          $this->assertTrue(count($studentgrade['tables']) == 1);
 137          $student1returnedgrade = (int) $studentgrade['tables'][0]['tabledata'][1]['grade']['content'];
 138          $this->assertEquals($s1grade, $student1returnedgrade);
 139  
 140      }
 141  
 142      /**
 143       * Test get_grades_table function case incorrect permissions
 144       */
 145      public function test_get_grades_table_permissions() {
 146          global $CFG, $DB;
 147  
 148          $this->resetAfterTest(true);
 149  
 150          $s1grade = 80;
 151          $s2grade = 60;
 152  
 153          list($course, $teacher, $student1, $student2, $assignment) = $this->load_data($s1grade, $s2grade);
 154  
 155          $this->setUser($student2);
 156  
 157          try {
 158              $studentgrade = gradereport_user_external::get_grades_table($course->id, $student1->id);
 159              $this->fail('Exception expected due to not perissions to view other user grades.');
 160          } catch (\moodle_exception $e) {
 161              $this->assertEquals('notingroup', $e->errorcode);
 162          }
 163      }
 164  
 165      /**
 166       * Test view_grade_report function
 167       */
 168      public function test_view_grade_report() {
 169          global $USER;
 170  
 171          $this->resetAfterTest(true);
 172  
 173          $s1grade = 80;
 174          $s2grade = 60;
 175          list($course, $teacher, $student1, $student2, $assignment) = $this->load_data($s1grade, $s2grade);
 176  
 177          // Redirect events to the sink, so we can recover them later.
 178          $sink = $this->redirectEvents();
 179  
 180          $this->setUser($student1);
 181          $result = gradereport_user_external::view_grade_report($course->id);
 182          $result = \external_api::clean_returnvalue(gradereport_user_external::view_grade_report_returns(), $result);
 183          $events = $sink->get_events();
 184          $this->assertCount(1, $events);
 185          $event = reset($events);
 186  
 187          // Check the event details are correct.
 188          $this->assertInstanceOf('\gradereport_user\event\grade_report_viewed', $event);
 189          $this->assertEquals(\context_course::instance($course->id), $event->get_context());
 190          $this->assertEquals($USER->id, $event->get_data()['relateduserid']);
 191  
 192          $this->setUser($teacher);
 193          $result = gradereport_user_external::view_grade_report($course->id, $student1->id);
 194          $result = \external_api::clean_returnvalue(gradereport_user_external::view_grade_report_returns(), $result);
 195          $events = $sink->get_events();
 196          $event = reset($events);
 197          $sink->close();
 198  
 199          // Check the event details are correct.
 200          $this->assertInstanceOf('\gradereport_user\event\grade_report_viewed', $event);
 201          $this->assertEquals(\context_course::instance($course->id), $event->get_context());
 202          $this->assertEquals($student1->id, $event->get_data()['relateduserid']);
 203  
 204          $this->setUser($student2);
 205          try {
 206              $studentgrade = gradereport_user_external::view_grade_report($course->id, $student1->id);
 207              $this->fail('Exception expected due to not permissions to view other user grades.');
 208          } catch (\moodle_exception $e) {
 209              $this->assertEquals('nopermissiontoviewgrades', $e->errorcode);
 210          }
 211      }
 212  
 213      /**
 214       * Test get_grades_items function case teacher
 215       */
 216      public function test_get_grade_items_teacher() {
 217  
 218          $this->resetAfterTest(true);
 219  
 220          $s1grade = 80;
 221          $s2grade = 60;
 222  
 223          list($course, $teacher, $student1, $student2, $assignment) = $this->load_data($s1grade, $s2grade);
 224  
 225          // A teacher must see all student grades (in their group only).
 226          $this->setUser($teacher);
 227  
 228          grade_set_setting($course->id, 'report_user_showrank', 1);
 229          grade_set_setting($course->id, 'report_user_showpercentage', 1);
 230          grade_set_setting($course->id, 'report_user_showhiddenitems', 1);
 231          grade_set_setting($course->id, 'report_user_showgrade', 1);
 232          grade_set_setting($course->id, 'report_user_showfeedback', 1);
 233          grade_set_setting($course->id, 'report_user_showweight', 1);
 234          grade_set_setting($course->id, 'report_user_showcontributiontocoursetotal', 1);
 235          grade_set_setting($course->id, 'report_user_showlettergrade', 1);
 236          grade_set_setting($course->id, 'report_user_showaverage', 1);
 237  
 238          $studentgrades = gradereport_user_external::get_grade_items($course->id);
 239          $studentgrades = \external_api::clean_returnvalue(gradereport_user_external::get_grade_items_returns(), $studentgrades);
 240          // No warnings returned.
 241          $this->assertCount(0, $studentgrades['warnings']);
 242  
 243          // Check that only grades for the student in the teacher group are returned.
 244          $this->assertCount(1, $studentgrades['usergrades']);
 245          $this->assertCount(2, $studentgrades['usergrades'][0]['gradeitems']);
 246  
 247          $this->assertEquals($course->id, $studentgrades['usergrades'][0]['courseid']);
 248          $this->assertEquals($student1->id, $studentgrades['usergrades'][0]['userid']);
 249          // Module grades.
 250          $this->assertEquals($assignment->name, $studentgrades['usergrades'][0]['gradeitems'][0]['itemname']);
 251          $this->assertEquals('mod', $studentgrades['usergrades'][0]['gradeitems'][0]['itemtype']);
 252          $this->assertEquals('assign', $studentgrades['usergrades'][0]['gradeitems'][0]['itemmodule']);
 253          $this->assertEquals($assignment->id, $studentgrades['usergrades'][0]['gradeitems'][0]['iteminstance']);
 254          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][0]['locked']);
 255          $this->assertEquals($assignment->cmidnumber, $studentgrades['usergrades'][0]['gradeitems'][0]['cmid']);
 256          $this->assertEquals(0, $studentgrades['usergrades'][0]['gradeitems'][0]['itemnumber']);
 257          $this->assertEmpty($studentgrades['usergrades'][0]['gradeitems'][0]['outcomeid']);
 258          $this->assertEmpty($studentgrades['usergrades'][0]['gradeitems'][0]['scaleid']);
 259          $this->assertEquals(80, $studentgrades['usergrades'][0]['gradeitems'][0]['graderaw']);
 260          $this->assertEquals('80.00', $studentgrades['usergrades'][0]['gradeitems'][0]['gradeformatted']);
 261          $this->assertEquals(0, $studentgrades['usergrades'][0]['gradeitems'][0]['grademin']);
 262          $this->assertEquals(100, $studentgrades['usergrades'][0]['gradeitems'][0]['grademax']);
 263          $this->assertEquals('0&ndash;100', $studentgrades['usergrades'][0]['gradeitems'][0]['rangeformatted']);
 264          $this->assertEquals('80.00 %', $studentgrades['usergrades'][0]['gradeitems'][0]['percentageformatted']);
 265          $this->assertEmpty($studentgrades['usergrades'][0]['gradeitems'][0]['feedback']);
 266          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][0]['gradehiddenbydate']);
 267          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][0]['gradeneedsupdate']);
 268          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][0]['gradeishidden']);
 269          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][0]['gradeislocked']);
 270          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][0]['gradeisoverridden']);
 271          $this->assertEquals('B-', $studentgrades['usergrades'][0]['gradeitems'][0]['lettergradeformatted']);
 272          $this->assertEquals(1, $studentgrades['usergrades'][0]['gradeitems'][0]['rank']);
 273          $this->assertEquals(2, $studentgrades['usergrades'][0]['gradeitems'][0]['numusers']);
 274          $this->assertEquals(70, $studentgrades['usergrades'][0]['gradeitems'][0]['averageformatted']);
 275  
 276          // Course grades.
 277          $this->assertEquals('course', $studentgrades['usergrades'][0]['gradeitems'][1]['itemtype']);
 278          $this->assertEquals(80, $studentgrades['usergrades'][0]['gradeitems'][1]['graderaw']);
 279          $this->assertEquals('80.00', $studentgrades['usergrades'][0]['gradeitems'][1]['gradeformatted']);
 280          $this->assertEquals(0, $studentgrades['usergrades'][0]['gradeitems'][1]['grademin']);
 281          $this->assertEquals(100, $studentgrades['usergrades'][0]['gradeitems'][1]['grademax']);
 282          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][1]['locked']);
 283          $this->assertEquals('0&ndash;100', $studentgrades['usergrades'][0]['gradeitems'][1]['rangeformatted']);
 284          $this->assertEquals('80.00 %', $studentgrades['usergrades'][0]['gradeitems'][1]['percentageformatted']);
 285          $this->assertEmpty($studentgrades['usergrades'][0]['gradeitems'][1]['feedback']);
 286          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][1]['gradehiddenbydate']);
 287          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][1]['gradeneedsupdate']);
 288          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][1]['gradeishidden']);
 289          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][1]['gradeislocked']);
 290          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][1]['gradeisoverridden']);
 291          $this->assertEquals('B-', $studentgrades['usergrades'][0]['gradeitems'][1]['lettergradeformatted']);
 292          $this->assertEquals(1, $studentgrades['usergrades'][0]['gradeitems'][1]['rank']);
 293          $this->assertEquals(2, $studentgrades['usergrades'][0]['gradeitems'][1]['numusers']);
 294          $this->assertEquals(70, $studentgrades['usergrades'][0]['gradeitems'][1]['averageformatted']);
 295  
 296          // Now, override and lock a grade.
 297          $gradegrade = \grade_grade::fetch(['itemid' => $studentgrades['usergrades'][0]['gradeitems'][0]['id'],
 298              'userid' => $studentgrades['usergrades'][0]['userid']]);
 299          $gradegrade->set_overridden(true);
 300          $gradegrade->set_locked(1);
 301  
 302          $studentgrades = gradereport_user_external::get_grade_items($course->id);
 303          $studentgrades = \external_api::clean_returnvalue(gradereport_user_external::get_grade_items_returns(), $studentgrades);
 304          // No warnings returned.
 305          $this->assertCount(0, $studentgrades['warnings']);
 306  
 307          // Module grades.
 308          $this->assertTrue($studentgrades['usergrades'][0]['gradeitems'][0]['gradeislocked']);
 309          $this->assertTrue($studentgrades['usergrades'][0]['gradeitems'][0]['gradeisoverridden']);
 310      }
 311  
 312      /**
 313       * Test get_grades_items function case student
 314       */
 315      public function test_get_grade_items_student() {
 316  
 317          $this->resetAfterTest(true);
 318  
 319          $s1grade = 80;
 320          $s2grade = 60;
 321  
 322          list($course, $teacher, $student1, $student2, $assignment) = $this->load_data($s1grade, $s2grade);
 323  
 324          grade_set_setting($course->id, 'report_user_showrank', 1);
 325          grade_set_setting($course->id, 'report_user_showpercentage', 1);
 326          grade_set_setting($course->id, 'report_user_showgrade', 1);
 327          grade_set_setting($course->id, 'report_user_showfeedback', 1);
 328          grade_set_setting($course->id, 'report_user_showweight', 1);
 329          grade_set_setting($course->id, 'report_user_showcontributiontocoursetotal', 1);
 330          grade_set_setting($course->id, 'report_user_showlettergrade', 1);
 331          grade_set_setting($course->id, 'report_user_showaverage', 1);
 332  
 333          $this->setUser($student1);
 334  
 335          $studentgrades = gradereport_user_external::get_grade_items($course->id, $student1->id);
 336          $studentgrades = \external_api::clean_returnvalue(gradereport_user_external::get_grade_items_returns(), $studentgrades);
 337          // No warnings returned.
 338          $this->assertCount(0, $studentgrades['warnings']);
 339  
 340          // Check that only grades for the student in the teacher group are returned.
 341          $this->assertCount(1, $studentgrades['usergrades']);
 342          $this->assertCount(2, $studentgrades['usergrades'][0]['gradeitems']);
 343  
 344          $this->assertEquals($course->id, $studentgrades['usergrades'][0]['courseid']);
 345          $this->assertEquals($student1->id, $studentgrades['usergrades'][0]['userid']);
 346          $this->assertEquals($student1->idnumber, $studentgrades['usergrades'][0]['useridnumber']);
 347          $this->assertEquals($assignment->name, $studentgrades['usergrades'][0]['gradeitems'][0]['itemname']);
 348          $this->assertEquals('mod', $studentgrades['usergrades'][0]['gradeitems'][0]['itemtype']);
 349          $this->assertEquals('assign', $studentgrades['usergrades'][0]['gradeitems'][0]['itemmodule']);
 350          $this->assertEquals($assignment->id, $studentgrades['usergrades'][0]['gradeitems'][0]['iteminstance']);
 351          $this->assertNull($studentgrades['usergrades'][0]['gradeitems'][0]['locked']);
 352          $this->assertEquals($assignment->cmidnumber, $studentgrades['usergrades'][0]['gradeitems'][0]['cmid']);
 353          $this->assertEquals(0, $studentgrades['usergrades'][0]['gradeitems'][0]['itemnumber']);
 354          $this->assertEmpty($studentgrades['usergrades'][0]['gradeitems'][0]['outcomeid']);
 355          $this->assertEmpty($studentgrades['usergrades'][0]['gradeitems'][0]['scaleid']);
 356          $this->assertEquals(80, $studentgrades['usergrades'][0]['gradeitems'][0]['graderaw']);
 357          $this->assertEquals('80.00', $studentgrades['usergrades'][0]['gradeitems'][0]['gradeformatted']);
 358          $this->assertEquals(0, $studentgrades['usergrades'][0]['gradeitems'][0]['grademin']);
 359          $this->assertEquals(100, $studentgrades['usergrades'][0]['gradeitems'][0]['grademax']);
 360          $this->assertEquals('0&ndash;100', $studentgrades['usergrades'][0]['gradeitems'][0]['rangeformatted']);
 361          $this->assertEquals('80.00 %', $studentgrades['usergrades'][0]['gradeitems'][0]['percentageformatted']);
 362          $this->assertEmpty($studentgrades['usergrades'][0]['gradeitems'][0]['feedback']);
 363          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][0]['gradehiddenbydate']);
 364          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][0]['gradeneedsupdate']);
 365          $this->assertFalse($studentgrades['usergrades'][0]['gradeitems'][0]['gradeishidden']);
 366          $this->assertNull($studentgrades['usergrades'][0]['gradeitems'][0]['gradeislocked']);
 367          $this->assertNull($studentgrades['usergrades'][0]['gradeitems'][0]['gradeisoverridden']);
 368          $this->assertEquals('B-', $studentgrades['usergrades'][0]['gradeitems'][0]['lettergradeformatted']);
 369          $this->assertEquals(1, $studentgrades['usergrades'][0]['gradeitems'][0]['rank']);
 370          $this->assertEquals(2, $studentgrades['usergrades'][0]['gradeitems'][0]['numusers']);
 371          $this->assertEquals(70, $studentgrades['usergrades'][0]['gradeitems'][0]['averageformatted']);
 372  
 373          // Check that the idnumber for assignment grades is equal to the cmid.
 374          $this->assertEquals((string) $studentgrades['usergrades'][0]['gradeitems'][0]['cmid'],
 375              $studentgrades['usergrades'][0]['gradeitems'][0]['idnumber']);
 376  
 377          // Hide one grade for the user.
 378          $gradegrade = new \grade_grade(array('userid' => $student1->id,
 379                                          'itemid' => $studentgrades['usergrades'][0]['gradeitems'][0]['id']), true);
 380          $gradegrade->set_hidden(1);
 381          $studentgrades = gradereport_user_external::get_grade_items($course->id, $student1->id);
 382          $studentgrades = \external_api::clean_returnvalue(gradereport_user_external::get_grade_items_returns(), $studentgrades);
 383  
 384          // Check we get only the course final grade.
 385          $this->assertCount(1, $studentgrades['usergrades']);
 386          $this->assertCount(1, $studentgrades['usergrades'][0]['gradeitems']);
 387          $this->assertEquals('course', $studentgrades['usergrades'][0]['gradeitems'][0]['itemtype']);
 388      }
 389  
 390  }