Merge branch 'MDL-58454-master' of git://github.com/junpataleta/moodle
[moodle.git] / lib / tests / grades_externallib_test.php
blobd0afb5e88e285363b9243651a4ce5197ae99722a
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.
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/>.
17 /**
18 * Unit tests for the grade API at /lib/classes/grades_external.php
20 * @package core_grades
21 * @category external
22 * @copyright 2012 Andrew Davis
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 * @since Moodle 2.7
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
31 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
33 /**
34 * Grades functions unit tests
36 * @package core_grades
37 * @category external
38 * @copyright 2012 Andrew Davis
39 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41 class core_grades_external_testcase extends externallib_advanced_testcase {
43 /**
44 * Load initial test information
46 * @param string $assignmentname Assignment name
47 * @param int $student1rawgrade Student 1 grade
48 * @param int $student2rawgrade Student 2 grade
49 * @return array Array of vars with test information
51 protected function load_test_data($assignmentname, $student1rawgrade, $student2rawgrade) {
52 global $DB;
54 // Adds a course, a teacher, 2 students, an assignment and grades for the students.
55 $course = $this->getDataGenerator()->create_course();
56 $coursecontext = context_course::instance($course->id);
58 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
60 $student1 = $this->getDataGenerator()->create_user();
61 $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
63 $student2 = $this->getDataGenerator()->create_user();
64 $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
66 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
67 $teacher = $this->getDataGenerator()->create_user();
68 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
70 $parent = $this->getDataGenerator()->create_user();
71 $this->setUser($parent);
72 $student1context = context_user::instance($student1->id);
73 // Creates a new role, gives it the capability and gives $USER that role.
74 $parentroleid = $this->assignUserCapability('moodle/grade:viewall', $student1context->id);
75 // Enrol the user in the course using the new role.
76 $this->getDataGenerator()->enrol_user($parent->id, $course->id, $parentroleid);
78 $assignment = $this->getDataGenerator()->create_module('assign', array('name' => $assignmentname, 'course' => $course->id));
79 $modcontext = get_coursemodule_from_instance('assign', $assignment->id, $course->id);
80 $assignment->cmidnumber = $modcontext->id;
82 $student1grade = array('userid' => $student1->id, 'rawgrade' => $student1rawgrade);
83 $student2grade = array('userid' => $student2->id, 'rawgrade' => $student2rawgrade);
84 $studentgrades = array($student1->id => $student1grade, $student2->id => $student2grade);
85 assign_grade_item_update($assignment, $studentgrades);
87 // Insert a custom grade scale to be used by an outcome.
88 $gradescale = new grade_scale();
89 $gradescale->name = 'unittestscale3';
90 $gradescale->courseid = $course->id;
91 $gradescale->userid = 0;
92 $gradescale->scale = 'Distinction, Very Good, Good, Pass, Fail';
93 $gradescale->description = 'This scale is used to mark standard assignments.';
94 $gradescale->insert();
96 // Insert an outcome.
97 $data = new stdClass();
98 $data->courseid = $course->id;
99 $data->fullname = 'Team work';
100 $data->shortname = 'Team work';
101 $data->scaleid = $gradescale->id;
102 $outcome = new grade_outcome($data, false);
103 $outcome->insert();
105 $outcomegradeitem = new grade_item();
106 $outcomegradeitem->itemname = $outcome->shortname;
107 $outcomegradeitem->itemtype = 'mod';
108 $outcomegradeitem->itemmodule = 'assign';
109 $outcomegradeitem->iteminstance = $assignment->id;
110 $outcomegradeitem->outcomeid = $outcome->id;
111 $outcomegradeitem->cmid = 0;
112 $outcomegradeitem->courseid = $course->id;
113 $outcomegradeitem->aggregationcoef = 0;
114 $outcomegradeitem->itemnumber = 1; // The activity's original grade item will be 0.
115 $outcomegradeitem->gradetype = GRADE_TYPE_SCALE;
116 $outcomegradeitem->scaleid = $outcome->scaleid;
117 // This next two values for testing that returns parameters are correcly formatted.
118 $outcomegradeitem->set_locked(true);
119 $outcomegradeitem->hidden = '';
120 $outcomegradeitem->insert();
122 $assignmentgradeitem = grade_item::fetch(
123 array(
124 'itemtype' => 'mod',
125 'itemmodule' => 'assign',
126 'iteminstance' => $assignment->id,
127 'itemnumber' => 0,
128 'courseid' => $course->id
131 $outcomegradeitem->set_parent($assignmentgradeitem->categoryid);
132 $outcomegradeitem->move_after_sortorder($assignmentgradeitem->sortorder);
134 return array($course, $assignment, $student1, $student2, $teacher, $parent);
138 * Test get_grades()
140 public function test_get_grades() {
141 global $CFG;
143 $this->resetAfterTest(true);
144 $CFG->enableoutcomes = 1;
146 $assignmentname = 'The assignment';
147 $student1rawgrade = 10;
148 $student2rawgrade = 20;
149 list($course, $assignment, $student1, $student2, $teacher, $parent) =
150 $this->load_test_data($assignmentname, $student1rawgrade, $student2rawgrade);
151 $assigmentcm = get_coursemodule_from_id('assign', $assignment->cmid, 0, false, MUST_EXIST);
153 // Teacher requesting a student grade for the assignment.
154 $this->setUser($teacher);
155 $grades = core_grades_external::get_grades(
156 $course->id,
157 'mod_assign',
158 $assigmentcm->id,
159 array($student1->id)
161 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
162 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id));
164 // Teacher requesting all the grades of student1 in a course.
165 $grades = core_grades_external::get_grades(
166 $course->id,
167 null,
168 null,
169 array($student1->id)
171 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
172 $this->assertTrue(count($grades['items']) == 2);
173 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id));
174 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, 'course', $student1->id));
176 $outcome = $this->get_outcome($grades, $assigmentcm->id);
177 $this->assertEquals($outcome['name'], 'Team work');
178 $this->assertEquals(0, $this->get_outcome_student_grade($grades, $assigmentcm->id, $student1->id));
180 // Teacher requesting all the grades of all the students in a course.
181 $grades = core_grades_external::get_grades(
182 $course->id,
183 null,
184 null,
185 array($student1->id, $student2->id)
187 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
188 $this->assertTrue(count($grades['items']) == 2);
189 $this->assertTrue(count($grades['items'][0]['grades']) == 2);
190 $this->assertTrue(count($grades['items'][1]['grades']) == 2);
192 // Student requesting another student's grade for the assignment (should fail).
193 $this->setUser($student1);
194 try {
195 $grades = core_grades_external::get_grades(
196 $course->id,
197 'mod_assign',
198 $assigmentcm->id,
199 array($student2->id)
201 $this->fail('moodle_exception expected');
202 } catch (moodle_exception $ex) {
203 $this->assertTrue(true);
206 // Parent requesting their child's grade for the assignment (should fail).
207 $this->setUser($parent);
208 try {
209 $grades = core_grades_external::get_grades(
210 $course->id,
211 'mod_assign',
212 $assigmentcm->id,
213 array($student1->id)
215 $this->fail('moodle_exception expected');
216 } catch (moodle_exception $ex) {
217 $this->assertTrue(true);
220 // Parent requesting another student's grade for the assignment(should fail).
221 try {
222 $grades = core_grades_external::get_grades(
223 $course->id,
224 'mod_assign',
225 $assigmentcm->id,
226 array($student2->id)
228 $this->fail('moodle_exception expected');
229 } catch (moodle_exception $ex) {
230 $this->assertTrue(true);
233 // Student requesting all other student grades for the assignment (should fail).
234 try {
235 $grades = core_grades_external::get_grades(
236 $course->id,
237 'mod_assign',
238 $assigmentcm->id,
239 array($student1->id, $student2->id)
241 $this->fail('moodle_exception expected');
242 } catch (moodle_exception $ex) {
243 $this->assertTrue(true);
246 // Student requesting only grade item information (should fail).
247 try {
248 $grades = core_grades_external::get_grades(
249 $course->id,
250 'mod_assign',
251 $assigmentcm->id,
252 array()
254 $this->fail('moodle_exception expected');
255 } catch (moodle_exception $ex) {
256 $this->assertTrue(true);
259 // Teacher requesting student grades for a course.
260 $this->setUser($teacher);
261 $grades = core_grades_external::get_grades(
262 $course->id,
263 'mod_assign',
264 $assigmentcm->id,
265 array($student1->id, $student2->id)
267 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
268 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id));
269 $this->assertEquals($student2rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student2->id));
271 // Teacher requesting grade item information.
272 $grades = core_grades_external::get_grades(
273 $course->id,
274 'mod_assign',
275 $assigmentcm->id
277 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
278 $activity = $this->get_activity($grades, $assigmentcm->id);
279 $this->assertEquals($activity['name'], $assignmentname);
280 $this->assertEquals(count($activity['grades']), 0);
282 // Teacher requesting all grade items in a course.
283 $grades = core_grades_external::get_grades(
284 $course->id
286 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
287 $this->assertTrue(count($grades['items']) == 2);
289 $activity = $this->get_activity($grades, $assigmentcm->id);
290 $this->assertEquals($activity['name'], $assignmentname);
291 $this->assertEquals(count($activity['grades']), 0);
293 $outcome = $this->get_outcome($grades, $assigmentcm->id);
294 $this->assertEquals($outcome['name'], 'Team work');
296 // Hide a grade item then have student request it.
297 $result = core_grades_external::update_grades(
298 'test',
299 $course->id,
300 'mod_assign',
301 $assigmentcm->id,
303 array(),
304 array('hidden' => 1)
306 $result = external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result);
307 $this->assertTrue($result == GRADE_UPDATE_OK);
309 // Check it's definitely hidden.
310 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id);
311 $this->assertEquals($grades->items[0]->hidden, 1);
313 // Teacher should still be able to see the hidden grades.
314 $this->setUser($teacher);
315 $grades = core_grades_external::get_grades(
316 $course->id,
317 'mod_assign',
318 $assigmentcm->id,
319 array($student1->id)
321 $grades = external_api::clean_returnvalue(core_grades_external::get_grades_returns(), $grades);
322 $this->assertEquals($student1rawgrade, $this->get_activity_student_grade($grades, $assigmentcm->id, $student1->id));
326 * Get an activity
328 * @param array $grades Array of grades
329 * @param int $cmid Activity course module id
330 * @return strdClass Activity object
332 private function get_activity($grades, $cmid) {
333 foreach ($grades['items'] as $item) {
334 if ($item['activityid'] == $cmid) {
335 return $item;
338 return null;
342 * Get a grade for an activity
344 * @param array $grades Array of grades
345 * @param int $cmid Activity course module id
346 * @param int $studentid Student it
347 * @return stdClass Activity Object
349 private function get_activity_student_grade($grades, $cmid, $studentid) {
350 $item = $this->get_activity($grades, $cmid);
351 foreach ($item['grades'] as $grade) {
352 if ($grade['userid'] == $studentid) {
353 return $grade['grade'];
356 return null;
360 * Get an ouctome
362 * @param array $grades Array of grades
363 * @param int $cmid Activity course module id
364 * @return stdClass Outcome object
366 private function get_outcome($grades, $cmid) {
367 foreach ($grades['outcomes'] as $outcome) {
368 if ($outcome['activityid'] == $cmid) {
369 return $outcome;
372 return null;
376 * Get a grade from an outcome
378 * @param array $grades Array of grades
379 * @param int $cmid Activity course module id
380 * @param int $studentid Student id
381 * @return stdClass Outcome object
383 private function get_outcome_student_grade($grades, $cmid, $studentid) {
384 $outcome = $this->get_outcome($grades, $cmid);
385 foreach ($outcome['grades'] as $grade) {
386 if ($grade['userid'] == $studentid) {
387 return $grade['grade'];
390 return null;
394 * Test update_grades()
396 public function test_update_grades() {
397 global $DB;
399 $this->resetAfterTest(true);
401 $assignmentname = 'The assignment';
402 $student1rawgrade = 10;
403 $student2rawgrade = 20;
404 list($course, $assignment, $student1, $student2, $teacher, $parent) =
405 $this->load_test_data($assignmentname, $student1rawgrade, $student2rawgrade);
406 $assigmentcm = get_coursemodule_from_id('assign', $assignment->cmid, 0, false, MUST_EXIST);
408 $this->setUser($teacher);
410 // Teacher updating grade item information.
411 $changedmax = 93;
412 $result = core_grades_external::update_grades(
413 'test',
414 $course->id,
415 'mod_assign',
416 $assigmentcm->id,
418 array(),
419 array('grademax' => $changedmax)
421 $result = external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result);
422 $this->assertTrue($result == GRADE_UPDATE_OK);
423 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id);
424 $this->assertTrue($grades->items[0]->grademax == $changedmax);
426 // Teacher updating 1 student grade.
427 $student1grade = 23;
428 $result = core_grades_external::update_grades(
429 'test',
430 $course->id,
431 'mod_assign',
432 $assigmentcm->id,
434 array(array('studentid' => $student1->id, 'grade' => $student1grade))
436 $result = external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result);
437 $this->assertTrue($result == GRADE_UPDATE_OK);
438 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id, array($student1->id));
439 $this->assertTrue($grades->items[0]->grades[$student1->id]->grade == $student1grade);
441 // Teacher updating multiple student grades.
442 $student1grade = 11;
443 $student2grade = 13;
444 $result = core_grades_external::update_grades(
445 'test',
446 $course->id,
447 'mod_assign',
448 $assigmentcm->id,
450 array(
451 array('studentid' => $student1->id, 'grade' => $student1grade),
452 array('studentid' => $student2->id, 'grade' => $student2grade)
455 $result = external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result);
456 $this->assertTrue($result == GRADE_UPDATE_OK);
457 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id, array($student1->id, $student2->id));
458 $this->assertTrue($grades->items[0]->grades[$student1->id]->grade == $student1grade);
459 $this->assertTrue($grades->items[0]->grades[$student2->id]->grade == $student2grade);
461 // Student attempting to update their own grade (should fail).
462 $this->setUser($student1);
463 try {
464 $student1grade = 17;
465 $result = core_grades_external::update_grades(
466 'test',
467 $course->id,
468 'mod_assign',
469 $assigmentcm->id,
471 array( array('studentid' => $student1->id, 'grade' => $student1grade))
473 $this->fail('moodle_exception expected');
474 } catch (moodle_exception $ex) {
475 $this->assertTrue(true);
478 // Parent attempting to update their child's grade (should fail).
479 $this->setUser($parent);
480 try {
481 $student1grade = 13;
482 $result = core_grades_external::update_grades(
483 'test',
484 $course->id,
485 'mod_assign',
486 $assigmentcm->id,
488 array( array('studentid' => $student1->id, 'grade' => $student1grade))
490 $this->fail('moodle_exception expected');
491 } catch (moodle_exception $ex) {
492 $this->assertTrue(true);
495 // Student trying to hide a grade item (should fail).
496 $this->setUser($student1);
497 try {
498 $result = core_grades_external::update_grades(
499 'test',
500 $course->id,
501 'mod_assign',
502 $assigmentcm->id,
504 array(),
505 array('hidden' => 1)
507 $this->fail('moodle_exception expected');
508 } catch (moodle_exception $ex) {
509 $this->assertTrue(true);
512 // Give the student role 'moodle/grade:hide' and they should now be able to hide the grade item.
513 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
514 $coursecontext = context_course::instance($course->id);
515 assign_capability('moodle/grade:hide', CAP_ALLOW, $studentrole->id, $coursecontext->id);
516 accesslib_clear_all_caches_for_unit_testing();
518 // Check the activity isn't already hidden.
519 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id);
520 $this->assertTrue($grades->items[0]->hidden == 0);
522 $result = core_grades_external::update_grades(
523 'test',
524 $course->id,
525 'mod_assign',
526 $assigmentcm->id,
528 array(),
529 array('hidden' => 1)
531 $result = external_api::clean_returnvalue(core_grades_external::update_grades_returns(), $result);
532 $this->assertTrue($result == GRADE_UPDATE_OK);
533 $grades = grade_get_grades($course->id, 'mod', 'assign', $assignment->id);
534 $this->assertTrue($grades->items[0]->hidden == 1);