Proyectos de Subversion Moodle

Rev

Rev 11 | | Comparar con el anterior | 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
/**
18
 * Provides the {@see mod_workshop\privacy\provider_test} class.
19
 *
20
 * @package     mod_workshop
21
 * @category    test
22
 * @copyright   2018 David Mudrák <david@moodle.com>
23
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24
 */
25
namespace mod_workshop\privacy;
26
 
27
defined('MOODLE_INTERNAL') || die();
28
 
29
global $CFG;
30
 
31
use core_privacy\local\request\writer;
32
use core_privacy\tests\provider_testcase;
33
 
34
/**
35
 * Unit tests for the privacy API implementation.
36
 *
37
 * @copyright 2018 David Mudrák <david@moodle.com>
38
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39
 */
1441 ariadna 40
final class provider_test extends provider_testcase {
1 efrain 41
 
42
    /** @var testing_data_generator */
43
    protected $generator;
44
 
45
    /** @var mod_workshop_generator */
46
    protected $workshopgenerator;
47
 
48
    /** @var stdClass */
49
    protected $course1;
50
 
51
    /** @var stdClass */
52
    protected $course2;
53
 
54
    /** @var stdClass */
55
    protected $student1;
56
 
57
    /** @var stdClass */
58
    protected $student2;
59
 
60
    /** @var stdClass */
61
    protected $student3;
62
 
63
    /** @var stdClass */
64
    protected $teacher4;
65
 
66
    /** @var stdClass first workshop in course1 */
67
    protected $workshop11;
68
 
69
    /** @var stdClass second workshop in course1 */
70
    protected $workshop12;
71
 
72
    /** @var stdClass first workshop in course2 */
73
    protected $workshop21;
74
 
75
    /** @var int ID of the submission in workshop11 by student1 */
76
    protected $submission111;
77
 
78
    /** @var int ID of the submission in workshop12 by student1 */
79
    protected $submission121;
80
 
81
    /** @var int ID of the submission in workshop12 by student2 */
82
    protected $submission122;
83
 
84
    /** @var int ID of the submission in workshop21 by student2 */
85
    protected $submission212;
86
 
87
    /** @var int ID of the assessment of submission111 by student1 */
88
    protected $assessment1111;
89
 
90
    /** @var int ID of the assessment of submission111 by student2 */
91
    protected $assessment1112;
92
 
93
    /** @var int ID of the assessment of submission111 by student3 */
94
    protected $assessment1113;
95
 
96
    /** @var int ID of the assessment of submission121 by student2 */
97
    protected $assessment1212;
98
 
99
    /** @var int ID of the assessment of submission212 by student1 */
100
    protected $assessment2121;
101
 
102
    /**
103
     * Set up the test environment.
104
     *
105
     * course1
106
     *  |
107
     *  +--workshop11 (first digit matches the course, second is incremental)
108
     *  |   |
109
     *  |   +--submission111 (first two digits match the workshop, last one matches the author)
110
     *  |       |
111
     *  |       +--assessment1111 (first three digits match the submission, last one matches the reviewer)
112
     *  |       +--assessment1112
113
     *  |       +--assessment1113
114
     *  |
115
     *  +--workshop12
116
     *      |
117
     *      +--submission121
118
     *      |   |
119
     *      |   +--assessment1212
120
     *      |
121
     *      +--submission122
122
     *
123
     *  etc.
124
     */
125
    protected function setUp(): void {
126
        global $DB;
1441 ariadna 127
        parent::setUp();
1 efrain 128
        $this->resetAfterTest();
129
        $this->setAdminUser();
130
 
131
        $this->generator = $this->getDataGenerator();
132
        $this->workshopgenerator = $this->generator->get_plugin_generator('mod_workshop');
133
 
134
        $this->course1 = $this->generator->create_course();
135
        $this->course2 = $this->generator->create_course();
136
 
137
        $this->workshop11 = $this->generator->create_module('workshop', [
138
            'course' => $this->course1,
139
            'name' => 'Workshop11',
140
        ]);
141
        $DB->set_field('workshop', 'phase', 50, ['id' => $this->workshop11->id]);
142
 
143
        $this->workshop12 = $this->generator->create_module('workshop', ['course' => $this->course1]);
144
        $this->workshop21 = $this->generator->create_module('workshop', ['course' => $this->course2]);
145
 
146
        $this->student1 = $this->generator->create_user();
147
        $this->student2 = $this->generator->create_user();
148
        $this->student3 = $this->generator->create_user();
149
        $this->teacher4 = $this->generator->create_user();
150
 
151
        $this->submission111 = $this->workshopgenerator->create_submission($this->workshop11->id, $this->student1->id);
152
        $this->submission121 = $this->workshopgenerator->create_submission($this->workshop12->id, $this->student1->id,
153
            ['gradeoverby' => $this->teacher4->id]);
154
        $this->submission122 = $this->workshopgenerator->create_submission($this->workshop12->id, $this->student2->id);
155
        $this->submission212 = $this->workshopgenerator->create_submission($this->workshop21->id, $this->student2->id);
156
 
157
        $this->assessment1111 = $this->workshopgenerator->create_assessment($this->submission111, $this->student1->id, [
158
            'grade' => null,
159
        ]);
160
        $this->assessment1112 = $this->workshopgenerator->create_assessment($this->submission111, $this->student2->id, [
161
            'grade' => 92,
162
        ]);
163
        $this->assessment1113 = $this->workshopgenerator->create_assessment($this->submission111, $this->student3->id);
164
 
165
        $this->assessment1212 = $this->workshopgenerator->create_assessment($this->submission121, $this->student2->id, [
166
            'feedbackauthor' => 'This is what student 2 thinks about submission 121',
167
            'feedbackreviewer' => 'This is what the teacher thinks about this assessment',
168
        ]);
169
 
170
        $this->assessment2121 = $this->workshopgenerator->create_assessment($this->submission212, $this->student1->id, [
171
            'grade' => 68,
172
            'gradinggradeover' => 80,
173
            'gradinggradeoverby' => $this->teacher4->id,
174
            'feedbackauthor' => 'This is what student 1 thinks about submission 212',
175
            'feedbackreviewer' => 'This is what the teacher thinks about this assessment',
176
        ]);
177
    }
178
 
179
    /**
180
     * Test {@link \mod_workshop\privacy\provider::get_contexts_for_userid()} implementation.
181
     */
11 efrain 182
    public function test_get_contexts_for_userid(): void {
1 efrain 183
 
184
        $cm11 = get_coursemodule_from_instance('workshop', $this->workshop11->id);
185
        $cm12 = get_coursemodule_from_instance('workshop', $this->workshop12->id);
186
        $cm21 = get_coursemodule_from_instance('workshop', $this->workshop21->id);
187
 
188
        $context11 = \context_module::instance($cm11->id);
189
        $context12 = \context_module::instance($cm12->id);
190
        $context21 = \context_module::instance($cm21->id);
191
 
192
        // Student1 has data in workshop11 (author + self reviewer), workshop12 (author) and workshop21 (reviewer).
193
        $contextlist = \mod_workshop\privacy\provider::get_contexts_for_userid($this->student1->id);
194
        $this->assertInstanceOf(\core_privacy\local\request\contextlist::class, $contextlist);
1441 ariadna 195
        $this->assertEqualsCanonicalizing([$context11->id, $context12->id, $context21->id], array_values($contextlist->get_contextids()));
1 efrain 196
 
197
        // Student2 has data in workshop11 (reviewer), workshop12 (reviewer) and workshop21 (author).
198
        $contextlist = \mod_workshop\privacy\provider::get_contexts_for_userid($this->student2->id);
1441 ariadna 199
        $this->assertEqualsCanonicalizing([$context11->id, $context12->id, $context21->id], array_values($contextlist->get_contextids()));
1 efrain 200
 
201
        // Student3 has data in workshop11 (reviewer).
202
        $contextlist = \mod_workshop\privacy\provider::get_contexts_for_userid($this->student3->id);
1441 ariadna 203
        $this->assertEqualsCanonicalizing([$context11->id], array_values($contextlist->get_contextids()));
1 efrain 204
 
205
        // Teacher4 has data in workshop12 (gradeoverby) and workshop21 (gradinggradeoverby).
206
        $contextlist = \mod_workshop\privacy\provider::get_contexts_for_userid($this->teacher4->id);
1441 ariadna 207
        $this->assertEqualsCanonicalizing([$context21->id, $context12->id], array_values($contextlist->get_contextids()));
1 efrain 208
    }
209
 
210
    /**
211
     * Test {@link \mod_workshop\privacy\provider::get_users_in_context()} implementation.
212
     */
11 efrain 213
    public function test_get_users_in_context(): void {
1 efrain 214
 
215
        $cm11 = get_coursemodule_from_instance('workshop', $this->workshop11->id);
216
        $cm12 = get_coursemodule_from_instance('workshop', $this->workshop12->id);
217
        $cm21 = get_coursemodule_from_instance('workshop', $this->workshop21->id);
218
 
219
        $context11 = \context_module::instance($cm11->id);
220
        $context12 = \context_module::instance($cm12->id);
221
        $context21 = \context_module::instance($cm21->id);
222
 
223
        // Users in the workshop11.
224
        $userlist11 = new \core_privacy\local\request\userlist($context11, 'mod_workshop');
225
        \mod_workshop\privacy\provider::get_users_in_context($userlist11);
226
        $expected11 = [
227
            $this->student1->id, // Student1 has data in workshop11 (author + self reviewer).
228
            $this->student2->id, // Student2 has data in workshop11 (reviewer).
229
            $this->student3->id, // Student3 has data in workshop11 (reviewer).
230
        ];
231
        $actual11 = $userlist11->get_userids();
232
        $this->assertEqualsCanonicalizing($expected11, $actual11);
233
 
234
        // Users in the workshop12.
235
        $userlist12 = new \core_privacy\local\request\userlist($context12, 'mod_workshop');
236
        \mod_workshop\privacy\provider::get_users_in_context($userlist12);
237
        $expected12 = [
238
            $this->student1->id, // Student1 has data in workshop12 (author).
239
            $this->student2->id, // Student2 has data in workshop12 (reviewer).
240
            $this->teacher4->id, // Teacher4 has data in workshop12 (gradeoverby).
241
        ];
242
        $actual12 = $userlist12->get_userids();
243
        $this->assertEqualsCanonicalizing($expected12, $actual12);
244
 
245
        // Users in the workshop21.
246
        $userlist21 = new \core_privacy\local\request\userlist($context21, 'mod_workshop');
247
        \mod_workshop\privacy\provider::get_users_in_context($userlist21);
248
        $expected21 = [
249
            $this->student1->id, // Student1 has data in workshop21 (reviewer).
250
            $this->student2->id, // Student2 has data in workshop21 (author).
251
            $this->teacher4->id, // Teacher4 has data in workshop21 (gradinggradeoverby).
252
        ];
253
        $actual21 = $userlist21->get_userids();
254
        $this->assertEqualsCanonicalizing($expected21, $actual21);
255
    }
256
 
257
    /**
258
     * Test {@link \mod_workshop\privacy\provider::export_user_data()} implementation.
259
     */
11 efrain 260
    public function test_export_user_data_1(): void {
1 efrain 261
 
262
        $contextlist = new \core_privacy\local\request\approved_contextlist($this->student1, 'mod_workshop', [
263
            \context_module::instance($this->workshop11->cmid)->id,
264
            \context_module::instance($this->workshop12->cmid)->id,
265
        ]);
266
 
267
        \mod_workshop\privacy\provider::export_user_data($contextlist);
268
 
269
        $writer = writer::with_context(\context_module::instance($this->workshop11->cmid));
270
 
271
        $workshop = $writer->get_data([]);
272
        $this->assertEquals('Workshop11', $workshop->name);
273
        $this->assertObjectHasProperty('phase', $workshop);
274
 
275
        $mysubmission = $writer->get_data([
276
            get_string('mysubmission', 'mod_workshop'),
277
        ]);
278
 
279
        $mysubmissionselfassessmentwithoutgrade = $writer->get_data([
280
            get_string('mysubmission', 'mod_workshop'),
281
            get_string('assessments', 'mod_workshop'),
282
            $this->assessment1111,
283
        ]);
284
        $this->assertNull($mysubmissionselfassessmentwithoutgrade->grade);
285
        $this->assertEquals(get_string('yes'), $mysubmissionselfassessmentwithoutgrade->selfassessment);
286
 
287
        $mysubmissionassessmentwithgrade = $writer->get_data([
288
            get_string('mysubmission', 'mod_workshop'),
289
            get_string('assessments', 'mod_workshop'),
290
            $this->assessment1112,
291
        ]);
292
        $this->assertEquals(92, $mysubmissionassessmentwithgrade->grade);
293
        $this->assertEquals(get_string('no'), $mysubmissionassessmentwithgrade->selfassessment);
294
 
295
        $mysubmissionassessmentwithoutgrade = $writer->get_data([
296
            get_string('mysubmission', 'mod_workshop'),
297
            get_string('assessments', 'mod_workshop'),
298
            $this->assessment1113,
299
        ]);
300
        $this->assertEquals(null, $mysubmissionassessmentwithoutgrade->grade);
301
        $this->assertEquals(get_string('no'), $mysubmissionassessmentwithoutgrade->selfassessment);
302
 
303
        $myassessments = $writer->get_data([
304
            get_string('myassessments', 'mod_workshop'),
305
        ]);
306
        $this->assertEmpty($myassessments);
307
    }
308
 
309
    /**
310
     * Test {@link \mod_workshop\privacy\provider::export_user_data()} implementation.
311
     */
11 efrain 312
    public function test_export_user_data_2(): void {
1 efrain 313
 
314
        $contextlist = new \core_privacy\local\request\approved_contextlist($this->student2, 'mod_workshop', [
315
            \context_module::instance($this->workshop11->cmid)->id,
316
        ]);
317
 
318
        \mod_workshop\privacy\provider::export_user_data($contextlist);
319
 
320
        $writer = writer::with_context(\context_module::instance($this->workshop11->cmid));
321
 
322
        $assessedsubmission = $writer->get_related_data([
323
            get_string('myassessments', 'mod_workshop'),
324
            $this->assessment1112,
325
        ], 'submission');
326
        $this->assertEquals(get_string('no'), $assessedsubmission->myownsubmission);
327
    }
328
 
329
    /**
330
     * Test {@link \mod_workshop\privacy\provider::delete_data_for_all_users_in_context()} implementation.
331
     */
11 efrain 332
    public function test_delete_data_for_all_users_in_context(): void {
1 efrain 333
        global $DB;
334
 
335
        $this->assertTrue($DB->record_exists('workshop_submissions', ['workshopid' => $this->workshop11->id]));
336
 
337
        // Passing a non-module context does nothing.
338
        \mod_workshop\privacy\provider::delete_data_for_all_users_in_context(\context_course::instance($this->course1->id));
339
        $this->assertTrue($DB->record_exists('workshop_submissions', ['workshopid' => $this->workshop11->id]));
340
 
341
        // Passing a workshop context removes all data.
342
        \mod_workshop\privacy\provider::delete_data_for_all_users_in_context(\context_module::instance($this->workshop11->cmid));
343
        $this->assertFalse($DB->record_exists('workshop_submissions', ['workshopid' => $this->workshop11->id]));
344
    }
345
 
346
    /**
347
     * Test {@link \mod_workshop\privacy\provider::delete_data_for_user()} implementation.
348
     */
11 efrain 349
    public function test_delete_data_for_user(): void {
1 efrain 350
        global $DB;
351
 
352
        $student1submissions = $DB->get_records('workshop_submissions', [
353
            'workshopid' => $this->workshop12->id,
354
            'authorid' => $this->student1->id,
355
        ]);
356
 
357
        $student2submissions = $DB->get_records('workshop_submissions', [
358
            'workshopid' => $this->workshop12->id,
359
            'authorid' => $this->student2->id,
360
        ]);
361
 
362
        $this->assertNotEmpty($student1submissions);
363
        $this->assertNotEmpty($student2submissions);
364
 
365
        foreach ($student1submissions as $submission) {
366
            $this->assertNotEquals(get_string('privacy:request:delete:title', 'mod_workshop'), $submission->title);
367
        }
368
 
369
        foreach ($student2submissions as $submission) {
370
            $this->assertNotEquals(get_string('privacy:request:delete:title', 'mod_workshop'), $submission->title);
371
        }
372
 
373
        $contextlist = new \core_privacy\local\request\approved_contextlist($this->student1, 'mod_workshop', [
374
            \context_module::instance($this->workshop12->cmid)->id,
375
            \context_module::instance($this->workshop21->cmid)->id,
376
        ]);
377
 
378
        \mod_workshop\privacy\provider::delete_data_for_user($contextlist);
379
 
380
        $student1submissions = $DB->get_records('workshop_submissions', [
381
            'workshopid' => $this->workshop12->id,
382
            'authorid' => $this->student1->id,
383
        ]);
384
 
385
        $student2submissions = $DB->get_records('workshop_submissions', [
386
            'workshopid' => $this->workshop12->id,
387
            'authorid' => $this->student2->id,
388
        ]);
389
 
390
        $this->assertNotEmpty($student1submissions);
391
        $this->assertNotEmpty($student2submissions);
392
 
393
        foreach ($student1submissions as $submission) {
394
            $this->assertEquals(get_string('privacy:request:delete:title', 'mod_workshop'), $submission->title);
395
        }
396
 
397
        foreach ($student2submissions as $submission) {
398
            $this->assertNotEquals(get_string('privacy:request:delete:title', 'mod_workshop'), $submission->title);
399
        }
400
 
401
        $student1assessments = $DB->get_records('workshop_assessments', [
402
            'submissionid' => $this->submission212,
403
            'reviewerid' => $this->student1->id,
404
        ]);
405
        $this->assertNotEmpty($student1assessments);
406
 
407
        foreach ($student1assessments as $assessment) {
408
            // In Moodle, feedback is seen to belong to the recipient user.
409
            $this->assertNotEquals(get_string('privacy:request:delete:content', 'mod_workshop'), $assessment->feedbackauthor);
410
            $this->assertEquals(get_string('privacy:request:delete:content', 'mod_workshop'), $assessment->feedbackreviewer);
411
            // We delete what we can without affecting others' grades.
412
            $this->assertEquals(68, $assessment->grade);
413
        }
414
 
415
        $assessments = $DB->get_records_list('workshop_assessments', 'submissionid', array_keys($student1submissions));
416
        $this->assertNotEmpty($assessments);
417
 
418
        foreach ($assessments as $assessment) {
419
            if ($assessment->reviewerid == $this->student1->id) {
420
                $this->assertNotEquals(get_string('privacy:request:delete:content', 'mod_workshop'), $assessment->feedbackauthor);
421
                $this->assertNotEquals(get_string('privacy:request:delete:content', 'mod_workshop'), $assessment->feedbackreviewer);
422
 
423
            } else {
424
                $this->assertEquals(get_string('privacy:request:delete:content', 'mod_workshop'), $assessment->feedbackauthor);
425
                $this->assertNotEquals(get_string('privacy:request:delete:content', 'mod_workshop'), $assessment->feedbackreviewer);
426
            }
427
        }
428
    }
429
 
430
    /**
431
     * Test {@link \mod_workshop\privacy\provider::delete_data_for_users()} implementation.
432
     */
11 efrain 433
    public function test_delete_data_for_users(): void {
1 efrain 434
        global $DB;
435
 
436
        // Student1 has submissions in two workshops.
437
        $this->assertFalse($this->is_submission_erased($this->submission111));
438
        $this->assertFalse($this->is_submission_erased($this->submission121));
439
 
440
        // Student1 has self-assessed one their submission.
441
        $this->assertFalse($this->is_given_assessment_erased($this->assessment1111));
442
        $this->assertFalse($this->is_received_assessment_erased($this->assessment1111));
443
 
444
        // Student2 and student3 peer-assessed student1's submission.
445
        $this->assertFalse($this->is_given_assessment_erased($this->assessment1112));
446
        $this->assertFalse($this->is_given_assessment_erased($this->assessment1113));
447
 
448
        // Delete data owned by student1 and student3 in the workshop11.
449
 
450
        $context11 = \context_module::instance($this->workshop11->cmid);
451
 
452
        $approveduserlist = new \core_privacy\local\request\approved_userlist($context11, 'mod_workshop', [
453
            $this->student1->id,
454
            $this->student3->id,
455
        ]);
456
        \mod_workshop\privacy\provider::delete_data_for_users($approveduserlist);
457
 
458
        // Student1's submission is erased in workshop11 but not in the other workshop12.
459
        $this->assertTrue($this->is_submission_erased($this->submission111));
460
        $this->assertFalse($this->is_submission_erased($this->submission121));
461
 
462
        // Student1's self-assessment is erased.
463
        $this->assertTrue($this->is_given_assessment_erased($this->assessment1111));
464
        $this->assertTrue($this->is_received_assessment_erased($this->assessment1111));
465
 
466
        // Student1's received peer-assessments are also erased because they are "owned" by the recipient of the assessment.
467
        $this->assertTrue($this->is_received_assessment_erased($this->assessment1112));
468
        $this->assertTrue($this->is_received_assessment_erased($this->assessment1113));
469
 
470
        // Student2's owned data in the given assessment are not erased.
471
        $this->assertFalse($this->is_given_assessment_erased($this->assessment1112));
472
 
473
        // Student3's owned data in the given assessment were erased because she/he was in the userlist.
474
        $this->assertTrue($this->is_given_assessment_erased($this->assessment1113));
475
 
476
        // Personal data in other contexts are not affected.
477
        $this->assertFalse($this->is_submission_erased($this->submission121));
478
        $this->assertFalse($this->is_given_assessment_erased($this->assessment2121));
479
        $this->assertFalse($this->is_received_assessment_erased($this->assessment2121));
480
    }
481
 
482
    /**
483
     * Check if the given submission has the author's personal data erased.
484
     *
485
     * @param int $submissionid Identifier of the submission.
486
     * @return boolean
487
     */
488
    protected function is_submission_erased(int $submissionid) {
489
        global $DB;
490
 
491
        $submission = $DB->get_record('workshop_submissions', ['id' => $submissionid], 'id, title, content', MUST_EXIST);
492
 
493
        $titledeleted = $submission->title === get_string('privacy:request:delete:title', 'mod_workshop');
494
        $contentdeleted = $submission->content === get_string('privacy:request:delete:content', 'mod_workshop');
495
 
496
        if ($titledeleted && $contentdeleted) {
497
            return true;
498
 
499
        } else {
500
            return false;
501
        }
502
    }
503
 
504
    /**
505
     * Check is the received assessment has recipient's (author's) personal data erased.
506
     *
507
     * @param int $assessmentid Identifier of the assessment.
508
     * @return boolean
509
     */
510
    protected function is_received_assessment_erased(int $assessmentid) {
511
        global $DB;
512
 
513
        $assessment = $DB->get_record('workshop_assessments', ['id' => $assessmentid], 'id, feedbackauthor', MUST_EXIST);
514
 
515
        if ($assessment->feedbackauthor === get_string('privacy:request:delete:content', 'mod_workshop')) {
516
            return true;
517
 
518
        } else {
519
            return false;
520
        }
521
    }
522
 
523
    /**
524
     * Check is the given assessment has reviewer's personal data erased.
525
     *
526
     * @param int $assessmentid Identifier of the assessment.
527
     * @return boolean
528
     */
529
    protected function is_given_assessment_erased(int $assessmentid) {
530
        global $DB;
531
 
532
        $assessment = $DB->get_record('workshop_assessments', ['id' => $assessmentid], 'id, feedbackreviewer', MUST_EXIST);
533
 
534
        if ($assessment->feedbackreviewer === get_string('privacy:request:delete:content', 'mod_workshop')) {
535
            return true;
536
 
537
        } else {
538
            return false;
539
        }
540
    }
541
}