Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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\scale\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' => 5,
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 grading with scales");
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
        [
112
            'forum' => $forum,
113
            'options' => $options,
114
            'student' => $student,
115
            'teacher' => $teacher,
116
        ] = $this->get_test_data();
117
 
118
        $this->setUser($teacher);
119
 
120
        $formdata = [
121
            'grade' => null,
122
        ];
123
 
124
        $gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
125
 
126
        $result = store::execute('mod_forum', (int) $forum->get_context()->id, 'forum',
127
                (int) $student->id, false, http_build_query($formdata));
128
        $result = external_api::clean_returnvalue(store::execute_returns(), $result);
129
 
130
        // The result should still be empty.
131
        $this->assertIsArray($result);
132
        $this->assertArrayHasKey('templatename', $result);
133
 
134
        $this->assertEquals('core_grades/grades/grader/gradingpanel/scale', $result['templatename']);
135
 
136
        $this->assertArrayHasKey('warnings', $result);
137
        $this->assertIsArray($result['warnings']);
138
        $this->assertEmpty($result['warnings']);
139
 
140
        // Test the grade array items.
141
        $this->assertArrayHasKey('grade', $result);
142
        $this->assertIsArray($result['grade']);
143
 
144
        $this->assertIsInt($result['grade']['timecreated']);
145
        $this->assertArrayHasKey('timemodified', $result['grade']);
146
        $this->assertIsInt($result['grade']['timemodified']);
147
 
148
        $this->assertArrayHasKey('usergrade', $result['grade']);
149
        $this->assertEquals('-', $result['grade']['usergrade']);
150
 
151
        $this->assertArrayHasKey('maxgrade', $result['grade']);
152
        $this->assertIsInt($result['grade']['maxgrade']);
153
        $this->assertEquals(3, $result['grade']['maxgrade']);
154
 
155
        $this->assertArrayHasKey('gradedby', $result['grade']);
156
        $this->assertEquals(fullname($teacher), $result['grade']['gradedby']);
157
 
158
        $this->assertArrayHasKey('options', $result['grade']);
159
        $this->assertCount(count($options), $result['grade']['options']);
160
        rsort($options);
161
        foreach ($options as $index => $option) {
162
            $this->assertArrayHasKey($index, $result['grade']['options']);
163
 
164
            $returnedoption = $result['grade']['options'][$index];
165
            $this->assertArrayHasKey('value', $returnedoption);
166
            $this->assertEquals(3 - $index, $returnedoption['value']);
167
 
168
            $this->assertArrayHasKey('title', $returnedoption);
169
            $this->assertEquals($option, $returnedoption['title']);
170
 
171
            $this->assertArrayHasKey('selected', $returnedoption);
172
            $this->assertFalse($returnedoption['selected']);
173
        }
174
 
175
        // Compare against the grade stored in the database.
176
        $storedgradeitem = grade_item::fetch([
177
            'courseid' => $forum->get_course_id(),
178
            'itemtype' => 'mod',
179
            'itemmodule' => 'forum',
180
            'iteminstance' => $forum->get_id(),
181
            'itemnumber' => $gradeitem->get_grade_itemid(),
182
        ]);
183
        $storedgrade = grade_grade::fetch([
184
            'userid' => $student->id,
185
            'itemid' => $storedgradeitem->id,
186
        ]);
187
 
188
        $this->assertEmpty($storedgrade->rawgrade);
189
    }
190
 
191
    /**
192
     * Ensure that an execute against the correct grading method returns the current state of the user.
193
     */
