MDL-63622 core_grading: Add support for removal of context users.
[moodle.git] / grade / grading / tests / privacy_test.php
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/>.
17 /**
18  * Privacy tests for core_grading.
19  *
20  * @package    core_grading
21  * @category   test
22  * @copyright  2018 Sara Arjona <sara@moodle.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->dirroot . '/grade/grading/tests/fixtures/marking_guide.php');
31 use \core_privacy\tests\provider_testcase;
32 use \core_privacy\local\request\approved_contextlist;
33 use \core_privacy\local\request\transform;
34 use \core_privacy\local\request\writer;
35 use \core_grading\privacy\provider;
37 /**
38  * Privacy tests for core_grading.
39  *
40  * @copyright  2018 Sara Arjona <sara@moodle.com>
41  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
42  */
43 class core_grading_privacy_testcase extends provider_testcase {
45     /** @var stdClass User without data. */
46     protected $user0;
48     /** @var stdClass User with data. */
49     protected $user1;
51     /** @var stdClass User with data. */
52     protected $user2;
54     /** @var context context_module of an activity without grading definitions. */
55     protected $instancecontext0;
57     /** @var context context_module of the activity where the grading definitions are. */
58     protected $instancecontext1;
60     /** @var context context_module of the activity where the grading definitions are. */
61     protected $instancecontext2;
63     /**
64      * Test getting the context for the user ID related to this plugin.
65      */
66     public function test_get_contexts_for_userid() {
67         global $DB;
69         $this->resetAfterTest();
70         $this->grading_setup_test_scenario_data();
71         $this->assertCount(2, $DB->get_records('grading_definitions'));
73         // User1 has created grading definitions for instance1 and instance2.
74         $contextlist = provider::get_contexts_for_userid($this->user1->id);
75         $this->assertCount(2, $contextlist);
76         $this->assertContains($this->instancecontext1->id, $contextlist->get_contextids());
77         $this->assertContains($this->instancecontext2->id, $contextlist->get_contextids());
78         $this->assertNotContains($this->instancecontext0->id, $contextlist->get_contextids());
80         // User2 has only modified grading definitions for instance2.
81         $contextlist = provider::get_contexts_for_userid($this->user2->id);
82         $this->assertCount(1, $contextlist);
83         $this->assertContains($this->instancecontext2->id, $contextlist->get_contextids());
85         // User0 hasn't created or modified any grading definition.
86         $contextlist = provider::get_contexts_for_userid($this->user0->id);
87         $this->assertCount(0, $contextlist);
88     }
90     /**
91      * Test retrieval of user ids in a given context.
92      */
93     public function test_get_users_in_context() {
94         $this->resetAfterTest();
95         $this->grading_setup_test_scenario_data();
96         // Instance two has one user who created the definitions and another who modified it.
97         $userlist = new \core_privacy\local\request\userlist($this->instancecontext2, 'core_grading');
98         provider::get_users_in_context($userlist);
99         // Check that we get both.
100         $this->assertCount(2, $userlist->get_userids());
101     }
103     /**
104      * Export for a user with no grading definitions created or modified will not have any data exported.
105      */
106     public function test_export_user_data_no_content() {
107         $this->resetAfterTest();
109         $user = $this->getDataGenerator()->create_user();
110         $this->setUser($user);
111         $context = \context_system::instance();
113         $writer = writer::with_context($context);
114         $this->assertFalse($writer->has_any_data());
115         $this->export_context_data_for_user($user->id, $context, 'core_grading');
116         $this->assertFalse(writer::with_context($context)->has_any_data());
117     }
119     /**
120      * Test that data is exported correctly for this plugin.
121      */
122     public function test_export_user_data() {
123         global $DB;
125         $this->resetAfterTest();
126         $now = time();
127         $defnameprefix = 'fakename';
128         $this->grading_setup_test_scenario_data($defnameprefix, $now);
129         $this->assertCount(2, $DB->get_records('grading_definitions'));
131         // Validate exported data: instance1 - user0 has NO data.
132         $this->setUser($this->user0);
133         writer::reset();
134         $writer = writer::with_context($this->instancecontext1);
135         $this->assertFalse($writer->has_any_data());
136         $this->export_context_data_for_user($this->user0->id, $this->instancecontext1, 'core_grading');
137         $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
138         $this->assertEmpty($data);
140         // Validate exported data: instance0 - user1 has NO data.
141         $this->setUser($this->user1);
142         writer::reset();
143         $writer = writer::with_context($this->instancecontext0);
144         $this->assertFalse($writer->has_any_data());
145         $this->export_context_data_for_user($this->user1->id, $this->instancecontext0, 'core_grading');
146         $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
147         $this->assertEmpty($data);
149         // Validate exported data: instance1 - user1 has data (user has created and modified it).
150         writer::reset();
151         $writer = writer::with_context($this->instancecontext1);
152         $this->assertFalse($writer->has_any_data());
153         $this->export_context_data_for_user($this->user1->id, $this->instancecontext1, 'core_grading');
154         $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
155         $this->assertCount(1, $data->definitions);
157         $firstkey = reset($data->definitions);
158         $this->assertNotEmpty($firstkey->name);
159         $this->assertEquals('test_method', $firstkey->method);
160         $this->assertEquals(transform::datetime($now), $firstkey->timecreated);
161         $this->assertEquals($this->user1->id, $firstkey->usercreated);
162         $this->assertEquals($defnameprefix.'1', $firstkey->name);
164         // Validate exported data: instance2 - user1 has data (user has created it).
165         writer::reset();
166         $writer = writer::with_context($this->instancecontext2);
167         $this->assertFalse($writer->has_any_data());
168         $this->export_context_data_for_user($this->user1->id, $this->instancecontext2, 'core_grading');
169         $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
170         $this->assertCount(1, $data->definitions);
172         $firstkey = reset($data->definitions);
173         $this->assertNotEmpty($firstkey->name);
174         $this->assertEquals('test_method', $firstkey->method);
175         $this->assertEquals(transform::datetime($now), $firstkey->timecreated);
176         $this->assertEquals($this->user1->id, $firstkey->usercreated);
177         $this->assertEquals($defnameprefix.'2', $firstkey->name);
179         // Validate exported data: instance1 - user2 has NO data.
180         $this->setUser($this->user2);
181         writer::reset();
182         $writer = writer::with_context($this->instancecontext1);
183         $this->assertFalse($writer->has_any_data());
184         $this->export_context_data_for_user($this->user2->id, $this->instancecontext1, 'core_grading');
185         $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
186         $this->assertEmpty($data);
188         // Validate exported data: instance2 - user2 has data (user has modified it).
189         $this->setUser($this->user2);
190         writer::reset();
191         $writer = writer::with_context($this->instancecontext2);
192         $this->assertFalse($writer->has_any_data());
193         $this->export_context_data_for_user($this->user2->id, $this->instancecontext2, 'core_grading');
194         $data = $writer->get_data([get_string('gradingmethod', 'grading')]);
195         $this->assertCount(1, $data->definitions);
196     }
198     /**
199      * Test for provider::delete_data_for_all_users_in_context().
200      */
201     public function test_delete_data_for_all_users_in_context() {
202         global $DB;
204         $this->resetAfterTest();
205         $this->grading_setup_test_scenario_data();
207         // Before deletion, we should have 2 grading_definitions.
208         $this->assertCount(2, $DB->get_records('grading_definitions'));
210         // Delete data.
211         provider::delete_data_for_all_users_in_context($this->instancecontext0);
212         provider::delete_data_for_all_users_in_context($this->instancecontext1);
213         provider::delete_data_for_all_users_in_context($this->instancecontext2);
215         // Before deletion, we should have same grading_definitions (nothing was deleted).
216         $this->assertCount(2, $DB->get_records('grading_definitions'));
217     }
219     /**
220      * Test for provider::delete_data_for_user().
221      */
222     public function test_delete_data_for_user() {
223         global $DB;
225         $this->resetAfterTest();
226         $this->grading_setup_test_scenario_data();
228         // Before deletion, we should have 2 grading_definitions.
229         $this->assertCount(2, $DB->get_records('grading_definitions'));
231         // Delete data for $user0.
232         $contextlist = provider::get_contexts_for_userid($this->user0->id);
233         $approvedcontextlist = new approved_contextlist(
234             $this->user0,
235             'core_grading',
236             $contextlist->get_contextids()
237         );
238         provider::delete_data_for_user($approvedcontextlist);
240         // Delete data for $user1.
241         $contextlist = provider::get_contexts_for_userid($this->user1->id);
242         $approvedcontextlist = new approved_contextlist(
243             $this->user1,
244             'core_grading',
245             $contextlist->get_contextids()
246         );
247         provider::delete_data_for_user($approvedcontextlist);
249         // Delete data for $user2.
250         $contextlist = provider::get_contexts_for_userid($this->user2->id);
251         $approvedcontextlist = new approved_contextlist(
252             $this->user2,
253             'core_grading',
254             $contextlist->get_contextids()
255         );
256         provider::delete_data_for_user($approvedcontextlist);
258         // Before deletion, we should have same grading_definitions (nothing was deleted).
259         $this->assertCount(2, $DB->get_records('grading_definitions'));
260     }
262     /**
263      * Test exporting user data relating to an item ID.
264      */
265     public function test_export_item_data() {
266         global $DB;
267         $this->resetAfterTest();
268         $course = $this->getDataGenerator()->create_course();
269         $module = $this->getDataGenerator()->create_module('assign', ['course' => $course]);
270         $user = $this->getDataGenerator()->create_user();
272         $this->setUser($user);
274         $modulecontext = context_module::instance($module->cmid);
275         $guide = new test_guide($modulecontext, 'testrubrib', 'Description text');
276         $guide->add_criteria(
277             'Spelling mistakes',
278             'Full marks will be given for no spelling mistakes.',
279             'Deduct 5 points per spelling mistake made.',
280             25
281         );
282         $guide->add_criteria(
283             'Pictures',
284             'Full marks will be given for including 3 pictures.',
285             'Give 5 points for each picture present',
286             15
287         );
288         $guide->create_guide();
290         // In the situation of mod_assign this would be the id from assign_grades.
291         $itemid = 1;
292         $gradedata = [
293             ['remark' => 'This user made several mistakes.', 'score' => 5],
294             ['remark' => 'This user has two pictures.', 'score' => 10]
295         ];
296         $instance = $guide->grade_item($user->id, $itemid, $gradedata);
298         provider::export_item_data($modulecontext, $itemid, ['Test']);
299         $data = (array) writer::with_context($modulecontext)->get_data(['Test', 'Marking guide', $instance->get_data('id')]);
300         $this->assertCount(2, $data);
301         $this->assertEquals('This user made several mistakes.', $data['Spelling mistakes']->remark);
302         $this->assertEquals(5, $data['Spelling mistakes']->score);
303         $this->assertEquals('This user has two pictures.', $data['Pictures']->remark);
304         $this->assertEquals(10, $data['Pictures']->score);
305     }
307     /**
308      * Test deleting user data related to a context and item ID.
309      */
310     public function test_delete_instance_data() {
311         global $DB;
312         $this->resetAfterTest();
313         $course = $this->getDataGenerator()->create_course();
314         $module = $this->getDataGenerator()->create_module('assign', ['course' => $course]);
315         $user = $this->getDataGenerator()->create_user();
317         $this->setUser($user);
319         $modulecontext = context_module::instance($module->cmid);
320         $guide = new test_guide($modulecontext, 'testrubrib', 'Description text');
321         $guide->add_criteria(
322             'Spelling mistakes',
323             'Full marks will be given for no spelling mistakes.',
324             'Deduct 5 points per spelling mistake made.',
325             25
326         );
327         $guide->add_criteria(
328             'Pictures',
329             'Full marks will be given for including 3 pictures.',
330             'Give 5 points for each picture present',
331             15
332         );
333         $guide->create_guide();
335         // In the situation of mod_assign this would be the id from assign_grades.
336         $itemid = 1;
337         $gradedata = [
338             ['remark' => 'This user made several mistakes.', 'score' => 5],
339             ['remark' => 'This user has two pictures.', 'score' => 10]
340         ];
341         $instance = $guide->grade_item($user->id, $itemid, $gradedata);
343         $itemid = 2;
344         $gradedata = [
345             ['remark' => 'This user made no mistakes.', 'score' => 25],
346             ['remark' => 'This user has one picture.', 'score' => 5]
347         ];
348         $instance = $guide->grade_item($user->id, $itemid, $gradedata);
350         // Check how many records we have in the fillings table.
351         $records = $DB->get_records('gradingform_guide_fillings');
352         $this->assertCount(4, $records);
353         // Let's delete one of the instances (the last one would be the easiest).
354         provider::delete_instance_data($modulecontext, $itemid);
355         $records = $DB->get_records('gradingform_guide_fillings');
356         $this->assertCount(2, $records);
357         foreach ($records as $record) {
358             $this->assertNotEquals($instance->get_id(), $record->instanceid);
359         }
360         // This will delete all the rest of the instances for this context.
361         provider::delete_instance_data($modulecontext);
362         $records = $DB->get_records('gradingform_guide_fillings');
363         $this->assertEmpty($records);
364     }
366     /**
367      * Test the deletion of multiple instances at once.
368      */
369     public function test_delete_data_for_instances() {
370         global $DB;
371         $this->resetAfterTest();
372         $course = $this->getDataGenerator()->create_course();
373         $module = $this->getDataGenerator()->create_module('assign', ['course' => $course]);
374         $user1 = $this->getDataGenerator()->create_user();
375         $user2 = $this->getDataGenerator()->create_user();
376         $user3 = $this->getDataGenerator()->create_user();
378         $this->setUser($user1);
380         $modulecontext = context_module::instance($module->cmid);
381         $guide = new test_guide($modulecontext, 'testrubrib', 'Description text');
382         $guide->add_criteria(
383             'Spelling mistakes',
384             'Full marks will be given for no spelling mistakes.',
385             'Deduct 5 points per spelling mistake made.',
386             25
387         );
388         $guide->add_criteria(
389             'Pictures',
390             'Full marks will be given for including 3 pictures.',
391             'Give 5 points for each picture present',
392             15
393         );
394         $guide->create_guide();
396         // In the situation of mod_assign this would be the id from assign_grades.
397         $itemid1 = 1;
398         $gradedata = [
399             ['remark' => 'This user made several mistakes.', 'score' => 5],
400             ['remark' => 'This user has two pictures.', 'score' => 10]
401         ];
402         $instance1 = $guide->grade_item($user1->id, $itemid1, $gradedata);
404         $itemid2 = 2;
405         $gradedata = [
406             ['remark' => 'This user made a couple of mistakes.', 'score' => 15],
407             ['remark' => 'This user has one picture.', 'score' => 10]
408         ];
409         $instance2 = $guide->grade_item($user2->id, $itemid2, $gradedata);
411         $itemid3 = 3;
412         $gradedata = [
413             ['remark' => 'This user made one mistakes.', 'score' => 20],
414             ['remark' => 'This user has one picture.', 'score' => 10]
415         ];
416         $instance3 = $guide->grade_item($user3->id, $itemid3, $gradedata);
418         $records = $DB->get_records('gradingform_guide_fillings');
419         $this->assertCount(6, $records);
421         // Delete all user data for items 1 and 3.
422         provider::delete_data_for_instances($modulecontext, [$itemid1, $itemid3]);
423         $records = $DB->get_records('gradingform_guide_fillings');
424         $this->assertCount(2, $records);
425         $instanceid = $instance2->get_data('id');
426         // The instance id should match for all remaining records.
427         foreach ($records as $record) {
428             $this->assertEquals($instanceid, $record->instanceid);
429         }
430     }
432     /**
433      * Helper function to setup the environment.
434      *
435      * course
436      *  |
437      *  +--instance0 (assignment)
438      *  |   |
439      *  +--instance1 (assignment)
440      *  |   |
441      *  |   +--grading_definition1 (created and modified by user1)
442      *  |   |
443      *  +--instance2 (assignment)
444      *  |   |
445      *  |   +--grading_definition2 (created by user1 and modified by user2)
446      *
447      *
448      * user0 hasn't any data.
449      *
450      * @param string $defnameprefix
451      * @param timestamp $now
452      */
453     protected function grading_setup_test_scenario_data($defnameprefix = null, $now = null) {
454         global $DB;
456         $this->user0 = $this->getDataGenerator()->create_user();
457         $this->user1 = $this->getDataGenerator()->create_user();
458         $this->user2 = $this->getDataGenerator()->create_user();
460         // Create a course.
461         $course = $this->getDataGenerator()->create_course();
462         $coursecontext = context_course::instance($course->id);
464         // Create some assignment instances.
465         $params = (object)array(
466             'course' => $course->id,
467             'name'   => 'Testing instance'
468         );
469         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
470         $instance0 = $generator->create_instance($params);
471         $cm0 = get_coursemodule_from_instance('assign', $instance0->id);
472         $this->instancecontext0 = context_module::instance($cm0->id);
473         $instance1 = $generator->create_instance($params);
474         $cm1 = get_coursemodule_from_instance('assign', $instance1->id);
475         $this->instancecontext1 = context_module::instance($cm1->id);
476         $instance2 = $generator->create_instance($params);
477         $cm2 = get_coursemodule_from_instance('assign', $instance2->id);
478         $this->instancecontext2 = context_module::instance($cm2->id);
480         // Create fake grading areas.
481         $fakearea1 = (object)array(
482             'contextid'    => $this->instancecontext1->id,
483             'component'    => 'mod_assign',
484             'areaname'     => 'submissions',
485             'activemethod' => 'test_method'
486         );
487         $fakeareaid1 = $DB->insert_record('grading_areas', $fakearea1);
488         $fakearea2 = clone($fakearea1);
489         $fakearea2->contextid = $this->instancecontext2->id;
490         $fakeareaid2 = $DB->insert_record('grading_areas', $fakearea2);
492         // Create fake grading definitions.
493         if (empty($now)) {
494             $now = time();
495         }
496         if (empty($defnameprefix)) {
497             $defnameprefix = 'fakename';
498         }
499         $fakedefinition1 = (object)array(
500             'areaid'       => $fakeareaid1,
501             'method'       => 'test_method',
502             'name'         => $defnameprefix.'1',
503             'status'       => 0,
504             'timecreated'  => $now,
505             'usercreated'  => $this->user1->id,
506             'timemodified' => $now + 1,
507             'usermodified' => $this->user1->id,
508         );
509         $fakedefid1 = $DB->insert_record('grading_definitions', $fakedefinition1);
510         $fakedefinition2 = clone($fakedefinition1);
511         $fakedefinition2->areaid = $fakeareaid2;
512         $fakedefinition2->name = $defnameprefix.'2';
513         $fakedefinition2->usermodified = $this->user2->id;
514         $fakedefid2 = $DB->insert_record('grading_definitions', $fakedefinition2);
515     }