weekly release 4.5dev
[moodle.git] / completion / tests / api_test.php
blobb7e84e7769962b5753cb1a7e94b82eee94000b2c
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 namespace core_completion;
19 /**
20 * Test completion API.
22 * @package core_completion
23 * @category test
24 * @copyright 2017 Mark Nelson <markn@moodle.com>
25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 * @covers \core_completion\api
28 class api_test extends \advanced_testcase {
30 /**
31 * Test setup.
33 public function setUp(): void {
34 parent::setUp();
35 $this->resetAfterTest();
38 public function test_update_completion_date_event(): void {
39 global $CFG, $DB;
41 $this->setAdminUser();
43 // Create a course.
44 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
46 // Create an assign activity.
47 $time = time();
48 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
50 // Create the completion event.
51 $CFG->enablecompletion = true;
52 \core_completion\api::update_completion_date_event($assign->cmid, 'assign', $assign, $time);
54 // Check that there is now an event in the database.
55 $events = $DB->get_records('event');
56 $this->assertCount(1, $events);
58 // Get the event.
59 $event = reset($events);
61 // Confirm the event is correct.
62 $this->assertEquals('assign', $event->modulename);
63 $this->assertEquals($assign->id, $event->instance);
64 $this->assertEquals(CALENDAR_EVENT_TYPE_ACTION, $event->type);
65 $this->assertEquals(\core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED, $event->eventtype);
66 $this->assertEquals($time, $event->timestart);
67 $this->assertEquals($time, $event->timesort);
69 require_once($CFG->dirroot . '/course/lib.php');
70 // Delete the module.
71 course_delete_module($assign->cmid);
73 // Check we don't get a failure when called on a deleted module.
74 \core_completion\api::update_completion_date_event($assign->cmid, 'assign', null, $time);
77 public function test_update_completion_date_event_update(): void {
78 global $CFG, $DB;
80 $this->setAdminUser();
82 // Create a course.
83 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
85 // Create an assign activity.
86 $time = time();
87 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
89 // Create the event.
90 $CFG->enablecompletion = true;
91 \core_completion\api::update_completion_date_event($assign->cmid, 'assign', $assign, $time);
93 // Call it again, but this time with a different time.
94 \core_completion\api::update_completion_date_event($assign->cmid, 'assign', $assign, $time + DAYSECS);
96 // Check that there is still only one event in the database.
97 $events = $DB->get_records('event');
98 $this->assertCount(1, $events);
100 // Get the event.
101 $event = reset($events);
103 // Confirm that the event has been updated.
104 $this->assertEquals('assign', $event->modulename);
105 $this->assertEquals($assign->id, $event->instance);
106 $this->assertEquals(CALENDAR_EVENT_TYPE_ACTION, $event->type);
107 $this->assertEquals(\core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED, $event->eventtype);
108 $this->assertEquals($time + DAYSECS, $event->timestart);
109 $this->assertEquals($time + DAYSECS, $event->timesort);
112 public function test_update_completion_date_event_delete(): void {
113 global $CFG, $DB;
115 $this->setAdminUser();
117 // Create a course.
118 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
120 // Create an assign activity.
121 $time = time();
122 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
124 // Create the event.
125 $CFG->enablecompletion = true;
126 \core_completion\api::update_completion_date_event($assign->cmid, 'assign', $assign, $time);
128 // Call it again, but the time specified as null.
129 \core_completion\api::update_completion_date_event($assign->cmid, 'assign', $assign, null);
131 // Check that there is no event in the database.
132 $this->assertEquals(0, $DB->count_records('event'));
135 public function test_update_completion_date_event_completion_disabled(): void {
136 global $CFG, $DB;
138 $this->setAdminUser();
140 // Create a course.
141 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
143 // Create an assign activity.
144 $time = time();
145 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
147 // Try and create the completion event with completion disabled.
148 $CFG->enablecompletion = false;
149 \core_completion\api::update_completion_date_event($assign->cmid, 'assign', $assign, $time);
151 // Check that there is no event in the database.
152 $this->assertEquals(0, $DB->count_records('event'));
155 public function test_update_completion_date_event_update_completion_disabled(): void {
156 global $CFG, $DB;
158 $this->setAdminUser();
160 // Create a course.
161 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
163 // Create an assign activity.
164 $time = time();
165 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
167 // Create the completion event.
168 $CFG->enablecompletion = true;
169 \core_completion\api::update_completion_date_event($assign->cmid, 'assign', $assign, $time);
171 // Disable completion.
172 $CFG->enablecompletion = false;
174 // Try and update the completion date.
175 \core_completion\api::update_completion_date_event($assign->cmid, 'assign', $assign, $time + DAYSECS);
177 // Check that there is an event in the database.
178 $events = $DB->get_records('event');
179 $this->assertCount(1, $events);
181 // Get the event.
182 $event = reset($events);
184 // Confirm the event has not changed.
185 $this->assertEquals('assign', $event->modulename);
186 $this->assertEquals($assign->id, $event->instance);
187 $this->assertEquals(CALENDAR_EVENT_TYPE_ACTION, $event->type);
188 $this->assertEquals(\core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED, $event->eventtype);
189 $this->assertEquals($time, $event->timestart);
190 $this->assertEquals($time, $event->timesort);
193 public function test_update_completion_date_event_delete_completion_disabled(): void {
194 global $CFG, $DB;
196 $this->setAdminUser();
198 // Create a course.
199 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
201 // Create an assign activity.
202 $time = time();
203 $assign = $this->getDataGenerator()->create_module('assign', array('course' => $course->id));
205 // Create the completion event.
206 $CFG->enablecompletion = true;
207 \core_completion\api::update_completion_date_event($assign->cmid, 'assign', $assign, $time);
209 // Disable completion.
210 $CFG->enablecompletion = false;
212 // Should still be able to delete completion events even when completion is disabled.
213 \core_completion\api::update_completion_date_event($assign->cmid, 'assign', $assign, null);
215 // Check that there is now no event in the database.
216 $this->assertEquals(0, $DB->count_records('event'));
220 * Test for mark_course_completions_activity_criteria().
222 public function test_mark_course_completions_activity_criteria(): void {
223 global $DB, $CFG;
224 require_once($CFG->dirroot.'/completion/criteria/completion_criteria_activity.php');
225 $this->resetAfterTest(true);
227 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
228 $student1 = $this->getDataGenerator()->create_user();
229 $student2 = $this->getDataGenerator()->create_user();
231 $teacher = $this->getDataGenerator()->create_user();
232 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
233 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
235 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
236 $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
237 $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
239 $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id),
240 array('completion' => 1));
241 $cmdata = get_coursemodule_from_id('data', $data->cmid);
242 $cm = get_coursemodule_from_instance('data', $data->id);
243 $c = new \completion_info($course);
245 // Add activity completion criteria.
246 $criteriadata = new \stdClass();
247 $criteriadata->id = $course->id;
248 $criteriadata->criteria_activity = array();
249 // Some activities.
250 $criteriadata->criteria_activity[$cmdata->id] = 1;
251 $criterion = new \completion_criteria_activity();
252 $criterion->update_config($criteriadata);
254 $this->setUser($teacher);
256 // Mark activity complete for both users.
257 $completion = new \stdClass();
258 $completion->coursemoduleid = $cm->id;
259 $completion->completionstate = COMPLETION_COMPLETE;
260 $completion->timemodified = time();
261 $completion->viewed = COMPLETION_NOT_VIEWED;
262 $completion->overrideby = null;
264 $completion->id = 0;
265 $completion->userid = $student1->id;
266 $c->internal_set_data($cm, $completion, true);
268 $completion->id = 0;
269 $completion->userid = $student2->id;
270 $c->internal_set_data($cm, $completion, true);
272 // Run instant course completions for student1. Only student1 will be marked as completed a course.
273 $userdata = ['userid' => $student1->id, 'courseid' => $course->id];
274 $actual = $DB->get_records('course_completions');
275 $this->assertEmpty($actual);
277 $coursecompletionid = \core_completion\api::mark_course_completions_activity_criteria($userdata);
279 $actual = $DB->get_records('course_completions');
280 $this->assertEquals(reset($actual)->id, $coursecompletionid);
281 $this->assertEquals(1, count($actual));
282 $this->assertEquals($student1->id, reset($actual)->userid);
284 // Run course completions cron. Both students will be marked as completed a course.
285 $coursecompletionid = \core_completion\api::mark_course_completions_activity_criteria();
286 $this->assertEquals(0, $coursecompletionid);
287 $actual = $DB->get_records('course_completions');
288 $students = [$student1->id, $student2->id];
289 $this->assertEquals(2, count($actual));
290 $this->assertContains(reset($actual)->userid, $students);
291 $this->assertContains(end($actual)->userid, $students);
295 * Test for mark_course_completions_activity_criteria() with different completionpassgrade settings.
297 public function test_mark_course_completions_activity_criteria_completion_states(): void {
298 global $DB, $CFG;
299 require_once($CFG->dirroot . '/completion/criteria/completion_criteria_activity.php');
300 $this->resetAfterTest(true);
302 $courses[] = $this->getDataGenerator()->create_course(['shortname' => 'completionpassgradenotset',
303 'enablecompletion' => 1]);
304 $courses[] = $this->getDataGenerator()->create_course(['shortname' => 'completionpassgradeset',
305 'enablecompletion' => 1]);
307 $student1 = $this->getDataGenerator()->create_user();
308 $student2 = $this->getDataGenerator()->create_user();
310 $teacher = $this->getDataGenerator()->create_user();
311 $studentrole = $DB->get_record('role', array('shortname' => 'student'));
312 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher'));
314 foreach ($courses as $course) {
315 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id);
316 $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id);
317 $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id);
319 $completioncriteria = [
320 'completionusegrade' => 1,
321 'gradepass' => 50
324 if ($course->shortname == 'completionpassgradeset') {
325 $completioncriteria['completionpassgrade'] = 1;
328 /** @var \mod_assign_generator $assigngenerator */
329 $assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
330 $assign = $assigngenerator->create_instance([
331 'course' => $course->id,
332 'completion' => COMPLETION_ENABLED,
333 ] + $completioncriteria);
335 $cmassing = get_coursemodule_from_id('assign', $assign->cmid);
336 $cm = get_coursemodule_from_instance('assign', $assign->id);
337 $c = new \completion_info($course);
339 // Add activity completion criteria.
340 $criteriadata = new \stdClass();
341 $criteriadata->id = $course->id;
342 $criteriadata->criteria_activity = array();
343 // Some activities.
344 $criteriadata->criteria_activity[$cmassing->id] = 1;
345 $criterion = new \completion_criteria_activity();
346 $criterion->update_config($criteriadata);
348 $this->setUser($teacher);
350 // Mark user completions.
351 $completion = new \stdClass();
352 $completion->coursemoduleid = $cm->id;
353 $completion->timemodified = time();
354 $completion->viewed = COMPLETION_NOT_VIEWED;
355 $completion->overrideby = null;
357 // Student1 achieved passgrade.
358 $completion->id = 0;
359 $completion->completionstate = COMPLETION_COMPLETE_PASS;
360 $completion->userid = $student1->id;
361 $c->internal_set_data($cm, $completion, true);
363 // Student2 has not achieved passgrade.
364 $completion->id = 0;
365 $completion->completionstate = COMPLETION_COMPLETE_FAIL;
366 $completion->userid = $student2->id;
367 $c->internal_set_data($cm, $completion, true);
369 $actual = $DB->get_records('course_completions', ['course' => $course->id]);
370 $this->assertEmpty($actual);
372 // Run course completions cron.
373 $coursecompletionid = \core_completion\api::mark_course_completions_activity_criteria();
374 $this->assertEquals(0, $coursecompletionid);
375 $actual = $DB->get_records('course_completions', ['course' => $course->id]);
377 if ($course->shortname == 'completionpassgradeset') {
378 // Only student1 has completed a course.
379 $this->assertEquals(1, count($actual));
380 $this->assertEquals($student1->id, reset($actual)->userid);
381 } else {
382 // Both students completed a course.
383 $students = [$student1->id, $student2->id];
384 $this->assertEquals(2, count($actual));
385 $this->assertContains(reset($actual)->userid, $students);
386 $this->assertContains(end($actual)->userid, $students);