2 // This file is part of Moodle - http://moodle.org/
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.
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/>.
18 * Base class for unit tests for 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();
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
;
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
{
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);
61 $assign->save_submission($submission, $notices);
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']);
80 * Test that getting the contexts for a user works.
82 public function test_get_contexts_for_userid() {
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.
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);
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() {
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([
166 'name' => 'Assign 1',
167 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL
,
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);
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);
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
);
229 $this->assertEquals($grade1, $writer->get_data(['attempt 1', 'grade'])->grade
);
230 $this->assertEquals($grade2, $writer->get_data(['attempt 2', 'grade'])->grade
);
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([
261 'name' => 'Assign 1',
262 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL
,
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);
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);
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);
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.
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() {
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([
369 'name' => 'Assign 1',
370 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL
,
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);
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);
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);
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() {
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([
474 'name' => 'Assign 1',
475 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL
,
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);
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);
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);
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
);