MDL-63969 core_course: Update course privacy provider unit tests
[moodle.git] / course / 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/>.
16 /**
17  * Privacy tests for core_course.
18  *
19  * @package    core_course
20  * @category   test
21  * @copyright  2018 Adrian Greeve <adrian@moodle.com>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die();
27 global $CFG;
28 require_once($CFG->dirroot . '/completion/tests/fixtures/completion_creation.php');
30 use \core_privacy\local\request\transform;
32 /**
33  * Unit tests for course/classes/privacy/policy
34  *
35  * @copyright  2018 Adrian Greeve <adrian@moodle.com>
36  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37  */
38 class core_course_privacy_testcase extends \core_privacy\tests\provider_testcase {
40     use completion_creation;
42     /**
43      * Test getting the appropriate context for the userid. This should only ever
44      * return the user context for the user id supplied.
45      */
46     public function test_get_contexts_for_userid() {
47         $this->resetAfterTest();
49         $user1 = $this->getDataGenerator()->create_user();
50         $user2 = $this->getDataGenerator()->create_user();
52         // Make sure contexts are not being returned for user1.
53         $contextlist = \core_course\privacy\provider::get_contexts_for_userid($user1->id);
54         $this->assertCount(0, $contextlist->get_contextids());
56         // Make sure contexts are not being returned for user2.
57         $contextlist = \core_course\privacy\provider::get_contexts_for_userid($user2->id);
58         $this->assertCount(0, $contextlist->get_contextids());
60         // Create course completion data for user1.
61         $this->create_course_completion();
62         $this->complete_course($user1);
64         // Make sure the course context is being returned for user1.
65         $contextlist = \core_course\privacy\provider::get_contexts_for_userid($user1->id);
66         $expected = [$this->coursecontext->id];
67         $actual = $contextlist->get_contextids();
68         $this->assertCount(1, $actual);
69         $this->assertEquals($expected, $actual);
71         // Make sure contexts are still not being returned for user2.
72         $contextlist = \core_course\privacy\provider::get_contexts_for_userid($user2->id);
73         $this->assertCount(0, $contextlist->get_contextids());
75         // User2 has a favourite course.
76         $user2context = \context_user::instance($user2->id);
77         $ufservice = \core_favourites\service_factory::get_service_for_user_context($user2context);
78         $ufservice->create_favourite('core_course', 'courses', $this->coursecontext->instanceid,
79             $this->coursecontext);
81         // Make sure the course context is being returned for user2.
82         $contextlist = \core_course\privacy\provider::get_contexts_for_userid($user2->id);
83         $expected = [$this->coursecontext->id];
84         $actual = $contextlist->get_contextids();
85         $this->assertCount(1, $actual);
86         $this->assertEquals($expected, $actual);
87     }
89     /**
90      * Test fetching users within a context.
91      */
92     public function test_get_users_in_context() {
93         $this->resetAfterTest();
94         $component = 'core_course';
96         $user1 = $this->getDataGenerator()->create_user();
97         $user2 = $this->getDataGenerator()->create_user();
98         $user3 = $this->getDataGenerator()->create_user();
99         $user4 = $this->getDataGenerator()->create_user();
101         // User1 and user2 complete course.
102         $this->create_course_completion();
103         $this->complete_course($user1);
104         $this->complete_course($user2);
106         // User3 is enrolled but has not completed course.
107         $this->getDataGenerator()->enrol_user($user3->id, $this->course->id, 'student');
109         // User4 has a favourited course.
110         $systemcontext = \context_system::instance();
111         $user4ctx = \context_user::instance($user4->id);
112         $ufservice = \core_favourites\service_factory::get_service_for_user_context($user4ctx);
113         $ufservice->create_favourite('core_course', 'courses', $this->coursecontext->instanceid,
114                 $this->coursecontext);
116         // Ensure only users that have course completion or favourites are returned.
117         $userlist = new \core_privacy\local\request\userlist($this->coursecontext, $component);
118         \core_course\privacy\provider::get_users_in_context($userlist);
119         $expected = [
120             $user1->id,
121             $user2->id,
122             $user4->id
123         ];
124         $actual = $userlist->get_userids();
125         sort($expected);
126         sort($actual);
127         $this->assertCount(3, $actual);
128         $this->assertEquals($expected, $actual);
130         // Ensure that users are not being returned in other contexts than the course context.
131         $userlist = new \core_privacy\local\request\userlist($systemcontext, $component);
132         \core_course\privacy\provider::get_users_in_context($userlist);
133         $actual = $userlist->get_userids();
134         $this->assertCount(0, $actual);
135     }
137     /**
138      * Test that user data is exported.
139      */
140     public function test_export_user_data() {
141         $this->resetAfterTest();
143         $user = $this->getDataGenerator()->create_user();
144         $this->create_course_completion();
145         $this->complete_course($user);
146         $approvedlist = new \core_privacy\local\request\approved_contextlist($user, 'core_course',
147                 [$this->coursecontext->id]);
148         $writer = \core_privacy\local\request\writer::with_context($this->coursecontext);
149         \core_course\privacy\provider::export_user_data($approvedlist);
150         $completiondata = $writer->get_data([get_string('privacy:completionpath', 'course')]);
151         $this->assertEquals('In progress', $completiondata->status);
152         $this->assertCount(2, $completiondata->criteria);
154         // User has a favourite course.
155         $usercontext = \context_user::instance($user->id);
156         $ufservice = \core_favourites\service_factory::get_service_for_user_context($usercontext);
157         $favourite = $ufservice->create_favourite('core_course', 'courses',
158                 $this->coursecontext->instanceid, $this->coursecontext);
160         // Ensure that user's favourites data in the course context is being exported.
161         $writer = \core_privacy\local\request\writer::with_context($this->coursecontext);
162         \core_course\privacy\provider::export_user_data($approvedlist);
163         $favouritedata = $writer->get_data([get_string('privacy:favouritespath', 'course')]);
165         $this->assertEquals(transform::yesno(true), $favouritedata->starred);
166         $this->assertEquals('', $favouritedata->ordering);
167         $this->assertEquals(transform::datetime($favourite->timecreated), $favouritedata->timecreated);
168         $this->assertEquals(transform::datetime($favourite->timemodified), $favouritedata->timemodified);
169     }
171     /**
172      * Verify that if a module context is included in the contextlist_collection and its parent course is not, the
173      * export_context_data() call picks this up, and that the contextual course information is included.
174      */
175     public function test_export_context_data_module_context_only() {
176         $this->resetAfterTest();
178         // Create a course and a single module.
179         $course1 = $this->getDataGenerator()->create_course(['fullname' => 'Course 1', 'shortname' => 'C1']);
180         $context1 = context_course::instance($course1->id);
181         $modassign = $this->getDataGenerator()->create_module('assign', ['course' => $course1->id, 'name' => 'assign test 1']);
182         $assigncontext = context_module::instance($modassign->cmid);
184         // Now, let's assume during user info export, only the coursemodule context is returned in the contextlist_collection.
185         $user = $this->getDataGenerator()->create_user();
186         $collection = new \core_privacy\local\request\contextlist_collection($user->id);
187         $approvedlist = new \core_privacy\local\request\approved_contextlist($user, 'mod_assign', [$assigncontext->id]);
188         $collection->add_contextlist($approvedlist);
190         // Now, verify that core_course will detect this, and add relevant contextual information.
191         \core_course\privacy\provider::export_context_data($collection);
192         $writer = \core_privacy\local\request\writer::with_context($context1);
193         $this->assertTrue($writer->has_any_data());
194         $writerdata = $writer->get_data();
195         $this->assertObjectHasAttribute('fullname', $writerdata);
196         $this->assertObjectHasAttribute('shortname', $writerdata);
197         $this->assertObjectHasAttribute('idnumber', $writerdata);
198         $this->assertObjectHasAttribute('summary', $writerdata);
199     }
201     /**
202      * Verify that if a module context and its parent course context are both included in the contextlist_collection, that course
203      * contextual information is present in the export.
204      */
205     public function test_export_context_data_course_and_module_contexts() {
206         $this->resetAfterTest();
208         // Create a course and a single module.
209         $course1 = $this->getDataGenerator()->create_course(['fullname' => 'Course 1', 'shortname' => 'C1', 'format' => 'site']);
210         $context1 = context_course::instance($course1->id);
211         $modassign = $this->getDataGenerator()->create_module('assign', ['course' => $course1->id, 'name' => 'assign test 1']);
212         $assigncontext = context_module::instance($modassign->cmid);
214         // Now, assume during user info export, that both module and course contexts are returned in the contextlist_collection.
215         $user = $this->getDataGenerator()->create_user();
216         $collection = new \core_privacy\local\request\contextlist_collection($user->id);
217         $approvedlist = new \core_privacy\local\request\approved_contextlist($user, 'mod_assign', [$assigncontext->id]);
218         $approvedlist2 = new \core_privacy\local\request\approved_contextlist($user, 'core_course', [$context1->id]);
219         $collection->add_contextlist($approvedlist);
220         $collection->add_contextlist($approvedlist2);
222         // Now, verify that core_course still adds relevant contextual information, even for courses which are explicitly listed in
223         // the contextlist_collection.
224         \core_course\privacy\provider::export_context_data($collection);
225         $writer = \core_privacy\local\request\writer::with_context($context1);
226         $this->assertTrue($writer->has_any_data());
227         $writerdata = $writer->get_data();
228         $this->assertObjectHasAttribute('fullname', $writerdata);
229         $this->assertObjectHasAttribute('shortname', $writerdata);
230         $this->assertObjectHasAttribute('idnumber', $writerdata);
231         $this->assertObjectHasAttribute('summary', $writerdata);
232     }
234     /**
235      * Test deleting all user data for one context.
236      */
237     public function test_delete_data_for_all_users_in_context() {
238         global $DB;
240         $this->resetAfterTest();
242         $user1 = $this->getDataGenerator()->create_user();
243         $user2 = $this->getDataGenerator()->create_user();
244         $this->create_course_completion();
246         $systemcontext = \context_system::instance();
247         $user1ctx = \context_user::instance($user1->id);
248         $user2ctx = \context_user::instance($user2->id);
249         // User1 and user2 have a favourite course.
250         $ufservice1 = \core_favourites\service_factory::get_service_for_user_context($user1ctx);
251         $ufservice1->create_favourite('core_course', 'courses', $this->coursecontext->instanceid,
252                 $this->coursecontext);
253         $ufservice2 = \core_favourites\service_factory::get_service_for_user_context($user2ctx);
254         $ufservice2->create_favourite('core_course', 'courses', $this->coursecontext->instanceid,
255                 $this->coursecontext);
257         // Ensure only users that have course favourites are returned in the course context (user1 and user2).
258         $userlist = new \core_privacy\local\request\userlist($this->coursecontext, 'core_course');
259         \core_course\privacy\provider::get_users_in_context($userlist);
260         $actual = $userlist->get_userids();
261         $this->assertCount(2, $actual);
263         // Ensure the users does not have a course completion data.
264         $records = $DB->get_records('course_modules_completion');
265         $this->assertCount(0, $records);
266         $records = $DB->get_records('course_completion_crit_compl');
267         $this->assertCount(0, $records);
269         // Create course completions for user1 and users.
270         $this->complete_course($user1);
271         $this->complete_course($user2);
272         $records = $DB->get_records('course_modules_completion');
273         $this->assertCount(2, $records);
274         $records = $DB->get_records('course_completion_crit_compl');
275         $this->assertCount(2, $records);
277         // Delete data for all users in a context different than the course context (system context).
278         \core_course\privacy\provider::delete_data_for_all_users_in_context($systemcontext);
280         // Ensure the data in the course context has not been deleted.
281         $userlist = new \core_privacy\local\request\userlist($this->coursecontext, 'core_course');
282         \core_course\privacy\provider::get_users_in_context($userlist);
283         $actual = $userlist->get_userids();
284         $this->assertCount(2, $actual);
286         // Delete data for all users in the course context.
287         \core_course\privacy\provider::delete_data_for_all_users_in_context($this->coursecontext);
289         // Ensure the completion data has been removed in the course context.
290         $records = $DB->get_records('course_modules_completion');
291         $this->assertCount(0, $records);
292         $records = $DB->get_records('course_completion_crit_compl');
293         $this->assertCount(0, $records);
295         // Ensure that users are not returned after the deletion in the course context.
296         $userlist = new \core_privacy\local\request\userlist($this->coursecontext, 'core_course');
297         \core_course\privacy\provider::get_users_in_context($userlist);
298         $actual = $userlist->get_userids();
299         $this->assertCount(0, $actual);
300     }
302     /**
303      * Test deleting data for only one user.
304      */
305     public function test_delete_data_for_user() {
306         $this->resetAfterTest();
308         $user1 = $this->getDataGenerator()->create_user();
309         $user2 = $this->getDataGenerator()->create_user();
310         $user3 = $this->getDataGenerator()->create_user();
312         // Create course completion for user1.
313         $this->create_course_completion();
314         $this->complete_course($user1);
316         // Ensure user1 is returned in the course context.
317         $userlist = new \core_privacy\local\request\userlist($this->coursecontext, 'core_course');
318         \core_course\privacy\provider::get_users_in_context($userlist);
319         $actual = $userlist->get_userids();
320         $expected = [$user1->id];
321         $this->assertCount(1, $actual);
322         $this->assertEquals($expected, $actual);
324         // User2 and user3 have a favourite course.
325         $systemcontext = \context_system::instance();
326         $user2ctx = \context_user::instance($user2->id);
327         $user3ctx = \context_user::instance($user3->id);
328         $ufservice2 = \core_favourites\service_factory::get_service_for_user_context($user2ctx);
329         $ufservice2->create_favourite('core_course', 'courses', $this->coursecontext->instanceid,
330                 $this->coursecontext);
331         $ufservice3 = \core_favourites\service_factory::get_service_for_user_context($user3ctx);
332         $ufservice3->create_favourite('core_course', 'courses', $this->coursecontext->instanceid,
333                 $this->coursecontext);
335         // Ensure user1, user2 and user3 are returned in the course context.
336         $userlist = new \core_privacy\local\request\userlist($this->coursecontext, 'core_course');
337         \core_course\privacy\provider::get_users_in_context($userlist);
338         $actual = $userlist->get_userids();
339         $expected = [
340             $user1->id,
341             $user2->id,
342             $user3->id
343         ];
344         sort($expected);
345         sort($actual);
346         $this->assertCount(3, $actual);
347         $this->assertEquals($expected, $actual);
349         // Delete user1's data in the course context.
350         $approvedlist = new \core_privacy\local\request\approved_contextlist($user1, 'core_course',
351                 [$this->coursecontext->id]);
352         \core_course\privacy\provider::delete_data_for_user($approvedlist);
354         // Ensure user1's data is deleted and only user2 and user3 are returned in the course context.
355         $userlist = new \core_privacy\local\request\userlist($this->coursecontext, 'core_course');
356         \core_course\privacy\provider::get_users_in_context($userlist);
357         $actual = $userlist->get_userids();
358         $expected = [
359             $user2->id,
360             $user3->id
361         ];
362         sort($expected);
363         sort($actual);
364         $this->assertEquals($expected, $actual);
366         // Delete user2's data in a context different than the course context (system context).
367         $approvedlist = new \core_privacy\local\request\approved_contextlist($user2, 'core_course',
368                 [$systemcontext->id]);
369         \core_course\privacy\provider::delete_data_for_user($approvedlist);
371         // Ensure user2 and user3 are still returned in the course context.
372         $userlist = new \core_privacy\local\request\userlist($this->coursecontext, 'core_course');
373         \core_course\privacy\provider::get_users_in_context($userlist);
374         $actual = $userlist->get_userids();
375         $expected = [
376             $user2->id,
377             $user3->id
378         ];
379         sort($expected);
380         sort($actual);
381         $this->assertEquals($expected, $actual);
383         // Delete user2's data in the course context.
384         $approvedlist = new \core_privacy\local\request\approved_contextlist($user2, 'core_course',
385                 [$this->coursecontext->id]);
386         \core_course\privacy\provider::delete_data_for_user($approvedlist);
388         // Ensure user2's is deleted and user3 is still returned in the course context.
389         $userlist = new \core_privacy\local\request\userlist($this->coursecontext, 'core_course');
390         \core_course\privacy\provider::get_users_in_context($userlist);
391         $actual = $userlist->get_userids();
392         $expected = [
393             $user3->id
394         ];
395         $this->assertEquals($expected, $actual);
396     }
398     /**
399      * Test deleting data within a context for an approved userlist.
400      */
401     public function test_delete_data_for_users() {
402         $this->resetAfterTest();
404         $component = 'core_course';
405         $user1 = $this->getDataGenerator()->create_user();
406         $user2 = $this->getDataGenerator()->create_user();
407         $user3 = $this->getDataGenerator()->create_user();
409         $this->create_course_completion();
410         $this->complete_course($user1);
411         $this->complete_course($user2);
413         // Ensure user1, user2 are returned in the course context.
414         $userlist = new \core_privacy\local\request\userlist($this->coursecontext, 'core_course');
415         \core_course\privacy\provider::get_users_in_context($userlist);
416         $actual = $userlist->get_userids();
417         $expected = [
418             $user1->id,
419             $user2->id
420         ];
421         sort($expected);
422         sort($actual);
423         $this->assertCount(2, $actual);
424         $this->assertEquals($expected, $actual);
426         $systemcontext = \context_system::instance();
427         // User3 has a favourite course.
428         $user3ctx = \context_user::instance($user3->id);
429         $ufservice = \core_favourites\service_factory::get_service_for_user_context($user3ctx);
430         $ufservice->create_favourite('core_course', 'courses', $this->coursecontext->instanceid,
431                 $this->coursecontext);
433         // Ensure user1, user2 and user3 are now returned in the course context.
434         $userlist = new \core_privacy\local\request\userlist($this->coursecontext, 'core_course');
435         \core_course\privacy\provider::get_users_in_context($userlist);
436         $actual = $userlist->get_userids();
437         $expected = [
438             $user1->id,
439             $user2->id,
440             $user3->id
441         ];
442         sort($expected);
443         sort($actual);
444         $this->assertCount(3, $actual);
445         $this->assertEquals($expected, $actual);
447         // Delete data for user1 and user3 in the course context.
448         $approveduserids = [$user1->id, $user3->id];
449         $approvedlist = new \core_privacy\local\request\approved_userlist($this->coursecontext, $component, $approveduserids);
450         \core_course\privacy\provider::delete_data_for_users($approvedlist);
452         // Ensure user1 and user3 are deleted and user2 is still returned in the course context.
453         $userlist = new \core_privacy\local\request\userlist($this->coursecontext, 'core_course');
454         \core_course\privacy\provider::get_users_in_context($userlist);
455         $actual = $userlist->get_userids();
456         $expected = [$user2->id];
457         $this->assertCount(1, $actual);
458         $this->assertEquals($expected, $actual);
460         // Try to delete user2's data in a context different than course (system context).
461         $approveduserids = [$user2->id];
462         $approvedlist = new \core_privacy\local\request\approved_userlist($systemcontext, $component, $approveduserids);
463         \core_course\privacy\provider::delete_data_for_users($approvedlist);
465         // Ensure user2 is still returned in the course context.
466         $userlist = new \core_privacy\local\request\userlist($this->coursecontext, 'core_course');
467         \core_course\privacy\provider::get_users_in_context($userlist);
468         $actual = $userlist->get_userids();
469         $expected = [
470             $user2->id
471         ];
472         $this->assertCount(1, $actual);
473         $this->assertEquals($expected, $actual);
474     }