MDL-37624 calendar: Added location support
[moodle.git] / calendar / tests / container_test.php
CommitLineData
5ca71c2d
CB
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/>.
16
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 */
24
25defined('MOODLE_INTERNAL') || die();
26
d7bc2c6f
MN
27global $CFG;
28
29require_once($CFG->dirroot . '/calendar/lib.php');
30
5ca71c2d
CB
31use core_calendar\local\event\entities\action_event;
32use core_calendar\local\event\entities\event;
42e76c3f 33use core_calendar\local\event\entities\event_interface;
5ca71c2d 34use core_calendar\local\event\factories\event_factory;
42e76c3f 35use core_calendar\local\event\factories\event_factory_interface;
5ca71c2d 36use core_calendar\local\event\mappers\event_mapper;
42e76c3f 37use core_calendar\local\event\mappers\event_mapper_interface;
5ca71c2d
CB
38
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 */
45class core_calendar_container_testcase extends advanced_testcase {
88d14007 46
5ca71c2d 47 /**
88d14007 48 * Test setup.
5ca71c2d 49 */
88d14007
MN
50 public function setUp() {
51 $this->resetAfterTest();
5ca71c2d 52 $this->setAdminUser();
88d14007 53 }
5ca71c2d 54
88d14007
MN
55 /**
56 * Test getting the event factory.
57 */
58 public function test_get_event_factory() {
d10693cb 59 $factory = \core_calendar\local\event\container::get_event_factory();
5ca71c2d 60
7aedfe32 61 // Test that the container is returning the right type.
5ca71c2d 62 $this->assertInstanceOf(event_factory_interface::class, $factory);
7aedfe32 63 // Test that the container is returning the right implementation.
5ca71c2d
CB
64 $this->assertInstanceOf(event_factory::class, $factory);
65
7aedfe32 66 // Test that getting the factory a second time returns the same instance.
d10693cb 67 $factory2 = \core_calendar\local\event\container::get_event_factory();
5ca71c2d 68 $this->assertTrue($factory === $factory2);
7aedfe32 69 }
5ca71c2d 70
7aedfe32
CB
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) {
7aedfe32 78 $legacyevent = $this->create_event($dbrow);
d10693cb 79 $factory = \core_calendar\local\event\container::get_event_factory();
7aedfe32
CB
80 $course = $this->getDataGenerator()->create_course();
81 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
82 $moduleinstance = $generator->create_instance(['course' => $course->id]);
83
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.
5ca71c2d 87 $dbrow->id = $legacyevent->id;
7aedfe32
CB
88 $dbrow->courseid = $course->id;
89 $dbrow->instance = $moduleinstance->id;
90 $dbrow->modulename = 'assign';
5ca71c2d
CB
91 $event = $factory->create_instance($dbrow);
92
7aedfe32 93 // Test that the factory is returning the right type.
5ca71c2d 94 $this->assertInstanceOf(event_interface::class, $event);
7aedfe32 95 // Test that the factory is returning the right implementation.
5ca71c2d
CB
96 $this->assertTrue($event instanceof event || $event instanceof action_event);
97
7aedfe32 98 // Test that the event created has the correct properties.
5ca71c2d
CB
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());
47a71017 102 $this->assertEquals($dbrow->courseid, $event->get_course()->get('id'));
ef4e04ee 103 $this->assertEquals($dbrow->location, $event->get_location());
5ca71c2d
CB
104
105 if ($dbrow->groupid == 0) {
106 $this->assertNull($event->get_group());
107 } else {
47a71017 108 $this->assertEquals($dbrow->groupid, $event->get_group()->get('id'));
5ca71c2d
CB
109 }
110
47a71017 111 $this->assertEquals($dbrow->userid, $event->get_user()->get('id'));
28852998 112 $this->assertEquals(null, $event->get_repeats());
5ca71c2d
CB
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());
118
119 if ($dbrow->visible == 1) {
120 $this->assertTrue($event->is_visible());
121 } else {
122 $this->assertFalse($event->is_visible());
123 }
124
125 if (!$dbrow->subscriptionid) {
126 $this->assertNull($event->get_subscription());
127 } else {
47a71017 128 $this->assertEquals($event->get_subscription()->get('id'));
5ca71c2d
CB
129 }
130 }
131
7aedfe32 132 /**
0c9dc98b 133 * Test that the event factory deals with invisible modules properly as admin.
7aedfe32
CB
134 *
135 * @dataProvider get_event_factory_testcases()
136 * @param \stdClass $dbrow Row from the "database".
137 */
20592f5f 138 public function test_event_factory_when_module_visibility_is_toggled_as_admin($dbrow) {
7aedfe32 139 $legacyevent = $this->create_event($dbrow);
d10693cb 140 $factory = \core_calendar\local\event\container::get_event_factory();
7aedfe32
CB
141 $course = $this->getDataGenerator()->create_course();
142 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
143 $moduleinstance = $generator->create_instance(['course' => $course->id]);
144
0c9dc98b
MN
145 $dbrow->id = $legacyevent->id;
146 $dbrow->courseid = $course->id;
147 $dbrow->instance = $moduleinstance->id;
148 $dbrow->modulename = 'assign';
149
150 set_coursemodule_visible($moduleinstance->cmid, 0);
151
152 $event = $factory->create_instance($dbrow);
153
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 }
157
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 */
20592f5f 164 public function test_event_factory_when_module_visibility_is_toggled_as_guest($dbrow) {
0c9dc98b 165 $legacyevent = $this->create_event($dbrow);
d10693cb 166 $factory = \core_calendar\local\event\container::get_event_factory();
0c9dc98b
MN
167 $course = $this->getDataGenerator()->create_course();
168 $generator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
169 $moduleinstance = $generator->create_instance(['course' => $course->id]);
170
171 $dbrow->id = $legacyevent->id;
7aedfe32
CB
172 $dbrow->courseid = $course->id;
173 $dbrow->instance = $moduleinstance->id;
174 $dbrow->modulename = 'assign';
175
176 set_coursemodule_visible($moduleinstance->cmid, 0);
0c9dc98b
MN
177
178 // Set to a user who can not view hidden course modules.
179 $this->setGuestUser();
180
7aedfe32 181 $event = $factory->create_instance($dbrow);
0c9dc98b
MN
182
183 // Module is invisible to guest users so this should return null.
7aedfe32
CB
184 $this->assertNull($event);
185 }
186
2fe7f706
MN
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();
196
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]);
201
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);
207
208 // Module is still visible to admins even if the course is invisible.
209 $this->assertInstanceOf(event_interface::class, $event);
210 }
211
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();
221
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]);
226
227 // Enrol a student into this course.
228 $student = $this->getDataGenerator()->create_user();
229 $this->getDataGenerator()->enrol_user($student->id, $course->id);
230
231 // Set the user to the student.
232 $this->setUser($student);
233
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);
239
240 // Module is invisible to students if the course is invisible.
241 $this->assertNull($event);
242 }
243
0085b0ea
AN
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]);
250
251 $eventdata = [
252 'categoryid' => $category->id,
253 'eventtype' => 'category',
254 ];
255 $legacyevent = $this->create_event($eventdata);
256
257 $dbrow = $this->get_dbrow_from_skeleton((object) $eventdata);
258 $dbrow->id = $legacyevent->id;
259
260 $factory = \core_calendar\local\event\container::get_event_factory();
261 $event = $factory->create_instance($dbrow);
262
263 // Module is still visible to admins even if the category is invisible.
264 $this->assertInstanceOf(event_interface::class, $event);
265 }
266
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]);
273
274 $eventdata = [
275 'categoryid' => $category->id,
276 'eventtype' => 'category',
277 ];
278 $legacyevent = $this->create_event($eventdata);
279
280 $dbrow = $this->get_dbrow_from_skeleton((object) $eventdata);
281 $dbrow->id = $legacyevent->id;
282
283 // Use a standard user.
284 $user = $this->getDataGenerator()->create_user();
285
286 // Set the user to the student.
287 $this->setUser($user);
288
289 $factory = \core_calendar\local\event\container::get_event_factory();
290 $event = $factory->create_instance($dbrow);
291
292 // Module is invisible to non-privileged users.
293 $this->assertNull($event);
294 }
295
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]);
302
303 $eventdata = [
304 'categoryid' => $category->id,
305 'eventtype' => 'category',
306 ];
307 $legacyevent = $this->create_event($eventdata);
308
309 $dbrow = $this->get_dbrow_from_skeleton((object) $eventdata);
310 $dbrow->id = $legacyevent->id;
311
312 // Set the user to the student.
313 $this->setGuestUser();
314
315 $factory = \core_calendar\local\event\container::get_event_factory();
316 $event = $factory->create_instance($dbrow);
317
318 // Module is invisible to guests.
319 $this->assertNull($event);
320 }
321
88d14007
MN
322 /**
323 * Test that the event factory deals with completion related events properly.
324 */
20592f5f 325 public function test_event_factory_with_completion_related_event() {
88d14007
MN
326 global $CFG;
327
328 $CFG->enablecompletion = true;
329
330 // Create the course we will be using.
331 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1));
332
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));
336
337 // Create a completion event.
338 $event = new \stdClass();
339 $event->name = 'An event';
340 $event->description = 'Event description';
ef4e04ee 341 $event->location = 'Event location';
88d14007
MN
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;
0085b0ea 347 $event->categoryid = 0;
88d14007
MN
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;
bd01fd15 355 $event->repeatid = 0;
88d14007
MN
356 $legacyevent = $this->create_event($event);
357
358 // Update the id of the event that was created.
359 $event->id = $legacyevent->id;
360
361 // Create the factory we are going to be testing the behaviour of.
d10693cb 362 $factory = \core_calendar\local\event\container::get_event_factory();
88d14007
MN
363
364 // Check that we get the correct instance.
365 $this->assertInstanceOf(event_interface::class, $factory->create_instance($event));
366
367 // Now, disable completion.
368 $CFG->enablecompletion = false;
369
370 // The result should now be null since we have disabled completion.
371 $this->assertNull($factory->create_instance($event));
372 }
373
405f8491
RW
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();
382
383 // Add the assignment.
384 $generator = $this->getDataGenerator()->get_plugin_generator('mod_lesson');
385 $lesson = $generator->create_instance(array('course' => $course->id));
386
387 // Create a user override event for the lesson.
388 $event = new \stdClass();
389 $event->name = 'An event';
390 $event->description = 'Event description';
ef4e04ee 391 $event->location = 'Event location';
405f8491
RW
392 $event->format = FORMAT_HTML;
393 $event->eventtype = 'close';
394 $event->userid = $user->id;
395 $event->modulename = 'lesson';
396 $event->instance = $lesson->id;
0085b0ea 397 $event->categoryid = 0;
405f8491
RW
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);
407
408 // Update the id of the event that was created.
409 $event->id = $legacyevent->id;
410
411 // Set the logged in user to the one we created.
412 $this->setUser($user);
413
414 // Create the factory we are going to be testing the behaviour of.
415 $factory = \core_calendar\local\event\container::get_event_factory();
416
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));
420
421 // Now enrol the user in the course.
422 $this->getDataGenerator()->enrol_user($user->id, $course->id);
423
424 // Check that we get the correct instance.
425 $this->assertInstanceOf(event_interface::class, $factory->create_instance($event));
426 }
427
70b62308
MG
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]);
437
438 foreach (core_component::get_plugin_list('mod') as $modname => $unused) {
5540c097
MG
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]);
70b62308
MG
446
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]);
452
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 }
458
5ca71c2d
CB
459 /**
460 * Test getting the event mapper.
461 */
462 public function test_get_event_mapper() {
d10693cb 463 $mapper = \core_calendar\local\event\container::get_event_mapper();
5ca71c2d
CB
464
465 $this->assertInstanceOf(event_mapper_interface::class, $mapper);
466 $this->assertInstanceOf(event_mapper::class, $mapper);
467
d10693cb 468 $mapper2 = \core_calendar\local\event\container::get_event_mapper();
5ca71c2d
CB
469
470 $this->assertTrue($mapper === $mapper2);
471 }
472
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,
0085b0ea 483 'categoryid' => 0,
5ca71c2d
CB
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,
ef4e04ee
MH
496 'subscriptionid' => null,
497 'location' => 'Test location',
5ca71c2d
CB
498 ]
499 ],
500
501 'Data set 2' => [
502 'dbrow' => (object)[
503 'name' => 'Test event',
504 'description' => 'Hello',
505 'format' => 1,
0085b0ea 506 'categoryid' => 0,
5ca71c2d
CB
507 'courseid' => 1,
508 'groupid' => 1,
509 'userid' => 1,
bd01fd15 510 'repeatid' => 0,
5ca71c2d
CB
511 'modulename' => 'assign',
512 'instance' => 2,
513 'eventtype' => 'due',
514 'timestart' => 1486396800,
515 'timeduration' => 0,
516 'timesort' => 1486396800,
517 'visible' => 1,
518 'timemodified' => 1485793098,
ef4e04ee
MH
519 'subscriptionid' => null,
520 'location' => 'Test location',
5ca71c2d
CB
521 ]
522 ]
523 ];
524 }
525
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
e1cd93ce 530 * @return calendar_event|bool
5ca71c2d
CB
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;
44ae0838 541 $record->categoryid = 0;
5ca71c2d
CB
542
543 foreach ($properties as $name => $value) {
544 $record->$name = $value;
545 }
546
e1cd93ce 547 $event = new calendar_event($record);
5ca71c2d
CB
548 return $event->create($record, false);
549 }
0085b0ea
AN
550
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,
ef4e04ee
MH
575 'subscriptionid' => null,
576 'location' => 'Test location',
0085b0ea
AN
577 ];
578
579 foreach ((array) $skeleton as $key => $value) {
580 $dbrow->$key = $value;
581 }
582
583 return $dbrow;
584 }
5ca71c2d 585}