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 gradingform_guide\grades\grader\gradingpanel\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
 
28
/**
29
 * Unit tests for core_grades\component_gradeitems;
30
 *
31
 * @package   gradingform_guide
32
 * @category  test
33
 * @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
34
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35
 */
36
class fetch_test extends advanced_testcase {
37
    /**
38
     * Ensure that an execute with an invalid component is rejected.
39
     */
40
    public function test_execute_invalid_component(): void {
41
        $this->resetAfterTest();
42
        $user = $this->getDataGenerator()->create_user();
43
        $this->setUser($user);
44
 
45
        $this->expectException(coding_exception::class);
46
        $this->expectExceptionMessage("The 'foo' item is not valid for the 'mod_invalid' component");
47
        fetch::execute('mod_invalid', 1, 'foo', 2);
48
    }
49
 
50
    /**
51
     * Ensure that an execute with an invalid itemname on a valid component is rejected.
52
     */
53
    public function test_execute_invalid_itemname(): void {
54
        $this->resetAfterTest();
55
        $user = $this->getDataGenerator()->create_user();
56
        $this->setUser($user);
57
 
58
        $this->expectException(coding_exception::class);
59
        $this->expectExceptionMessage("The 'foo' item is not valid for the 'mod_forum' component");
60
        fetch::execute('mod_forum', 1, 'foo', 2);
61
    }
62
 
63
    /**
64
     * Ensure that an execute against a different grading method is rejected.
65
     */
66
    public function test_execute_incorrect_type(): void {
67
        $this->resetAfterTest();
68
 
69
        $forum = $this->get_forum_instance([
70
            // Negative numbers mean a scale.
71
            'grade_forum' => 5,
72
        ]);
73
        $course = $forum->get_course_record();
74
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
75
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
76
        $this->setUser($teacher);
77
 
78
        $gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
79
 
80
        $this->expectException(moodle_exception::class);
81
        $this->expectExceptionMessage("not configured for advanced grading with a marking guide");
82
        fetch::execute('mod_forum', (int) $forum->get_context()->id, 'forum', (int) $student->id);
83
    }
84
 
85
    /**
86
     * Ensure that an execute against the correct grading method returns the current state of the user.
87
     */
88
    public function test_execute_fetch_empty(): void {
89
        $this->resetAfterTest();
90
 
91
        [
92
            'forum' => $forum,
93
            'controller' => $controller,
94
            'definition' => $definition,
95
            'student' => $student,
96
            'teacher' => $teacher,
97
        ] = $this->get_test_data();
98
 
99
        $this->setUser($teacher);
100
 
101
        $gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
102
 
103
        $result = fetch::execute('mod_forum', (int) $forum->get_context()->id, 'forum', (int) $student->id);
104
        $result = external_api::clean_returnvalue(fetch::execute_returns(), $result);
105
 
106
        $this->assertIsArray($result);
107
        $this->assertArrayHasKey('templatename', $result);
108
 
109
        $this->assertEquals('gradingform_guide/grades/grader/gradingpanel', $result['templatename']);
110
 
111
        $this->assertArrayHasKey('warnings', $result);
112
        $this->assertIsArray($result['warnings']);
113
        $this->assertEmpty($result['warnings']);
114
 
115
        // Test the grade array items.
116
        $this->assertArrayHasKey('grade', $result);
117
        $this->assertIsArray($result['grade']);
118
        $this->assertIsInt($result['grade']['timecreated']);
119
 
120
        $this->assertArrayHasKey('timemodified', $result['grade']);
121
        $this->assertIsInt($result['grade']['timemodified']);
122
 
123
        $this->assertArrayHasKey('usergrade', $result['grade']);
124
        $this->assertEquals('- / 100.00', $result['grade']['usergrade']);
125
 
126
        $this->assertArrayHasKey('maxgrade', $result['grade']);
127
        $this->assertIsInt($result['grade']['maxgrade']);
128
        $this->assertEquals(100, $result['grade']['maxgrade']);
129
 
130
        $this->assertArrayHasKey('gradedby', $result['grade']);
131
        $this->assertEquals(null, $result['grade']['gradedby']);
132
 
133
        $this->assertArrayHasKey('criterion', $result['grade']);
134
        $criteria = $result['grade']['criterion'];
135
        $this->assertCount(count($definition->guide_criteria), $criteria);
136
        foreach ($criteria as $criterion) {
137
            $this->assertArrayHasKey('id', $criterion);
138
            $criterionid = $criterion['id'];
139
            $sourcecriterion = $definition->guide_criteria[$criterionid];
140
 
141
            $this->assertArrayHasKey('name', $criterion);
142
            $this->assertEquals($sourcecriterion['shortname'], $criterion['name']);
143
 
144
            $this->assertArrayHasKey('maxscore', $criterion);
145
            $this->assertEquals($sourcecriterion['maxscore'], $criterion['maxscore']);
146
 
147
            $this->assertArrayHasKey('description', $criterion);
148
            $this->assertEquals($sourcecriterion['description'], $criterion['description']);
149
 
150
            $this->assertArrayHasKey('descriptionmarkers', $criterion);
151
            $this->assertEquals($sourcecriterion['descriptionmarkers'], $criterion['descriptionmarkers']);
152
 
153
            $this->assertArrayHasKey('score', $criterion);
154
            $this->assertEmpty($criterion['score']);
155
 
156
            $this->assertArrayHasKey('remark', $criterion);
157
            $this->assertEmpty($criterion['remark']);
158
        }
159
    }
160
 
161
    /**
162
     * Ensure that an execute against the correct grading method returns the current state of the user.
163
     */
164
    public function test_execute_fetch_graded(): void {
165
        $this->resetAfterTest();
166
 
167
        [
168
            'forum' => $forum,
169
            'controller' => $controller,
170
            'definition' => $definition,
171
            'student' => $student,
172
            'teacher' => $teacher,
173
        ] = $this->get_test_data();
174
 
175
        $this->execute_and_assert_fetch($forum, $controller, $definition, $teacher, $teacher, $student);
176
    }
177
 
178
    /**
179
     * Class mates should not get other's grades.
180
     */
181
    public function test_execute_fetch_does_not_return_data_to_other_students(): void {
182
        $this->resetAfterTest();
183
 
184
        [
185
            'forum' => $forum,
186
            'controller' => $controller,
187
            'definition' => $definition,
188
            'student' => $student,
189
            'teacher' => $teacher,
190
            'course' => $course,
191
        ] = $this->get_test_data();
192
 
193
        $evilstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
194
 
195
        $this->expectException(\required_capability_exception::class);
196
        $this->execute_and_assert_fetch($forum, $controller, $definition, $evilstudent, $teacher, $student);
197
    }
198
 
199
    /**
200
     * Grades can be returned to graded user.
201
     */
202
    public function test_execute_fetch_return_data_to_graded_user(): void {
203
        $this->resetAfterTest();
204
 
205
        [
206
            'forum' => $forum,
207
            'controller' => $controller,
208
            'definition' => $definition,
209
            'student' => $student,
210
            'teacher' => $teacher,
211
        ] = $this->get_test_data();
212
 
213
        $this->execute_and_assert_fetch($forum, $controller, $definition, $student, $teacher, $student);
214
    }
215
 
216
    /**
217
     * Executes and performs all the assertions of the fetch method with the given parameters.
218
     */
219
    private function execute_and_assert_fetch($forum, $controller, $definition, $fetcheruser, $grader, $gradeduser) {
220
        $generator = \testing_util::get_data_generator();
221
        $guidegenerator = $generator->get_plugin_generator('gradingform_guide');
222
 
223
        $this->setUser($grader);
224
 
225
        $gradeitem = component_gradeitem::instance('mod_forum', $forum->get_context(), 'forum');
226
        $grade = $gradeitem->get_grade_for_user($gradeduser, $grader);
227
        $instance = $gradeitem->get_advanced_grading_instance($grader, $grade);
228
 
229
        $submissiondata = $guidegenerator->get_test_form_data($controller, (int) $gradeduser->id,
230
            10, 'Propper good speling',
231
            0, 'ASCII art is not a picture'
232
        );
233
 
234
        $gradeitem->store_grade_from_formdata($gradeduser, $grader, (object) [
235
            'instanceid' => $instance->get_id(),
236
            'advancedgrading' => $submissiondata,
237
        ]);
238
 
239
        $this->setUser($fetcheruser);
240
 
241
        // Set up some items we need to return on other interfaces.
242
        $result = fetch::execute('mod_forum', (int) $forum->get_context()->id, 'forum', (int) $gradeduser->id);
243
        $result = external_api::clean_returnvalue(fetch::execute_returns(), $result);
244
 
245
        $this->assertIsArray($result);
246
        $this->assertArrayHasKey('templatename', $result);
247
 
248
        $this->assertEquals('gradingform_guide/grades/grader/gradingpanel', $result['templatename']);
249
 
250
        $this->assertArrayHasKey('warnings', $result);
251
        $this->assertIsArray($result['warnings']);
252
        $this->assertEmpty($result['warnings']);
253
 
254
        // Test the grade array items.
255
        $this->assertArrayHasKey('grade', $result);
256
        $this->assertIsArray($result['grade']);
257
        $this->assertIsInt($result['grade']['timecreated']);
258
 
259
        $this->assertArrayHasKey('timemodified', $result['grade']);
260
        $this->assertIsInt($result['grade']['timemodified']);
261
 
262
        $this->assertArrayHasKey('usergrade', $result['grade']);
263
        $this->assertEquals('25.00 / 100.00', $result['grade']['usergrade']);
264
 
265
        $this->assertArrayHasKey('maxgrade', $result['grade']);
266
        $this->assertIsInt($result['grade']['maxgrade']);
267
        $this->assertEquals(100, $result['grade']['maxgrade']);
268
 
269
        $this->assertArrayHasKey('gradedby', $result['grade']);
270
        $this->assertEquals(fullname($grader), $result['grade']['gradedby']);
271
 
272
        $this->assertArrayHasKey('criterion', $result['grade']);
273
        $criteria = $result['grade']['criterion'];
274
        $this->assertCount(count($definition->guide_criteria), $criteria);
275
        foreach ($criteria as $criterion) {
276
            $this->assertArrayHasKey('id', $criterion);
277
            $criterionid = $criterion['id'];
278
            $sourcecriterion = $definition->guide_criteria[$criterionid];
279
 
280
            $this->assertArrayHasKey('name', $criterion);
281
            $this->assertEquals($sourcecriterion['shortname'], $criterion['name']);
282
 
283
            $this->assertArrayHasKey('maxscore', $criterion);
284
            $this->assertEquals($sourcecriterion['maxscore'], $criterion['maxscore']);
285
 
286
            $this->assertArrayHasKey('description', $criterion);
287
            $this->assertEquals($sourcecriterion['description'], $criterion['description']);
288
 
289
            $this->assertArrayHasKey('descriptionmarkers', $criterion);
290
            $this->assertEquals($sourcecriterion['descriptionmarkers'], $criterion['descriptionmarkers']);
291
 
292
            $this->assertArrayHasKey('score', $criterion);
293
            $this->assertArrayHasKey('remark', $criterion);
294
        }
295
 
296
        $this->assertEquals(10, $criteria[0]['score']);
297
        $this->assertEquals('Propper good speling', $criteria[0]['remark']);
298
        $this->assertEquals(0, $criteria[1]['score']);
299
        $this->assertEquals('ASCII art is not a picture', $criteria[1]['remark']);
300
    }
301
 
302
    /**
303
     * Get a forum instance.
304
     *
305
     * @param array $config
306
     * @return forum_entity
307
     */
308
    protected function get_forum_instance(array $config = []): forum_entity {
309
        $this->resetAfterTest();
310
 
311
        $datagenerator = $this->getDataGenerator();
312
        $course = $datagenerator->create_course();
313
        $forum = $datagenerator->create_module('forum', array_merge($config, ['course' => $course->id, 'grade_forum' => 100]));
314
 
315
        $vaultfactory = \mod_forum\local\container::get_vault_factory();
316
        $vault = $vaultfactory->get_forum_vault();
317
 
318
        return $vault->get_from_id((int) $forum->id);
319
    }
320
 
321
    /**
322
     * Get test data for forums graded using a marking guide.
323
     *
324
     * @return array
325
     */
326
    protected function get_test_data(): array {
327
        global $DB;
328
 
329
        $this->resetAfterTest();
330
 
331
        $generator = \testing_util::get_data_generator();
332
        $guidegenerator = $generator->get_plugin_generator('gradingform_guide');
333
 
334
        $forum = $this->get_forum_instance();
335
        $course = $forum->get_course_record();
336
        $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
337
        $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
338
 
339
        $this->setUser($teacher);
340
        $controller = $guidegenerator->get_test_guide($forum->get_context(), 'forum', 'forum');
341
        $definition = $controller->get_definition();
342
 
343
        // In the situation of mod_forum this would be the id from forum_grades.
344
        $itemid = 1;
345
        $instance = $controller->create_instance($student->id, $itemid);
346
 
347
        $data = $this->get_test_form_data(
348
            $controller,
349
            $itemid,
350
            5, 'This user made several mistakes.',
351
            10, 'This user has two pictures.'
352
        );
353
 
354
        // Update this instance with data.
355
        $instance->update($data);
356
 
357
        return [
358
            'forum' => $forum,
359
            'controller' => $controller,
360
            'definition' => $definition,
361
            'student' => $student,
362
            'teacher' => $teacher,
363
            'course' => $course,
364
        ];
365
    }
366
 
367
    /**
368
     * Fetch a set of sample data.
369
     *
370
     * @param \gradingform_guide_controller $controller
371
     * @param int $itemid
372
     * @param float $spellingscore
373
     * @param string $spellingremark
374
     * @param float $picturescore
375
     * @param string $pictureremark
376
     * @return array
377
     */
378
    protected function get_test_form_data(
379
        \gradingform_guide_controller $controller,
380
        int $itemid,
381
        float $spellingscore,
382
        string $spellingremark,
383
        float $picturescore,
384
        string $pictureremark
385
    ): array {
386
        $generator = \testing_util::get_data_generator();
387
        $guidegenerator = $generator->get_plugin_generator('gradingform_guide');
388
 
389
        return $guidegenerator->get_test_form_data(
390
            $controller,
391
            $itemid,
392
            $spellingscore,
393
            $spellingremark,
394
            $picturescore,
395
            $pictureremark
396
        );
397
    }
398
}