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 * Unit tests for (some of) mod/assign/lib.php.
22 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27 defined('MOODLE_INTERNAL') || die();
30 require_once($CFG->dirroot . '/mod/assign/lib.php');
31 require_once($CFG->dirroot . '/mod/assign/locallib.php');
32 require_once($CFG->dirroot . '/mod/assign/tests/generator.php');
34 use \core_calendar\local\api as calendar_local_api;
35 use \core_calendar\local\event\container as calendar_event_container;
38 * Unit tests for (some of) mod/assign/lib.php.
40 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44 class mod_assign_lib_testcase extends advanced_testcase {
46 // Use the generator helper.
47 use mod_assign_test_generator;
49 public function test_assign_print_overview() {
52 $this->resetAfterTest();
54 $course = $this->getDataGenerator()->create_course();
55 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
56 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
58 $this->setAdminUser();
60 // Assignment with default values.
61 $firstassign = $this->create_instance($course, ['name' => 'First Assignment']);
63 // Assignment with submissions.
64 $secondassign = $this->create_instance($course, [
65 'name' => 'Assignment with submissions',
67 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL,
69 'submissiondrafts' => 1,
70 'assignsubmission_onlinetext_enabled' => 1,
72 $this->add_submission($student, $secondassign);
73 $this->submit_for_grading($student, $secondassign);
74 $this->mark_submission($teacher, $secondassign, $student, 50.0);
76 // Past assignments should not show up.
77 $pastassign = $this->create_instance($course, [
78 'name' => 'Past Assignment',
79 'duedate' => time() - DAYSECS - 1,
80 'cutoffdate' => time() - DAYSECS,
82 'assignsubmission_onlinetext_enabled' => 1,
85 // Open assignments should show up only if relevant.
86 $openassign = $this->create_instance($course, [
87 'name' => 'Open Assignment',
89 'cutoffdate' => time() + DAYSECS,
91 'assignsubmission_onlinetext_enabled' => 1,
93 $pastsubmission = $pastassign->get_user_submission($student->id, true);
94 $opensubmission = $openassign->get_user_submission($student->id, true);
96 // Check the overview as the different users.
97 // For students , open assignments should show only when there are no valid submissions.
98 $this->setUser($student);
100 $courses = $DB->get_records('course', array('id' => $course->id));
101 assign_print_overview($courses, $overview);
102 $this->assertDebuggingCalledCount(3);
103 $this->assertEquals(1, count($overview));
104 $this->assertRegExp('/.*Open Assignment.*/', $overview[$course->id]['assign']); // No valid submission.
105 $this->assertNotRegExp('/.*First Assignment.*/', $overview[$course->id]['assign']); // Has valid submission.
107 // And now submit the submission.
108 $opensubmission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
109 $openassign->testable_update_submission($opensubmission, $student->id, true, false);
112 assign_print_overview($courses, $overview);
113 $this->assertDebuggingCalledCount(3);
114 $this->assertEquals(0, count($overview));
116 $this->setUser($teacher);
118 assign_print_overview($courses, $overview);
119 $this->assertDebuggingCalledCount(3);
120 $this->assertEquals(1, count($overview));
121 // Submissions without a grade.
122 $this->assertRegExp('/.*Open Assignment.*/', $overview[$course->id]['assign']);
123 $this->assertNotRegExp('/.*Assignment with submissions.*/', $overview[$course->id]['assign']);
125 $this->setUser($teacher);
127 assign_print_overview($courses, $overview);
128 $this->assertDebuggingCalledCount(3);
129 $this->assertEquals(1, count($overview));
130 // Submissions without a grade.
131 $this->assertRegExp('/.*Open Assignment.*/', $overview[$course->id]['assign']);
132 $this->assertNotRegExp('/.*Assignment with submissions.*/', $overview[$course->id]['assign']);
134 // Let us grade a submission.
135 $this->setUser($teacher);
136 $data = new stdClass();
137 $data->grade = '50.0';
138 $openassign->testable_apply_grade_to_user($data, $student->id, 0);
140 // The assign_print_overview expects the grade date to be after the submission date.
141 $graderecord = $DB->get_record('assign_grades', array('assignment' => $openassign->get_instance()->id,
142 'userid' => $student->id, 'attemptnumber' => 0));
143 $graderecord->timemodified += 1;
144 $DB->update_record('assign_grades', $graderecord);
147 assign_print_overview($courses, $overview);
148 // Now assignment 4 should not show up.
149 $this->assertDebuggingCalledCount(3);
150 $this->assertEmpty($overview);
152 $this->setUser($teacher);
154 assign_print_overview($courses, $overview);
155 $this->assertDebuggingCalledCount(3);
156 // Now assignment 4 should not show up.
157 $this->assertEmpty($overview);
161 * Test that assign_print_overview does not return any assignments which are Open Offline.
163 public function test_assign_print_overview_open_offline() {
164 $this->resetAfterTest();
165 $course = $this->getDataGenerator()->create_course();
166 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
168 $this->setAdminUser();
169 $openassign = $this->create_instance($course, [
170 'duedate' => time() + DAYSECS,
171 'cutoffdate' => time() + (DAYSECS * 2),
174 $this->setUser($student);
176 assign_print_overview([$course], $overview);
178 $this->assertDebuggingCalledCount(1);
179 $this->assertEquals(0, count($overview));
183 * Test that assign_print_recent_activity shows ungraded submitted assignments.
185 public function test_print_recent_activity() {
186 $this->resetAfterTest();
187 $course = $this->getDataGenerator()->create_course();
188 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
189 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
190 $assign = $this->create_instance($course);
191 $this->submit_for_grading($student, $assign);
193 $this->setUser($teacher);
194 $this->expectOutputRegex('/submitted:/');
195 assign_print_recent_activity($course, true, time() - 3600);
199 * Test that assign_print_recent_activity does not display any warnings when a custom fullname has been configured.
201 public function test_print_recent_activity_fullname() {
202 $this->resetAfterTest();
203 $course = $this->getDataGenerator()->create_course();
204 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
205 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
206 $assign = $this->create_instance($course);
207 $this->submit_for_grading($student, $assign);
209 $this->setUser($teacher);
210 $this->expectOutputRegex('/submitted:/');
211 set_config('fullnamedisplay', 'firstname, lastnamephonetic');
212 assign_print_recent_activity($course, false, time() - 3600);
216 * Test that assign_print_recent_activity shows the blind marking ID.
218 public function test_print_recent_activity_fullname_blind_marking() {
219 $this->resetAfterTest();
220 $course = $this->getDataGenerator()->create_course();
221 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
222 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
224 $assign = $this->create_instance($course, [
227 $this->add_submission($student, $assign);
228 $this->submit_for_grading($student, $assign);
230 $this->setUser($teacher);
231 $uniqueid = $assign->get_uniqueid_for_user($student->id);
232 $expectedstr = preg_quote(get_string('participant', 'mod_assign'), '/') . '.*' . $uniqueid;
233 $this->expectOutputRegex("/{$expectedstr}/");
234 assign_print_recent_activity($course, false, time() - 3600);
238 * Test that assign_get_recent_mod_activity fetches the assignment correctly.
240 public function test_assign_get_recent_mod_activity() {
241 $this->resetAfterTest();
242 $course = $this->getDataGenerator()->create_course();
243 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
244 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
245 $assign = $this->create_instance($course);
246 $this->add_submission($student, $assign);
247 $this->submit_for_grading($student, $assign);
253 'cmid' => $assign->get_course_module()->id,
257 $this->setUser($teacher);
258 assign_get_recent_mod_activity($activities, $index, time() - HOURSECS, $course->id, $assign->get_course_module()->id);
260 $activity = $activities[1];
261 $this->assertEquals("assign", $activity->type);
262 $this->assertEquals($student->id, $activity->user->id);
266 * Ensure that assign_user_complete displays information about drafts.
268 public function test_assign_user_complete() {
271 $this->resetAfterTest();
272 $course = $this->getDataGenerator()->create_course();
273 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
274 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
275 $assign = $this->create_instance($course, ['submissiondrafts' => 1]);
276 $this->add_submission($student, $assign);
278 $PAGE->set_url(new moodle_url('/mod/assign/view.php', array('id' => $assign->get_course_module()->id)));
280 $submission = $assign->get_user_submission($student->id, true);
281 $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
282 $DB->update_record('assign_submission', $submission);
284 $this->expectOutputRegex('/Draft/');
285 assign_user_complete($course, $student, $assign->get_course_module(), $assign->get_instance());
289 * Ensure that assign_user_outline fetches updated grades.
291 public function test_assign_user_outline() {
292 $this->resetAfterTest();
293 $course = $this->getDataGenerator()->create_course();
294 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
295 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
296 $assign = $this->create_instance($course);
298 $this->add_submission($student, $assign);
299 $this->submit_for_grading($student, $assign);
300 $this->mark_submission($teacher, $assign, $student, 50.0);
302 $this->setUser($teacher);
303 $data = $assign->get_user_grade($student->id, true);
304 $data->grade = '50.5';
305 $assign->update_grade($data);
307 $result = assign_user_outline($course, $student, $assign->get_course_module(), $assign->get_instance());
309 $this->assertRegExp('/50.5/', $result->info);
313 * Ensure that assign_get_completion_state reflects the correct status at each point.
315 public function test_assign_get_completion_state() {
318 $this->resetAfterTest();
319 $course = $this->getDataGenerator()->create_course();
320 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
321 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
322 $assign = $this->create_instance($course, [
323 'submissiondrafts' => 0,
324 'completionsubmit' => 1
327 $this->setUser($student);
328 $result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false);
329 $this->assertFalse($result);
331 $this->add_submission($student, $assign);
332 $result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false);
333 $this->assertFalse($result);
335 $this->submit_for_grading($student, $assign);
336 $result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false);
337 $this->assertTrue($result);
339 $this->mark_submission($teacher, $assign, $student, 50.0);
340 $result = assign_get_completion_state($course, $assign->get_course_module(), $student->id, false);
341 $this->assertTrue($result);
345 * Tests for mod_assign_refresh_events.
347 public function test_assign_refresh_events() {
350 $this->resetAfterTest();
353 $newduedate = $duedate + DAYSECS;
355 $this->setAdminUser();
357 $course = $this->getDataGenerator()->create_course();
358 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
359 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
360 $assign = $this->create_instance($course, [
361 'duedate' => $duedate,
364 $instance = $assign->get_instance();
365 $eventparams = ['modulename' => 'assign', 'instance' => $instance->id];
367 // Make sure the calendar event for assignment 1 matches the initial due date.
368 $eventtime = $DB->get_field('event', 'timestart', $eventparams, MUST_EXIST);
369 $this->assertEquals($eventtime, $duedate);
371 // Manually update assignment 1's due date.
372 $DB->update_record('assign', (object) ['id' => $instance->id, 'duedate' => $newduedate]);
374 // Then refresh the assignment events of assignment 1's course.
375 $this->assertTrue(assign_refresh_events($course->id));
377 // Confirm that the assignment 1's due date event now has the new due date after refresh.
378 $eventtime = $DB->get_field('event', 'timestart', $eventparams, MUST_EXIST);
379 $this->assertEquals($eventtime, $newduedate);
381 // Create a second course and assignment.
382 $othercourse = $this->getDataGenerator()->create_course();;
383 $otherassign = $this->create_instance($othercourse, ['duedate' => $duedate, 'course' => $othercourse->id]);
384 $otherinstance = $otherassign->get_instance();
386 // Manually update assignment 1 and 2's due dates.
387 $newduedate += DAYSECS;
388 $DB->update_record('assign', (object)['id' => $instance->id, 'duedate' => $newduedate]);
389 $DB->update_record('assign', (object)['id' => $otherinstance->id, 'duedate' => $newduedate]);
391 // Refresh events of all courses.
392 $this->assertTrue(assign_refresh_events());
394 // Check the due date calendar event for assignment 1.
395 $eventtime = $DB->get_field('event', 'timestart', $eventparams, MUST_EXIST);
396 $this->assertEquals($eventtime, $newduedate);
398 // Check the due date calendar event for assignment 2.
399 $eventparams['instance'] = $otherinstance->id;
400 $eventtime = $DB->get_field('event', 'timestart', $eventparams, MUST_EXIST);
401 $this->assertEquals($eventtime, $newduedate);
403 // In case the course ID is passed as a numeric string.
404 $this->assertTrue(assign_refresh_events('' . $course->id));
406 // Non-existing course ID.
407 $this->assertFalse(assign_refresh_events(-1));
409 // Invalid course ID.
410 $this->assertFalse(assign_refresh_events('aaa'));
413 public function test_assign_core_calendar_is_event_visible_duedate_event_as_teacher() {
414 $this->resetAfterTest();
415 $course = $this->getDataGenerator()->create_course();
416 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
417 $assign = $this->create_instance($course);
419 $this->setAdminUser();
421 // Create a calendar event.
422 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
424 // The teacher should see the due date event.
425 $this->setUser($teacher);
426 $this->assertTrue(mod_assign_core_calendar_is_event_visible($event));
429 public function test_assign_core_calendar_is_event_visible_duedate_event_for_teacher() {
430 $this->resetAfterTest();
431 $course = $this->getDataGenerator()->create_course();
432 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
433 $assign = $this->create_instance($course);
435 $this->setAdminUser();
437 // Create a calendar event.
438 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
443 // The teacher should see the due date event.
444 $this->assertTrue(mod_assign_core_calendar_is_event_visible($event, $teacher->id));
447 public function test_assign_core_calendar_is_event_visible_duedate_event_as_student() {
448 $this->resetAfterTest();
449 $course = $this->getDataGenerator()->create_course();
450 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
451 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
452 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
454 $this->setAdminUser();
456 // Create a calendar event.
457 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
459 // The student should care about the due date event.
460 $this->setUser($student);
461 $this->assertTrue(mod_assign_core_calendar_is_event_visible($event));
464 public function test_assign_core_calendar_is_event_visible_duedate_event_for_student() {
465 $this->resetAfterTest();
466 $course = $this->getDataGenerator()->create_course();
467 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
468 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
469 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
471 $this->setAdminUser();
473 // Create a calendar event.
474 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
479 // The student should care about the due date event.
480 $this->assertTrue(mod_assign_core_calendar_is_event_visible($event, $student->id));
483 public function test_assign_core_calendar_is_event_visible_gradingduedate_event_as_teacher() {
484 $this->resetAfterTest();
485 $course = $this->getDataGenerator()->create_course();
486 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
487 $assign = $this->create_instance($course);
489 // Create a calendar event.
490 $this->setAdminUser();
491 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);
493 // The teacher should see the due date event.
494 $this->setUser($teacher);
495 $this->assertTrue(mod_assign_core_calendar_is_event_visible($event));
499 public function test_assign_core_calendar_is_event_visible_gradingduedate_event_for_teacher() {
500 $this->resetAfterTest();
501 $course = $this->getDataGenerator()->create_course();
502 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
503 $assign = $this->create_instance($course);
505 // Create a calendar event.
506 $this->setAdminUser();
507 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);
512 // The teacher should see the due date event.
513 $this->assertTrue(mod_assign_core_calendar_is_event_visible($event, $teacher->id));
516 public function test_assign_core_calendar_is_event_visible_gradingduedate_event_as_student() {
517 $this->resetAfterTest();
518 $course = $this->getDataGenerator()->create_course();
519 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
520 $assign = $this->create_instance($course);
522 // Create a calendar event.
523 $this->setAdminUser();
524 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);
526 // The student should not see the due date event.
527 $this->setUser($student);
528 $this->assertFalse(mod_assign_core_calendar_is_event_visible($event));
532 public function test_assign_core_calendar_is_event_visible_gradingduedate_event_for_student() {
533 $this->resetAfterTest();
534 $course = $this->getDataGenerator()->create_course();
535 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
536 $assign = $this->create_instance($course);
538 // Create a calendar event.
539 $this->setAdminUser();
540 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);
545 // The student should not see the due date event.
546 $this->assertFalse(mod_assign_core_calendar_is_event_visible($event, $student->id));
549 public function test_assign_core_calendar_provide_event_action_duedate_as_teacher() {
550 $this->resetAfterTest();
551 $course = $this->getDataGenerator()->create_course();
552 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
553 $assign = $this->create_instance($course);
555 // Create a calendar event.
556 $this->setAdminUser();
557 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
559 // The teacher should see the event.
560 $this->setUser($teacher);
561 $factory = new \core_calendar\action_factory();
562 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory);
564 // The teacher should not have an action for a due date event.
565 $this->assertNull($actionevent);
568 public function test_assign_core_calendar_provide_event_action_duedate_for_teacher() {
569 $this->resetAfterTest();
570 $course = $this->getDataGenerator()->create_course();
571 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
572 $assign = $this->create_instance($course);
574 // Create a calendar event.
575 $this->setAdminUser();
576 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
581 // Decorate action event for a teacher.
582 $factory = new \core_calendar\action_factory();
583 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $teacher->id);
585 // The teacher should not have an action for a due date event.
586 $this->assertNull($actionevent);
589 public function test_assign_core_calendar_provide_event_action_duedate_as_student() {
590 $this->resetAfterTest();
591 $course = $this->getDataGenerator()->create_course();
592 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
593 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
595 // Create a calendar event.
596 $this->setAdminUser();
597 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
599 // The student should see the event.
600 $this->setUser($student);
601 $factory = new \core_calendar\action_factory();
602 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory);
604 // Confirm the event was decorated.
605 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
606 $this->assertEquals(get_string('addsubmission', 'assign'), $actionevent->get_name());
607 $this->assertInstanceOf('moodle_url', $actionevent->get_url());
608 $this->assertEquals(1, $actionevent->get_item_count());
609 $this->assertTrue($actionevent->is_actionable());
612 public function test_assign_core_calendar_provide_event_action_duedate_for_student() {
613 $this->resetAfterTest();
614 $course = $this->getDataGenerator()->create_course();
615 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
616 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
618 // Create a calendar event.
619 $this->setAdminUser();
620 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
625 // Decorate action event for a student.
626 $factory = new \core_calendar\action_factory();
627 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id);
629 // Confirm the event was decorated.
630 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
631 $this->assertEquals(get_string('addsubmission', 'assign'), $actionevent->get_name());
632 $this->assertInstanceOf('moodle_url', $actionevent->get_url());
633 $this->assertEquals(1, $actionevent->get_item_count());
634 $this->assertTrue($actionevent->is_actionable());
637 public function test_assign_core_calendar_provide_event_action_gradingduedate_as_teacher() {
638 $this->resetAfterTest();
639 $course = $this->getDataGenerator()->create_course();
640 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
641 $assign = $this->create_instance($course);
643 // Create a calendar event.
644 $this->setAdminUser();
645 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);
647 $this->setUser($teacher);
648 $factory = new \core_calendar\action_factory();
649 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory);
651 // Confirm the event was decorated.
652 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
653 $this->assertEquals(get_string('grade'), $actionevent->get_name());
654 $this->assertInstanceOf('moodle_url', $actionevent->get_url());
655 $this->assertEquals(0, $actionevent->get_item_count());
656 $this->assertTrue($actionevent->is_actionable());
659 public function test_assign_core_calendar_provide_event_action_gradingduedate_for_teacher() {
660 $this->resetAfterTest();
661 $course = $this->getDataGenerator()->create_course();
662 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
663 $assign = $this->create_instance($course);
665 // Create a calendar event.
666 $this->setAdminUser();
667 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);
672 // Decorate action event for a teacher.
673 $factory = new \core_calendar\action_factory();
674 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $teacher->id);
676 // Confirm the event was decorated.
677 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
678 $this->assertEquals(get_string('grade'), $actionevent->get_name());
679 $this->assertInstanceOf('moodle_url', $actionevent->get_url());
680 $this->assertEquals(0, $actionevent->get_item_count());
681 $this->assertTrue($actionevent->is_actionable());
684 public function test_assign_core_calendar_provide_event_action_gradingduedate_as_student() {
685 $this->resetAfterTest();
686 $course = $this->getDataGenerator()->create_course();
687 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
688 $assign = $this->create_instance($course);
690 // Create a calendar event.
691 $this->setAdminUser();
692 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);
694 $this->setUser($student);
695 $factory = new \core_calendar\action_factory();
696 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory);
698 // Confirm the event was decorated.
699 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
700 $this->assertEquals(get_string('grade'), $actionevent->get_name());
701 $this->assertInstanceOf('moodle_url', $actionevent->get_url());
702 $this->assertEquals(0, $actionevent->get_item_count());
703 $this->assertFalse($actionevent->is_actionable());
706 public function test_assign_core_calendar_provide_event_action_gradingduedate_for_student() {
707 $this->resetAfterTest();
708 $course = $this->getDataGenerator()->create_course();
709 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
710 $assign = $this->create_instance($course);
712 // Create a calendar event.
713 $this->setAdminUser();
714 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_GRADINGDUE);
719 // Decorate action event for a student.
720 $factory = new \core_calendar\action_factory();
721 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id);
723 // Confirm the event was decorated.
724 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent);
725 $this->assertEquals(get_string('grade'), $actionevent->get_name());
726 $this->assertInstanceOf('moodle_url', $actionevent->get_url());
727 $this->assertEquals(0, $actionevent->get_item_count());
728 $this->assertFalse($actionevent->is_actionable());
731 public function test_assign_core_calendar_provide_event_action_duedate_as_student_submitted() {
732 $this->resetAfterTest();
733 $course = $this->getDataGenerator()->create_course();
734 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
735 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
736 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
738 $this->setAdminUser();
740 // Create a calendar event.
741 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
743 // Create an action factory.
744 $factory = new \core_calendar\action_factory();
746 // Submit as the student.
747 $this->add_submission($student, $assign);
748 $this->submit_for_grading($student, $assign);
750 // Confirm there was no event to action.
751 $factory = new \core_calendar\action_factory();
752 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory);
753 $this->assertNull($actionevent);
756 public function test_assign_core_calendar_provide_event_action_duedate_for_student_submitted() {
757 $this->resetAfterTest();
758 $course = $this->getDataGenerator()->create_course();
759 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
760 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
761 $assign = $this->create_instance($course, ['assignsubmission_onlinetext_enabled' => 1]);
763 $this->setAdminUser();
765 // Create a calendar event.
766 $event = $this->create_action_event($course, $assign, ASSIGN_EVENT_TYPE_DUE);
768 // Create an action factory.
769 $factory = new \core_calendar\action_factory();
771 // Submit as the student.
772 $this->add_submission($student, $assign);
773 $this->submit_for_grading($student, $assign);
778 // Confirm there was no event to action.
779 $factory = new \core_calendar\action_factory();
780 $actionevent = mod_assign_core_calendar_provide_event_action($event, $factory, $student->id);
781 $this->assertNull($actionevent);
785 * Creates an action event.
787 * @param \stdClass $course The course the assignment is in
788 * @param assign $assign The assignment to create an event for
789 * @param string $eventtype The event type. eg. ASSIGN_EVENT_TYPE_DUE.
790 * @return bool|calendar_event
792 private function create_action_event($course, $assign, $eventtype) {
793 $event = new stdClass();
794 $event->name = 'Calendar event';
795 $event->modulename = 'assign';
796 $event->courseid = $course->id;
797 $event->instance = $assign->get_instance()->id;
798 $event->type = CALENDAR_EVENT_TYPE_ACTION;
799 $event->eventtype = $eventtype;
800 $event->timestart = time();
802 return calendar_event::create($event);
806 * Test the callback responsible for returning the completion rule descriptions.
807 * This function should work given either an instance of the module (cm_info), such as when checking the active rules,
808 * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type.
810 public function test_mod_assign_completion_get_active_rule_descriptions() {
811 $this->resetAfterTest();
812 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]);
814 $this->setAdminUser();
816 // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't.
817 $cm1 = $this->create_instance($course, ['completion' => '2', 'completionsubmit' => '1'])->get_course_module();
818 $cm2 = $this->create_instance($course, ['completion' => '2', 'completionsubmit' => '0'])->get_course_module();
820 // Data for the stdClass input type.
821 // This type of input would occur when checking the default completion rules for an activity type, where we don't have
822 // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info.
823 $moddefaults = (object) [
825 'customcompletionrules' => [
826 'completionsubmit' => '1',
832 $activeruledescriptions = [get_string('completionsubmit', 'assign')];
833 $this->assertEquals(mod_assign_get_completion_active_rule_descriptions($cm1), $activeruledescriptions);
834 $this->assertEquals(mod_assign_get_completion_active_rule_descriptions($cm2), []);
835 $this->assertEquals(mod_assign_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions);
836 $this->assertEquals(mod_assign_get_completion_active_rule_descriptions(new stdClass()), []);
840 * Test that if some grades are not set, they are left alone and not rescaled
842 public function test_assign_rescale_activity_grades_some_unset() {
843 $this->resetAfterTest();
844 $course = $this->getDataGenerator()->create_course();
845 $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
846 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
847 $otherstudent = $this->getDataGenerator()->create_and_enrol($course, 'student');
850 $this->setUser($teacher);
851 $assign = $this->create_instance($course);
853 // Grade the student.
854 $data = ['grade' => 50];
855 $assign->testable_apply_grade_to_user((object)$data, $student->id, 0);
857 // Try getting another students grade. This will give a grade of ASSIGN_GRADE_NOT_SET (-1).
858 $assign->get_user_grade($otherstudent->id, true);
861 assign_rescale_activity_grades($course, $assign->get_course_module(), 0, 100, 0, 10);
863 // Get the grades for both students.
864 $studentgrade = $assign->get_user_grade($student->id, true);
865 $otherstudentgrade = $assign->get_user_grade($otherstudent->id, true);
867 // Make sure the real grade is scaled, but the ASSIGN_GRADE_NOT_SET stays the same.
868 $this->assertEquals($studentgrade->grade, 5);
869 $this->assertEquals($otherstudentgrade->grade, ASSIGN_GRADE_NOT_SET);
873 * Return false when there are not overrides for this assign instance.
875 public function test_assign_is_override_calendar_event_no_override() {
877 require_once($CFG->dirroot . '/calendar/lib.php');
879 $this->resetAfterTest();
880 $course = $this->getDataGenerator()->create_course();
881 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
883 $this->setAdminUser();
886 $assign = $this->create_instance($course, ['duedate' => $duedate]);
888 $instance = $assign->get_instance();
889 $event = new \calendar_event((object)[
890 'modulename' => 'assign',
891 'instance' => $instance->id,
892 'userid' => $student->id,
895 $this->assertFalse($assign->is_override_calendar_event($event));
899 * Return false if the given event isn't an assign module event.
901 public function test_assign_is_override_calendar_event_no_nodule_event() {
903 require_once($CFG->dirroot . '/calendar/lib.php');
905 $this->resetAfterTest();
906 $course = $this->getDataGenerator()->create_course();
907 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
909 $this->setAdminUser();
911 $userid = $student->id;
913 $assign = $this->create_instance($course, ['duedate' => $duedate]);
915 $instance = $assign->get_instance();
916 $event = new \calendar_event((object)[
920 $this->assertFalse($assign->is_override_calendar_event($event));
924 * Return false if there is overrides for this use but they belong to another assign
927 public function test_assign_is_override_calendar_event_different_assign_instance() {
929 require_once($CFG->dirroot . '/calendar/lib.php');
931 $this->resetAfterTest();
932 $course = $this->getDataGenerator()->create_course();
933 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
935 $this->setAdminUser();
938 $assign = $this->create_instance($course, ['duedate' => $duedate]);
939 $instance = $assign->get_instance();
941 $otherassign = $this->create_instance($course, ['duedate' => $duedate]);
942 $otherinstance = $otherassign->get_instance();
944 $event = new \calendar_event((object) [
945 'modulename' => 'assign',
946 'instance' => $instance->id,
947 'userid' => $student->id,
950 $DB->insert_record('assign_overrides', (object) [
951 'assignid' => $otherinstance->id,
952 'userid' => $student->id,
955 $this->assertFalse($assign->is_override_calendar_event($event));
959 * Return true if there is a user override for this event and assign instance.
961 public function test_assign_is_override_calendar_event_user_override() {
963 require_once($CFG->dirroot . '/calendar/lib.php');
965 $this->resetAfterTest();
966 $course = $this->getDataGenerator()->create_course();
967 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
969 $this->setAdminUser();
972 $assign = $this->create_instance($course, ['duedate' => $duedate]);
974 $instance = $assign->get_instance();
975 $event = new \calendar_event((object) [
976 'modulename' => 'assign',
977 'instance' => $instance->id,
978 'userid' => $student->id,
982 $DB->insert_record('assign_overrides', (object) [
983 'assignid' => $instance->id,
984 'userid' => $student->id,
987 $this->assertTrue($assign->is_override_calendar_event($event));
991 * Return true if there is a group override for the event and assign instance.
993 public function test_assign_is_override_calendar_event_group_override() {
995 require_once($CFG->dirroot . '/calendar/lib.php');
997 $this->resetAfterTest();
998 $course = $this->getDataGenerator()->create_course();
1000 $this->setAdminUser();
1003 $assign = $this->create_instance($course, ['duedate' => $duedate]);
1004 $instance = $assign->get_instance();
1005 $group = $this->getDataGenerator()->create_group(array('courseid' => $instance->course));
1007 $event = new \calendar_event((object) [
1008 'modulename' => 'assign',
1009 'instance' => $instance->id,
1010 'groupid' => $group->id,
1013 $DB->insert_record('assign_overrides', (object) [
1014 'assignid' => $instance->id,
1015 'groupid' => $group->id,
1018 $this->assertTrue($assign->is_override_calendar_event($event));
1022 * Unknown event types should not have any limit restrictions returned.
1024 public function test_mod_assign_core_calendar_get_valid_event_timestart_range_unkown_event_type() {
1026 require_once($CFG->dirroot . '/calendar/lib.php');
1028 $this->resetAfterTest();
1029 $course = $this->getDataGenerator()->create_course();
1031 $this->setAdminUser();
1034 $assign = $this->create_instance($course, ['duedate' => $duedate]);
1035 $instance = $assign->get_instance();
1037 $event = new \calendar_event((object) [
1038 'courseid' => $instance->course,
1039 'modulename' => 'assign',
1040 'instance' => $instance->id,
1041 'eventtype' => 'SOME RANDOM EVENT'
1044 list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance);
1045 $this->assertNull($min);
1046 $this->assertNull($max);
1050 * Override events should not have any limit restrictions returned.
1052 public function test_mod_assign_core_calendar_get_valid_event_timestart_range_override_event() {
1054 require_once($CFG->dirroot . '/calendar/lib.php');
1056 $this->resetAfterTest();
1057 $course = $this->getDataGenerator()->create_course();
1058 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1060 $this->setAdminUser();
1063 $assign = $this->create_instance($course, ['duedate' => $duedate]);
1064 $instance = $assign->get_instance();
1066 $event = new \calendar_event((object) [
1067 'courseid' => $instance->course,
1068 'modulename' => 'assign',
1069 'instance' => $instance->id,
1070 'userid' => $student->id,
1071 'eventtype' => ASSIGN_EVENT_TYPE_DUE
1074 $record = (object) [
1075 'assignid' => $instance->id,
1076 'userid' => $student->id,
1079 $DB->insert_record('assign_overrides', $record);
1081 list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance);
1082 $this->assertFalse($min);
1083 $this->assertFalse($max);
1087 * Assignments configured without a submissions from and cutoff date should not have
1088 * any limits applied.
1090 public function test_mod_assign_core_calendar_get_valid_event_timestart_range_due_no_limit() {
1092 require_once($CFG->dirroot . '/calendar/lib.php');
1094 $this->resetAfterTest();
1095 $course = $this->getDataGenerator()->create_course();
1097 $this->setAdminUser();
1100 $assign = $this->create_instance($course, [
1101 'duedate' => $duedate,
1102 'allowsubmissionsfromdate' => 0,
1105 $instance = $assign->get_instance();
1107 $event = new \calendar_event((object) [
1108 'courseid' => $instance->course,
1109 'modulename' => 'assign',
1110 'instance' => $instance->id,
1111 'eventtype' => ASSIGN_EVENT_TYPE_DUE
1114 list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance);
1115 $this->assertNull($min);
1116 $this->assertNull($max);
1120 * Assignments should be bottom and top bound by the submissions from date and cutoff date
1123 public function test_mod_assign_core_calendar_get_valid_event_timestart_range_due_with_limits() {
1125 require_once($CFG->dirroot . '/calendar/lib.php');
1127 $this->resetAfterTest();
1128 $course = $this->getDataGenerator()->create_course();
1130 $this->setAdminUser();
1133 $submissionsfromdate = $duedate - DAYSECS;
1134 $cutoffdate = $duedate + DAYSECS;
1135 $assign = $this->create_instance($course, [
1136 'duedate' => $duedate,
1137 'allowsubmissionsfromdate' => $submissionsfromdate,
1138 'cutoffdate' => $cutoffdate,
1140 $instance = $assign->get_instance();
1142 $event = new \calendar_event((object) [
1143 'courseid' => $instance->course,
1144 'modulename' => 'assign',
1145 'instance' => $instance->id,
1146 'eventtype' => ASSIGN_EVENT_TYPE_DUE
1149 list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance);
1150 $this->assertEquals($submissionsfromdate, $min[0]);
1151 $this->assertNotEmpty($min[1]);
1152 $this->assertEquals($cutoffdate, $max[0]);
1153 $this->assertNotEmpty($max[1]);
1157 * Assignment grading due date should not have any limits of no due date and cutoff date is set.
1159 public function test_mod_assign_core_calendar_get_valid_event_timestart_range_gradingdue_no_limit() {
1161 require_once($CFG->dirroot . '/calendar/lib.php');
1163 $this->resetAfterTest();
1164 $course = $this->getDataGenerator()->create_course();
1166 $this->setAdminUser();
1168 $assign = $this->create_instance($course, [
1170 'allowsubmissionsfromdate' => 0,
1173 $instance = $assign->get_instance();
1175 $event = new \calendar_event((object) [
1176 'courseid' => $instance->course,
1177 'modulename' => 'assign',
1178 'instance' => $instance->id,
1179 'eventtype' => ASSIGN_EVENT_TYPE_GRADINGDUE
1182 list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance);
1183 $this->assertNull($min);
1184 $this->assertNull($max);
1188 * Assignment grading due event is minimum bound by the due date, if it is set.
1190 public function test_mod_assign_core_calendar_get_valid_event_timestart_range_gradingdue_with_due_date() {
1192 require_once($CFG->dirroot . '/calendar/lib.php');
1194 $this->resetAfterTest();
1195 $course = $this->getDataGenerator()->create_course();
1197 $this->setAdminUser();
1200 $assign = $this->create_instance($course, ['duedate' => $duedate]);
1201 $instance = $assign->get_instance();
1203 $event = new \calendar_event((object) [
1204 'courseid' => $instance->course,
1205 'modulename' => 'assign',
1206 'instance' => $instance->id,
1207 'eventtype' => ASSIGN_EVENT_TYPE_GRADINGDUE
1210 list($min, $max) = mod_assign_core_calendar_get_valid_event_timestart_range($event, $instance);
1211 $this->assertEquals($duedate, $min[0]);
1212 $this->assertNotEmpty($min[1]);
1213 $this->assertNull($max);
1217 * Non due date events should not update the assignment due date.
1219 public function test_mod_assign_core_calendar_event_timestart_updated_non_due_event() {
1221 require_once($CFG->dirroot . '/calendar/lib.php');
1223 $this->resetAfterTest();
1224 $course = $this->getDataGenerator()->create_course();
1225 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1227 $this->setAdminUser();
1230 $submissionsfromdate = $duedate - DAYSECS;
1231 $cutoffdate = $duedate + DAYSECS;
1232 $assign = $this->create_instance($course, [
1233 'duedate' => $duedate,
1234 'allowsubmissionsfromdate' => $submissionsfromdate,
1235 'cutoffdate' => $cutoffdate,
1237 $instance = $assign->get_instance();
1239 $event = new \calendar_event((object) [
1240 'courseid' => $instance->course,
1241 'modulename' => 'assign',
1242 'instance' => $instance->id,
1243 'eventtype' => ASSIGN_EVENT_TYPE_GRADINGDUE,
1244 'timestart' => $duedate + 1
1247 mod_assign_core_calendar_event_timestart_updated($event, $instance);
1249 $newinstance = $DB->get_record('assign', ['id' => $instance->id]);
1250 $this->assertEquals($duedate, $newinstance->duedate);
1254 * Due date override events should not change the assignment due date.
1256 public function test_mod_assign_core_calendar_event_timestart_updated_due_event_override() {
1258 require_once($CFG->dirroot . '/calendar/lib.php');
1260 $this->resetAfterTest();
1261 $course = $this->getDataGenerator()->create_course();
1262 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1264 $this->setAdminUser();
1267 $submissionsfromdate = $duedate - DAYSECS;
1268 $cutoffdate = $duedate + DAYSECS;
1269 $assign = $this->create_instance($course, [
1270 'duedate' => $duedate,
1271 'allowsubmissionsfromdate' => $submissionsfromdate,
1272 'cutoffdate' => $cutoffdate,
1274 $instance = $assign->get_instance();
1276 $event = new \calendar_event((object) [
1277 'courseid' => $instance->course,
1278 'modulename' => 'assign',
1279 'instance' => $instance->id,
1280 'userid' => $student->id,
1281 'eventtype' => ASSIGN_EVENT_TYPE_DUE,
1282 'timestart' => $duedate + 1
1285 $record = (object) [
1286 'assignid' => $instance->id,
1287 'userid' => $student->id,
1288 'duedate' => $duedate + 1,
1291 $DB->insert_record('assign_overrides', $record);
1293 mod_assign_core_calendar_event_timestart_updated($event, $instance);
1295 $newinstance = $DB->get_record('assign', ['id' => $instance->id]);
1296 $this->assertEquals($duedate, $newinstance->duedate);
1300 * Due date events should update the assignment due date.
1302 public function test_mod_assign_core_calendar_event_timestart_updated_due_event() {
1304 require_once($CFG->dirroot . '/calendar/lib.php');
1306 $this->resetAfterTest();
1307 $course = $this->getDataGenerator()->create_course();
1308 $student = $this->getDataGenerator()->create_and_enrol($course, 'student');
1310 $this->setAdminUser();
1313 $newduedate = $duedate + 1;
1314 $submissionsfromdate = $duedate - DAYSECS;
1315 $cutoffdate = $duedate + DAYSECS;
1316 $assign = $this->create_instance($course, [
1317 'duedate' => $duedate,
1318 'allowsubmissionsfromdate' => $submissionsfromdate,
1319 'cutoffdate' => $cutoffdate,
1321 $instance = $assign->get_instance();
1323 $event = new \calendar_event((object) [
1324 'courseid' => $instance->course,
1325 'modulename' => 'assign',
1326 'instance' => $instance->id,
1327 'eventtype' => ASSIGN_EVENT_TYPE_DUE,
1328 'timestart' => $newduedate
1331 mod_assign_core_calendar_event_timestart_updated($event, $instance);
1333 $newinstance = $DB->get_record('assign', ['id' => $instance->id]);
1334 $this->assertEquals($newduedate, $newinstance->duedate);
1338 * If a student somehow finds a way to update the due date calendar event
1339 * then the callback should not be executed to update the assignment due
1340 * date as well otherwise that would be a security issue.
1342 public function test_student_role_cant_update_due_event() {
1344 require_once($CFG->dirroot . '/calendar/lib.php');
1346 $this->resetAfterTest();
1347 $course = $this->getDataGenerator()->create_course();
1348 $context = context_course::instance($course->id);
1350 $roleid = $this->getDataGenerator()->create_role();
1351 $role = $DB->get_record('role', ['id' => $roleid]);
1352 $user = $this->getDataGenerator()->create_and_enrol($course, $role->shortname);
1354 $this->setAdminUser();
1356 $mapper = calendar_event_container::get_event_mapper();
1358 $duedate = (new DateTime())->setTimestamp($now);
1359 $newduedate = (new DateTime())->setTimestamp($now)->modify('+1 day');
1360 $assign = $this->create_instance($course, [
1361 'course' => $course->id,
1362 'duedate' => $duedate->getTimestamp(),
1364 $instance = $assign->get_instance();
1366 $record = $DB->get_record('event', [
1367 'courseid' => $course->id,
1368 'modulename' => 'assign',
1369 'instance' => $instance->id,
1370 'eventtype' => ASSIGN_EVENT_TYPE_DUE
1373 $event = new \calendar_event($record);
1375 assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true);
1376 assign_capability('moodle/course:manageactivities', CAP_PROHIBIT, $roleid, $context, true);
1378 $this->setUser($user);
1380 calendar_local_api::update_event_start_day(
1381 $mapper->from_legacy_event_to_event($event),
1385 $newinstance = $DB->get_record('assign', ['id' => $instance->id]);
1386 $newevent = \calendar_event::load($event->id);
1387 // The due date shouldn't have changed even though we updated the calendar
1389 $this->assertEquals($duedate->getTimestamp(), $newinstance->duedate);
1390 $this->assertEquals($newduedate->getTimestamp(), $newevent->timestart);
1394 * A teacher with the capability to modify an assignment module should be
1395 * able to update the assignment due date by changing the due date calendar
1398 public function test_teacher_role_can_update_due_event() {
1400 require_once($CFG->dirroot . '/calendar/lib.php');
1402 $this->resetAfterTest();
1403 $course = $this->getDataGenerator()->create_course();
1404 $context = context_course::instance($course->id);
1405 $user = $this->getDataGenerator()->create_and_enrol($course, 'teacher');
1406 $roleid = $DB->get_field('role', 'id', ['shortname' => 'teacher']);
1408 $this->setAdminUser();
1410 $mapper = calendar_event_container::get_event_mapper();
1412 $duedate = (new DateTime())->setTimestamp($now);
1413 $newduedate = (new DateTime())->setTimestamp($now)->modify('+1 day');
1414 $assign = $this->create_instance($course, [
1415 'course' => $course->id,
1416 'duedate' => $duedate->getTimestamp(),
1418 $instance = $assign->get_instance();
1420 $record = $DB->get_record('event', [
1421 'courseid' => $course->id,
1422 'modulename' => 'assign',
1423 'instance' => $instance->id,
1424 'eventtype' => ASSIGN_EVENT_TYPE_DUE
1427 $event = new \calendar_event($record);
1429 assign_capability('moodle/calendar:manageentries', CAP_ALLOW, $roleid, $context, true);
1430 assign_capability('moodle/course:manageactivities', CAP_ALLOW, $roleid, $context, true);
1432 $this->setUser($user);
1433 // Trigger and capture the event when adding a contact.
1434 $sink = $this->redirectEvents();
1436 calendar_local_api::update_event_start_day(
1437 $mapper->from_legacy_event_to_event($event),
1441 $triggeredevents = $sink->get_events();
1442 $moduleupdatedevents = array_filter($triggeredevents, function($e) {
1443 return is_a($e, 'core\event\course_module_updated');
1446 $newinstance = $DB->get_record('assign', ['id' => $instance->id]);
1447 $newevent = \calendar_event::load($event->id);
1448 // The due date shouldn't have changed even though we updated the calendar
1450 $this->assertEquals($newduedate->getTimestamp(), $newinstance->duedate);
1451 $this->assertEquals($newduedate->getTimestamp(), $newevent->timestart);
1452 // Confirm that a module updated event is fired when the module
1454 $this->assertNotEmpty($moduleupdatedevents);