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 * Raw event retrieval strategy 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
27global $CFG;
28require_once($CFG->dirroot . '/calendar/tests/helpers.php');
29
30use core_calendar\local\event\strategies\raw_event_retrieval_strategy;
31
32/**
33 * Raw event retrieval strategy testcase.
34 *
35 * @copyright 2017 Cameron Ball <cameron@cameron1729.xyz>
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37 */
38class core_calendar_raw_event_retrieval_strategy_testcase extends advanced_testcase {
39    /**
40     * Test retrieval strategy when module is disabled.
41     */
42    public function test_get_raw_events_with_disabled_module() {
43        global $DB;
44
45        $this->resetAfterTest();
46        $retrievalstrategy = new raw_event_retrieval_strategy();
47        $generator = $this->getDataGenerator();
48        $course = $generator->create_course();
49        $student = $generator->create_user();
50        $generator->enrol_user($student->id, $course->id, 'student');
51        $this->setUser($student);
52        $events = [
53            [
54                'name' => 'Start of assignment',
55                'description' => '',
56                'location' => 'Test',
57                'format' => 1,
58                'courseid' => $course->id,
59                'groupid' => 0,
60                'userid' => 2,
61                'modulename' => 'assign',
62                'instance' => 1,
63                'eventtype' => 'due',
64                'timestart' => time(),
65                'timeduration' => 86400,
66                'visible' => 1
67            ], [
68                'name' => 'Start of lesson',
69                'description' => '',
70                'location' => 'Test',
71                'format' => 1,
72                'courseid' => $course->id,
73                'groupid' => 0,
74                'userid' => 2,
75                'modulename' => 'lesson',
76                'instance' => 1,
77                'eventtype' => 'end',
78                'timestart' => time(),
79                'timeduration' => 86400,
80                'visible' => 1
81            ]
82        ];
83
84        foreach ($events as $event) {
85            calendar_event::create($event, false);
86        }
87
88        // Get all events.
89        $events = $retrievalstrategy->get_raw_events(null, [0], null);
90        $this->assertCount(2, $events);
91
92        // Disable the lesson module.
93        $DB->set_field('modules', 'visible', 0, ['name' => 'lesson']);
94
95        // Check that we only return the assign event.
96        $events = $retrievalstrategy->get_raw_events(null, [0], null);
97        $this->assertCount(1, $events);
98        $event = reset($events);
99        $this->assertEquals('assign', $event->modulename);
100
101        // Now, log out and repeat the above test in the reverse order.
102        $this->setUser();
103
104        // Check that we only return the assign event (given that the lesson module is still disabled).
105        $events = $retrievalstrategy->get_raw_events([$student->id], [0], null);
106        $this->assertCount(1, $events);
107        $event = reset($events);
108        $this->assertEquals('assign', $event->modulename);
109
110        // Enable the lesson module.
111        $DB->set_field('modules', 'visible', 1, ['name' => 'lesson']);
112
113        // Get all events.
114        $events = $retrievalstrategy->get_raw_events(null, [0], null);
115        $this->assertCount(2, $events);
116    }
117
118    /**
119     * Test retrieval strategy when there are overrides.
120     */
121    public function test_get_raw_event_strategy_with_overrides() {
122        $this->resetAfterTest();
123
124        $retrievalstrategy = new raw_event_retrieval_strategy();
125        $generator = $this->getDataGenerator();
126        $course = $generator->create_course();
127        $plugingenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
128
129        $instance = $plugingenerator->create_instance(['course' => $course->id]);
130
131        // Create users.
132        $useroverridestudent = $generator->create_user();
133        $group1student = $generator->create_user();
134        $group2student = $generator->create_user();
135        $group12student = $generator->create_user();
136        $nogroupstudent = $generator->create_user();
137
138        // Enrol users.
139        $generator->enrol_user($useroverridestudent->id, $course->id, 'student');
140        $generator->enrol_user($group1student->id, $course->id, 'student');
141        $generator->enrol_user($group2student->id, $course->id, 'student');
142        $generator->enrol_user($group12student->id, $course->id, 'student');
143
144        $generator->enrol_user($nogroupstudent->id, $course->id, 'student');
145
146        // Create groups.
147        $group1 = $generator->create_group(['courseid' => $course->id, 'name' => 'Group 1']);
148        $group2 = $generator->create_group(['courseid' => $course->id, 'name' => 'Group 2']);
149
150        // Add members to groups.
151        $generator->create_group_member(['groupid' => $group1->id, 'userid' => $group1student->id]);
152        $generator->create_group_member(['groupid' => $group2->id, 'userid' => $group2student->id]);
153        $generator->create_group_member(['groupid' => $group1->id, 'userid' => $group12student->id]);
154        $generator->create_group_member(['groupid' => $group2->id, 'userid' => $group12student->id]);
155
156        $now = time();
157
158        // Events with the same module name, instance and event type.
159        $events = [
160            [
161                'name' => 'Assignment 1 due date',
162                'description' => '',
163                'location' => 'Test',
164                'format' => 0,
165                'courseid' => $course->id,
166                'groupid' => 0,
167                'userid' => 2,
168                'modulename' => 'assign',
169                'instance' => $instance->id,
170                'eventtype' => 'due',
171                'timestart' => $now,
172                'timeduration' => 0,
173                'visible' => 1
174            ], [
175                'name' => 'Assignment 1 due date - User override',
176                'description' => '',
177                'location' => 'Test',
178                'format' => 1,
179                'courseid' => 0,
180                'groupid' => 0,
181                'userid' => $useroverridestudent->id,
182                'modulename' => 'assign',
183                'instance' => $instance->id,
184                'eventtype' => 'due',
185                'timestart' => $now + 86400,
186                'timeduration' => 0,
187                'visible' => 1,
188                'priority' => CALENDAR_EVENT_USER_OVERRIDE_PRIORITY
189            ], [
190                'name' => 'Assignment 1 due date - Group A override',
191                'description' => '',
192                'location' => 'Test',
193                'format' => 1,
194                'courseid' => $course->id,
195                'groupid' => $group1->id,
196                'userid' => 2,
197                'modulename' => 'assign',
198                'instance' => $instance->id,
199                'eventtype' => 'due',
200                'timestart' => $now + (2 * 86400),
201                'timeduration' => 0,
202                'visible' => 1,
203                'priority' => 1,
204            ], [
205                'name' => 'Assignment 1 due date - Group B override',
206                'description' => '',
207                'location' => 'Test',
208                'format' => 1,
209                'courseid' => $course->id,
210                'groupid' => $group2->id,
211                'userid' => 2,
212                'modulename' => 'assign',
213                'instance' => $instance->id,
214                'eventtype' => 'due',
215                'timestart' => $now + (3 * 86400),
216                'timeduration' => 0,
217                'visible' => 1,
218                'priority' => 2,
219            ],
220        ];
221
222        foreach ($events as $event) {
223            calendar_event::create($event, false);
224        }
225
226        $groups = [$group1->id, $group2->id];
227
228        // Do the following tests multiple times when logged in with different users. Also run the whole set when logged out.
229        // In any cases, the tests should not depend on the logged-in user.
230        foreach ([$useroverridestudent, $nogroupstudent, $group12student, $group1student, null] as $login) {
231            $this->setUser($login);
232
233            // Get user override events.
234            $events = $retrievalstrategy->get_raw_events([$useroverridestudent->id], $groups, [$course->id]);
235            $this->assertCount(1, $events);
236            $event = reset($events);
237            $this->assertEquals('Assignment 1 due date - User override', $event->name);
238
239            // Get events for user that does not belong to any group and has no user override events.
240            $events = $retrievalstrategy->get_raw_events([$nogroupstudent->id], $groups, [$course->id]);
241            $this->assertCount(1, $events);
242            $event = reset($events);
243            $this->assertEquals('Assignment 1 due date', $event->name);
244
245            // Get events for user that belongs to groups A and B and has no user override events.
246            $events = $retrievalstrategy->get_raw_events([$group12student->id], $groups, [$course->id]);
247            $this->assertCount(1, $events);
248            $event = reset($events);
249            $this->assertEquals('Assignment 1 due date - Group A override', $event->name);
250
251            // Get events for user that belongs to group A and has no user override events.
252            $events = $retrievalstrategy->get_raw_events([$group1student->id], $groups, [$course->id]);
253            $this->assertCount(1, $events);
254            $event = reset($events);
255            $this->assertEquals('Assignment 1 due date - Group A override', $event->name);
256        }
257
258        // Add repeating events.
259        $repeatingevents = [
260            [
261                'name' => 'Repeating site event',
262                'description' => '',
263                'location' => 'Test',
264                'format' => 1,
265                'courseid' => SITEID,
266                'groupid' => 0,
267                'userid' => 2,
268                'repeatid' => 1,
269                'modulename' => '0',
270                'instance' => 0,
271                'eventtype' => 'site',
272                'timestart' => $now + 86400,
273                'timeduration' => 0,
274                'visible' => 1,
275            ],
276            [
277                'name' => 'Repeating site event',
278                'description' => '',
279                'location' => 'Test',
280                'format' => 1,
281                'courseid' => SITEID,
282                'groupid' => 0,
283                'userid' => 2,
284                'repeatid' => 1,
285                'modulename' => '0',
286                'instance' => 0,
287                'eventtype' => 'site',
288                'timestart' => $now + (2 * 86400),
289                'timeduration' => 0,
290                'visible' => 1,
291            ],
292        ];
293
294        foreach ($repeatingevents as $event) {
295            calendar_event::create($event, false);
296        }
297
298        // Make sure repeating events are not filtered out.
299        $events = $retrievalstrategy->get_raw_events();
300        $this->assertCount(3, $events);
301    }
302
303    /**
304     * Test retrieval strategy with category specifications.
305     */
306    public function test_get_raw_events_category() {
307        $this->resetAfterTest();
308        $retrievalstrategy = new raw_event_retrieval_strategy();
309        $generator = $this->getDataGenerator();
310        $category1 = $generator->create_category();
311        $category2 = $generator->create_category();
312        $events = [
313            [
314                'name' => 'E1',
315                'eventtype' => 'category',
316                'description' => '',
317                'location' => 'Test',
318                'format' => 1,
319                'categoryid' => $category1->id,
320                'userid' => 2,
321                'timestart' => time(),
322            ],
323            [
324                'name' => 'E2',
325                'eventtype' => 'category',
326                'description' => '',
327                'location' => 'Test',
328                'format' => 1,
329                'categoryid' => $category2->id,
330                'userid' => 2,
331                'timestart' => time() + 1,
332            ],
333        ];
334
335        foreach ($events as $event) {
336            calendar_event::create($event, false);
337        }
338
339        // Get all events.
340        $events = $retrievalstrategy->get_raw_events(null, null, null, null);
341        $this->assertCount(2, $events);
342
343        $event = array_shift($events);
344        $this->assertEquals('E1', $event->name);
345        $event = array_shift($events);
346        $this->assertEquals('E2', $event->name);
347
348        // Get events for C1 events.
349        $events = $retrievalstrategy->get_raw_events(null, null, null, [$category1->id]);
350        $this->assertCount(1, $events);
351
352        $event = array_shift($events);
353        $this->assertEquals('E1', $event->name);
354
355        // Get events for C2 events.
356        $events = $retrievalstrategy->get_raw_events(null, null, null, [$category2->id]);
357        $this->assertCount(1, $events);
358
359        $event = array_shift($events);
360        $this->assertEquals('E2', $event->name);
361
362        // Get events for several categories.
363        $events = $retrievalstrategy->get_raw_events(null, null, null, [$category1->id, $category2->id]);
364        $this->assertCount(2, $events);
365    }
366
367    public function test_get_raw_events_for_multiple_users() {
368        $this->resetAfterTest();
369
370        $generator = $this->getDataGenerator();
371
372        // Create users.
373        $user1 = $generator->create_user();
374        $user2 = $generator->create_user();
375        $user3 = $generator->create_user();
376
377        // Create user events.
378        $events = [
379            [
380                'name' => 'User1 Event',
381                'eventtype' => 'user',
382                'userid' => $user1->id,
383                'timestart' => time(),
384            ], [
385                'name' => 'User2 Event',
386                'eventtype' => 'user',
387                'userid' => $user2->id,
388                'timestart' => time(),
389            ], [
390                'name' => 'User3 Event',
391                'eventtype' => 'user',
392                'userid' => $user3->id,
393                'timestart' => time(),
394            ]
395        ];
396        foreach ($events as $event) {
397            calendar_event::create($event, false);
398        }
399
400        $retrievalstrategy = new raw_event_retrieval_strategy();
401
402        // Get all events.
403        $events = $retrievalstrategy->get_raw_events([$user1->id, $user2->id]);
404        $this->assertCount(2, $events);
405        $this->assertEqualsCanonicalizing(
406                ['User1 Event', 'User2 Event'],
407                array_column($events, 'name'));
408    }
409
410    public function test_get_raw_events_for_groups_with_no_members() {
411        $this->resetAfterTest();
412
413        $generator = $this->getDataGenerator();
414
415        $course = $generator->create_course();
416
417        // Create groups.
418        $group1 = $generator->create_group(['courseid' => $course->id, 'name' => 'Group 1']);
419        $group2 = $generator->create_group(['courseid' => $course->id, 'name' => 'Group 2']);
420
421        // Create group events.
422        $events = [
423            [
424                'name' => 'Group 1 Event',
425                'eventtype' => 'group',
426                'groupid' => $group1->id,
427                'timestart' => time(),
428            ], [
429                'name' => 'Group 2 Event',
430                'eventtype' => 'group',
431                'groupid' => $group2->id,
432                'timestart' => time(),
433            ]
434        ];
435        foreach ($events as $event) {
436            calendar_event::create($event, false);
437        }
438
439        $retrievalstrategy = new raw_event_retrieval_strategy;
440
441        // Get group eventsl.
442        $events = $retrievalstrategy->get_raw_events(null, [$group1->id, $group2->id]);
443        $this->assertCount(2, $events);
444        $this->assertEqualsCanonicalizing(
445                ['Group 1 Event', 'Group 2 Event'],
446                array_column($events, 'name'));
447    }
448
449    /**
450     * Test retrieval strategy with empty filters.
451     * This covers a edge case not covered elsewhere to ensure its SQL is cross
452     * db compatible. The test is ensuring we don't get a DML Exception with
453     * the filters setup this way.
454     */
455    public function test_get_raw_events_with_empty_user_and_category_lists() {
456        $retrievalstrategy = new raw_event_retrieval_strategy;
457        $retrievalstrategy->get_raw_events([], null, null, []);
458    }
459}
460