194
    public function test_execute_store_not_selected(): void {
195
        [
196
            'forum' => $forum,
197
            'options' => $options,
198
            'student' => $student,
199
            'teacher' => $teacher,
200
        ] = $this->get_test_data();
201
 
202
        $this->setUser($teacher);
203
 
204
        $formdata = [
205
            'grade' => -1,
206
        ];
207
 
208
        $gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
209
 
210
        $result = store::execute('mod_forum', (int) $forum->get_context()->id, 'forum',
211
                (int) $student->id, false, http_build_query($formdata));
212
        $result = external_api::clean_returnvalue(store::execute_returns(), $result);
213
 
214
        // The result should still be empty.
215
        $this->assertIsArray($result);
216
        $this->assertArrayHasKey('templatename', $result);
217
 
218
        $this->assertEquals('core_grades/grades/grader/gradingpanel/scale', $result['templatename']);
219
 
220
        $this->assertArrayHasKey('warnings', $result);
221
        $this->assertIsArray($result['warnings']);
222
        $this->assertEmpty($result['warnings']);
223
 
224
        // Test the grade array items.
225
        $this->assertArrayHasKey('grade', $result);
226
        $this->assertIsArray($result['grade']);
227
 
228
        $this->assertIsInt($result['grade']['timecreated']);
229
        $this->assertArrayHasKey('timemodified', $result['grade']);
230
        $this->assertIsInt($result['grade']['timemodified']);
231
 
232
        $this->assertArrayHasKey('usergrade', $result['grade']);
233
        $this->assertEquals('-', $result['grade']['usergrade']);
234
 
235
        $this->assertArrayHasKey('maxgrade', $result['grade']);
236
        $this->assertIsInt($result['grade']['maxgrade']);
237
        $this->assertEquals(3, $result['grade']['maxgrade']);
238
 
239
        $this->assertArrayHasKey('gradedby', $result['grade']);
240
        $this->assertEquals(null, $result['grade']['gradedby']);
241
 
242
        $this->assertArrayHasKey('options', $result['grade']);
243
        $this->assertCount(count($options), $result['grade']['options']);
244
        rsort($options);
245
        foreach ($options as $index => $option) {
246
            $this->assertArrayHasKey($index, $result['grade']['options']);
247
 
248
            $returnedoption = $result['grade']['options'][$index];
249
            $this->assertArrayHasKey('value', $returnedoption);
250
            $this->assertEquals(3 - $index, $returnedoption['value']);
251
 
252
            $this->assertArrayHasKey('title', $returnedoption);
253
            $this->assertEquals($option, $returnedoption['title']);
254
 
255
            $this->assertArrayHasKey('selected', $returnedoption);
256
            $this->assertFalse($returnedoption['selected']);
257
        }
258
 
259
        // Compare against the grade stored in the database.
260
        $storedgradeitem = grade_item::fetch([
261
            'courseid' => $forum->get_course_id(),
262
            'itemtype' => 'mod',
263
            'itemmodule' => 'forum',
264
            'iteminstance' => $forum->get_id(),
265
            'itemnumber' => $gradeitem->get_grade_itemid(),
266
        ]);
267
        $storedgrade = grade_grade::fetch([
268
            'userid' => $student->id,
269
            'itemid' => $storedgradeitem->id,
270
        ]);
271
 
272
        // No grade will have been saved.
273
        $this->assertFalse($storedgrade);
274
    }
275
 
276
    /**
277
     * Ensure that an execute against the correct grading method returns the current state of the user.
278
     */
279
    public function test_execute_store_graded(): void {
280
        [
281
            'scale' => $scale,
282
            'forum' => $forum,
283
            'options' => $options,
284
            'student' => $student,
285
            'teacher' => $teacher,
286
        ] = $this->get_test_data();
287
 
288
        $this->setUser($teacher);
289
 
290
        $formdata = [
291
            'grade' => 2,
292
        ];
293
        $formattedvalue = grade_floatval(unformat_float($formdata['grade']));
294
 
295
        $gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
296
 
297
        $result = store::execute('mod_forum', (int) $forum->get_context()->id, 'forum',
298
                (int) $student->id, false, http_build_query($formdata));
299
        $result = external_api::clean_returnvalue(store::execute_returns(), $result);
300
 
301
        // The result should still be empty.
302
        $this->assertIsArray($result);
303
        $this->assertArrayHasKey('templatename', $result);
304
 
305
        $this->assertEquals('core_grades/grades/grader/gradingpanel/scale', $result['templatename']);
306
 
307
        $this->assertArrayHasKey('warnings', $result);
308
        $this->assertIsArray($result['warnings']);
309
        $this->assertEmpty($result['warnings']);
310
 
311
        // Test the grade array items.
312
        $this->assertArrayHasKey('grade', $result);
313
        $this->assertIsArray($result['grade']);
314
 
315
        $this->assertIsInt($result['grade']['timecreated']);
316
        $this->assertArrayHasKey('timemodified', $result['grade']);
317
        $this->assertIsInt($result['grade']['timemodified']);
318
 
319
        $this->assertArrayHasKey('usergrade', $result['grade']);
320
        $this->assertEquals('B', $result['grade']['usergrade']);
321
 
322
        $this->assertArrayHasKey('maxgrade', $result['grade']);
323
        $this->assertIsInt($result['grade']['maxgrade']);
324
        $this->assertEquals(3, $result['grade']['maxgrade']);
325
 
326
        $this->assertArrayHasKey('gradedby', $result['grade']);
327
        $this->assertEquals(fullname($teacher), $result['grade']['gradedby']);
328
 
329
        $this->assertArrayHasKey('options', $result['grade']);
330
        $this->assertCount(count($options), $result['grade']['options']);
331
        rsort($options);
332
        foreach ($options as $index => $option) {
333
            $this->assertArrayHasKey($index, $result['grade']['options']);
334
 
335
            $returnedoption = $result['grade']['options'][$index];
336
            $this->assertArrayHasKey('value', $returnedoption);
337
            $this->assertEquals(3 - $index, $returnedoption['value']);
338
 
339
            $this->assertArrayHasKey('title', $returnedoption);
340
            $this->assertEquals($option, $returnedoption['title']);
341
 
342
            $this->assertArrayHasKey('selected', $returnedoption);
343
        }
344
 
345
        // Compare against the grade stored in the database.
346
        $storedgradeitem = grade_item::fetch([
347
            'courseid' => $forum->get_course_id(),
348
            'itemtype' => 'mod',
349
            'itemmodule' => 'forum',
350
            'iteminstance' => $forum->get_id(),
351
            'itemnumber' => $gradeitem->get_grade_itemid(),
352
        ]);
353
        $storedgrade = grade_grade::fetch([
354
            'userid' => $student->id,
355
            'itemid' => $storedgradeitem->id,
356
        ]);
357
 
358
        $this->assertEquals($formattedvalue, $storedgrade->rawgrade);
359
        $this->assertEquals($scale->id, $storedgrade->rawscaleid);
360
 
361
        // The grade was 2, which relates to the middle option.
362
        $this->assertFalse($result['grade']['options'][0]['selected']);
363
        $this->assertTrue($result['grade']['options'][1]['selected']);
364
        $this->assertFalse($result['grade']['options'][2]['selected']);
365
    }
