e21692eba5a6f645f0ef851b2113ab517f5af3ec
[moodle.git] / calendar / tests / container_test.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Event container tests.
19  *
20  * @package    core_calendar
21  * @copyright  2017 Cameron Ball <cameron@cameron1729.xyz>
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die();
27 global $CFG;
29 require_once($CFG->dirroot . '/calendar/lib.php');
31 use core_calendar\local\event\entities\action_event;
32 use core_calendar\local\event\entities\event;
33 use core_calendar\local\event\entities\event_interface;
34 use core_calendar\local\event\factories\event_factory;
35 use core_calendar\local\event\factories\event_factory_interface;
36 use core_calendar\local\event\mappers\event_mapper;
37 use core_calendar\local\event\mappers\event_mapper_interface;
39 /**
40  * Core container testcase.
41  *
42  * @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
43  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
44  */
45 class core_calendar_container_testcase extends advanced_testcase {
47     /**
48      * Test setup.
49      */
50     public function setUp() {
51         $this->resetAfterTest();
52         $this->setAdminUser();
53     }
55     /**
56      * Test getting the event factory.
57      */
58     public function test_get_event_factory() {
59         $factory = \core_calendar\local\event\container::get_event_factory();
61         // Test that the container is returning the right type.
62         $this->assertInstanceOf(event_factory_interface::class, $factory);
63         // Test that the container is returning the right implementation.
64         $this->assertInstanceOf(event_factory::class, $factory);
66         // Test that getting the factory a second time returns the same instance.
67         $factory2 = \core_calendar\local\event\container::get_event_factory();
68         $this->assertTrue($factory === $factory2);
69     }
71     /**
72      * Test that the event factory correctly creates instances of events.
73      *
74      * @dataProvider get_event_factory_testcases()
75      * @param \stdClass $dbrow Row from the "database".
76      */
77     public function test_event_factory_create_instance($dbrow) {
78         $legacyevent = $this->create_event($dbrow);
79         $factory = \core_calendar\local\event\container::get_event_factory();
80         $course = $this->getDataGenerator()->create_course();
81         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
82         $moduleinstance = $generator->create_instance(['course' => $course->id]);
84         // Set some of the fake dbrow properties to match real data in the DB
85         // this is necessary as the factory hides things that modinfo doesn't
86         // know about.
87         $dbrow->id = $legacyevent->id;
88         $dbrow->courseid = $course->id;
89         $dbrow->instance = $moduleinstance->id;
90         $dbrow->modulename = 'assign';
91         $event = $factory->create_instance($dbrow);
93         // Test that the factory is returning the right type.
94         $this->assertInstanceOf(event_interface::class, $event);
95         // Test that the factory is returning the right implementation.
96         $this->assertTrue($event instanceof event || $event instanceof action_event);
98         // Test that the event created has the correct properties.
99         $this->assertEquals($legacyevent->id, $event->get_id());
100         $this->assertEquals($dbrow->description, $event->get_description()->get_value());
101         $this->assertEquals($dbrow->format, $event->get_description()->get_format());
102         $this->assertEquals($dbrow->courseid, $event->get_course()->get('id'));
104         if ($dbrow->groupid == 0) {
105             $this->assertNull($event->get_group());
106         } else {
107             $this->assertEquals($dbrow->groupid, $event->get_group()->get('id'));
108         }
110         $this->assertEquals($dbrow->userid, $event->get_user()->get('id'));
111         $this->assertEquals($legacyevent->id, $event->get_repeats()->get_id());
112         $this->assertEquals($dbrow->modulename, $event->get_course_module()->get('modname'));
113         $this->assertEquals($dbrow->instance, $event->get_course_module()->get('instance'));
114         $this->assertEquals($dbrow->timestart, $event->get_times()->get_start_time()->getTimestamp());
115         $this->assertEquals($dbrow->timemodified, $event->get_times()->get_modified_time()->getTimestamp());
116         $this->assertEquals($dbrow->timesort, $event->get_times()->get_sort_time()->getTimestamp());
118         if ($dbrow->visible == 1) {
119             $this->assertTrue($event->is_visible());
120         } else {
121             $this->assertFalse($event->is_visible());
122         }
124         if (!$dbrow->subscriptionid) {
125             $this->assertNull($event->get_subscription());
126         } else {
127             $this->assertEquals($event->get_subscription()->get('id'));
128         }
129     }
131     /**
132      * Test that the event factory deals with invisible modules properly as admin.
133      *
134      * @dataProvider get_event_factory_testcases()
135      * @param \stdClass $dbrow Row from the "database".
136      */
137     public function test_event_factory_when_module_visibility_is_toggled_as_admin($dbrow) {
138         $legacyevent = $this->create_event($dbrow);
139         $factory = \core_calendar\local\event\container::get_event_factory();
140         $course = $this->getDataGenerator()->create_course();
141         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
142         $moduleinstance = $generator->create_instance(['course' => $course->id]);
144         $dbrow->id = $legacyevent->id;
145         $dbrow->courseid = $course->id;
146         $dbrow->instance = $moduleinstance->id;
147         $dbrow->modulename = 'assign';
149         set_coursemodule_visible($moduleinstance->cmid, 0);
151         $event = $factory->create_instance($dbrow);
153         // Test that the factory is returning an event as the admin can see hidden course modules.
154         $this->assertInstanceOf(event_interface::class, $event);
155     }
157     /**
158      * Test that the event factory deals with invisible modules properly as a guest.
159      *
160      * @dataProvider get_event_factory_testcases()
161      * @param \stdClass $dbrow Row from the "database".
162      */
163     public function test_event_factory_when_module_visibility_is_toggled_as_guest($dbrow) {
164         $legacyevent = $this->create_event($dbrow);
165         $factory = \core_calendar\local\event\container::get_event_factory();
166         $course = $this->getDataGenerator()->create_course();
167         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
168         $moduleinstance = $generator->create_instance(['course' => $course->id]);
170         $dbrow->id = $legacyevent->id;
171         $dbrow->courseid = $course->id;
172         $dbrow->instance = $moduleinstance->id;
173         $dbrow->modulename = 'assign';
175         set_coursemodule_visible($moduleinstance->cmid, 0);
177         // Set to a user who can not view hidden course modules.
178         $this->setGuestUser();
180         $event = $factory->create_instance($dbrow);
182         // Module is invisible to guest users so this should return null.
183         $this->assertNull($event);
184     }
186     /**
187      * Test that the event factory deals with invisible courses as an admin.
188      *
189      * @dataProvider get_event_factory_testcases()
190      * @param \stdClass $dbrow Row from the "database".
191      */
192     public function test_event_factory_when_course_visibility_is_toggled_as_admin($dbrow) {
193         $legacyevent = $this->create_event($dbrow);
194         $factory = \core_calendar\local\event\container::get_event_factory();
196         // Create a hidden course with an assignment.
197         $course = $this->getDataGenerator()->create_course(['visible' => 0]);
198         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
199         $moduleinstance = $generator->create_instance(['course' => $course->id]);
201         $dbrow->id = $legacyevent->id;
202         $dbrow->courseid = $course->id;
203         $dbrow->instance = $moduleinstance->id;
204         $dbrow->modulename = 'assign';
205         $event = $factory->create_instance($dbrow);
207         // Module is still visible to admins even if the course is invisible.
208         $this->assertInstanceOf(event_interface::class, $event);
209     }
211     /**
212      * Test that the event factory deals with invisible courses as a student.
213      *
214      * @dataProvider get_event_factory_testcases()
215      * @param \stdClass $dbrow Row from the "database".
216      */
217     public function test_event_factory_when_course_visibility_is_toggled_as_student($dbrow) {
218         $legacyevent = $this->create_event($dbrow);
219         $factory = \core_calendar\local\event\container::get_event_factory();
221         // Create a hidden course with an assignment.
222         $course = $this->getDataGenerator()->create_course(['visible' => 0]);
223         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
224         $moduleinstance = $generator->create_instance(['course' => $course->id]);
226         // Enrol a student into this course.
227         $student = $this->getDataGenerator()->create_user();
228         $this->getDataGenerator()->enrol_user($student->id, $course->id);
230         // Set the user to the student.
231         $this->setUser($student);
233         $dbrow->id = $legacyevent->id;
234         $dbrow->courseid = $course->id;
235         $dbrow->instance = $moduleinstance->id;
236         $dbrow->modulename = 'assign';
237         $event = $factory->create_instance($dbrow);
239         // Module is invisible to students if the course is invisible.
240         $this->assertNull($event);
241     }
243     /**
244      * Test that the event factory deals with completion related events properly.
245      */
246     public function test_event_factory_with_completion_related_event() {
247         global $CFG;
249         $CFG->enablecompletion = true;
251         // Create the course we will be using.
252         $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
254         // Add the assignment.
255         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
256         $assign = $generator->create_instance(array('course' => $course->id), array('completion' => 1));
258         // Create a completion event.
259         $event = new \stdClass();
260         $event->name = 'An event';
261         $event->description = 'Event description';
262         $event->format = FORMAT_HTML;
263         $event->eventtype = \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED;
264         $event->userid = 1;
265         $event->modulename = 'assign';
266         $event->instance = $assign->id;
267         $event->courseid = $course->id;
268         $event->groupid = 0;
269         $event->timestart = time();
270         $event->timesort = time();
271         $event->timemodified = time();
272         $event->timeduration = 0;
273         $event->subscriptionid = null;
274         $event->repeatid = 0;
275         $legacyevent = $this->create_event($event);
277         // Update the id of the event that was created.
278         $event->id = $legacyevent->id;
280         // Create the factory we are going to be testing the behaviour of.
281         $factory = \core_calendar\local\event\container::get_event_factory();
283         // Check that we get the correct instance.
284         $this->assertInstanceOf(event_interface::class, $factory->create_instance($event));
286         // Now, disable completion.
287         $CFG->enablecompletion = false;
289         // The result should now be null since we have disabled completion.
290         $this->assertNull($factory->create_instance($event));
291     }
293     /**
294      * Test that the event factory only returns an event if the logged in user
295      * is enrolled in the course.
296      */
297     public function test_event_factory_unenrolled_user() {
298         $user = $this->getDataGenerator()->create_user();
299         // Create the course we will be using.
300         $course = $this->getDataGenerator()->create_course();
302         // Add the assignment.
303         $generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
304         $lesson = $generator->create_instance(array('course' => $course->id));
306         // Create a user override event for the lesson.
307         $event = new \stdClass();
308         $event->name = 'An event';
309         $event->description = 'Event description';
310         $event->format = FORMAT_HTML;
311         $event->eventtype = 'close';
312         $event->userid = $user->id;
313         $event->modulename = 'lesson';
314         $event->instance = $lesson->id;
315         $event->courseid = $course->id;
316         $event->groupid = 0;
317         $event->timestart = time();
318         $event->timesort = time();
319         $event->timemodified = time();
320         $event->timeduration = 0;
321         $event->subscriptionid = null;
322         $event->repeatid = 0;
323         $legacyevent = $this->create_event($event);
325         // Update the id of the event that was created.
326         $event->id = $legacyevent->id;
328         // Set the logged in user to the one we created.
329         $this->setUser($user);
331         // Create the factory we are going to be testing the behaviour of.
332         $factory = \core_calendar\local\event\container::get_event_factory();
334         // The result should be null since the user is not enrolled in the
335         // course the event is for.
336         $this->assertNull($factory->create_instance($event));
338         // Now enrol the user in the course.
339         $this->getDataGenerator()->enrol_user($user->id, $course->id);
341         // Check that we get the correct instance.
342         $this->assertInstanceOf(event_interface::class, $factory->create_instance($event));
343     }
345     /**
346      * Test that when course module is deleted all events are also deleted.
347      */
348     public function test_delete_module_delete_events() {
349         global $DB;
350         $user = $this->getDataGenerator()->create_user();
351         // Create the course we will be using.
352         $course = $this->getDataGenerator()->create_course();
353         $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
355         foreach (core_component::get_plugin_list('mod') as $modname => $unused) {
356             try {
357                 $generator = $this->getDataGenerator()->get_plugin_generator('mod_'.$modname);
358             } catch (coding_exception $e) {
359                 // Module generator is not implemented.
360                 continue;
361             }
362             $module = $generator->create_instance(['course' => $course->id]);
364             // Create bunch of events of different type (user override, group override, module event).
365             $this->create_event(['userid' => $user->id, 'modulename' => $modname, 'instance' => $module->id]);
366             $this->create_event(['groupid' => $group->id, 'modulename' => $modname, 'instance' => $module->id]);
367             $this->create_event(['modulename' => $modname, 'instance' => $module->id]);
368             $this->create_event(['modulename' => $modname, 'instance' => $module->id, 'courseid' => $course->id]);
370             // Delete module and make sure all events are deleted.
371             course_delete_module($module->cmid);
372             $this->assertEmpty($DB->get_record('event', ['modulename' => $modname, 'instance' => $module->id]));
373         }
374     }
376     /**
377      * Test getting the event mapper.
378      */
379     public function test_get_event_mapper() {
380         $mapper = \core_calendar\local\event\container::get_event_mapper();
382         $this->assertInstanceOf(event_mapper_interface::class, $mapper);
383         $this->assertInstanceOf(event_mapper::class, $mapper);
385         $mapper2 = \core_calendar\local\event\container::get_event_mapper();
387         $this->assertTrue($mapper === $mapper2);
388     }
390     /**
391      * Test cases for the get event factory test.
392      */
393     public function get_event_factory_testcases() {
394         return [
395             'Data set 1' => [
396                 'dbrow' => (object)[
397                     'name' => 'Test event',
398                     'description' => 'Hello',
399                     'format' => 1,
400                     'courseid' => 1,
401                     'groupid' => 0,
402                     'userid' => 1,
403                     'repeatid' => 0,
404                     'modulename' => 'assign',
405                     'instance' => 2,
406                     'eventtype' => 'due',
407                     'timestart' => 1486396800,
408                     'timeduration' => 0,
409                     'timesort' => 1486396800,
410                     'visible' => 1,
411                     'timemodified' => 1485793098,
412                     'subscriptionid' => null
413                 ]
414             ],
416             'Data set 2' => [
417                 'dbrow' => (object)[
418                     'name' => 'Test event',
419                     'description' => 'Hello',
420                     'format' => 1,
421                     'courseid' => 1,
422                     'groupid' => 1,
423                     'userid' => 1,
424                     'repeatid' => 0,
425                     'modulename' => 'assign',
426                     'instance' => 2,
427                     'eventtype' => 'due',
428                     'timestart' => 1486396800,
429                     'timeduration' => 0,
430                     'timesort' => 1486396800,
431                     'visible' => 1,
432                     'timemodified' => 1485793098,
433                     'subscriptionid' => null
434                 ]
435             ]
436         ];
437     }
439     /**
440      * Helper function to create calendar events using the old code.
441      *
442      * @param array $properties A list of calendar event properties to set
443      * @return calendar_event|bool
444      */
445     protected function create_event($properties = []) {
446         $record = new \stdClass();
447         $record->name = 'event name';
448         $record->eventtype = 'global';
449         $record->timestart = time();
450         $record->timeduration = 0;
451         $record->timesort = 0;
452         $record->type = 1;
453         $record->courseid = 0;
455         foreach ($properties as $name => $value) {
456             $record->$name = $value;
457         }
459         $event = new calendar_event($record);
460         return $event->create($record, false);
461     }