MDL-61899 tool_dataprivacy: Refined patch fixing cibot complains
[moodle.git] / admin / tool / dataprivacy / tests / api_test.php
blobf8892ab827569f85b3a9ec43269c7a5459e89cda
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.
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 * API tests.
20 * @package tool_dataprivacy
21 * @copyright 2018 Jun Pataleta
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 use core\invalid_persistent_exception;
26 use core\task\manager;
27 use tool_dataprivacy\context_instance;
28 use tool_dataprivacy\api;
29 use tool_dataprivacy\data_registry;
30 use tool_dataprivacy\expired_context;
31 use tool_dataprivacy\data_request;
32 use tool_dataprivacy\task\initiate_data_request_task;
33 use tool_dataprivacy\task\process_data_request_task;
35 defined('MOODLE_INTERNAL') || die();
36 global $CFG;
38 /**
39 * API tests.
41 * @package tool_dataprivacy
42 * @copyright 2018 Jun Pataleta
43 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
45 class tool_dataprivacy_api_testcase extends advanced_testcase {
47 /**
48 * setUp.
50 public function setUp() {
51 $this->resetAfterTest();
54 /**
55 * Test for api::update_request_status().
57 public function test_update_request_status() {
58 $generator = new testing_data_generator();
59 $s1 = $generator->create_user();
61 // Create the sample data request.
62 $datarequest = api::create_data_request($s1->id, api::DATAREQUEST_TYPE_EXPORT);
64 $requestid = $datarequest->get('id');
66 // Update with a valid status.
67 $result = api::update_request_status($requestid, api::DATAREQUEST_STATUS_COMPLETE);
68 $this->assertTrue($result);
70 // Fetch the request record again.
71 $datarequest = new data_request($requestid);
72 $this->assertEquals(api::DATAREQUEST_STATUS_COMPLETE, $datarequest->get('status'));
74 // Update with an invalid status.
75 $this->expectException(invalid_persistent_exception::class);
76 api::update_request_status($requestid, -1);
79 /**
80 * Test for api::get_site_dpos() when there are no users with the DPO role.
82 public function test_get_site_dpos_no_dpos() {
83 $admin = get_admin();
85 $dpos = api::get_site_dpos();
86 $this->assertCount(1, $dpos);
87 $dpo = reset($dpos);
88 $this->assertEquals($admin->id, $dpo->id);
91 /**
92 * Test for api::get_site_dpos() when there are no users with the DPO role.
94 public function test_get_site_dpos() {
95 global $DB;
96 $generator = new testing_data_generator();
97 $u1 = $generator->create_user();
98 $u2 = $generator->create_user();
100 $context = context_system::instance();
102 // Give the manager role with the capability to manage data requests.
103 $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager'));
104 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true);
105 // Assign u1 as a manager.
106 role_assign($managerroleid, $u1->id, $context->id);
108 // Give the editing teacher role with the capability to manage data requests.
109 $editingteacherroleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher'));
110 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $editingteacherroleid, $context->id, true);
111 // Assign u1 as an editing teacher as well.
112 role_assign($editingteacherroleid, $u1->id, $context->id);
113 // Assign u2 as an editing teacher.
114 role_assign($editingteacherroleid, $u2->id, $context->id);
116 // Only map the manager role to the DPO role.
117 set_config('dporoles', $managerroleid, 'tool_dataprivacy');
119 $dpos = api::get_site_dpos();
120 $this->assertCount(1, $dpos);
121 $dpo = reset($dpos);
122 $this->assertEquals($u1->id, $dpo->id);
126 * Test for api::approve_data_request().
128 public function test_approve_data_request() {
129 global $DB;
131 $generator = new testing_data_generator();
132 $s1 = $generator->create_user();
133 $u1 = $generator->create_user();
135 $context = context_system::instance();
137 // Manager role.
138 $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager'));
139 // Give the manager role with the capability to manage data requests.
140 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true);
141 // Assign u1 as a manager.
142 role_assign($managerroleid, $u1->id, $context->id);
144 // Map the manager role to the DPO role.
145 set_config('dporoles', $managerroleid, 'tool_dataprivacy');
147 // Create the sample data request.
148 $datarequest = api::create_data_request($s1->id, api::DATAREQUEST_TYPE_EXPORT);
149 $requestid = $datarequest->get('id');
151 // Make this ready for approval.
152 api::update_request_status($requestid, api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
154 $this->setUser($u1);
155 $result = api::approve_data_request($requestid);
156 $this->assertTrue($result);
157 $datarequest = new data_request($requestid);
158 $this->assertEquals($u1->id, $datarequest->get('dpo'));
159 $this->assertEquals(api::DATAREQUEST_STATUS_APPROVED, $datarequest->get('status'));
161 // Test adhoc task creation.
162 $adhoctasks = manager::get_adhoc_tasks(process_data_request_task::class);
163 $this->assertCount(1, $adhoctasks);
167 * Test for api::approve_data_request() with the request not yet waiting for approval.
169 public function test_approve_data_request_not_yet_ready() {
170 global $DB;
172 $generator = new testing_data_generator();
173 $s1 = $generator->create_user();
174 $u1 = $generator->create_user();
176 $context = context_system::instance();
178 // Manager role.
179 $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager'));
180 // Give the manager role with the capability to manage data requests.
181 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true);
182 // Assign u1 as a manager.
183 role_assign($managerroleid, $u1->id, $context->id);
185 // Map the manager role to the DPO role.
186 set_config('dporoles', $managerroleid, 'tool_dataprivacy');
188 // Create the sample data request.
189 $datarequest = api::create_data_request($s1->id, api::DATAREQUEST_TYPE_EXPORT);
190 $requestid = $datarequest->get('id');
192 $this->setUser($u1);
193 $this->expectException(moodle_exception::class);
194 api::approve_data_request($requestid);
198 * Test for api::approve_data_request() when called by a user who doesn't have the DPO role.
200 public function test_approve_data_request_non_dpo_user() {
201 $generator = new testing_data_generator();
202 $student = $generator->create_user();
203 $teacher = $generator->create_user();
205 // Create the sample data request.
206 $datarequest = api::create_data_request($student->id, api::DATAREQUEST_TYPE_EXPORT);
208 $requestid = $datarequest->get('id');
210 // Login as a user without DPO role.
211 $this->setUser($teacher);
212 $this->expectException(required_capability_exception::class);
213 api::approve_data_request($requestid);
217 * Test for api::can_contact_dpo()
219 public function test_can_contact_dpo() {
220 // Default ('contactdataprotectionofficer' is enabled by default).
221 $this->assertTrue(api::can_contact_dpo());
223 // Disable.
224 set_config('contactdataprotectionofficer', 0, 'tool_dataprivacy');
225 $this->assertFalse(api::can_contact_dpo());
227 // Enable again.
228 set_config('contactdataprotectionofficer', 1, 'tool_dataprivacy');
229 $this->assertTrue(api::can_contact_dpo());
233 * Test for api::can_manage_data_requests()
235 public function test_can_manage_data_requests() {
236 global $DB;
238 // No configured site DPOs yet.
239 $admin = get_admin();
240 $this->assertTrue(api::can_manage_data_requests($admin->id));
242 $generator = new testing_data_generator();
243 $dpo = $generator->create_user();
244 $nondpocapable = $generator->create_user();
245 $nondpoincapable = $generator->create_user();
247 $context = context_system::instance();
249 // Manager role.
250 $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager'));
251 // Give the manager role with the capability to manage data requests.
252 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true);
253 // Assign u1 as a manager.
254 role_assign($managerroleid, $dpo->id, $context->id);
256 // Editing teacher role.
257 $editingteacherroleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher'));
258 // Give the editing teacher role with the capability to manage data requests.
259 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true);
260 // Assign u2 as an editing teacher.
261 role_assign($editingteacherroleid, $nondpocapable->id, $context->id);
263 // Map only the manager role to the DPO role.
264 set_config('dporoles', $managerroleid, 'tool_dataprivacy');
266 // User with capability and has DPO role.
267 $this->assertTrue(api::can_manage_data_requests($dpo->id));
268 // User with capability but has no DPO role.
269 $this->assertFalse(api::can_manage_data_requests($nondpocapable->id));
270 // User without the capability and has no DPO role.
271 $this->assertFalse(api::can_manage_data_requests($nondpoincapable->id));
275 * Test for api::create_data_request()
277 public function test_create_data_request() {
278 $generator = new testing_data_generator();
279 $user = $generator->create_user();
280 $comment = 'sample comment';
282 // Login as user.
283 $this->setUser($user->id);
285 // Test data request creation.
286 $datarequest = api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
287 $this->assertEquals($user->id, $datarequest->get('userid'));
288 $this->assertEquals($user->id, $datarequest->get('requestedby'));
289 $this->assertEquals(api::DATAREQUEST_TYPE_EXPORT, $datarequest->get('type'));
290 $this->assertEquals(api::DATAREQUEST_STATUS_PENDING, $datarequest->get('status'));
291 $this->assertEquals($comment, $datarequest->get('comments'));
293 // Test adhoc task creation.
294 $adhoctasks = manager::get_adhoc_tasks(initiate_data_request_task::class);
295 $this->assertCount(1, $adhoctasks);
299 * Test for api::deny_data_request()
301 public function test_deny_data_request() {
302 $generator = new testing_data_generator();
303 $user = $generator->create_user();
304 $comment = 'sample comment';
306 // Login as user.
307 $this->setUser($user->id);
309 // Test data request creation.
310 $datarequest = api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
312 // Login as the admin (default DPO when no one is set).
313 $this->setAdminUser();
315 // Make this ready for approval.
316 api::update_request_status($datarequest->get('id'), api::DATAREQUEST_STATUS_AWAITING_APPROVAL);
318 // Deny the data request.
319 $result = api::deny_data_request($datarequest->get('id'));
320 $this->assertTrue($result);
324 * Test for api::deny_data_request()
326 public function test_deny_data_request_without_permissions() {
327 $generator = new testing_data_generator();
328 $user = $generator->create_user();
329 $comment = 'sample comment';
331 // Login as user.
332 $this->setUser($user->id);
334 // Test data request creation.
335 $datarequest = api::create_data_request($user->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
337 // Login as a non-DPO user and try to call deny_data_request.
338 $user2 = $generator->create_user();
339 $this->setUser($user2);
340 $this->expectException(required_capability_exception::class);
341 api::deny_data_request($datarequest->get('id'));
345 * Test for api::get_data_requests()
347 public function test_get_data_requests() {
348 $generator = new testing_data_generator();
349 $user1 = $generator->create_user();
350 $user2 = $generator->create_user();
351 $comment = 'sample comment';
353 // Make a data request as user 1.
354 $d1 = api::create_data_request($user1->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
355 // Make a data request as user 2.
356 $d2 = api::create_data_request($user2->id, api::DATAREQUEST_TYPE_EXPORT, $comment);
358 // Fetching data requests of specific users.
359 $requests = api::get_data_requests($user1->id);
360 $this->assertCount(1, $requests);
361 $datarequest = reset($requests);
362 $this->assertEquals($d1->to_record(), $datarequest->to_record());
364 $requests = api::get_data_requests($user2->id);
365 $this->assertCount(1, $requests);
366 $datarequest = reset($requests);
367 $this->assertEquals($d2->to_record(), $datarequest->to_record());
369 // Fetching data requests of all users.
370 // As guest.
371 $this->setGuestUser();
372 $requests = api::get_data_requests();
373 $this->assertEmpty($requests);
375 // As DPO (admin in this case, which is default if no site DPOs are set).
376 $this->setAdminUser();
377 $requests = api::get_data_requests();
378 $this->assertCount(2, $requests);
382 * Data provider for test_has_ongoing_request.
384 public function status_provider() {
385 return [
386 [api::DATAREQUEST_STATUS_PENDING, true],
387 [api::DATAREQUEST_STATUS_PREPROCESSING, true],
388 [api::DATAREQUEST_STATUS_AWAITING_APPROVAL, true],
389 [api::DATAREQUEST_STATUS_APPROVED, true],
390 [api::DATAREQUEST_STATUS_PROCESSING, true],
391 [api::DATAREQUEST_STATUS_COMPLETE, false],
392 [api::DATAREQUEST_STATUS_CANCELLED, false],
393 [api::DATAREQUEST_STATUS_REJECTED, false],
398 * Test for api::has_ongoing_request()
400 * @dataProvider status_provider
401 * @param int $status The request status.
402 * @param bool $expected The expected result.
404 public function test_has_ongoing_request($status, $expected) {
405 $generator = new testing_data_generator();
406 $user1 = $generator->create_user();
408 // Make a data request as user 1.
409 $request = api::create_data_request($user1->id, api::DATAREQUEST_TYPE_EXPORT);
410 // Set the status.
411 api::update_request_status($request->get('id'), $status);
413 // Check if this request is ongoing.
414 $result = api::has_ongoing_request($user1->id, api::DATAREQUEST_TYPE_EXPORT);
415 $this->assertEquals($expected, $result);
419 * Test for api::is_active()
421 * @dataProvider status_provider
422 * @param int $status The request status
423 * @param bool $expected The expected result
425 public function test_is_active($status, $expected) {
426 // Check if this request is ongoing.
427 $result = api::is_active($status);
428 $this->assertEquals($expected, $result);
432 * Test for api::is_site_dpo()
434 public function test_is_site_dpo() {
435 global $DB;
437 // No configured site DPOs yet.
438 $admin = get_admin();
439 $this->assertTrue(api::is_site_dpo($admin->id));
441 $generator = new testing_data_generator();
442 $dpo = $generator->create_user();
443 $nondpo = $generator->create_user();
445 $context = context_system::instance();
447 // Manager role.
448 $managerroleid = $DB->get_field('role', 'id', array('shortname' => 'manager'));
449 // Give the manager role with the capability to manage data requests.
450 assign_capability('tool/dataprivacy:managedatarequests', CAP_ALLOW, $managerroleid, $context->id, true);
451 // Assign u1 as a manager.
452 role_assign($managerroleid, $dpo->id, $context->id);
454 // Map only the manager role to the DPO role.
455 set_config('dporoles', $managerroleid, 'tool_dataprivacy');
457 // User is a DPO.
458 $this->assertTrue(api::is_site_dpo($dpo->id));
459 // User is not a DPO.
460 $this->assertFalse(api::is_site_dpo($nondpo->id));
464 * Data provider function for test_notify_dpo
466 * @return array
468 public function notify_dpo_provider() {
469 return [
470 [api::DATAREQUEST_TYPE_EXPORT, 'requesttypeexport'],
471 [api::DATAREQUEST_TYPE_DELETE, 'requesttypedelete'],
472 [api::DATAREQUEST_TYPE_OTHERS, 'requesttypeothers'],
477 * Test for api::notify_dpo()
479 * @dataProvider notify_dpo_provider
480 * @param int $type The request type
481 * @param string $typestringid The request lang string identifier
483 public function test_notify_dpo($type, $typestringid) {
484 $generator = new testing_data_generator();
485 $user1 = $generator->create_user();
487 // Make a data request as user 1.
488 $this->setUser($user1);
489 $request = api::create_data_request($user1->id, $type);
491 $sink = $this->redirectMessages();
492 // Let's just use admin as DPO (It's the default if not set).
493 $dpo = get_admin();
494 $messageid = api::notify_dpo($dpo, $request);
495 $this->assertNotFalse($messageid);
496 $messages = $sink->get_messages();
497 $this->assertCount(1, $messages);
498 $message = reset($messages);
500 // Check some of the message properties.
501 $this->assertEquals($user1->id, $message->useridfrom);
502 $this->assertEquals($dpo->id, $message->useridto);
503 $typestring = get_string($typestringid, 'tool_dataprivacy');
504 $subject = get_string('datarequestemailsubject', 'tool_dataprivacy', $typestring);
505 $this->assertEquals($subject, $message->subject);
506 $this->assertEquals('tool_dataprivacy', $message->component);
507 $this->assertEquals('contactdataprotectionofficer', $message->eventtype);
508 $this->assertContains(fullname($dpo), $message->fullmessage);
509 $this->assertContains(fullname($user1), $message->fullmessage);
513 * Test of creating purpose as a user without privileges.
515 public function test_create_purpose_non_dpo_user() {
516 $pleb = $this->getDataGenerator()->create_user();
518 $this->setUser($pleb);
519 $this->expectException(required_capability_exception::class);
520 api::create_purpose((object)[
521 'name' => 'aaa',
522 'description' => '<b>yeah</b>',
523 'descriptionformat' => 1,
524 'retentionperiod' => 'PT1M'
529 * Test fetching of purposes as a user without privileges.
531 public function test_get_purposes_non_dpo_user() {
532 $pleb = $this->getDataGenerator()->create_user();
533 $this->setAdminUser();
534 api::create_purpose((object)[
535 'name' => 'bbb',
536 'description' => '<b>yeah</b>',
537 'descriptionformat' => 1,
538 'retentionperiod' => 'PT1M'
541 $this->setUser($pleb);
542 $this->expectException(required_capability_exception::class);
543 api::get_purposes();
547 * Test updating of purpose as a user without privileges.
549 public function test_update_purposes_non_dpo_user() {
550 $pleb = $this->getDataGenerator()->create_user();
551 $this->setAdminUser();
552 $purpose = api::create_purpose((object)[
553 'name' => 'bbb',
554 'description' => '<b>yeah</b>',
555 'descriptionformat' => 1,
556 'retentionperiod' => 'PT1M'
559 $this->setUser($pleb);
560 $this->expectException(required_capability_exception::class);
561 $purpose->set('retentionperiod', 'PT2M');
562 api::update_purpose($purpose->to_record());
566 * Test purpose deletion as a user without privileges.
568 public function test_delete_purpose_non_dpo_user() {
569 $pleb = $this->getDataGenerator()->create_user();
570 $this->setAdminUser();
571 $purpose = api::create_purpose((object)[
572 'name' => 'bbb',
573 'description' => '<b>yeah</b>',
574 'descriptionformat' => 1,
575 'retentionperiod' => 'PT1M'
578 $this->setUser($pleb);
579 $this->expectException(required_capability_exception::class);
580 api::delete_purpose($purpose->get('id'));
584 * Test data purposes CRUD actions.
586 * @return null
588 public function test_purpose_crud() {
590 $this->setAdminUser();
592 // Add.
593 $purpose = api::create_purpose((object)[
594 'name' => 'bbb',
595 'description' => '<b>yeah</b>',
596 'descriptionformat' => 1,
597 'retentionperiod' => 'PT1M'
599 $this->assertInstanceOf('\tool_dataprivacy\purpose', $purpose);
600 $this->assertEquals('bbb', $purpose->get('name'));
601 $this->assertEquals('PT1M', $purpose->get('retentionperiod'));
603 // Update.
604 $purpose->set('retentionperiod', 'PT2M');
605 $purpose = api::update_purpose($purpose->to_record());
606 $this->assertEquals('PT2M', $purpose->get('retentionperiod'));
608 // Retrieve.
609 $purpose = api::create_purpose((object)['name' => 'aaa', 'retentionperiod' => 'PT1M']);
610 $purposes = api::get_purposes();
611 $this->assertCount(2, $purposes);
612 $this->assertEquals('aaa', $purposes[0]->get('name'));
613 $this->assertEquals('bbb', $purposes[1]->get('name'));
615 // Delete.
616 api::delete_purpose($purposes[0]->get('id'));
617 $this->assertCount(1, api::get_purposes());
618 api::delete_purpose($purposes[1]->get('id'));
619 $this->assertCount(0, api::get_purposes());
623 * Test creation of data categories as a user without privileges.
625 public function test_create_category_non_dpo_user() {
626 $pleb = $this->getDataGenerator()->create_user();
628 $this->setUser($pleb);
629 $this->expectException(required_capability_exception::class);
630 api::create_category((object)[
631 'name' => 'bbb',
632 'description' => '<b>yeah</b>',
633 'descriptionformat' => 1
638 * Test fetching of data categories as a user without privileges.
640 public function test_get_categories_non_dpo_user() {
641 $pleb = $this->getDataGenerator()->create_user();
643 $this->setAdminUser();
644 api::create_category((object)[
645 'name' => 'bbb',
646 'description' => '<b>yeah</b>',
647 'descriptionformat' => 1
650 // Back to a regular user.
651 $this->setUser($pleb);
652 $this->expectException(required_capability_exception::class);
653 api::get_categories();
657 * Test updating of data category as a user without privileges.
659 public function test_update_category_non_dpo_user() {
660 $pleb = $this->getDataGenerator()->create_user();
662 $this->setAdminUser();
663 $category = api::create_category((object)[
664 'name' => 'bbb',
665 'description' => '<b>yeah</b>',
666 'descriptionformat' => 1
669 // Back to a regular user.
670 $this->setUser($pleb);
671 $this->expectException(required_capability_exception::class);
672 $category->set('name', 'yeah');
673 api::update_category($category->to_record());
677 * Test deletion of data category as a user without privileges.
679 public function test_delete_category_non_dpo_user() {
680 $pleb = $this->getDataGenerator()->create_user();
682 $this->setAdminUser();
683 $category = api::create_category((object)[
684 'name' => 'bbb',
685 'description' => '<b>yeah</b>',
686 'descriptionformat' => 1
689 // Back to a regular user.
690 $this->setUser($pleb);
691 $this->expectException(required_capability_exception::class);
692 api::delete_category($category->get('id'));
693 $this->fail('Users shouldn\'t be allowed to manage categories by default');
697 * Test data categories CRUD actions.
699 * @return null
701 public function test_category_crud() {
703 $this->setAdminUser();
705 // Add.
706 $category = api::create_category((object)[
707 'name' => 'bbb',
708 'description' => '<b>yeah</b>',
709 'descriptionformat' => 1
711 $this->assertInstanceOf('\tool_dataprivacy\category', $category);
712 $this->assertEquals('bbb', $category->get('name'));
714 // Update.
715 $category->set('name', 'bcd');
716 $category = api::update_category($category->to_record());
717 $this->assertEquals('bcd', $category->get('name'));
719 // Retrieve.
720 $category = api::create_category((object)['name' => 'aaa']);
721 $categories = api::get_categories();
722 $this->assertCount(2, $categories);
723 $this->assertEquals('aaa', $categories[0]->get('name'));
724 $this->assertEquals('bcd', $categories[1]->get('name'));
726 // Delete.
727 api::delete_category($categories[0]->get('id'));
728 $this->assertCount(1, api::get_categories());
729 api::delete_category($categories[1]->get('id'));
730 $this->assertCount(0, api::get_categories());
734 * Test context instances.
736 * @return null
738 public function test_context_instances() {
739 global $DB;
741 $this->setAdminUser();
743 list($purposes, $categories, $courses, $modules) = $this->add_purposes_and_categories();
745 $coursecontext1 = \context_course::instance($courses[0]->id);
746 $coursecontext2 = \context_course::instance($courses[1]->id);
748 $record1 = (object)['contextid' => $coursecontext1->id, 'purposeid' => $purposes[0]->get('id'),
749 'categoryid' => $categories[0]->get('id')];
750 $contextinstance1 = api::set_context_instance($record1);
752 $record2 = (object)['contextid' => $coursecontext2->id, 'purposeid' => $purposes[1]->get('id'),
753 'categoryid' => $categories[1]->get('id')];
754 $contextinstance2 = api::set_context_instance($record2);
756 $this->assertCount(2, $DB->get_records('tool_dataprivacy_ctxinstance'));
758 api::unset_context_instance($contextinstance1);
759 $this->assertCount(1, $DB->get_records('tool_dataprivacy_ctxinstance'));
761 $update = (object)['id' => $contextinstance2->get('id'), 'contextid' => $coursecontext2->id,
762 'purposeid' => $purposes[0]->get('id'), 'categoryid' => $categories[0]->get('id')];
763 $contextinstance2 = api::set_context_instance($update);
764 $this->assertCount(1, $DB->get_records('tool_dataprivacy_ctxinstance'));
768 * Test contextlevel.
770 * @return null
772 public function test_contextlevel() {
773 global $DB;
775 $this->setAdminUser();
776 list($purposes, $categories, $courses, $modules) = $this->add_purposes_and_categories();
778 $record = (object)[
779 'purposeid' => $purposes[0]->get('id'),
780 'categoryid' => $categories[0]->get('id'),
781 'contextlevel' => CONTEXT_SYSTEM,
783 $contextlevel = api::set_contextlevel($record);
784 $this->assertInstanceOf('\tool_dataprivacy\contextlevel', $contextlevel);
785 $this->assertEquals($record->contextlevel, $contextlevel->get('contextlevel'));
786 $this->assertEquals($record->purposeid, $contextlevel->get('purposeid'));
787 $this->assertEquals($record->categoryid, $contextlevel->get('categoryid'));
789 // Now update it.
790 $record->purposeid = $purposes[1]->get('id');
791 $contextlevel = api::set_contextlevel($record);
792 $this->assertEquals($record->contextlevel, $contextlevel->get('contextlevel'));
793 $this->assertEquals($record->purposeid, $contextlevel->get('purposeid'));
794 $this->assertEquals(1, $DB->count_records('tool_dataprivacy_ctxlevel'));
796 $record->contextlevel = CONTEXT_USER;
797 $contextlevel = api::set_contextlevel($record);
798 $this->assertEquals(2, $DB->count_records('tool_dataprivacy_ctxlevel'));
802 * Test effective context levels purpose and category defaults.
804 * @return null
806 public function test_effective_contextlevel_defaults() {
807 $this->setAdminUser();
809 list($purposes, $categories, $courses, $modules) = $this->add_purposes_and_categories();
811 list($purposeid, $categoryid) = data_registry::get_effective_default_contextlevel_purpose_and_category(CONTEXT_SYSTEM);
812 $this->assertEquals(false, $purposeid);
813 $this->assertEquals(false, $categoryid);
815 list($purposevar, $categoryvar) = data_registry::var_names_from_context(
816 \context_helper::get_class_for_level(CONTEXT_SYSTEM)
818 set_config($purposevar, $purposes[0]->get('id'), 'tool_dataprivacy');
820 list($purposeid, $categoryid) = data_registry::get_effective_default_contextlevel_purpose_and_category(CONTEXT_SYSTEM);
821 $this->assertEquals($purposes[0]->get('id'), $purposeid);
822 $this->assertEquals(false, $categoryid);
824 // Course inherits from system if not defined.
825 list($purposeid, $categoryid) = data_registry::get_effective_default_contextlevel_purpose_and_category(CONTEXT_COURSE);
826 $this->assertEquals($purposes[0]->get('id'), $purposeid);
827 $this->assertEquals(false, $categoryid);
829 // Course defined values should have preference.
830 list($purposevar, $categoryvar) = data_registry::var_names_from_context(
831 \context_helper::get_class_for_level(CONTEXT_COURSE)
833 set_config($purposevar, $purposes[1]->get('id'), 'tool_dataprivacy');
834 set_config($categoryvar, $categories[0]->get('id'), 'tool_dataprivacy');
836 list($purposeid, $categoryid) = data_registry::get_effective_default_contextlevel_purpose_and_category(CONTEXT_COURSE);
837 $this->assertEquals($purposes[1]->get('id'), $purposeid);
838 $this->assertEquals($categories[0]->get('id'), $categoryid);
840 // Context level defaults are also allowed to be set to 'inherit'.
841 set_config($purposevar, context_instance::INHERIT, 'tool_dataprivacy');
843 list($purposeid, $categoryid) = data_registry::get_effective_default_contextlevel_purpose_and_category(CONTEXT_COURSE);
844 $this->assertEquals($purposes[0]->get('id'), $purposeid);
845 $this->assertEquals($categories[0]->get('id'), $categoryid);
847 list($purposeid, $categoryid) = data_registry::get_effective_default_contextlevel_purpose_and_category(CONTEXT_MODULE);
848 $this->assertEquals($purposes[0]->get('id'), $purposeid);
849 $this->assertEquals($categories[0]->get('id'), $categoryid);
853 * Test effective contextlevel return.
855 * @return null
857 public function test_effective_contextlevel() {
858 $this->setAdminUser();
860 list($purposes, $categories, $courses, $modules) = $this->add_purposes_and_categories();
862 // Set the system context level to purpose 1.
863 $record = (object)[
864 'contextlevel' => CONTEXT_SYSTEM,
865 'purposeid' => $purposes[1]->get('id'),
866 'categoryid' => $categories[1]->get('id'),
868 api::set_contextlevel($record);
870 $purpose = api::get_effective_contextlevel_purpose(CONTEXT_SYSTEM);
871 $this->assertEquals($purposes[1]->get('id'), $purpose->get('id'));
873 // Value 'not set' will get the default value for the context level. For context level defaults
874 // both 'not set' and 'inherit' result in inherit, so the parent context (system) default
875 // will be retrieved.
876 $purpose = api::get_effective_contextlevel_purpose(CONTEXT_USER);
877 $this->assertEquals($purposes[1]->get('id'), $purpose->get('id'));
879 // The behaviour forcing an inherit from context system should result in the same effective
880 // purpose.
881 $record->purposeid = context_instance::INHERIT;
882 $record->contextlevel = CONTEXT_USER;
883 api::set_contextlevel($record);
884 $purpose = api::get_effective_contextlevel_purpose(CONTEXT_USER);
885 $this->assertEquals($purposes[1]->get('id'), $purpose->get('id'));
887 $record->purposeid = $purposes[2]->get('id');
888 $record->contextlevel = CONTEXT_USER;
889 api::set_contextlevel($record);
891 $purpose = api::get_effective_contextlevel_purpose(CONTEXT_USER);
892 $this->assertEquals($purposes[2]->get('id'), $purpose->get('id'));
894 // Only system and user allowed.
895 $this->expectException(coding_exception::class);
896 $record->contextlevel = CONTEXT_COURSE;
897 $record->purposeid = $purposes[1]->get('id');
898 api::set_contextlevel($record);
902 * Test effective context purposes and categories.
904 * @return null
906 public function test_effective_context() {
907 $this->setAdminUser();
909 list($purposes, $categories, $courses, $modules) = $this->add_purposes_and_categories();
911 // Define system defaults (all context levels below will inherit).
912 list($purposevar, $categoryvar) = data_registry::var_names_from_context(
913 \context_helper::get_class_for_level(CONTEXT_SYSTEM)
915 set_config($purposevar, $purposes[0]->get('id'), 'tool_dataprivacy');
916 set_config($categoryvar, $categories[0]->get('id'), 'tool_dataprivacy');
918 // Define course defaults.
919 list($purposevar, $categoryvar) = data_registry::var_names_from_context(
920 \context_helper::get_class_for_level(CONTEXT_COURSE)
922 set_config($purposevar, $purposes[1]->get('id'), 'tool_dataprivacy');
923 set_config($categoryvar, $categories[1]->get('id'), 'tool_dataprivacy');
925 $course0context = \context_course::instance($courses[0]->id);
926 $course1context = \context_course::instance($courses[1]->id);
927 $mod0context = \context_module::instance($modules[0]->cmid);
928 $mod1context = \context_module::instance($modules[1]->cmid);
930 // Set course instance values.
931 $record = (object)[
932 'contextid' => $course0context->id,
933 'purposeid' => $purposes[1]->get('id'),
934 'categoryid' => $categories[2]->get('id'),
936 api::set_context_instance($record);
937 $category = api::get_effective_context_category($course0context);
938 $this->assertEquals($record->categoryid, $category->get('id'));
940 // Module instances get the context level default if nothing specified.
941 $category = api::get_effective_context_category($mod0context);
942 $this->assertEquals($categories[1]->get('id'), $category->get('id'));
944 // Module instances get the parent context category if they inherit.
945 $record->contextid = $mod0context->id;
946 $record->categoryid = context_instance::INHERIT;
947 api::set_context_instance($record);
948 $category = api::get_effective_context_category($mod0context);
949 $this->assertEquals($categories[2]->get('id'), $category->get('id'));
951 // The $forcedvalue param allows us to override the actual value (method php-docs for more info).
952 $category = api::get_effective_context_category($mod0context, $categories[1]->get('id'));
953 $this->assertEquals($categories[1]->get('id'), $category->get('id'));
954 $category = api::get_effective_context_category($mod0context, $categories[0]->get('id'));
955 $this->assertEquals($categories[0]->get('id'), $category->get('id'));
957 // Module instances get the parent context category if they inherit; in
958 // this case the parent context category is not set so it should use the
959 // context level default (see 'Define course defaults' above).
960 $record->contextid = $mod1context->id;
961 $record->categoryid = context_instance::INHERIT;
962 api::set_context_instance($record);
963 $category = api::get_effective_context_category($mod1context);
964 $this->assertEquals($categories[1]->get('id'), $category->get('id'));
966 // User instances use the value set at user context level instead of the user default.
968 // User defaults to cat 0 and user context level to 1.
969 list($purposevar, $categoryvar) = data_registry::var_names_from_context(
970 \context_helper::get_class_for_level(CONTEXT_USER)
972 set_config($purposevar, $purposes[0]->get('id'), 'tool_dataprivacy');
973 set_config($categoryvar, $categories[0]->get('id'), 'tool_dataprivacy');
974 $usercontextlevel = (object)[
975 'contextlevel' => CONTEXT_USER,
976 'purposeid' => $purposes[1]->get('id'),
977 'categoryid' => $categories[1]->get('id'),
979 api::set_contextlevel($usercontextlevel);
981 $newuser = $this->getDataGenerator()->create_user();
982 $usercontext = \context_user::instance($newuser->id);
983 $category = api::get_effective_context_category($usercontext);
984 $this->assertEquals($categories[1]->get('id'), $category->get('id'));
988 * Tests the deletion of expired contexts.
990 * @return null
992 public function test_expired_context_deletion() {
993 global $DB;
995 $this->setAdminUser();
997 list($purposes, $categories, $courses, $modules) = $this->add_purposes_and_categories();
999 $course0context = \context_course::instance($courses[0]->id);
1000 $course1context = \context_course::instance($courses[1]->id);
1002 $expiredcontext0 = api::create_expired_context($course0context->id);
1003 $this->assertEquals(1, $DB->count_records('tool_dataprivacy_ctxexpired'));
1004 $expiredcontext1 = api::create_expired_context($course1context->id);
1005 $this->assertEquals(2, $DB->count_records('tool_dataprivacy_ctxexpired'));
1007 api::delete_expired_context($expiredcontext0->get('id'));
1008 $this->assertEquals(1, $DB->count_records('tool_dataprivacy_ctxexpired'));
1012 * Tests the status of expired contexts.
1014 * @return null
1016 public function test_expired_context_status() {
1017 global $DB;
1019 $this->setAdminUser();
1021 list($purposes, $categories, $courses, $modules) = $this->add_purposes_and_categories();
1023 $course0context = \context_course::instance($courses[0]->id);
1025 $expiredcontext = api::create_expired_context($course0context->id);
1027 // Default status.
1028 $this->assertEquals(expired_context::STATUS_EXPIRED, $expiredcontext->get('status'));
1030 api::set_expired_context_status($expiredcontext, expired_context::STATUS_APPROVED);
1031 $this->assertEquals(expired_context::STATUS_APPROVED, $expiredcontext->get('status'));
1035 * Creates test purposes and categories.
1037 * @return null
1039 protected function add_purposes_and_categories() {
1041 $purpose1 = api::create_purpose((object)['name' => 'p1', 'retentionperiod' => 'PT1H']);
1042 $purpose2 = api::create_purpose((object)['name' => 'p2', 'retentionperiod' => 'PT2H']);
1043 $purpose3 = api::create_purpose((object)['name' => 'p3', 'retentionperiod' => 'PT3H']);
1045 $cat1 = api::create_category((object)['name' => 'a']);
1046 $cat2 = api::create_category((object)['name' => 'b']);
1047 $cat3 = api::create_category((object)['name' => 'c']);
1049 $course1 = $this->getDataGenerator()->create_course();
1050 $course2 = $this->getDataGenerator()->create_course();
1052 $module1 = $this->getDataGenerator()->create_module('resource', array('course' => $course1));
1053 $module2 = $this->getDataGenerator()->create_module('resource', array('course' => $course2));
1055 return [
1056 [$purpose1, $purpose2, $purpose3],
1057 [$cat1, $cat2, $cat3],
1058 [$course1, $course2],
1059 [$module1, $module2]