366
 
367
    /**
368
     * Ensure that an out-of-range value is rejected.
369
     *
370
     * @dataProvider execute_out_of_range_provider
371
     * @param int $suppliedvalue The value that was submitted
372
     */
373
    public function test_execute_store_out_of_range(int $suppliedvalue): void {
374
        [
375
            'scale' => $scale,
376
            'forum' => $forum,
377
            'options' => $options,
378
            'student' => $student,
379
            'teacher' => $teacher,
380
        ] = $this->get_test_data();
381
 
382
        $this->setUser($teacher);
383
 
384
        $formdata = [
385
            'grade' => $suppliedvalue,
386
        ];
387
 
388
        $gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
389
 
390
        $this->expectException(moodle_exception::class);
391
        $this->expectExceptionMessage("Invalid grade '{$suppliedvalue}' provided. Grades must be between 0 and 3.");
392
        store::execute('mod_forum', (int) $forum->get_context()->id, 'forum',
393
                (int) $student->id, false, http_build_query($formdata));
394
    }
395
 
396
    /**
397
     * Data provider for out of range tests.
398
     *
399
     * @return array
400
     */
401
    public function execute_out_of_range_provider(): array {
402
        return [
403
            'above' => [
404
                'supplied' => 500,
405
            ],
406
            'above just' => [
407
                'supplied' => 4,
408
            ],
409
            'below' => [
410
                'supplied' => -100,
411
            ],
412
            '-10' => [
413
                'supplied' => -10,
414
            ],
415
        ];
416
    }
417
 
418
 
419
    /**
420
     * Get a forum instance.
421
     *
422
     * @param array $config
423
     * @return forum_entity
424
     */
425
    protected function get_forum_instance(array $config = []): forum_entity {
426
        $this->resetAfterTest();
427
 
428
        $datagenerator = $this->getDataGenerator();
429
        $course = $datagenerator->create_course();
430
        $forum = $datagenerator->create_module('forum', array_merge($config, ['course' => $course->id]));
431
 
432
        $vaultfactory = \mod_forum\local\container::get_vault_factory();
433
        $vault = $vaultfactory->get_forum_vault();
434
 
435
        return $vault->get_from_id((int) $forum->id);
436
    }
437
 
438
    /**
439
     * Get test data for scaled forums.
440
     *
441
     * @return array
442
     */
443
    protected function get_test_data(): array {
444
        $this->resetAfterTest();
445
 
446
        $options = [
447
            'A',
448
            'B',
449
            'C'
450
        ];
451
        $scale = $this->getDataGenerator()->create_scale(['scale' => implode(',', $options)]);
452
 
453
        $forum = $this->get_forum_instance([
454
            // Negative numbers mean a scale.
455
            'grade_forum' => -1 * $scale->id
456
        ]);
457
        $course = $forum->get_course_record();
458
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
459
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
460
 
461
        return [
462
            'forum' => $forum,
463
            'scale' => $scale,
464
            'options' => $options,
465
            'student' => $student,
466
            'teacher' => $teacher,
467
        ];
468
    }
469
}