Proyectos de Subversion Moodle

Rev

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