Differences Between: [Versions 311 and 403] [Versions 400 and 403] [Versions 401 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 declare(strict_types = 1); 18 19 namespace core_grades\grades\grader\gradingpanel\point\external; 20 21 use advanced_testcase; 22 use coding_exception; 23 use core_grades\component_gradeitem; 24 use core_external\external_api; 25 use mod_forum\local\entities\forum as forum_entity; 26 use moodle_exception; 27 use grade_grade; 28 use grade_item; 29 30 /** 31 * Unit tests for core_grades\component_gradeitems; 32 * 33 * @package core_grades 34 * @category test 35 * @copyright 2019 Andrew Nicols <andrew@nicols.co.uk> 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class store_test extends advanced_testcase { 39 40 /** 41 * Ensure that an execute with an invalid component is rejected. 42 */ 43 public function test_execute_invalid_component(): void { 44 $this->resetAfterTest(); 45 $user = $this->getDataGenerator()->create_user(); 46 $this->setUser($user); 47 48 $this->expectException(coding_exception::class); 49 $this->expectExceptionMessage("The 'foo' item is not valid for the 'mod_invalid' component"); 50 store::execute('mod_invalid', 1, 'foo', 2, false, 'formdata'); 51 } 52 53 /** 54 * Ensure that an execute with an invalid itemname on a valid component is rejected. 55 */ 56 public function test_execute_invalid_itemname(): void { 57 $this->resetAfterTest(); 58 $user = $this->getDataGenerator()->create_user(); 59 $this->setUser($user); 60 61 $this->expectException(coding_exception::class); 62 $this->expectExceptionMessage("The 'foo' item is not valid for the 'mod_forum' component"); 63 store::execute('mod_forum', 1, 'foo', 2, false, 'formdata'); 64 } 65 66 /** 67 * Ensure that an execute against a different grading method is rejected. 68 */ 69 public function test_execute_incorrect_type(): void { 70 $this->resetAfterTest(); 71 72 $forum = $this->get_forum_instance([ 73 // Negative numbers mean a scale. 74 'grade_forum' => -1, 75 ]); 76 $course = $forum->get_course_record(); 77 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 78 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 79 $this->setUser($teacher); 80 81 $gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum'); 82 83 $this->expectException(moodle_exception::class); 84 $this->expectExceptionMessage("not configured for direct grading"); 85 store::execute('mod_forum', (int) $forum->get_context()->id, 'forum', (int) $student->id, false, 'formdata'); 86 } 87 88 /** 89 * Ensure that an execute against a different grading method is rejected. 90 */ 91 public function test_execute_disabled(): void { 92 $this->resetAfterTest(); 93 94 $forum = $this->get_forum_instance(); 95 $course = $forum->get_course_record(); 96 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 97 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 98 $this->setUser($teacher); 99 100 $gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum'); 101 102 $this->expectException(moodle_exception::class); 103 $this->expectExceptionMessage("Grading is not enabled"); 104 store::execute('mod_forum', (int) $forum->get_context()->id, 'forum', (int) $student->id, false, 'formdata'); 105 } 106 107 /** 108 * Ensure that an execute against the correct grading method returns the current state of the user. 109 */ 110 public function test_execute_store_empty(): void { 111 $this->resetAfterTest(); 112 113 $forum = $this->get_forum_instance([ 114 // Negative numbers mean a scale. 115 'grade_forum' => 5, 116 ]); 117 $course = $forum->get_course_record(); 118 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 119 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 120 $this->setUser($teacher); 121 122 $formdata = [ 123 'grade' => null, 124 ]; 125 126 $gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum'); 127 128 $result = store::execute('mod_forum', (int) $forum->get_context()->id, 'forum', 129 (int) $student->id, false, http_build_query($formdata)); 130 $result = external_api::clean_returnvalue(store::execute_returns(), $result); 131 132 // The result should still be empty. 133 $this->assertIsArray($result); 134 $this->assertArrayHasKey('templatename', $result); 135 136 $this->assertEquals('core_grades/grades/grader/gradingpanel/point', $result['templatename']); 137 138 $this->assertArrayHasKey('grade', $result); 139 $this->assertIsArray($result['grade']); 140 $this->assertArrayHasKey('grade', $result['grade']); 141 $this->assertEmpty($result['grade']['grade']); 142 $this->assertArrayHasKey('timecreated', $result['grade']); 143 $this->assertIsInt($result['grade']['timecreated']); 144 $this->assertArrayHasKey('timemodified', $result['grade']); 145 $this->assertIsInt($result['grade']['timemodified']); 146 147 $this->assertArrayHasKey('warnings', $result); 148 $this->assertIsArray($result['warnings']); 149 $this->assertEmpty($result['warnings']); 150 151 // Test the grade array items. 152 $this->assertArrayHasKey('grade', $result); 153 $this->assertIsArray($result['grade']); 154 155 $this->assertArrayHasKey('grade', $result['grade']); 156 $this->assertEquals(null, $result['grade']['grade']); 157 158 $this->assertIsInt($result['grade']['timecreated']); 159 $this->assertArrayHasKey('timemodified', $result['grade']); 160 $this->assertIsInt($result['grade']['timemodified']); 161 162 $this->assertArrayHasKey('usergrade', $result['grade']); 163 $this->assertEquals('- / 5.00', $result['grade']['usergrade']); 164 165 $this->assertArrayHasKey('maxgrade', $result['grade']); 166 $this->assertIsInt($result['grade']['maxgrade']); 167 $this->assertEquals(5, $result['grade']['maxgrade']); 168 169 $this->assertArrayHasKey('gradedby', $result['grade']); 170 $this->assertEquals(fullname($teacher), $result['grade']['gradedby']); 171 172 // Compare against the grade stored in the database. 173 $storedgradeitem = grade_item::fetch([ 174 'courseid' => $forum->get_course_id(), 175 'itemtype' => 'mod', 176 'itemmodule' => 'forum', 177 'iteminstance' => $forum->get_id(), 178 'itemnumber' => $gradeitem->get_grade_itemid(), 179 ]); 180 $storedgrade = grade_grade::fetch([ 181 'userid' => $student->id, 182 'itemid' => $storedgradeitem->id, 183 ]); 184 185 $this->assertEmpty($storedgrade->rawgrade); 186 } 187 188 /** 189 * Ensure that an execute against the correct grading method returns the current state of the user. 190 */ 191 public function test_execute_store_graded(): void { 192 $this->resetAfterTest(); 193 194 $forum = $this->get_forum_instance([ 195 // Negative numbers mean a scale. 196 'grade_forum' => 5, 197 ]); 198 $course = $forum->get_course_record(); 199 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 200 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 201 $this->setUser($teacher); 202 203 $formdata = [ 204 'grade' => 4, 205 ]; 206 207 $gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum'); 208 209 $result = store::execute('mod_forum', (int) $forum->get_context()->id, 'forum', 210 (int) $student->id, false, http_build_query($formdata)); 211 $result = external_api::clean_returnvalue(store::execute_returns(), $result); 212 213 // The result should still be empty. 214 $this->assertIsArray($result); 215 $this->assertArrayHasKey('templatename', $result); 216 217 $this->assertEquals('core_grades/grades/grader/gradingpanel/point', $result['templatename']); 218 219 $this->assertArrayHasKey('warnings', $result); 220 $this->assertIsArray($result['warnings']); 221 $this->assertEmpty($result['warnings']); 222 223 // Test the grade array items. 224 $this->assertArrayHasKey('grade', $result); 225 $this->assertIsArray($result['grade']); 226 227 $this->assertArrayHasKey('grade', $result['grade']); 228 $this->assertEquals(grade_floatval(unformat_float(4)), $result['grade']['grade']); 229 230 $this->assertIsInt($result['grade']['timecreated']); 231 $this->assertArrayHasKey('timemodified', $result['grade']); 232 $this->assertIsInt($result['grade']['timemodified']); 233 234 $this->assertArrayHasKey('usergrade', $result['grade']); 235 $this->assertEquals('4.00 / 5.00', $result['grade']['usergrade']); 236 237 $this->assertArrayHasKey('maxgrade', $result['grade']); 238 $this->assertIsInt($result['grade']['maxgrade']); 239 $this->assertEquals(5, $result['grade']['maxgrade']); 240 241 $this->assertArrayHasKey('gradedby', $result['grade']); 242 $this->assertEquals(fullname($teacher), $result['grade']['gradedby']); 243 244 // Compare against the grade stored in the database. 245 $storedgradeitem = grade_item::fetch([ 246 'courseid' => $forum->get_course_id(), 247 'itemtype' => 'mod', 248 'itemmodule' => 'forum', 249 'iteminstance' => $forum->get_id(), 250 'itemnumber' => $gradeitem->get_grade_itemid(), 251 ]); 252 $storedgrade = grade_grade::fetch([ 253 'userid' => $student->id, 254 'itemid' => $storedgradeitem->id, 255 ]); 256 257 $this->assertEquals(grade_floatval(unformat_float(4)), $storedgrade->rawgrade); 258 } 259 260 /** 261 * Ensure that an out-of-range value is rejected. 262 * 263 * @dataProvider execute_out_of_range_provider 264 * @param int $maxvalue The max value of the forum 265 * @param int $suppliedvalue The value that was submitted 266 */ 267 public function test_execute_store_out_of__range(int $maxvalue, float $suppliedvalue): void { 268 $this->resetAfterTest(); 269 270 $forum = $this->get_forum_instance([ 271 // Negative numbers mean a scale. 272 'grade_forum' => $maxvalue, 273 ]); 274 $course = $forum->get_course_record(); 275 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); 276 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 277 $this->setUser($teacher); 278 279 $formdata = [ 280 'grade' => $suppliedvalue, 281 ]; 282 283 $gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum'); 284 285 $this->expectException(moodle_exception::class); 286 $this->expectExceptionMessage("Invalid grade '{$suppliedvalue}' provided. Grades must be between 0 and {$maxvalue}."); 287 store::execute('mod_forum', (int) $forum->get_context()->id, 'forum', 288 (int) $student->id, false, http_build_query($formdata)); 289 } 290 291 /** 292 * Data provider for out of range tests. 293 * 294 * @return array 295 */ 296 public function execute_out_of_range_provider(): array { 297 return [ 298 'above' => [ 299 'max' => 100, 300 'supplied' => 101, 301 ], 302 'above just' => [ 303 'max' => 100, 304 'supplied' => 101.001, 305 ], 306 'below' => [ 307 'max' => 100, 308 'supplied' => -100, 309 ], 310 '-1' => [ 311 'max' => 100, 312 'supplied' => -1, 313 ], 314 ]; 315 } 316 317 318 /** 319 * Get a forum instance. 320 * 321 * @param array $config 322 * @return forum_entity 323 */ 324 protected function get_forum_instance(array $config = []): forum_entity { 325 $this->resetAfterTest(); 326 327 $datagenerator = $this->getDataGenerator(); 328 $course = $datagenerator->create_course(); 329 $forum = $datagenerator->create_module('forum', array_merge($config, ['course' => $course->id])); 330 331 $vaultfactory = \mod_forum\local\container::get_vault_factory(); 332 $vault = $vaultfactory->get_forum_vault(); 333 334 return $vault->get_from_id((int) $forum->id); 335 } 336 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body