Merge branch 'MDL-63999-master' of git://github.com/lameze/moodle
[moodle.git] / calendar / tests / event_vault_test.php
blobe4a1b21236e7b55ae3b086cba4d69ae0d8377ca6
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 /**
18 * This file contains the class that handles testing of the calendar event vault.
20 * @package core_calendar
21 * @copyright 2017 Ryan Wyllie <ryan@moodle.com>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 defined('MOODLE_INTERNAL') || die();
27 global $CFG;
28 require_once($CFG->dirroot . '/calendar/tests/helpers.php');
30 use core_calendar\local\event\data_access\event_vault;
31 use core_calendar\local\event\strategies\raw_event_retrieval_strategy;
33 /**
34 * This file contains the class that handles testing of the calendar event vault.
36 * @package core_calendar
37 * @copyright 2017 Ryan Wyllie <ryan@moodle.com>
38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40 class core_calendar_event_vault_testcase extends advanced_testcase {
42 /**
43 * Test that get_action_events_by_timesort returns events after the
44 * provided timesort value.
46 public function test_get_action_events_by_timesort_after_time() {
47 $this->resetAfterTest(true);
49 $user = $this->getDataGenerator()->create_user();
50 $factory = new action_event_test_factory();
51 $strategy = new raw_event_retrieval_strategy();
52 $vault = new event_vault($factory, $strategy);
54 $this->setUser($user);
56 for ($i = 1; $i < 6; $i++) {
57 create_event([
58 'name' => sprintf('Event %d', $i),
59 'eventtype' => 'user',
60 'userid' => $user->id,
61 'timesort' => $i,
62 'type' => CALENDAR_EVENT_TYPE_ACTION
63 ]);
66 $events = $vault->get_action_events_by_timesort($user, 3);
68 $this->assertCount(3, $events);
69 $this->assertEquals('Event 3', $events[0]->get_name());
70 $this->assertEquals('Event 4', $events[1]->get_name());
71 $this->assertEquals('Event 5', $events[2]->get_name());
73 $events = $vault->get_action_events_by_timesort($user, 3, null, null, 1);
75 $this->assertCount(1, $events);
76 $this->assertEquals('Event 3', $events[0]->get_name());
78 $events = $vault->get_action_events_by_timesort($user, 6);
80 $this->assertCount(0, $events);
83 /**
84 * Test that get_action_events_by_timesort returns events before the
85 * provided timesort value.
87 public function test_get_action_events_by_timesort_before_time() {
88 $this->resetAfterTest(true);
89 $this->setAdminuser();
91 $user = $this->getDataGenerator()->create_user();
92 $factory = new action_event_test_factory();
93 $strategy = new raw_event_retrieval_strategy();
94 $vault = new event_vault($factory, $strategy);
96 for ($i = 1; $i < 6; $i++) {
97 create_event([
98 'name' => sprintf('Event %d', $i),
99 'eventtype' => 'user',
100 'userid' => $user->id,
101 'timesort' => $i,
102 'type' => CALENDAR_EVENT_TYPE_ACTION,
103 'courseid' => 1
107 $events = $vault->get_action_events_by_timesort($user, null, 3);
109 $this->assertCount(3, $events);
110 $this->assertEquals('Event 1', $events[0]->get_name());
111 $this->assertEquals('Event 2', $events[1]->get_name());
112 $this->assertEquals('Event 3', $events[2]->get_name());
114 $events = $vault->get_action_events_by_timesort($user, null, 3, null, 1);
116 $this->assertCount(1, $events);
117 $this->assertEquals('Event 1', $events[0]->get_name());
119 $events = $vault->get_action_events_by_timesort($user, 6);
121 $this->assertCount(0, $events);
125 * Test that get_action_events_by_timesort returns events between the
126 * provided timesort values.
128 public function test_get_action_events_by_timesort_between_time() {
129 $this->resetAfterTest(true);
130 $this->setAdminuser();
132 $user = $this->getDataGenerator()->create_user();
133 $factory = new action_event_test_factory();
134 $strategy = new raw_event_retrieval_strategy();
135 $vault = new event_vault($factory, $strategy);
137 for ($i = 1; $i < 6; $i++) {
138 create_event([
139 'name' => sprintf('Event %d', $i),
140 'eventtype' => 'user',
141 'userid' => $user->id,
142 'timesort' => $i,
143 'type' => CALENDAR_EVENT_TYPE_ACTION,
144 'courseid' => 1
148 $events = $vault->get_action_events_by_timesort($user, 2, 4);
150 $this->assertCount(3, $events);
151 $this->assertEquals('Event 2', $events[0]->get_name());
152 $this->assertEquals('Event 3', $events[1]->get_name());
153 $this->assertEquals('Event 4', $events[2]->get_name());
155 $events = $vault->get_action_events_by_timesort($user, 2, 4, null, 1);
157 $this->assertCount(1, $events);
158 $this->assertEquals('Event 2', $events[0]->get_name());
162 * Test that get_action_events_by_timesort returns events between the
163 * provided timesort values and after the last seen event when one is
164 * provided.
166 public function test_get_action_events_by_timesort_between_time_after_event() {
167 $this->resetAfterTest(true);
168 $this->setAdminuser();
170 $user = $this->getDataGenerator()->create_user();
171 $factory = new action_event_test_factory();
172 $strategy = new raw_event_retrieval_strategy();
173 $vault = new event_vault($factory, $strategy);
175 $records = [];
176 for ($i = 1; $i < 21; $i++) {
177 $records[] = create_event([
178 'name' => sprintf('Event %d', $i),
179 'eventtype' => 'user',
180 'userid' => $user->id,
181 'timesort' => $i,
182 'type' => CALENDAR_EVENT_TYPE_ACTION,
183 'courseid' => 1
187 $aftereventid = $records[6]->id;
188 $afterevent = $vault->get_event_by_id($aftereventid);
189 $events = $vault->get_action_events_by_timesort($user, 3, 15, $afterevent);
191 $this->assertCount(8, $events);
192 $this->assertEquals('Event 8', $events[0]->get_name());
194 $events = $vault->get_action_events_by_timesort($user, 3, 15, $afterevent, 3);
196 $this->assertCount(3, $events);
200 * Test that get_action_events_by_timesort returns events between the
201 * provided timesort values and the last seen event can be provided to
202 * get paginated results.
204 public function test_get_action_events_by_timesort_between_time_skip_even_records() {
205 $this->resetAfterTest(true);
206 $this->setAdminuser();
208 $user = $this->getDataGenerator()->create_user();
209 // The factory will return every event that is divisible by 2.
210 $factory = new action_event_test_factory(function($actionevent) {
211 static $count = 0;
212 $count++;
213 return ($count % 2) ? true : false;
215 $strategy = new raw_event_retrieval_strategy();
216 $vault = new event_vault($factory, $strategy);
218 for ($i = 1; $i < 41; $i++) {
219 create_event([
220 'name' => sprintf('Event %d', $i),
221 'eventtype' => 'user',
222 'userid' => $user->id,
223 'timesort' => $i,
224 'type' => CALENDAR_EVENT_TYPE_ACTION,
225 'courseid' => 1
229 $events = $vault->get_action_events_by_timesort($user, 3, 35, null, 5);
231 $this->assertCount(5, $events);
232 $this->assertEquals('Event 3', $events[0]->get_name());
233 $this->assertEquals('Event 5', $events[1]->get_name());
234 $this->assertEquals('Event 7', $events[2]->get_name());
235 $this->assertEquals('Event 9', $events[3]->get_name());
236 $this->assertEquals('Event 11', $events[4]->get_name());
238 $afterevent = $events[4];
239 $events = $vault->get_action_events_by_timesort($user, 3, 35, $afterevent, 5);
241 $this->assertCount(5, $events);
242 $this->assertEquals('Event 13', $events[0]->get_name());
243 $this->assertEquals('Event 15', $events[1]->get_name());
244 $this->assertEquals('Event 17', $events[2]->get_name());
245 $this->assertEquals('Event 19', $events[3]->get_name());
246 $this->assertEquals('Event 21', $events[4]->get_name());
250 * Test that get_action_events_by_timesort returns events between the
251 * provided timesort values. The database will continue to be read until the
252 * number of events requested has been satisfied. In this case the first
253 * five events are rejected so it should require two database requests.
255 public function test_get_action_events_by_timesort_between_time_skip_first_records() {
256 $this->resetAfterTest(true);
257 $this->setAdminuser();
259 $user = $this->getDataGenerator()->create_user();
260 $limit = 5;
261 $seen = 0;
262 // The factory will skip the first $limit events.
263 $factory = new action_event_test_factory(function($actionevent) use (&$seen, $limit) {
264 if ($seen < $limit) {
265 $seen++;
266 return false;
267 } else {
268 return true;
271 $strategy = new raw_event_retrieval_strategy();
272 $vault = new event_vault($factory, $strategy);
274 for ($i = 1; $i < 21; $i++) {
275 create_event([
276 'name' => sprintf('Event %d', $i),
277 'eventtype' => 'user',
278 'userid' => $user->id,
279 'timesort' => $i,
280 'type' => CALENDAR_EVENT_TYPE_ACTION,
281 'courseid' => 1
285 $events = $vault->get_action_events_by_timesort($user, 1, 20, null, $limit);
287 $this->assertCount($limit, $events);
288 $this->assertEquals(sprintf('Event %d', $limit + 1), $events[0]->get_name());
289 $this->assertEquals(sprintf('Event %d', $limit + 2), $events[1]->get_name());
290 $this->assertEquals(sprintf('Event %d', $limit + 3), $events[2]->get_name());
291 $this->assertEquals(sprintf('Event %d', $limit + 4), $events[3]->get_name());
292 $this->assertEquals(sprintf('Event %d', $limit + 5), $events[4]->get_name());
296 * Test that get_action_events_by_timesort returns events between the
297 * provided timesort values and after the last seen event when one is
298 * provided. This should work even when the event ids aren't ordered the
299 * same as the timesort order.
301 public function test_get_action_events_by_timesort_non_consecutive_ids() {
302 $this->resetAfterTest(true);
303 $this->setAdminuser();
305 $user = $this->getDataGenerator()->create_user();
306 $factory = new action_event_test_factory();
307 $strategy = new raw_event_retrieval_strategy();
308 $vault = new event_vault($factory, $strategy);
311 * The events should be ordered by timesort as follows:
313 * 1 event 1
314 * 2 event 1
315 * 1 event 2
316 * 2 event 2
317 * 1 event 3
318 * 2 event 3
319 * 1 event 4
320 * 2 event 4
321 * 1 event 5
322 * 2 event 5
323 * 1 event 6
324 * 2 event 6
325 * 1 event 7
326 * 2 event 7
327 * 1 event 8
328 * 2 event 8
329 * 1 event 9
330 * 2 event 9
331 * 1 event 10
332 * 2 event 10
334 $records = [];
335 for ($i = 1; $i < 11; $i++) {
336 $records[] = create_event([
337 'name' => sprintf('1 event %d', $i),
338 'eventtype' => 'user',
339 'userid' => $user->id,
340 'timesort' => $i,
341 'type' => CALENDAR_EVENT_TYPE_ACTION,
342 'courseid' => 1
346 for ($i = 1; $i < 11; $i++) {
347 $records[] = create_event([
348 'name' => sprintf('2 event %d', $i),
349 'eventtype' => 'user',
350 'userid' => $user->id,
351 'timesort' => $i,
352 'type' => CALENDAR_EVENT_TYPE_ACTION,
353 'courseid' => 1
358 * Expected result set:
360 * 2 event 4
361 * 1 event 5
362 * 2 event 5
363 * 1 event 6
364 * 2 event 6
365 * 1 event 7
366 * 2 event 7
367 * 1 event 8
368 * 2 event 8
370 $aftereventid = $records[3]->id;
371 $afterevent = $vault->get_event_by_id($aftereventid);
372 // Offset results by event with name "1 event 4" which has the same timesort
373 // value as the lower boundary of this query (3). Confirm that the given
374 // $afterevent is used to ignore events with the same timesortfrom values.
375 $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent);
377 $this->assertCount(9, $events);
378 $this->assertEquals('2 event 4', $events[0]->get_name());
379 $this->assertEquals('2 event 8', $events[8]->get_name());
382 * Expected result set:
384 * 2 event 4
385 * 1 event 5
387 $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent, 2);
389 $this->assertCount(2, $events);
390 $this->assertEquals('2 event 4', $events[0]->get_name());
391 $this->assertEquals('1 event 5', $events[1]->get_name());
394 * Expected result set:
396 * 2 event 8
398 $aftereventid = $records[7]->id;
399 $afterevent = $vault->get_event_by_id($aftereventid);
400 // Offset results by event with name "1 event 8" which has the same timesort
401 // value as the upper boundary of this query (8). Confirm that the given
402 // $afterevent is used to ignore events with the same timesortto values.
403 $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent);
405 $this->assertCount(1, $events);
406 $this->assertEquals('2 event 8', $events[0]->get_name());
409 * Expected empty result set.
411 $aftereventid = $records[18]->id;
412 $afterevent = $vault->get_event_by_id($aftereventid);
413 // Offset results by event with name "2 event 9" which has a timesort
414 // value larger than the upper boundary of this query (9 > 8). Confirm
415 // that the given $afterevent is used for filtering events.
416 $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent);
417 $this->assertEmpty($events);
421 * There are subtle cases where the priority of an event override may be identical to another.
422 * For example, if you duplicate a group override, but make it apply to a different group. Now
423 * there are two overrides with exactly the same overridden dates. In this case the priority of
424 * both is 1.
426 * In this situation:
427 * - A user in group A should see only the A override
428 * - A user in group B should see only the B override
429 * - A user in both A and B should see both
431 public function test_get_action_events_by_timesort_with_identical_group_override_priorities() {
432 $this->resetAfterTest();
433 $this->setAdminuser();
435 $course = $this->getDataGenerator()->create_course();
437 // Create an assign instance.
438 $assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
439 $assigninstance = $assigngenerator->create_instance(['course' => $course->id]);
441 // Create users.
442 $users = [
443 'Only in group A' => $this->getDataGenerator()->create_user(),
444 'Only in group B' => $this->getDataGenerator()->create_user(),
445 'In group A and B' => $this->getDataGenerator()->create_user(),
446 'In no groups' => $this->getDataGenerator()->create_user()
449 // Enrol users.
450 foreach ($users as $user) {
451 $this->getDataGenerator()->enrol_user($user->id, $course->id);
454 // Create groups.
455 $groupa = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
456 $groupb = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
458 // Add members to groups.
459 // Group A.
460 $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['Only in group A']->id]);
461 $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['In group A and B']->id]);
463 // Group B.
464 $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['Only in group B']->id]);
465 $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['In group A and B']->id]);
467 // Events with the same module name, instance and event type.
468 $events = [
470 'name' => 'Assignment 1 due date - Group A override',
471 'description' => '',
472 'format' => 1,
473 'courseid' => $course->id,
474 'groupid' => $groupa->id,
475 'userid' => 2,
476 'modulename' => 'assign',
477 'instance' => $assigninstance->id,
478 'eventtype' => 'due',
479 'type' => CALENDAR_EVENT_TYPE_ACTION,
480 'timestart' => 1,
481 'timeduration' => 0,
482 'visible' => 1,
483 'priority' => 1
486 'name' => 'Assignment 1 due date - Group B override',
487 'description' => '',
488 'format' => 1,
489 'courseid' => $course->id,
490 'groupid' => $groupb->id,
491 'userid' => 2,
492 'modulename' => 'assign',
493 'instance' => $assigninstance->id,
494 'eventtype' => 'due',
495 'type' => CALENDAR_EVENT_TYPE_ACTION,
496 'timestart' => 1,
497 'timeduration' => 0,
498 'visible' => 1,
499 'priority' => 1
502 'name' => 'Assignment 1 due date',
503 'description' => '',
504 'format' => 1,
505 'courseid' => $course->id,
506 'groupid' => 0,
507 'userid' => 2,
508 'modulename' => 'assign',
509 'instance' => $assigninstance->id,
510 'eventtype' => 'due',
511 'type' => CALENDAR_EVENT_TYPE_ACTION,
512 'timestart' => 1,
513 'timeduration' => 0,
514 'visible' => 1,
515 'priority' => null,
519 foreach ($events as $event) {
520 calendar_event::create($event, false);
523 $factory = new action_event_test_factory();
524 $strategy = new raw_event_retrieval_strategy();
525 $vault = new event_vault($factory, $strategy);
527 $usersevents = array_reduce(array_keys($users), function($carry, $description) use ($users, $vault) {
528 // NB: This is currently needed to make get_action_events_by_timesort return the right thing.
529 // It needs to be fixed, see MDL-58736.
530 $this->setUser($users[$description]);
531 return $carry + ['For user ' . lcfirst($description) => $vault->get_action_events_by_timesort($users[$description])];
532 }, []);
534 foreach ($usersevents as $description => $userevents) {
535 if ($description == 'For user in group A and B') {
536 // User is in both A and B, so they should see the override for both
537 // given that the priority is the same.
538 $this->assertCount(2, $userevents);
539 continue;
542 // Otherwise there should be only one assign event for each user.
543 $this->assertCount(1, $userevents);
546 // User in only group A should see the group A override.
547 $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user only in group A'][0]->get_name());
549 // User in only group B should see the group B override.
550 $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user only in group B'][0]->get_name());
552 // User in group A and B should see see both overrides since the priorities are the same.
553 $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user in group A and B'][0]->get_name());
554 $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user in group A and B'][1]->get_name());
556 // User in no groups should see the plain assignment event.
557 $this->assertEquals('Assignment 1 due date', $usersevents['For user in no groups'][0]->get_name());
561 * Test that get_action_events_by_course returns events after the
562 * provided timesort value.
564 public function test_get_action_events_by_course_after_time() {
565 $user = $this->getDataGenerator()->create_user();
566 $course1 = $this->getDataGenerator()->create_course();
567 $course2 = $this->getDataGenerator()->create_course();
568 $factory = new action_event_test_factory();
569 $strategy = new raw_event_retrieval_strategy();
570 $vault = new event_vault($factory, $strategy);
572 $this->resetAfterTest(true);
573 $this->setAdminuser();
574 $this->getDataGenerator()->enrol_user($user->id, $course1->id);
575 $this->getDataGenerator()->enrol_user($user->id, $course2->id);
577 for ($i = 1; $i < 6; $i++) {
578 create_event([
579 'name' => sprintf('Event %d', $i),
580 'eventtype' => 'user',
581 'userid' => $user->id,
582 'timesort' => $i,
583 'type' => CALENDAR_EVENT_TYPE_ACTION,
584 'courseid' => $course1->id,
588 for ($i = 6; $i < 12; $i++) {
589 create_event([
590 'name' => sprintf('Event %d', $i),
591 'eventtype' => 'user',
592 'userid' => $user->id,
593 'timesort' => $i,
594 'type' => CALENDAR_EVENT_TYPE_ACTION,
595 'courseid' => $course2->id,
599 $events = $vault->get_action_events_by_course($user, $course1, 3);
600 $this->assertCount(3, $events);
601 $this->assertEquals('Event 3', $events[0]->get_name());
602 $this->assertEquals('Event 4', $events[1]->get_name());
603 $this->assertEquals('Event 5', $events[2]->get_name());
605 $events = $vault->get_action_events_by_course($user, $course1, 3, null, null, 1);
607 $this->assertCount(1, $events);
608 $this->assertEquals('Event 3', $events[0]->get_name());
610 $events = $vault->get_action_events_by_course($user, $course1, 6);
612 $this->assertCount(0, $events);
616 * Test that get_action_events_by_course returns events before the
617 * provided timesort value.
619 public function test_get_action_events_by_course_before_time() {
620 $user = $this->getDataGenerator()->create_user();
621 $course1 = $this->getDataGenerator()->create_course();
622 $course2 = $this->getDataGenerator()->create_course();
623 $factory = new action_event_test_factory();
624 $strategy = new raw_event_retrieval_strategy();
625 $vault = new event_vault($factory, $strategy);
627 $this->resetAfterTest(true);
628 $this->setAdminuser();
629 $this->getDataGenerator()->enrol_user($user->id, $course1->id);
630 $this->getDataGenerator()->enrol_user($user->id, $course2->id);
632 for ($i = 1; $i < 6; $i++) {
633 create_event([
634 'name' => sprintf('Event %d', $i),
635 'eventtype' => 'user',
636 'userid' => $user->id,
637 'timesort' => $i,
638 'type' => CALENDAR_EVENT_TYPE_ACTION,
639 'courseid' => $course1->id,
643 for ($i = 6; $i < 12; $i++) {
644 create_event([
645 'name' => sprintf('Event %d', $i),
646 'eventtype' => 'user',
647 'userid' => $user->id,
648 'timesort' => $i,
649 'type' => CALENDAR_EVENT_TYPE_ACTION,
650 'courseid' => $course2->id,
654 $events = $vault->get_action_events_by_course($user, $course1, null, 3);
656 $this->assertCount(3, $events);
657 $this->assertEquals('Event 1', $events[0]->get_name());
658 $this->assertEquals('Event 2', $events[1]->get_name());
659 $this->assertEquals('Event 3', $events[2]->get_name());
661 $events = $vault->get_action_events_by_course($user, $course1, null, 3, null, 1);
663 $this->assertCount(1, $events);
664 $this->assertEquals('Event 1', $events[0]->get_name());
666 $events = $vault->get_action_events_by_course($user, $course1, 6);
668 $this->assertCount(0, $events);
672 * Test that get_action_events_by_course returns events between the
673 * provided timesort values.
675 public function test_get_action_events_by_course_between_time() {
676 $user = $this->getDataGenerator()->create_user();
677 $course1 = $this->getDataGenerator()->create_course();
678 $course2 = $this->getDataGenerator()->create_course();
679 $factory = new action_event_test_factory();
680 $strategy = new raw_event_retrieval_strategy();
681 $vault = new event_vault($factory, $strategy);
683 $this->resetAfterTest(true);
684 $this->setAdminuser();
685 $this->getDataGenerator()->enrol_user($user->id, $course1->id);
686 $this->getDataGenerator()->enrol_user($user->id, $course2->id);
688 for ($i = 1; $i < 6; $i++) {
689 create_event([
690 'name' => sprintf('Event %d', $i),
691 'eventtype' => 'user',
692 'userid' => $user->id,
693 'timesort' => $i,
694 'type' => CALENDAR_EVENT_TYPE_ACTION,
695 'courseid' => $course1->id,
699 for ($i = 6; $i < 12; $i++) {
700 create_event([
701 'name' => sprintf('Event %d', $i),
702 'eventtype' => 'user',
703 'userid' => $user->id,
704 'timesort' => $i,
705 'type' => CALENDAR_EVENT_TYPE_ACTION,
706 'courseid' => $course2->id,
710 $events = $vault->get_action_events_by_course($user, $course1, 2, 4);
712 $this->assertCount(3, $events);
713 $this->assertEquals('Event 2', $events[0]->get_name());
714 $this->assertEquals('Event 3', $events[1]->get_name());
715 $this->assertEquals('Event 4', $events[2]->get_name());
717 $events = $vault->get_action_events_by_course($user, $course1, 2, 4, null, 1);
719 $this->assertCount(1, $events);
720 $this->assertEquals('Event 2', $events[0]->get_name());
724 * Test that get_action_events_by_course returns events between the
725 * provided timesort values and after the last seen event when one is
726 * provided.
728 public function test_get_action_events_by_course_between_time_after_event() {
729 $user = $this->getDataGenerator()->create_user();
730 $course1 = $this->getDataGenerator()->create_course();
731 $course2 = $this->getDataGenerator()->create_course();
732 $factory = new action_event_test_factory();
733 $strategy = new raw_event_retrieval_strategy();
734 $vault = new event_vault($factory, $strategy);
735 $records = [];
737 $this->resetAfterTest(true);
738 $this->setAdminuser();
739 $this->getDataGenerator()->enrol_user($user->id, $course1->id);
740 $this->getDataGenerator()->enrol_user($user->id, $course2->id);
742 for ($i = 1; $i < 21; $i++) {
743 $records[] = create_event([
744 'name' => sprintf('Event %d', $i),
745 'eventtype' => 'user',
746 'userid' => $user->id,
747 'timesort' => $i,
748 'type' => CALENDAR_EVENT_TYPE_ACTION,
749 'courseid' => $course1->id,
753 for ($i = 21; $i < 41; $i++) {
754 $records[] = create_event([
755 'name' => sprintf('Event %d', $i),
756 'eventtype' => 'user',
757 'userid' => $user->id,
758 'timesort' => $i,
759 'type' => CALENDAR_EVENT_TYPE_ACTION,
760 'courseid' => $course2->id,
764 $aftereventid = $records[6]->id;
765 $afterevent = $vault->get_event_by_id($aftereventid);
766 $events = $vault->get_action_events_by_course($user, $course1, 3, 15, $afterevent);
768 $this->assertCount(8, $events);
769 $this->assertEquals('Event 8', $events[0]->get_name());
771 $events = $vault->get_action_events_by_course($user, $course1, 3, 15, $afterevent, 3);
773 $this->assertCount(3, $events);
777 * Test that get_action_events_by_course returns events between the
778 * provided timesort values and the last seen event can be provided to
779 * get paginated results.
781 public function test_get_action_events_by_course_between_time_skip_even_records() {
782 $user = $this->getDataGenerator()->create_user();
783 $course1 = $this->getDataGenerator()->create_course();
784 $course2 = $this->getDataGenerator()->create_course();
785 // The factory will return every event that is divisible by 2.
786 $factory = new action_event_test_factory(function($actionevent) {
787 static $count = 0;
788 $count++;
789 return ($count % 2) ? true : false;
791 $strategy = new raw_event_retrieval_strategy();
792 $vault = new event_vault($factory, $strategy);
794 $this->resetAfterTest(true);
795 $this->setAdminuser();
796 $this->getDataGenerator()->enrol_user($user->id, $course1->id);
797 $this->getDataGenerator()->enrol_user($user->id, $course2->id);
799 for ($i = 1; $i < 41; $i++) {
800 create_event([
801 'name' => sprintf('Event %d', $i),
802 'eventtype' => 'user',
803 'userid' => $user->id,
804 'timesort' => $i,
805 'type' => CALENDAR_EVENT_TYPE_ACTION,
806 'courseid' => $course1->id,
810 for ($i = 41; $i < 81; $i++) {
811 create_event([
812 'name' => sprintf('Event %d', $i),
813 'eventtype' => 'user',
814 'userid' => $user->id,
815 'timesort' => $i,
816 'type' => CALENDAR_EVENT_TYPE_ACTION,
817 'courseid' => $course2->id,
821 $events = $vault->get_action_events_by_course($user, $course1, 3, 35, null, 5);
823 $this->assertCount(5, $events);
824 $this->assertEquals('Event 3', $events[0]->get_name());
825 $this->assertEquals('Event 5', $events[1]->get_name());
826 $this->assertEquals('Event 7', $events[2]->get_name());
827 $this->assertEquals('Event 9', $events[3]->get_name());
828 $this->assertEquals('Event 11', $events[4]->get_name());
830 $afterevent = $events[4];
831 $events = $vault->get_action_events_by_course($user, $course1, 3, 35, $afterevent, 5);
833 $this->assertCount(5, $events);
834 $this->assertEquals('Event 13', $events[0]->get_name());
835 $this->assertEquals('Event 15', $events[1]->get_name());
836 $this->assertEquals('Event 17', $events[2]->get_name());
837 $this->assertEquals('Event 19', $events[3]->get_name());
838 $this->assertEquals('Event 21', $events[4]->get_name());
842 * Test that get_action_events_by_course returns events between the
843 * provided timesort values. The database will continue to be read until the
844 * number of events requested has been satisfied. In this case the first
845 * five events are rejected so it should require two database requests.
847 public function test_get_action_events_by_course_between_time_skip_first_records() {
848 $user = $this->getDataGenerator()->create_user();
849 $course1 = $this->getDataGenerator()->create_course();
850 $course2 = $this->getDataGenerator()->create_course();
851 $limit = 5;
852 $seen = 0;
853 // The factory will skip the first $limit events.
854 $factory = new action_event_test_factory(function($actionevent) use (&$seen, $limit) {
855 if ($seen < $limit) {
856 $seen++;
857 return false;
858 } else {
859 return true;
862 $strategy = new raw_event_retrieval_strategy();
863 $vault = new event_vault($factory, $strategy);
865 $this->resetAfterTest(true);
866 $this->setAdminuser();
867 $this->getDataGenerator()->enrol_user($user->id, $course1->id);
868 $this->getDataGenerator()->enrol_user($user->id, $course2->id);
870 for ($i = 1; $i < 21; $i++) {
871 create_event([
872 'name' => sprintf('Event %d', $i),
873 'eventtype' => 'user',
874 'userid' => $user->id,
875 'timesort' => $i,
876 'type' => CALENDAR_EVENT_TYPE_ACTION,
877 'courseid' => $course1->id,
881 for ($i = 21; $i < 41; $i++) {
882 create_event([
883 'name' => sprintf('Event %d', $i),
884 'eventtype' => 'user',
885 'userid' => $user->id,
886 'timesort' => $i,
887 'type' => CALENDAR_EVENT_TYPE_ACTION,
888 'courseid' => $course2->id,
892 $events = $vault->get_action_events_by_course($user, $course1, 1, 20, null, $limit);
894 $this->assertCount($limit, $events);
895 $this->assertEquals(sprintf('Event %d', $limit + 1), $events[0]->get_name());
896 $this->assertEquals(sprintf('Event %d', $limit + 2), $events[1]->get_name());
897 $this->assertEquals(sprintf('Event %d', $limit + 3), $events[2]->get_name());
898 $this->assertEquals(sprintf('Event %d', $limit + 4), $events[3]->get_name());
899 $this->assertEquals(sprintf('Event %d', $limit + 5), $events[4]->get_name());
903 * Test that get_action_events_by_course returns events between the
904 * provided timesort values and after the last seen event when one is
905 * provided. This should work even when the event ids aren't ordered the
906 * same as the timesort order.
908 public function test_get_action_events_by_course_non_consecutive_ids() {
909 $this->resetAfterTest(true);
910 $this->setAdminuser();
912 $user = $this->getDataGenerator()->create_user();
913 $course1 = $this->getDataGenerator()->create_course();
914 $course2 = $this->getDataGenerator()->create_course();
915 $factory = new action_event_test_factory();
916 $strategy = new raw_event_retrieval_strategy();
917 $vault = new event_vault($factory, $strategy);
919 $this->setAdminuser();
920 $this->getDataGenerator()->enrol_user($user->id, $course1->id);
921 $this->getDataGenerator()->enrol_user($user->id, $course2->id);
924 * The events should be ordered by timesort as follows:
926 * 1 event 1
927 * 2 event 1
928 * 1 event 2
929 * 2 event 2
930 * 1 event 3
931 * 2 event 3
932 * 1 event 4
933 * 2 event 4
934 * 1 event 5
935 * 2 event 5
936 * 1 event 6
937 * 2 event 6
938 * 1 event 7
939 * 2 event 7
940 * 1 event 8
941 * 2 event 8
942 * 1 event 9
943 * 2 event 9
944 * 1 event 10
945 * 2 event 10
947 $records = [];
948 for ($i = 1; $i < 11; $i++) {
949 $records[] = create_event([
950 'name' => sprintf('1 event %d', $i),
951 'eventtype' => 'user',
952 'userid' => $user->id,
953 'timesort' => $i,
954 'type' => CALENDAR_EVENT_TYPE_ACTION,
955 'courseid' => $course1->id,
959 for ($i = 1; $i < 11; $i++) {
960 $records[] = create_event([
961 'name' => sprintf('2 event %d', $i),
962 'eventtype' => 'user',
963 'userid' => $user->id,
964 'timesort' => $i,
965 'type' => CALENDAR_EVENT_TYPE_ACTION,
966 'courseid' => $course1->id,
970 // Create events for the other course.
971 for ($i = 1; $i < 11; $i++) {
972 $records[] = create_event([
973 'name' => sprintf('3 event %d', $i),
974 'eventtype' => 'user',
975 'userid' => $user->id,
976 'timesort' => $i,
977 'type' => CALENDAR_EVENT_TYPE_ACTION,
978 'courseid' => $course2->id,
983 * Expected result set:
985 * 2 event 4
986 * 1 event 5
987 * 2 event 5
988 * 1 event 6
989 * 2 event 6
990 * 1 event 7
991 * 2 event 7
992 * 1 event 8
993 * 2 event 8
995 $aftereventid = $records[3]->id;
996 $afterevent = $vault->get_event_by_id($aftereventid);
997 // Offset results by event with name "1 event 4" which has the same timesort
998 // value as the lower boundary of this query (3). Confirm that the given
999 // $afterevent is used to ignore events with the same timesortfrom values.
1000 $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent);
1002 $this->assertCount(9, $events);
1003 $this->assertEquals('2 event 4', $events[0]->get_name());
1004 $this->assertEquals('2 event 8', $events[8]->get_name());
1007 * Expected result set:
1009 * 2 event 4
1010 * 1 event 5
1012 $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent, 2);
1014 $this->assertCount(2, $events);
1015 $this->assertEquals('2 event 4', $events[0]->get_name());
1016 $this->assertEquals('1 event 5', $events[1]->get_name());
1019 * Expected result set:
1021 * 2 event 8
1023 $aftereventid = $records[7]->id;
1024 $afterevent = $vault->get_event_by_id($aftereventid);
1025 // Offset results by event with name "1 event 8" which has the same timesort
1026 // value as the upper boundary of this query (8). Confirm that the given
1027 // $afterevent is used to ignore events with the same timesortto values.
1028 $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent);
1030 $this->assertCount(1, $events);
1031 $this->assertEquals('2 event 8', $events[0]->get_name());
1034 * Expected empty result set.
1036 $aftereventid = $records[18]->id;
1037 $afterevent = $vault->get_event_by_id($aftereventid);
1038 // Offset results by event with name "2 event 9" which has a timesort
1039 // value larger than the upper boundary of this query (9 > 8). Confirm
1040 // that the given $afterevent is used for filtering events.
1041 $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent);
1043 $this->assertEmpty($events);
1047 * There are subtle cases where the priority of an event override may be identical to another.
1048 * For example, if you duplicate a group override, but make it apply to a different group. Now
1049 * there are two overrides with exactly the same overridden dates. In this case the priority of
1050 * both is 1.
1052 * In this situation:
1053 * - A user in group A should see only the A override
1054 * - A user in group B should see only the B override
1055 * - A user in both A and B should see both
1057 public function test_get_action_events_by_course_with_identical_group_override_priorities() {
1058 $this->resetAfterTest();
1059 $this->setAdminuser();
1061 $course = $this->getDataGenerator()->create_course();
1063 // Create an assign instance.
1064 $assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');
1065 $assigninstance = $assigngenerator->create_instance(['course' => $course->id]);
1067 // Create users.
1068 $users = [
1069 'Only in group A' => $this->getDataGenerator()->create_user(),
1070 'Only in group B' => $this->getDataGenerator()->create_user(),
1071 'In group A and B' => $this->getDataGenerator()->create_user(),
1072 'In no groups' => $this->getDataGenerator()->create_user()
1075 // Enrol users.
1076 foreach ($users as $user) {
1077 $this->getDataGenerator()->enrol_user($user->id, $course->id);
1080 // Create groups.
1081 $groupa = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1082 $groupb = $this->getDataGenerator()->create_group(['courseid' => $course->id]);
1084 // Add members to groups.
1085 // Group A.
1086 $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['Only in group A']->id]);
1087 $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['In group A and B']->id]);
1089 // Group B.
1090 $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['Only in group B']->id]);
1091 $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['In group A and B']->id]);
1093 // Events with the same module name, instance and event type.
1094 $events = [
1096 'name' => 'Assignment 1 due date - Group A override',
1097 'description' => '',
1098 'format' => 1,
1099 'courseid' => $course->id,
1100 'groupid' => $groupa->id,
1101 'userid' => 2,
1102 'modulename' => 'assign',
1103 'instance' => $assigninstance->id,
1104 'eventtype' => 'due',
1105 'type' => CALENDAR_EVENT_TYPE_ACTION,
1106 'timestart' => 1,
1107 'timeduration' => 0,
1108 'visible' => 1,
1109 'priority' => 1
1112 'name' => 'Assignment 1 due date - Group B override',
1113 'description' => '',
1114 'format' => 1,
1115 'courseid' => $course->id,
1116 'groupid' => $groupb->id,
1117 'userid' => 2,
1118 'modulename' => 'assign',
1119 'instance' => $assigninstance->id,
1120 'eventtype' => 'due',
1121 'type' => CALENDAR_EVENT_TYPE_ACTION,
1122 'timestart' => 1,
1123 'timeduration' => 0,
1124 'visible' => 1,
1125 'priority' => 1
1128 'name' => 'Assignment 1 due date',
1129 'description' => '',
1130 'format' => 1,
1131 'courseid' => $course->id,
1132 'groupid' => 0,
1133 'userid' => 2,
1134 'modulename' => 'assign',
1135 'instance' => $assigninstance->id,
1136 'eventtype' => 'due',
1137 'type' => CALENDAR_EVENT_TYPE_ACTION,
1138 'timestart' => 1,
1139 'timeduration' => 0,
1140 'visible' => 1,
1141 'priority' => null,
1145 foreach ($events as $event) {
1146 calendar_event::create($event, false);
1149 $factory = new action_event_test_factory();
1150 $strategy = new raw_event_retrieval_strategy();
1151 $vault = new event_vault($factory, $strategy);
1153 $usersevents = array_reduce(array_keys($users), function($carry, $description) use ($users, $course, $vault) {
1154 // NB: This is currently needed to make get_action_events_by_timesort return the right thing.
1155 // It needs to be fixed, see MDL-58736.
1156 $this->setUser($users[$description]);
1157 return $carry + [
1158 'For user ' . lcfirst($description) => $vault->get_action_events_by_course($users[$description], $course)
1160 }, []);
1162 foreach ($usersevents as $description => $userevents) {
1163 if ($description == 'For user in group A and B') {
1164 // User is in both A and B, so they should see the override for both
1165 // given that the priority is the same.
1166 $this->assertCount(2, $userevents);
1167 continue;
1170 // Otherwise there should be only one assign event for each user.
1171 $this->assertCount(1, $userevents);
1174 // User in only group A should see the group A override.
1175 $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user only in group A'][0]->get_name());
1177 // User in only group B should see the group B override.
1178 $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user only in group B'][0]->get_name());
1180 // User in group A and B should see see both overrides since the priorities are the same.
1181 $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user in group A and B'][0]->get_name());
1182 $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user in group A and B'][1]->get_name());
1184 // User in no groups should see the plain assignment event.
1185 $this->assertEquals('Assignment 1 due date', $usersevents['For user in no groups'][0]->get_name());