MDL-60826 calendar: further performance improvements
[moodle.git] / mod / assign / tests / privacy_test.php
blobbcae99d17713f6db30e20aac4eba8ec05050d356
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 * Base class for unit tests for mod_assign.
20 * @package mod_assign
21 * @copyright 2018 Adrian Greeve <adrian@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 namespace mod_assign\tests;
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
30 require_once($CFG->dirroot . '/mod/assign/locallib.php');
32 use \core_privacy\tests\provider_testcase;
33 use \core_privacy\local\request\writer;
34 use \core_privacy\local\request\approved_contextlist;
35 use \mod_assign\privacy\provider;
37 /**
38 * Unit tests for mod/assign/classes/privacy/
40 * @copyright 2018 Adrian Greeve <adrian@moodle.com>
41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43 class mod_assign_privacy_testcase extends provider_testcase {
45 /**
46 * Convenience method for creating a submission.
48 * @param assign $assign The assign object
49 * @param stdClass $user The user object
50 * @param string $submissiontext Submission text
51 * @param integer $attemptnumber The attempt number
52 * @return object A submission object.
54 protected function create_submission($assign, $user, $submissiontext, $attemptnumber = 0) {
55 $submission = $assign->get_user_submission($user->id, true, $attemptnumber);
56 $submission->onlinetext_editor = ['text' => $submissiontext,
57 'format' => FORMAT_MOODLE];
59 $this->setUser($user);
60 $notices = [];
61 $assign->save_submission($submission, $notices);
62 return $submission;
65 /**
66 * Convenience function to create an instance of an assignment.
68 * @param array $params Array of parameters to pass to the generator
69 * @return assign The assign class.
71 protected function create_instance($params = array()) {
72 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
73 $instance = $generator->create_instance($params);
74 $cm = get_coursemodule_from_instance('assign', $instance->id);
75 $context = \context_module::instance($cm->id);
76 return new \assign($context, $cm, $params['course']);
79 /**
80 * Test that getting the contexts for a user works.
82 public function test_get_contexts_for_userid() {
83 global $DB;
84 $this->resetAfterTest();
86 $course1 = $this->getDataGenerator()->create_course();
87 $course2 = $this->getDataGenerator()->create_course();
88 $course3 = $this->getDataGenerator()->create_course();
90 $user1 = $this->getDataGenerator()->create_user();
91 $this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'student');
92 $this->getDataGenerator()->enrol_user($user1->id, $course3->id, 'student');
93 // Need a second user to create content in other assignments.
94 $user2 = $this->getDataGenerator()->create_user();
95 $this->getDataGenerator()->enrol_user($user2->id, $course2->id, 'student');
97 // Create multiple assignments.
98 // Assignment with a text submission.
99 $assign1 = $this->create_instance(['course' => $course1]);
100 // Assignment two in a different course that the user is not enrolled in.
101 $assign2 = $this->create_instance(['course' => $course2]);
102 // Assignment three has an entry in the override table.
103 $assign3 = $this->create_instance(['course' => $course3, 'cutoffdate' => time()]);
104 // Assignment four - blind marking.
105 $assign4 = $this->create_instance(['course' => $course1, 'blindmarking' => 1]);
106 // Assignment five - user flags.
107 $assign5 = $this->create_instance(['course' => $course3]);
109 // Override has to be manually inserted into the DB.
110 $overridedata = new \stdClass();
111 $overridedata->assignid = $assign3->get_instance()->id;
112 $overridedata->userid = $user1->id;
113 $overridedata->duedate = time();
114 $DB->insert_record('assign_overrides', $overridedata);
115 // Assign unique id for blind marking in assignment four for user 1.
116 \assign::get_uniqueid_for_user_static($assign4->get_instance()->id, $user1->id);
117 // Create an entry in the user flags table.
118 $assign5->get_user_flags($user1->id, true);
120 // The user will be in these contexts.
121 $usercontextids = [
122 $assign1->get_context()->id,
123 $assign3->get_context()->id,
124 $assign4->get_context()->id,
125 $assign5->get_context()->id,
128 $submission = new \stdClass();
129 $submission->assignment = $assign1->get_instance()->id;
130 $submission->userid = $user1->id;
131 $submission->timecreated = time();
132 $submission->onlinetext_editor = ['text' => 'Submission text',
133 'format' => FORMAT_MOODLE];
135 $this->setUser($user1);
136 $notices = [];
137 $assign1->save_submission($submission, $notices);
139 // Create a submission for the second assignment.
140 $submission->assignment = $assign2->get_instance()->id;
141 $submission->userid = $user2->id;
142 $this->setUser($user2);
143 $assign2->save_submission($submission, $notices);
145 $contextlist = provider::get_contexts_for_userid($user1->id);
146 $this->assertEquals(count($usercontextids), count($contextlist->get_contextids()));
147 // There should be no difference between the contexts.
148 $this->assertEmpty(array_diff($usercontextids, $contextlist->get_contextids()));
152 * Test that a student with multiple submissions and grades is returned with the correct data.
154 public function test_export_user_data_student() {
155 global $DB;
156 $this->resetAfterTest();
157 $course = $this->getDataGenerator()->create_course();
158 $coursecontext = \context_course::instance($course->id);
160 $user = $this->getDataGenerator()->create_user();
161 $teacher = $this->getDataGenerator()->create_user();
162 $this->getDataGenerator()->enrol_user($user->id, $course->id, 'student');
163 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
164 $assign = $this->create_instance([
165 'course' => $course,
166 'name' => 'Assign 1',
167 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
168 'maxattempts' => 3,
169 'assignsubmission_onlinetext_enabled' => true,
170 'assignfeedback_comments_enabled' => true
173 $context = $assign->get_context();
174 // Create some submissions (multiple attempts) for a student.
175 $submissiontext = 'My first submission';
176 $submission = $this->create_submission($assign, $user, $submissiontext);
178 $this->setUser($teacher);
180 $overridedata = new \stdClass();
181 $overridedata->assignid = $assign->get_instance()->id;
182 $overridedata->userid = $user->id;
183 $overridedata->duedate = time();
184 $overridedata->allowsubmissionsfromdate = time();
185 $overridedata->cutoffdate = time();
186 $DB->insert_record('assign_overrides', $overridedata);
188 $grade1 = '67.00';
189 $teachercommenttext = 'Please try again.';
190 $data = new \stdClass();
191 $data->attemptnumber = 0;
192 $data->grade = $grade1;
193 $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext, 'format' => FORMAT_MOODLE];
195 // Give the submission a grade.
196 $assign->save_grade($user->id, $data);
198 $submissiontext2 = 'My second submission';
199 $submission = $this->create_submission($assign, $user, $submissiontext2, 1);
201 $this->setUser($teacher);
203 $grade2 = '72.00';
204 $teachercommenttext2 = 'This is better. Thanks.';
205 $data = new \stdClass();
206 $data->attemptnumber = 1;
207 $data->grade = $grade2;
208 $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext2, 'format' => FORMAT_MOODLE];
210 // Give the submission a grade.
211 $assign->save_grade($user->id, $data);
213 $writer = writer::with_context($context);
214 $this->assertFalse($writer->has_any_data());
216 // The student should have some text submitted.
217 // Add the course context as well to make sure there is no error.
218 $approvedlist = new approved_contextlist($user, 'mod_assign', [$context->id, $coursecontext->id]);
219 provider::export_user_data($approvedlist);
221 // Check that we have general details about the assignment.
222 $this->assertEquals('Assign 1', $writer->get_data()->name);
223 // Check Submissions.
224 $this->assertEquals($submissiontext, $writer->get_data(['attempt 1', 'Submission Text'])->text);
225 $this->assertEquals($submissiontext2, $writer->get_data(['attempt 2', 'Submission Text'])->text);
226 $this->assertEquals(1, $writer->get_data(['attempt 1', 'submission'])->attemptnumber);
227 $this->assertEquals(2, $writer->get_data(['attempt 2', 'submission'])->attemptnumber);
228 // Check grades.
229 $this->assertEquals($grade1, $writer->get_data(['attempt 1', 'grade'])->grade);
230 $this->assertEquals($grade2, $writer->get_data(['attempt 2', 'grade'])->grade);
231 // Check feedback.
232 $this->assertContains($teachercommenttext, $writer->get_data(['attempt 1', 'Feedback comments'])->commenttext);
233 $this->assertContains($teachercommenttext2, $writer->get_data(['attempt 2', 'Feedback comments'])->commenttext);
235 // Check override data was exported correctly.
236 $overrideexport = $writer->get_data(['Overrides']);
237 $this->assertEquals(\core_privacy\local\request\transform::datetime($overridedata->duedate),
238 $overrideexport->duedate);
239 $this->assertEquals(\core_privacy\local\request\transform::datetime($overridedata->cutoffdate),
240 $overrideexport->cutoffdate);
241 $this->assertEquals(\core_privacy\local\request\transform::datetime($overridedata->allowsubmissionsfromdate),
242 $overrideexport->allowsubmissionsfromdate);
246 * Tests the data returned for a teacher.
248 public function test_export_user_data_teacher() {
249 $this->resetAfterTest();
250 $course = $this->getDataGenerator()->create_course();
251 $coursecontext = \context_course::instance($course->id);
253 $user1 = $this->getDataGenerator()->create_user();
254 $user2 = $this->getDataGenerator()->create_user();
255 $teacher = $this->getDataGenerator()->create_user();
256 $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
257 $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
258 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
259 $assign = $this->create_instance([
260 'course' => $course,
261 'name' => 'Assign 1',
262 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
263 'maxattempts' => 3,
264 'assignsubmission_onlinetext_enabled' => true,
265 'assignfeedback_comments_enabled' => true
268 $context = $assign->get_context();
270 // Create and grade some submissions from the students.
271 $submissiontext = 'My first submission';
272 $submission = $this->create_submission($assign, $user1, $submissiontext);
274 $this->setUser($teacher);
276 $grade1 = '54.00';
277 $teachercommenttext = 'Comment on user 1 attempt 1.';
278 $data = new \stdClass();
279 $data->attemptnumber = 0;
280 $data->grade = $grade1;
281 $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext, 'format' => FORMAT_MOODLE];
283 // Give the submission a grade.
284 $assign->save_grade($user1->id, $data);
286 // Create and grade some submissions from the students.
287 $submissiontext2 = 'My first submission for user 2';
288 $submission = $this->create_submission($assign, $user2, $submissiontext2);
290 $this->setUser($teacher);
292 $grade2 = '56.00';
293 $teachercommenttext2 = 'Comment on user 2 first attempt.';
294 $data = new \stdClass();
295 $data->attemptnumber = 0;
296 $data->grade = $grade2;
297 $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext2, 'format' => FORMAT_MOODLE];
299 // Give the submission a grade.
300 $assign->save_grade($user2->id, $data);
302 // Create and grade some submissions from the students.
303 $submissiontext3 = 'My second submission for user 2';
304 $submission = $this->create_submission($assign, $user2, $submissiontext3, 1);
306 $this->setUser($teacher);
308 $grade3 = '83.00';
309 $teachercommenttext3 = 'Comment on user 2 another attempt.';
310 $data = new \stdClass();
311 $data->attemptnumber = 1;
312 $data->grade = $grade3;
313 $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext3, 'format' => FORMAT_MOODLE];
315 // Give the submission a grade.
316 $assign->save_grade($user2->id, $data);
318 // Set up some flags.
319 $duedate = time();
320 $flagdata = $assign->get_user_flags($teacher->id, true);
321 $flagdata->mailed = 1;
322 $flagdata->extensionduedate = $duedate;
323 $assign->update_user_flags($flagdata);
325 $writer = writer::with_context($context);
326 $this->assertFalse($writer->has_any_data());
328 // The student should have some text submitted.
329 $approvedlist = new approved_contextlist($teacher, 'mod_assign', [$context->id, $coursecontext->id]);
330 provider::export_user_data($approvedlist);
332 // Check flag metadata.
333 $metadata = $writer->get_all_metadata();
334 $this->assertEquals(\core_privacy\local\request\transform::yesno(1), $metadata['mailed']->value);
335 $this->assertEquals(\core_privacy\local\request\transform::datetime($duedate), $metadata['extensionduedate']->value);
337 // Check for student grades given.
338 $student1grade = $writer->get_data(['studentsubmissions', $user1->id, 'attempt 1', 'grade']);
339 $this->assertEquals($grade1, $student1grade->grade);
340 $student2grade1 = $writer->get_data(['studentsubmissions', $user2->id, 'attempt 1', 'grade']);
341 $this->assertEquals($grade2, $student2grade1->grade);
342 $student2grade2 = $writer->get_data(['studentsubmissions', $user2->id, 'attempt 2', 'grade']);
343 $this->assertEquals($grade3, $student2grade2->grade);
344 // Check for feedback given to students.
345 $this->assertContains($teachercommenttext, $writer->get_data(['studentsubmissions', $user1->id, 'attempt 1',
346 'Feedback comments'])->commenttext);
347 $this->assertContains($teachercommenttext2, $writer->get_data(['studentsubmissions', $user2->id, 'attempt 1',
348 'Feedback comments'])->commenttext);
349 $this->assertContains($teachercommenttext3, $writer->get_data(['studentsubmissions', $user2->id, 'attempt 2',
350 'Feedback comments'])->commenttext);
354 * A test for deleting all user data for a given context.
356 public function test_delete_data_for_all_users_in_context() {
357 global $DB;
358 $this->resetAfterTest();
359 $course = $this->getDataGenerator()->create_course();
361 $user1 = $this->getDataGenerator()->create_user();
362 $user2 = $this->getDataGenerator()->create_user();
363 $teacher = $this->getDataGenerator()->create_user();
364 $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
365 $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
366 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
367 $assign = $this->create_instance([
368 'course' => $course,
369 'name' => 'Assign 1',
370 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
371 'maxattempts' => 3,
372 'assignsubmission_onlinetext_enabled' => true,
373 'assignfeedback_comments_enabled' => true
376 $context = $assign->get_context();
378 // Create and grade some submissions from the students.
379 $submissiontext = 'My first submission';
380 $submission = $this->create_submission($assign, $user1, $submissiontext);
382 $this->setUser($teacher);
384 // Overrides for both students.
385 $overridedata = new \stdClass();
386 $overridedata->assignid = $assign->get_instance()->id;
387 $overridedata->userid = $user1->id;
388 $overridedata->duedate = time();
389 $DB->insert_record('assign_overrides', $overridedata);
390 $overridedata->userid = $user2->id;
391 $DB->insert_record('assign_overrides', $overridedata);
392 assign_update_events($assign);
394 $grade1 = '54.00';
395 $teachercommenttext = 'Comment on user 1 attempt 1.';
396 $data = new \stdClass();
397 $data->attemptnumber = 0;
398 $data->grade = $grade1;
399 $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext, 'format' => FORMAT_MOODLE];
401 // Give the submission a grade.
402 $assign->save_grade($user1->id, $data);
404 // Create and grade some submissions from the students.
405 $submissiontext2 = 'My first submission for user 2';
406 $submission = $this->create_submission($assign, $user2, $submissiontext2);
408 $this->setUser($teacher);
410 $grade2 = '56.00';
411 $teachercommenttext2 = 'Comment on user 2 first attempt.';
412 $data = new \stdClass();
413 $data->attemptnumber = 0;
414 $data->grade = $grade2;
415 $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext2, 'format' => FORMAT_MOODLE];
417 // Give the submission a grade.
418 $assign->save_grade($user2->id, $data);
420 // Create and grade some submissions from the students.
421 $submissiontext3 = 'My second submission for user 2';
422 $submission = $this->create_submission($assign, $user2, $submissiontext3, 1);
424 $this->setUser($teacher);
426 $grade3 = '83.00';
427 $teachercommenttext3 = 'Comment on user 2 another attempt.';
428 $data = new \stdClass();
429 $data->attemptnumber = 1;
430 $data->grade = $grade3;
431 $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext3, 'format' => FORMAT_MOODLE];
433 // Give the submission a grade.
434 $assign->save_grade($user2->id, $data);
436 // Delete all user data for this assignment.
437 provider::delete_data_for_all_users_in_context($context);
439 // Check all relevant tables.
440 $records = $DB->get_records('assign_submission');
441 $this->assertEmpty($records);
442 $records = $DB->get_records('assign_grades');
443 $this->assertEmpty($records);
444 $records = $DB->get_records('assignsubmission_onlinetext');
445 $this->assertEmpty($records);
446 $records = $DB->get_records('assignfeedback_comments');
447 $this->assertEmpty($records);
449 // Check that overrides and the calendar events are deleted.
450 $records = $DB->get_records('event');
451 $this->assertEmpty($records);
452 $records = $DB->get_records('assign_overrides');
453 $this->assertEmpty($records);
457 * A test for deleting all user data for one user.
459 public function test_delete_data_for_user() {
460 global $DB;
461 $this->resetAfterTest();
462 $course = $this->getDataGenerator()->create_course();
464 $coursecontext = \context_course::instance($course->id);
466 $user1 = $this->getDataGenerator()->create_user();
467 $user2 = $this->getDataGenerator()->create_user();
468 $teacher = $this->getDataGenerator()->create_user();
469 $this->getDataGenerator()->enrol_user($user1->id, $course->id, 'student');
470 $this->getDataGenerator()->enrol_user($user2->id, $course->id, 'student');
471 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, 'editingteacher');
472 $assign = $this->create_instance([
473 'course' => $course,
474 'name' => 'Assign 1',
475 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
476 'maxattempts' => 3,
477 'assignsubmission_onlinetext_enabled' => true,
478 'assignfeedback_comments_enabled' => true
481 $context = $assign->get_context();
483 // Create and grade some submissions from the students.
484 $submissiontext = 'My first submission';
485 $submission1 = $this->create_submission($assign, $user1, $submissiontext);
487 $this->setUser($teacher);
489 // Overrides for both students.
490 $overridedata = new \stdClass();
491 $overridedata->assignid = $assign->get_instance()->id;
492 $overridedata->userid = $user1->id;
493 $overridedata->duedate = time();
494 $DB->insert_record('assign_overrides', $overridedata);
495 $overridedata->userid = $user2->id;
496 $DB->insert_record('assign_overrides', $overridedata);
497 assign_update_events($assign);
499 $grade1 = '54.00';
500 $teachercommenttext = 'Comment on user 1 attempt 1.';
501 $data = new \stdClass();
502 $data->attemptnumber = 0;
503 $data->grade = $grade1;
504 $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext, 'format' => FORMAT_MOODLE];
506 // Give the submission a grade.
507 $assign->save_grade($user1->id, $data);
509 // Create and grade some submissions from the students.
510 $submissiontext2 = 'My first submission for user 2';
511 $submission2 = $this->create_submission($assign, $user2, $submissiontext2);
513 $this->setUser($teacher);
515 $grade2 = '56.00';
516 $teachercommenttext2 = 'Comment on user 2 first attempt.';
517 $data = new \stdClass();
518 $data->attemptnumber = 0;
519 $data->grade = $grade2;
520 $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext2, 'format' => FORMAT_MOODLE];
522 // Give the submission a grade.
523 $assign->save_grade($user2->id, $data);
525 // Create and grade some submissions from the students.
526 $submissiontext3 = 'My second submission for user 2';
527 $submission3 = $this->create_submission($assign, $user2, $submissiontext3, 1);
529 $this->setUser($teacher);
531 $grade3 = '83.00';
532 $teachercommenttext3 = 'Comment on user 2 another attempt.';
533 $data = new \stdClass();
534 $data->attemptnumber = 1;
535 $data->grade = $grade3;
536 $data->assignfeedbackcomments_editor = ['text' => $teachercommenttext3, 'format' => FORMAT_MOODLE];
538 // Give the submission a grade.
539 $assign->save_grade($user2->id, $data);
541 // Delete user 2's data.
542 $approvedlist = new approved_contextlist($user2, 'mod_assign', [$context->id, $coursecontext->id]);
543 provider::delete_data_for_user($approvedlist);
545 // Check all relevant tables.
546 $records = $DB->get_records('assign_submission');
547 foreach ($records as $record) {
548 $this->assertEquals($user1->id, $record->userid);
549 $this->assertNotEquals($user2->id, $record->userid);
551 $records = $DB->get_records('assign_grades');
552 foreach ($records as $record) {
553 $this->assertEquals($user1->id, $record->userid);
554 $this->assertNotEquals($user2->id, $record->userid);
556 $records = $DB->get_records('assignsubmission_onlinetext');
557 $this->assertCount(1, $records);
558 $record = array_shift($records);
559 // The only submission is for user 1.
560 $this->assertEquals($submission1->id, $record->submission);
561 $records = $DB->get_records('assignfeedback_comments');
562 $this->assertCount(1, $records);
563 $record = array_shift($records);
564 // The only record is the feedback comment for user 1.
565 $this->assertEquals($teachercommenttext, $record->commenttext);
567 // Check calendar events as well as assign overrides.
568 $records = $DB->get_records('event');
569 $this->assertCount(1, $records);
570 $record = array_shift($records);
571 // The remaining event should be for user 1.
572 $this->assertEquals($user1->id, $record->userid);
573 // Now for assign_overrides
574 $records = $DB->get_records('assign_overrides');
575 $this->assertCount(1, $records);
576 $record = array_shift($records);
577 // The remaining event should be for user 1.
578 $this->assertEquals($user1->id, $record->userid);