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
 * Privacy provider tests.
19
 *
20
 * @package    core_question
21
 * @copyright  2018 Andrew Nicols <andrew@nicols.co.uk>
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
namespace core_question\privacy;
25
 
26
use core_privacy\local\metadata\collection;
27
use core_privacy\local\request\deletion_criteria;
28
use core_privacy\local\request\writer;
29
use core_question\privacy\provider;
30
 
31
defined('MOODLE_INTERNAL') || die();
32
 
33
global $CFG;
34
require_once($CFG->libdir . '/xmlize.php');
35
require_once(__DIR__ . '/../privacy_helper.php');
36
require_once(__DIR__ . '/../../engine/tests/helpers.php');
37
 
38
/**
39
 * Privacy provider tests class.
40
 *
41
 * @package    core_question
42
 * @copyright  2018 Andrew Nicols <andrew@nicols.co.uk>
43
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44
 */
45
class provider_test extends \core_privacy\tests\provider_testcase {
46
 
47
    // Include the privacy helper which has assertions on it.
48
    use \core_question_privacy_helper;
49
 
50
    /**
51
     * Prepare a question attempt.
52
     *
53
     * @return  question_usage_by_activity
54
     */
55
    protected function prepare_question_attempt() {
56
        // Create a question with a usage from the current user.
57
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
58
        $cat = $questiongenerator->create_question_category();
59
        $quba = \question_engine::make_questions_usage_by_activity('core_question_preview', \context_system::instance());
60
        $quba->set_preferred_behaviour('deferredfeedback');
61
        $questiondata = $questiongenerator->create_question('numerical', null, ['category' => $cat->id]);
62
        $question = \question_bank::load_question($questiondata->id);
63
        $quba->add_question($question);
64
        $quba->start_all_questions();
65
 
66
        \question_engine::save_questions_usage_by_activity($quba);
67
 
68
        return $quba;
69
    }
70
 
71
    /**
72
     * Test that calling export_question_usage on a usage belonging to a
73
     * different user does not export any data.
74
     */
11 efrain 75
    public function test_export_question_usage_no_usage(): void {
1 efrain 76
        $this->resetAfterTest();
77
 
78
        $quba = $this->prepare_question_attempt();
79
 
80
        // Create a question with a usage from the current user.
81
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
82
        $cat = $questiongenerator->create_question_category();
83
        $quba = \question_engine::make_questions_usage_by_activity('core_question_preview', \context_system::instance());
84
        $quba->set_preferred_behaviour('deferredfeedback');
85
        $questiondata = $questiongenerator->create_question('numerical', null, ['category' => $cat->id]);
86
        $question = \question_bank::load_question($questiondata->id);
87
        $quba->add_question($question);
88
        $quba->start_all_questions();
89
 
90
        \question_engine::save_questions_usage_by_activity($quba);
91
 
92
        // Set the user.
93
        $testuser = $this->getDataGenerator()->create_user();
94
        $this->setUser($testuser);
95
        $context = $quba->get_owning_context();
96
        $options = new \question_display_options();
97
 
98
        provider::export_question_usage($testuser->id, $context, [], $quba->get_id(), $options, false);
99
        /** @var \core_privacy\tests\request\content_writer $writer */
100
        $writer = writer::with_context($context);
101
 
102
        $this->assertFalse($writer->has_any_data_in_any_context());
103
    }
104
 
105
    /**
106
     * Test that calling export_question_usage on a usage belonging to a
107
     * different user but ignoring the user match
108
     */
11 efrain 109
    public function test_export_question_usage_with_usage(): void {
1 efrain 110
        $this->resetAfterTest();
111
 
112
        $quba = $this->prepare_question_attempt();
113
 
114
        // Create a question with a usage from the current user.
115
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
116
        $cat = $questiongenerator->create_question_category();
117
        $quba = \question_engine::make_questions_usage_by_activity('core_question_preview', \context_system::instance());
118
        $quba->set_preferred_behaviour('deferredfeedback');
119
 
120
        $questiondata = $questiongenerator->create_question('truefalse', 'true', ['category' => $cat->id]);
121
        $quba->add_question(\question_bank::load_question($questiondata->id));
122
        $questiondata = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
123
        $quba->add_question(\question_bank::load_question($questiondata->id));
124
 
125
        // Set the user and answer the questions.
126
        $testuser = $this->getDataGenerator()->create_user();
127
        $this->setUser($testuser);
128
 
129
        $quba->start_all_questions();
130
        $quba->process_action(1, ['answer' => 1]);
131
        $quba->process_action(2, ['answer' => 'cat']);
132
        $quba->finish_all_questions();
133
 
134
        \question_engine::save_questions_usage_by_activity($quba);
135
 
136
        $context = $quba->get_owning_context();
137
 
138
        // Export all questions for this attempt.
139
        $options = new \question_display_options();
140
        provider::export_question_usage($testuser->id, $context, [], $quba->get_id(), $options, true);
141
        /** @var \core_privacy\tests\request\content_writer $writer */
142
        $writer = writer::with_context($context);
143
 
144
        $this->assertTrue($writer->has_any_data_in_any_context());
145
        $this->assertTrue($writer->has_any_data());
146
 
147
        $slots = $quba->get_slots();
148
        $this->assertCount(2, $slots);
149
 
150
        foreach ($slots as $slotno) {
151
            $data = $writer->get_data([get_string('questions', 'core_question'), $slotno]);
152
            $this->assertNotNull($data);
153
            $this->assert_question_slot_equals($quba, $slotno, $options, $data);
154
        }
155
 
156
        $this->assertEmpty($writer->get_data([get_string('questions', 'core_question'), $quba->next_slot_number()]));
157
 
158
        // Disable some options and re-export.
159
        writer::reset();
160
        $options = new \question_display_options();
161
        $options->hide_all_feedback();
162
        $options->flags = \question_display_options::HIDDEN;
163
        $options->marks = \question_display_options::HIDDEN;
164
 
165
        provider::export_question_usage($testuser->id, $context, [], $quba->get_id(), $options, true);
166
        /** @var \core_privacy\tests\request\content_writer $writer */
167
        $writer = writer::with_context($context);
168
 
169
        $this->assertTrue($writer->has_any_data_in_any_context());
170
        $this->assertTrue($writer->has_any_data());
171
 
172
        $slots = $quba->get_slots();
173
        $this->assertCount(2, $slots);
174
 
175
        foreach ($slots as $slotno) {
176
            $data = $writer->get_data([get_string('questions', 'core_question'), $slotno]);
177
            $this->assertNotNull($data);
178
            $this->assert_question_slot_equals($quba, $slotno, $options, $data);
179
        }
180
 
181
        $this->assertEmpty($writer->get_data([get_string('questions', 'core_question'), $quba->next_slot_number()]));
182
    }
183
 
184
    /**
185
     * Test that questions owned by a user are exported and never deleted.
186
     */
11 efrain 187
    public function test_question_owned_is_handled(): void {
1 efrain 188
        global $DB;
189
        $this->resetAfterTest();
190
 
191
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
192
 
193
        // Create the two test users.
194
        $user = $this->getDataGenerator()->create_user();
195
        $otheruser = $this->getDataGenerator()->create_user();
196
 
197
        // Create one question as each user in diferent contexts.
198
        $this->setUser($user);
199
        $userdata = $questiongenerator->setup_course_and_questions();
200
        $expectedcontext = \context_course::instance($userdata[1]->id);
201
 
202
        $this->setUser($otheruser);
203
        $otheruserdata = $questiongenerator->setup_course_and_questions();
204
        $unexpectedcontext = \context_course::instance($otheruserdata[1]->id);
205
 
206
        // And create another one where we'll update a question as the test user.
207
        $moreotheruserdata = $questiongenerator->setup_course_and_questions();
208
        $otherexpectedcontext = \context_course::instance($moreotheruserdata[1]->id);
209
        $morequestions = $moreotheruserdata[3];
210
 
211
        // Update the third set of questions.
212
        $this->setUser($user);
213
 
214
        foreach ($morequestions as $question) {
215
            $questiongenerator->update_question($question);
216
        }
217
 
218
        // Run the get_contexts_for_userid as default user.
219
        $this->setUser();
220
 
221
        // There should be two contexts returned - the first course, and the third.
222
        $contextlist = provider::get_contexts_for_userid($user->id);
223
        $this->assertCount(2, $contextlist);
224
 
225
        $expectedcontexts = [
226
                $expectedcontext->id,
227
                $otherexpectedcontext->id,
228
            ];
229
        $this->assertEqualsCanonicalizing($expectedcontexts, $contextlist->get_contextids(), 'Contexts not equal');
230
 
231
        // Run the export_user_Data as the test user.
232
        $this->setUser($user);
233
 
234
        $approvedcontextlist = new \core_privacy\tests\request\approved_contextlist(
235
            \core_user::get_user($user->id),
236
            'core_question',
237
            $expectedcontexts
238
        );
239
        provider::export_user_data($approvedcontextlist);
240
 
241
        // There should be data for the user's question context.
242
        $writer = writer::with_context($expectedcontext);
243
        $this->assertTrue($writer->has_any_data());
244
 
245
        // And for the course we updated.
246
        $otherwriter = writer::with_context($otherexpectedcontext);
247
        $this->assertTrue($otherwriter->has_any_data());
248
 
249
        // But not for the other user's course.
250
        $otherwriter = writer::with_context($unexpectedcontext);
251
        $this->assertFalse($otherwriter->has_any_data());
252
 
253
        // The question data is exported as an XML export in custom files.
254
        $writer = writer::with_context($expectedcontext);
255
        $subcontext = [get_string('questionbank', 'core_question')];
256
 
257
        $exportfile = $writer->get_custom_file($subcontext, 'questions.xml');
258
        $this->assertNotEmpty($exportfile);
259
 
260
        $xmlized = xmlize($exportfile);
261
        $xmlquestions = $xmlized['quiz']['#']['question'];
262
 
263
        $this->assertCount(2, $xmlquestions);
264
 
265
        // Run the delete functions as default user.
266
        $this->setUser();
267
 
268
        // Find out how many questions are in the question bank to start with.
269
        $questioncount = $DB->count_records('question');
270
 
271
        // The delete functions should do nothing here.
272
 
273
        // Delete for all users in context.
274
        provider::delete_data_for_all_users_in_context($expectedcontext);
275
        $this->assertEquals($questioncount, $DB->count_records('question'));
276
 
277
        provider::delete_data_for_user($approvedcontextlist);
278
        $this->assertEquals($questioncount, $DB->count_records('question'));
279
    }
280
 
281
    /**
282
     * Deleting questions should only unset their created and modified user.
283
     */
11 efrain 284
    public function test_question_delete_data_for_user_anonymised(): void {
1 efrain 285
        global $DB;
286
        $this->resetAfterTest(true);
287
 
288
        $user = \core_user::get_user_by_username('admin');
289
        $otheruser = $this->getDataGenerator()->create_user();
290
 
291
        $course = $this->getDataGenerator()->create_course();
292
        $context = \context_course::instance($course->id);
293
        $othercourse = $this->getDataGenerator()->create_course();
294
        $othercontext = \context_course::instance($othercourse->id);
295
 
296
        // Create a couple of questions.
297
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
298
        $cat = $questiongenerator->create_question_category([
299
            'contextid' => $context->id,
300
        ]);
301
        $othercat = $questiongenerator->create_question_category([
302
            'contextid' => $othercontext->id,
303
        ]);
304
 
305
        // Create questions:
306
        // Q1 - Created by the UUT, Modified by UUT.
307
        // Q2 - Created by the UUT, Modified by the other user.
308
        // Q3 - Created by the other user, Modified by UUT
309
        // Q4 - Created by the other user, Modified by the other user.
310
        // Q5 - Created by the UUT, Modified by the UUT, but in a different context.
311
        $this->setUser($user);
312
        $q1 = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
313
        $q2 = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
314
 
315
        $this->setUser($otheruser);
316
        // When we update a question, a new question/version is created.
317
        $q2updated = $questiongenerator->update_question($q2);
318
        $q3 = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
319
        $q4 = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]);
