MDL-37624 calendar: Added location support
[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'));
103         $this->assertEquals($dbrow->location, $event->get_location());
105         if ($dbrow->groupid == 0) {
106             $this->assertNull($event->get_group());
107         } else {
108             $this->assertEquals($dbrow->groupid, $event->get_group()->get('id'));
109         }
111         $this->assertEquals($dbrow->userid, $event->get_user()->get('id'));
112         $this->assertEquals(null, $event->get_repeats());
113         $this->assertEquals($dbrow->modulename, $event->get_course_module()->get('modname'));
114         $this->assertEquals($dbrow->instance, $event->get_course_module()->get('instance'));
115         $this->assertEquals($dbrow->timestart, $event->get_times()->get_start_time()->getTimestamp());
116         $this->assertEquals($dbrow->timemodified, $event->get_times()->get_modified_time()->getTimestamp());
117         $this->assertEquals($dbrow->timesort, $event->get_times()->get_sort_time()->getTimestamp());
119         if ($dbrow->visible == 1) {
120             $this->assertTrue($event->is_visible());
121         } else {
122             $this->assertFalse($event->is_visible());
123         }
125         if (!$dbrow->subscriptionid) {
126             $this->assertNull($event->get_subscription());
127         } else {
128             $this->assertEquals($event->get_subscription()->get('id'));
129         }
130     }
132     /**
133      * Test that the event factory deals with invisible modules properly as admin.
134      *
135      * @dataProvider get_event_factory_testcases()
136      * @param \stdClass $dbrow Row from the "database".
137      */
138     public function test_event_factory_when_module_visibility_is_toggled_as_admin($dbrow) {
139         $legacyevent = $this->create_event($dbrow);
140         $factory = \core_calendar\local\event\container::get_event_factory();
141         $course = $this->getDataGenerator()->create_course();
142         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
143         $moduleinstance = $generator->create_instance(['course' => $course->id]);
145         $dbrow->id = $legacyevent->id;
146         $dbrow->courseid = $course->id;
147         $dbrow->instance = $moduleinstance->id;
148         $dbrow->modulename = 'assign';
150         set_coursemodule_visible($moduleinstance->cmid, 0);
152         $event = $factory->create_instance($dbrow);
154         // Test that the factory is returning an event as the admin can see hidden course modules.
155         $this->assertInstanceOf(event_interface::class, $event);
156     }
158     /**
159      * Test that the event factory deals with invisible modules properly as a guest.
160      *
161      * @dataProvider get_event_factory_testcases()
162      * @param \stdClass $dbrow Row from the "database".
163      */
164     public function test_event_factory_when_module_visibility_is_toggled_as_guest($dbrow) {
165         $legacyevent = $this->create_event($dbrow);
166         $factory = \core_calendar\local\event\container::get_event_factory();
167         $course = $this->getDataGenerator()->create_course();
168         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
169         $moduleinstance = $generator->create_instance(['course' => $course->id]);
171         $dbrow->id = $legacyevent->id;
172         $dbrow->courseid = $course->id;
173         $dbrow->instance = $moduleinstance->id;
174         $dbrow->modulename = 'assign';
176         set_coursemodule_visible($moduleinstance->cmid, 0);
178         // Set to a user who can not view hidden course modules.
179         $this->setGuestUser();
181         $event = $factory->create_instance($dbrow);
183         // Module is invisible to guest users so this should return null.
184         $this->assertNull($event);
185     }
187     /**
188      * Test that the event factory deals with invisible courses as an admin.
189      *
190      * @dataProvider get_event_factory_testcases()
191      * @param \stdClass $dbrow Row from the "database".
192      */
193     public function test_event_factory_when_course_visibility_is_toggled_as_admin($dbrow) {
194         $legacyevent = $this->create_event($dbrow);
195         $factory = \core_calendar\local\event\container::get_event_factory();
197         // Create a hidden course with an assignment.
198         $course = $this->getDataGenerator()->create_course(['visible' => 0]);
199         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
200         $moduleinstance = $generator->create_instance(['course' => $course->id]);
202         $dbrow->id = $legacyevent->id;
203         $dbrow->courseid = $course->id;
204         $dbrow->instance = $moduleinstance->id;
205         $dbrow->modulename = 'assign';
206         $event = $factory->create_instance($dbrow);
208         // Module is still visible to admins even if the course is invisible.
209         $this->assertInstanceOf(event_interface::class, $event);
210     }
212     /**
213      * Test that the event factory deals with invisible courses as a student.
214      *
215      * @dataProvider get_event_factory_testcases()
216      * @param \stdClass $dbrow Row from the "database".
217      */
218     public function test_event_factory_when_course_visibility_is_toggled_as_student($dbrow) {
219         $legacyevent = $this->create_event($dbrow);
220         $factory = \core_calendar\local\event\container::get_event_factory();
222         // Create a hidden course with an assignment.
223         $course = $this->getDataGenerator()->create_course(['visible' => 0]);
224         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
225         $moduleinstance = $generator->create_instance(['course' => $course->id]);
227         // Enrol a student into this course.
228         $student = $this->getDataGenerator()->create_user();
229         $this->getDataGenerator()->enrol_user($student->id, $course->id);
231         // Set the user to the student.
232         $this->setUser($student);
234         $dbrow->id = $legacyevent->id;
235         $dbrow->courseid = $course->id;
236         $dbrow->instance = $moduleinstance->id;
237         $dbrow->modulename = 'assign';
238         $event = $factory->create_instance($dbrow);
240         // Module is invisible to students if the course is invisible.
241         $this->assertNull($event);
242     }
244     /**
245      * Test that the event factory deals with invisible categorys as an admin.
246      */
247     public function test_event_factory_when_category_visibility_is_toggled_as_admin() {
248         // Create a hidden category.
249         $category = $this->getDataGenerator()->create_category(['visible' => 0]);
251         $eventdata = [
252                 'categoryid' => $category->id,
253                 'eventtype' => 'category',
254             ];
255         $legacyevent = $this->create_event($eventdata);
257         $dbrow = $this->get_dbrow_from_skeleton((object) $eventdata);
258         $dbrow->id = $legacyevent->id;
260         $factory = \core_calendar\local\event\container::get_event_factory();
261         $event = $factory->create_instance($dbrow);
263         // Module is still visible to admins even if the category is invisible.
264         $this->assertInstanceOf(event_interface::class, $event);
265     }
267     /**
268      * Test that the event factory deals with invisible categorys as an user.
269      */
270     public function test_event_factory_when_category_visibility_is_toggled_as_user() {
271         // Create a hidden category.
272         $category = $this->getDataGenerator()->create_category(['visible' => 0]);
274         $eventdata = [
275                 'categoryid' => $category->id,
276                 'eventtype' => 'category',
277             ];
278         $legacyevent = $this->create_event($eventdata);
280         $dbrow = $this->get_dbrow_from_skeleton((object) $eventdata);
281         $dbrow->id = $legacyevent->id;
283         // Use a standard user.
284         $user = $this->getDataGenerator()->create_user();
286         // Set the user to the student.
287         $this->setUser($user);
289         $factory = \core_calendar\local\event\container::get_event_factory();
290         $event = $factory->create_instance($dbrow);
292         // Module is invisible to non-privileged users.
293         $this->assertNull($event);
294     }
296     /**
297      * Test that the event factory deals with invisible categorys as an guest.
298      */
299     public function test_event_factory_when_category_visibility_is_toggled_as_guest() {
300         // Create a hidden category.
301         $category = $this->getDataGenerator()->create_category(['visible' => 0]);
303         $eventdata = [
304                 'categoryid' => $category->id,
305                 'eventtype' => 'category',
306             ];
307         $legacyevent = $this->create_event($eventdata);
309         $dbrow = $this->get_dbrow_from_skeleton((object) $eventdata);
310         $dbrow->id = $legacyevent->id;
312         // Set the user to the student.
313         $this->setGuestUser();
315         $factory = \core_calendar\local\event\container::get_event_factory();
316         $event = $factory->create_instance($dbrow);
318         // Module is invisible to guests.
319         $this->assertNull($event);
320     }
322     /**
323      * Test that the event factory deals with completion related events properly.
324      */
325     public function test_event_factory_with_completion_related_event() {
326         global $CFG;
328         $CFG->enablecompletion = true;
330         // Create the course we will be using.
331         $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
333         // Add the assignment.
334         $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
335         $assign = $generator->create_instance(array('course' => $course->id), array('completion' => 1));
337         // Create a completion event.
338         $event = new \stdClass();
339         $event->name = 'An event';
340         $event->description = 'Event description';
341         $event->location = 'Event location';
342         $event->format = FORMAT_HTML;
343         $event->eventtype = \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED;
344         $event->userid = 1;
345         $event->modulename = 'assign';
346         $event->instance = $assign->id;
347         $event->categoryid = 0;
348         $event->courseid = $course->id;
349         $event->groupid = 0;
350         $event->timestart = time();
351         $event->timesort = time();
352         $event->timemodified = time();
353         $event->timeduration = 0;
354         $event->subscriptionid = null;
355         $event->repeatid = 0;
356         $legacyevent = $this->create_event($event);
358         // Update the id of the event that was created.
359         $event->id = $legacyevent->id;
361         // Create the factory we are going to be testing the behaviour of.
362         $factory = \core_calendar\local\event\container::get_event_factory();
364         // Check that we get the correct instance.
365         $this->assertInstanceOf(event_interface::class, $factory->create_instance($event));
367         // Now, disable completion.
368         $CFG->enablecompletion = false;
370         // The result should now be null since we have disabled completion.
371         $this->assertNull($factory->create_instance($event));
372     }
374     /**
375      * Test that the event factory only returns an event if the logged in user
376      * is enrolled in the course.
377      */
378     public function test_event_factory_unenrolled_user() {
379         $user = $this->getDataGenerator()->create_user();
380         // Create the course we will be using.
381         $course = $this->getDataGenerator()->create_course();
383         // Add the assignment.
384         $generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
385         $lesson = $generator->create_instance(array('course' => $course->id));
387         // Create a user override event for the lesson.
388         $event = new \stdClass();
389         $event->name = 'An event';
390         $event->description = 'Event description';
391         $event->location = 'Event location';
392         $event->format = FORMAT_HTML;
393         $event->eventtype = 'close';
394         $event->userid = $user->id;
395         $event->modulename = 'lesson';
396         $event->instance = $lesson->id;
397         $event->categoryid = 0;
398         $event->courseid = $course->id;
399         $event->groupid = 0;
400         $event->timestart = time();
401         $event->timesort = time();
402         $event->timemodified = time();
403         $event->timeduration = 0;
404         $event->subscriptionid = null;
405         $event->repeatid = 0;
406         $legacyevent = $this->create_event($event);
408         // Update the id of the event that was created.
409         $event->id = $legacyevent->id;
411         // Set the logged in user to the one we created.
412         $this->setUser($user);
414         // Create the factory we are going to be testing the behaviour of.
415         $factory = \core_calendar\local\event\container::get_event_factory();
417         // The result should be null since the user is not enrolled in the
418         // course the event is for.
419         $this->assertNull($factory->create_instance($event));
421         // Now enrol the user in the course.
422         $this->getDataGenerator()->enrol_user($user->id, $course->id);
424         // Check that we get the correct instance.
425         $this->assertInstanceOf(event_interface::class, $factory->create_instance($event));
426     }
428     /**
429      * Test that when course module is deleted all events are also deleted.
430      */
431     public function test_delete_module_delete_events() {
432         global $DB;
433         $user = $this->getDataGenerator()->create_user();
434         // Create the course we will be using.
435         $course = $this->getDataGenerator()->create_course();
436         $group = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
438         foreach (core_component::get_plugin_list('mod') as $modname => $unused) {
439             try {
440                 $generator = $this->getDataGenerator()->get_plugin_generator('mod_'.$modname);
441             } catch (coding_exception $e) {
442                 // Module generator is not implemented.
443                 continue;
444             }
445             $module = $generator->create_instance(['course' => $course->id]);
447             // Create bunch of events of different type (user override, group override, module event).
448             $this->create_event(['userid' => $user->id, 'modulename' => $modname, 'instance' => $module->id]);
449             $this->create_event(['groupid' => $group->id, 'modulename' => $modname, 'instance' => $module->id]);
450             $this->create_event(['modulename' => $modname, 'instance' => $module->id]);
451             $this->create_event(['modulename' => $modname, 'instance' => $module->id, 'courseid' => $course->id]);
453             // Delete module and make sure all events are deleted.
454             course_delete_module($module->cmid);
455             $this->assertEmpty($DB->get_record('event', ['modulename' => $modname, 'instance' => $module->id]));
456         }
457     }
459     /**
460      * Test getting the event mapper.
461      */
462     public function test_get_event_mapper() {
463         $mapper = \core_calendar\local\event\container::get_event_mapper();
465         $this->assertInstanceOf(event_mapper_interface::class, $mapper);
466         $this->assertInstanceOf(event_mapper::class, $mapper);
468         $mapper2 = \core_calendar\local\event\container::get_event_mapper();
470         $this->assertTrue($mapper === $mapper2);
471     }
473     /**
474      * Test cases for the get event factory test.
475      */
476     public function get_event_factory_testcases() {
477         return [
478             'Data set 1' => [
479                 'dbrow' => (object)[
480                     'name' => 'Test event',
481                     'description' => 'Hello',
482                     'format' => 1,
483                     'categoryid' => 0,
484                     'courseid' => 1,
485                     'groupid' => 0,
486                     'userid' => 1,
487                     'repeatid' => 0,
488                     'modulename' => 'assign',
489                     'instance' => 2,
490                     'eventtype' => 'due',
491                     'timestart' => 1486396800,
492                     'timeduration' => 0,
493                     'timesort' => 1486396800,
494                     'visible' => 1,
495                     'timemodified' => 1485793098,
496                     'subscriptionid' => null,
497                     'location' => 'Test location',
498                 ]
499             ],
501             'Data set 2' => [
502                 'dbrow' => (object)[
503                     'name' => 'Test event',
504                     'description' => 'Hello',
505                     'format' => 1,
506                     'categoryid' => 0,
507                     'courseid' => 1,
508                     'groupid' => 1,
509                     'userid' => 1,
510                     'repeatid' => 0,
511                     'modulename' => 'assign',
512                     'instance' => 2,
513                     'eventtype' => 'due',
514                     'timestart' => 1486396800,
515                     'timeduration' => 0,
516                     'timesort' => 1486396800,
517                     'visible' => 1,
518                     'timemodified' => 1485793098,
519                     'subscriptionid' => null,
520                     'location' => 'Test location',
521                 ]
522             ]
523         ];
524     }
526     /**
527      * Helper function to create calendar events using the old code.
528      *
529      * @param array $properties A list of calendar event properties to set
530      * @return calendar_event|bool
531      */
532     protected function create_event($properties = []) {
533         $record = new \stdClass();
534         $record->name = 'event name';
535         $record->eventtype = 'global';
536         $record->timestart = time();
537         $record->timeduration = 0;
538         $record->timesort = 0;
539         $record->type = 1;
540         $record->courseid = 0;
541         $record->categoryid = 0;
543         foreach ($properties as $name => $value) {
544             $record->$name = $value;
545         }
547         $event = new calendar_event($record);
548         return $event->create($record, false);
549     }
551     /**
552      * Pad out a basic DB row with basic information.
553      *
554      * @param   \stdClass   $skeleton the current skeleton
555      * @return  \stdClass
556      */
557     protected function get_dbrow_from_skeleton($skeleton) {
558         $dbrow = (object) [
559             'name' => 'Name',
560             'description' => 'Description',
561             'format' => 1,
562             'categoryid' => 0,
563             'courseid' => 0,
564             'groupid' => 0,
565             'userid' => 0,
566             'repeatid' => 0,
567             'modulename' => '',
568             'instance' => 0,
569             'eventtype' => 'user',
570             'timestart' => 1486396800,
571             'timeduration' => 0,
572             'timesort' => 1486396800,
573             'visible' => 1,
574             'timemodified' => 1485793098,
575             'subscriptionid' => null,
576             'location' => 'Test location',
577         ];
579         foreach ((array) $skeleton as $key => $value) {
580             $dbrow->$key = $value;
581         }
583         return $dbrow;
584     }