320
 
321
        $this->setUser($user);
322
        // When we update a question, a new question/version is created.
323
        $q3updated = $questiongenerator->update_question($q3);
324
        $q5 = $questiongenerator->create_question('shortanswer', null, ['category' => $othercat->id]);
325
 
326
        $approvedcontextlist = new \core_privacy\tests\request\approved_contextlist(
327
            $user,
328
            'core_question',
329
            [$context->id]
330
        );
331
 
332
        // Find out how many questions are in the question bank to start with.
333
        $questioncount = $DB->count_records('question');
334
 
335
        // Delete the data and check it is removed.
336
        $this->setUser();
337
        provider::delete_data_for_user($approvedcontextlist);
338
 
339
        $this->assertEquals($questioncount, $DB->count_records('question'));
340
 
341
        $qrecord = $DB->get_record('question', ['id' => $q1->id]);
342
        $this->assertEquals(0, $qrecord->createdby);
343
        $this->assertEquals(0, $qrecord->modifiedby);
344
 
345
        $qrecord = $DB->get_record('question', ['id' => $q2updated->id]);
346
        $this->assertEquals($otheruser->id, $qrecord->createdby);
347
        $this->assertEquals($otheruser->id, $qrecord->modifiedby);
348
 
349
        $qrecord = $DB->get_record('question', ['id' => $q3updated->id]);
350
        $this->assertEquals(0, $qrecord->createdby);
351
        $this->assertEquals(0, $qrecord->modifiedby);
352
 
353
        $qrecord = $DB->get_record('question', ['id' => $q4->id]);
354
        $this->assertEquals($otheruser->id, $qrecord->createdby);
355
        $this->assertEquals($otheruser->id, $qrecord->modifiedby);
356
 
357
        $qrecord = $DB->get_record('question', ['id' => $q5->id]);
358
        $this->assertEquals($user->id, $qrecord->createdby);
359
        $this->assertEquals($user->id, $qrecord->modifiedby);
360
    }
361
 
362
    /**
363
     * Deleting questions should only unset their created and modified user for all questions in a context.
364
     */
11 efrain 365
    public function test_question_delete_data_for_all_users_in_context_anonymised(): void {
1 efrain 366
        global $DB;
367
        $this->resetAfterTest(true);
368
 
369
        $user = \core_user::get_user_by_username('admin');
370
        $otheruser = $this->getDataGenerator()->create_user();
371
 
372
        $course = $this->getDataGenerator()->create_course();
373
        $context = \context_course::instance($course->id);
374
        $othercourse = $this->getDataGenerator()->create_course();
375
        $othercontext = \context_course::instance($othercourse->id);
376
 
377
        // Create a couple of questions.
378
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
379
        $cat = $questiongenerator->create_question_category([
380
            'contextid' => $context->id,
381
        ]);
382
        $othercat = $questiongenerator->create_question_category([
383
            'contextid' => $othercontext->id,
384
        ]);
385
 
386
        // Create questions:
387
        // Q1 - Created by the UUT, Modified by UUT.
388
        // Q2 - Created by the UUT, Modified by the other user.
389
        // Q3 - Created by the other user, Modified by UUT
390
        // Q4 - Created by the other user, Modified by the other user.
391
        // Q5 - Created by the UUT, Modified by the UUT, but in a different context.
392
        $this->setUser($user);
393
        $q1 = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
394
        $q2 = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
395
 
396
        $this->setUser($otheruser);
397
        $questiongenerator->update_question($q2);
398
        $q3 = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
399
        $q4 = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id));
400
 
401
        $this->setUser($user);
402
        $questiongenerator->update_question($q3);
403
        $q5 = $questiongenerator->create_question('shortanswer', null, array('category' => $othercat->id));
404
 
405
        // Find out how many questions are in the question bank to start with.
406
        $questioncount = $DB->count_records('question');
407
 
408
        // Delete the data and check it is removed.
409
        $this->setUser();
410
        provider::delete_data_for_all_users_in_context($context);
411
 
412
        $this->assertEquals($questioncount, $DB->count_records('question'));
413
 
414
        $qrecord = $DB->get_record('question', ['id' => $q1->id]);
415
        $this->assertEquals(0, $qrecord->createdby);
416
        $this->assertEquals(0, $qrecord->modifiedby);
417
 
418
        $qrecord = $DB->get_record('question', ['id' => $q2->id]);
419
        $this->assertEquals(0, $qrecord->createdby);
420
        $this->assertEquals(0, $qrecord->modifiedby);
421
 
422
        $qrecord = $DB->get_record('question', ['id' => $q3->id]);
423
        $this->assertEquals(0, $qrecord->createdby);
424
        $this->assertEquals(0, $qrecord->modifiedby);
425
 
426
        $qrecord = $DB->get_record('question', ['id' => $q4->id]);
427
        $this->assertEquals(0, $qrecord->createdby);
428
        $this->assertEquals(0, $qrecord->modifiedby);
429
 
430
        $qrecord = $DB->get_record('question', ['id' => $q5->id]);
431
        $this->assertEquals($user->id, $qrecord->createdby);
432
        $this->assertEquals($user->id, $qrecord->modifiedby);
433
    }
434
 
435
    /**
436
     * Test for provider::get_users_in_context().
437
     */
11 efrain 438
    public function test_get_users_in_context(): void {
1 efrain 439
        $this->resetAfterTest();
440
 
441
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
442
 
443
        // Create three test users.
444
        $user1 = $this->getDataGenerator()->create_user();
445
        $user2 = $this->getDataGenerator()->create_user();
446
        $user3 = $this->getDataGenerator()->create_user();
447
 
448
        // Create one question as each user in different contexts.
449
        $this->setUser($user1);
450
        $user1data = $questiongenerator->setup_course_and_questions();
451
        $this->setUser($user2);
452
        $user2data = $questiongenerator->setup_course_and_questions();
453
 
454
        $course1context = \context_course::instance($user1data[1]->id);
455
        $course1questions = $user1data[3];
456
 
457
        // Log in as user3 and update the questions in course1.
458
        $this->setUser($user3);
459
 
460
        foreach ($course1questions as $question) {
461
            $questiongenerator->update_question($question);
462
        }
463
 
464
        $userlist = new \core_privacy\local\request\userlist($course1context, 'core_question');
465
        provider::get_users_in_context($userlist);
466
 
467
        // User1 has created questions and user3 has edited them.
468
        $this->assertCount(2, $userlist);
469
        $this->assertEqualsCanonicalizing([$user1->id, $user3->id], $userlist->get_userids());
470
    }
471
 
472
    /**
473
     * Test for provider::delete_data_for_users().
474
     */
11 efrain 475
    public function test_delete_data_for_users(): void {
1 efrain 476
        global $DB;
477
 
478
        $this->resetAfterTest();
479
 
480
        $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
481
 
482
        // Create three test users.
483
        $user1 = $this->getDataGenerator()->create_user();
484
        $user2 = $this->getDataGenerator()->create_user();
485
        $user3 = $this->getDataGenerator()->create_user();
486
 
487
        // Create one question as each user in different contexts.
488
        $this->setUser($user1);
489
        $course1data = $questiongenerator->setup_course_and_questions();
490
        $course1 = $course1data[1];
491
        $course1qcat = $course1data[2];
492
        $course1questions = $course1data[3];
493
        $course1context = \context_course::instance($course1->id);
494
 
495
        // Log in as user2 and update the questions in course1.
496
        $this->setUser($user2);
497
 
498
        foreach ($course1questions as $question) {
499
            $questiongenerator->update_question($question);
500
        }
501
 
502
        // Add 2 more questions to course1 by user3.
503
        $this->setUser($user3);
504
        $questiongenerator->create_question('shortanswer', null, ['category' => $course1qcat->id]);
505
        $questiongenerator->create_question('shortanswer', null, ['category' => $course1qcat->id]);
506
 
507
        // Now, log in as user1 again, and then create a new course and add questions to that.
508
        $this->setUser($user1);
509
        $questiongenerator->setup_course_and_questions();
510
 
511
        $approveduserlist = new \core_privacy\local\request\approved_userlist($course1context, 'core_question',
512
                [$user1->id, $user2->id]);
513
        provider::delete_data_for_users($approveduserlist);
514
 
515
        // Now, there should be no question related to user1 or user2 in course1.
516
        $this->assertEquals(0,
517
                $DB->count_records_sql("SELECT COUNT(q.id)
518
                                          FROM {question} q
519
                                          JOIN {question_versions} qv ON qv.questionid = q.id
520
                                          JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
521
                                          JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid
522
                                         WHERE qc.contextid = ?
523
                                           AND (q.createdby = ? OR q.modifiedby = ? OR q.createdby = ? OR q.modifiedby = ?)",
524
                        [$course1context->id, $user1->id, $user1->id, $user2->id, $user2->id])
525
        );
526
 
527
        // User3 data in course1 should not change.
528
        $this->assertEquals(2,
529
                $DB->count_records_sql("SELECT COUNT(q.id)
530
                                          FROM {question} q
531
                                          JOIN {question_versions} qv ON qv.questionid = q.id
532
                                          JOIN {question_bank_entries} qbe ON qbe.id = qv.questionbankentryid
533
                                          JOIN {question_categories} qc ON qc.id = qbe.questioncategoryid
534
                                         WHERE qc.contextid = ? AND (q.createdby = ? OR q.modifiedby = ?)",
535
                        [$course1context->id, $user3->id, $user3->id])
536
        );
537
 
538
        // User1 has authored 2 questions in another course.
539
        $this->assertEquals(
540
                2,
541
                $DB->count_records_select('question', "createdby = ? OR modifiedby = ?", [$user1->id, $user1->id])
542
        );
543
    }
544
}