Rev 1 | Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>.namespace core_calendar;use action_event_test_factory;use core_calendar\local\event\data_access\event_vault;use core_calendar\local\event\strategies\raw_event_retrieval_strategy;defined('MOODLE_INTERNAL') || die();global $CFG;require_once($CFG->dirroot . '/calendar/tests/helpers.php');/*** This file contains the class that handles testing of the calendar event vault.** @package core_calendar* @copyright 2017 Ryan Wyllie <ryan@moodle.com>* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later*/class event_vault_test extends \advanced_testcase {/*** Test that get_action_events_by_timesort returns events after the* provided timesort value.*/public function test_get_action_events_by_timesort_after_time(): void {$this->resetAfterTest(true);$user = $this->getDataGenerator()->create_user();$factory = new action_event_test_factory();$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);$this->setUser($user);for ($i = 1; $i < 6; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION]);}$events = $vault->get_action_events_by_timesort($user, 3);$this->assertCount(3, $events);$this->assertEquals('Event 3', $events[0]->get_name());$this->assertEquals('Event 4', $events[1]->get_name());$this->assertEquals('Event 5', $events[2]->get_name());$events = $vault->get_action_events_by_timesort($user, 3, null, null, 1);$this->assertCount(1, $events);$this->assertEquals('Event 3', $events[0]->get_name());$events = $vault->get_action_events_by_timesort($user, 6);$this->assertCount(0, $events);}/*** Test that get_action_events_by_timesort returns events before the* provided timesort value.*/public function test_get_action_events_by_timesort_before_time(): void {$this->resetAfterTest(true);$this->setAdminuser();$user = $this->getDataGenerator()->create_user();$factory = new action_event_test_factory();$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);for ($i = 1; $i < 6; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => 1]);}$events = $vault->get_action_events_by_timesort($user, null, 3);$this->assertCount(3, $events);$this->assertEquals('Event 1', $events[0]->get_name());$this->assertEquals('Event 2', $events[1]->get_name());$this->assertEquals('Event 3', $events[2]->get_name());$events = $vault->get_action_events_by_timesort($user, null, 3, null, 1);$this->assertCount(1, $events);$this->assertEquals('Event 1', $events[0]->get_name());$events = $vault->get_action_events_by_timesort($user, 6);$this->assertCount(0, $events);}/*** Test that get_action_events_by_timesort returns events between the* provided timesort values.*/public function test_get_action_events_by_timesort_between_time(): void {$this->resetAfterTest(true);$this->setAdminuser();$user = $this->getDataGenerator()->create_user();$factory = new action_event_test_factory();$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);for ($i = 1; $i < 6; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => 1]);}$events = $vault->get_action_events_by_timesort($user, 2, 4);$this->assertCount(3, $events);$this->assertEquals('Event 2', $events[0]->get_name());$this->assertEquals('Event 3', $events[1]->get_name());$this->assertEquals('Event 4', $events[2]->get_name());$events = $vault->get_action_events_by_timesort($user, 2, 4, null, 1);$this->assertCount(1, $events);$this->assertEquals('Event 2', $events[0]->get_name());}/*** Test that get_action_events_by_timesort returns events between the* provided timesort values and after the last seen event when one is* provided.*/public function test_get_action_events_by_timesort_between_time_after_event(): void {$this->resetAfterTest(true);$this->setAdminuser();$user = $this->getDataGenerator()->create_user();$factory = new action_event_test_factory();$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);$records = [];for ($i = 1; $i < 21; $i++) {$records[] = create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => 1]);}$aftereventid = $records[6]->id;$afterevent = $vault->get_event_by_id($aftereventid);$events = $vault->get_action_events_by_timesort($user, 3, 15, $afterevent);$this->assertCount(8, $events);$this->assertEquals('Event 8', $events[0]->get_name());$events = $vault->get_action_events_by_timesort($user, 3, 15, $afterevent, 3);$this->assertCount(3, $events);}/*** Test that get_action_events_by_timesort returns events between the* provided timesort values and the last seen event can be provided to* get paginated results.*/public function test_get_action_events_by_timesort_between_time_skip_even_records(): void {$this->resetAfterTest(true);$this->setAdminuser();$user = $this->getDataGenerator()->create_user();// The factory will return every event that is divisible by 2.$factory = new action_event_test_factory(function($actionevent) {static $count = 0;$count++;return ($count % 2) ? true : false;});$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);for ($i = 1; $i < 41; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => 1]);}$events = $vault->get_action_events_by_timesort($user, 3, 35, null, 5);$this->assertCount(5, $events);$this->assertEquals('Event 3', $events[0]->get_name());$this->assertEquals('Event 5', $events[1]->get_name());$this->assertEquals('Event 7', $events[2]->get_name());$this->assertEquals('Event 9', $events[3]->get_name());$this->assertEquals('Event 11', $events[4]->get_name());$afterevent = $events[4];$events = $vault->get_action_events_by_timesort($user, 3, 35, $afterevent, 5);$this->assertCount(5, $events);$this->assertEquals('Event 13', $events[0]->get_name());$this->assertEquals('Event 15', $events[1]->get_name());$this->assertEquals('Event 17', $events[2]->get_name());$this->assertEquals('Event 19', $events[3]->get_name());$this->assertEquals('Event 21', $events[4]->get_name());}/*** Test that get_action_events_by_timesort returns events between the* provided timesort values. The database will continue to be read until the* number of events requested has been satisfied. In this case the first* five events are rejected so it should require two database requests.*/public function test_get_action_events_by_timesort_between_time_skip_first_records(): void {$this->resetAfterTest(true);$this->setAdminuser();$user = $this->getDataGenerator()->create_user();$limit = 5;$seen = 0;// The factory will skip the first $limit events.$factory = new action_event_test_factory(function($actionevent) use (&$seen, $limit) {if ($seen < $limit) {$seen++;return false;} else {return true;}});$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);for ($i = 1; $i < 21; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => 1]);}$events = $vault->get_action_events_by_timesort($user, 1, 20, null, $limit);$this->assertCount($limit, $events);$this->assertEquals(sprintf('Event %d', $limit + 1), $events[0]->get_name());$this->assertEquals(sprintf('Event %d', $limit + 2), $events[1]->get_name());$this->assertEquals(sprintf('Event %d', $limit + 3), $events[2]->get_name());$this->assertEquals(sprintf('Event %d', $limit + 4), $events[3]->get_name());$this->assertEquals(sprintf('Event %d', $limit + 5), $events[4]->get_name());}/*** Test that get_action_events_by_timesort returns events between the* provided timesort values and after the last seen event when one is* provided. This should work even when the event ids aren't ordered the* same as the timesort order.*/public function test_get_action_events_by_timesort_non_consecutive_ids(): void {$this->resetAfterTest(true);$this->setAdminuser();$user = $this->getDataGenerator()->create_user();$factory = new action_event_test_factory();$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);/** The events should be ordered by timesort as follows:** 1 event 1* 2 event 1* 1 event 2* 2 event 2* 1 event 3* 2 event 3* 1 event 4* 2 event 4* 1 event 5* 2 event 5* 1 event 6* 2 event 6* 1 event 7* 2 event 7* 1 event 8* 2 event 8* 1 event 9* 2 event 9* 1 event 10* 2 event 10*/$records = [];for ($i = 1; $i < 11; $i++) {$records[] = create_event(['name' => sprintf('1 event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => 1]);}for ($i = 1; $i < 11; $i++) {$records[] = create_event(['name' => sprintf('2 event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => 1]);}/** Expected result set:** 2 event 4* 1 event 5* 2 event 5* 1 event 6* 2 event 6* 1 event 7* 2 event 7* 1 event 8* 2 event 8*/$aftereventid = $records[3]->id;$afterevent = $vault->get_event_by_id($aftereventid);// Offset results by event with name "1 event 4" which has the same timesort// value as the lower boundary of this query (3). Confirm that the given// $afterevent is used to ignore events with the same timesortfrom values.$events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent);$this->assertCount(9, $events);$this->assertEquals('2 event 4', $events[0]->get_name());$this->assertEquals('2 event 8', $events[8]->get_name());/** Expected result set:** 2 event 4* 1 event 5*/$events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent, 2);$this->assertCount(2, $events);$this->assertEquals('2 event 4', $events[0]->get_name());$this->assertEquals('1 event 5', $events[1]->get_name());/** Expected result set:** 2 event 8*/$aftereventid = $records[7]->id;$afterevent = $vault->get_event_by_id($aftereventid);// Offset results by event with name "1 event 8" which has the same timesort// value as the upper boundary of this query (8). Confirm that the given// $afterevent is used to ignore events with the same timesortto values.$events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent);$this->assertCount(1, $events);$this->assertEquals('2 event 8', $events[0]->get_name());/** Expected empty result set.*/$aftereventid = $records[18]->id;$afterevent = $vault->get_event_by_id($aftereventid);// Offset results by event with name "2 event 9" which has a timesort// value larger than the upper boundary of this query (9 > 8). Confirm// that the given $afterevent is used for filtering events.$events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent);$this->assertEmpty($events);}/*** There are subtle cases where the priority of an event override may be identical to another.* For example, if you duplicate a group override, but make it apply to a different group. Now* there are two overrides with exactly the same overridden dates. In this case the priority of* both is 1.** In this situation:* - A user in group A should see only the A override* - A user in group B should see only the B override* - A user in both A and B should see both*/public function test_get_action_events_by_timesort_with_identical_group_override_priorities(): void {$this->resetAfterTest();$this->setAdminuser();$course = $this->getDataGenerator()->create_course();// Create an assign instance.$assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');$assigninstance = $assigngenerator->create_instance(['course' => $course->id]);// Create users.$users = ['Only in group A' => $this->getDataGenerator()->create_user(),'Only in group B' => $this->getDataGenerator()->create_user(),'In group A and B' => $this->getDataGenerator()->create_user(),'In no groups' => $this->getDataGenerator()->create_user()];// Enrol users.foreach ($users as $user) {$this->getDataGenerator()->enrol_user($user->id, $course->id);}// Create groups.$groupa = $this->getDataGenerator()->create_group(['courseid' => $course->id]);$groupb = $this->getDataGenerator()->create_group(['courseid' => $course->id]);// Add members to groups.// Group A.$this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['Only in group A']->id]);$this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['In group A and B']->id]);// Group B.$this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['Only in group B']->id]);$this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['In group A and B']->id]);// Events with the same module name, instance and event type.$events = [['name' => 'Assignment 1 due date - Group A override','description' => '','format' => 1,'courseid' => $course->id,'groupid' => $groupa->id,'userid' => 2,'modulename' => 'assign','instance' => $assigninstance->id,'eventtype' => 'due','type' => CALENDAR_EVENT_TYPE_ACTION,'timestart' => 1,'timeduration' => 0,'visible' => 1,'priority' => 1],['name' => 'Assignment 1 due date - Group B override','description' => '','format' => 1,'courseid' => $course->id,'groupid' => $groupb->id,'userid' => 2,'modulename' => 'assign','instance' => $assigninstance->id,'eventtype' => 'due','type' => CALENDAR_EVENT_TYPE_ACTION,'timestart' => 1,'timeduration' => 0,'visible' => 1,'priority' => 1],['name' => 'Assignment 1 due date','description' => '','format' => 1,'courseid' => $course->id,'groupid' => 0,'userid' => 2,'modulename' => 'assign','instance' => $assigninstance->id,'eventtype' => 'due','type' => CALENDAR_EVENT_TYPE_ACTION,'timestart' => 1,'timeduration' => 0,'visible' => 1,'priority' => null,]];foreach ($events as $event) {\calendar_event::create($event, false);}$factory = new action_event_test_factory();$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);$usersevents = array_reduce(array_keys($users), function($carry, $description) use ($users, $vault) {// NB: This is currently needed to make get_action_events_by_timesort return the right thing.// It needs to be fixed, see MDL-58736.$this->setUser($users[$description]);return $carry + ['For user ' . lcfirst($description) => $vault->get_action_events_by_timesort($users[$description])];}, []);foreach ($usersevents as $description => $userevents) {if ($description == 'For user in group A and B') {// User is in both A and B, so they should see the override for both// given that the priority is the same.$this->assertCount(2, $userevents);continue;}// Otherwise there should be only one assign event for each user.$this->assertCount(1, $userevents);}// User in only group A should see the group A override.$this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user only in group A'][0]->get_name());// User in only group B should see the group B override.$this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user only in group B'][0]->get_name());// User in group A and B should see see both overrides since the priorities are the same.$this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user in group A and B'][0]->get_name());$this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user in group A and B'][1]->get_name());// User in no groups should see the plain assignment event.$this->assertEquals('Assignment 1 due date', $usersevents['For user in no groups'][0]->get_name());}/*** Test that if a user is suspended that events related to that course are not shown.* User 1 is suspended. User 2 is active.*/public function test_get_action_events_by_timesort_with_suspended_user(): void {$this->resetAfterTest();$user1 = $this->getDataGenerator()->create_user();$user2 = $this->getDataGenerator()->create_user();$course = $this->getDataGenerator()->create_course();$this->setAdminuser();$lesson = $this->getDataGenerator()->create_module('lesson', ['name' => 'Lesson 1','course' => $course->id,'available' => time(),'deadline' => (time() + (60 * 60 * 24 * 5))]);$this->getDataGenerator()->enrol_user($user1->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);$this->getDataGenerator()->enrol_user($user2->id, $course->id);$factory = new action_event_test_factory();$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);$user1events = $vault->get_action_events_by_timesort($user1, null, null, null, 20, true);$this->assertEmpty($user1events);$user2events = $vault->get_action_events_by_timesort($user2, null, null, null, 20, true);$this->assertCount(1, $user2events);$this->assertEquals('Lesson 1 closes', $user2events[0]->get_name());}/*** Test that get_action_events_by_course returns events after the* provided timesort value.*/public function test_get_action_events_by_course_after_time(): void {$user = $this->getDataGenerator()->create_user();$course1 = $this->getDataGenerator()->create_course();$course2 = $this->getDataGenerator()->create_course();$factory = new action_event_test_factory();$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);$this->resetAfterTest(true);$this->setAdminuser();$this->getDataGenerator()->enrol_user($user->id, $course1->id);$this->getDataGenerator()->enrol_user($user->id, $course2->id);for ($i = 1; $i < 6; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course1->id,]);}for ($i = 6; $i < 12; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course2->id,]);}$events = $vault->get_action_events_by_course($user, $course1, 3);$this->assertCount(3, $events);$this->assertEquals('Event 3', $events[0]->get_name());$this->assertEquals('Event 4', $events[1]->get_name());$this->assertEquals('Event 5', $events[2]->get_name());$events = $vault->get_action_events_by_course($user, $course1, 3, null, null, 1);$this->assertCount(1, $events);$this->assertEquals('Event 3', $events[0]->get_name());$events = $vault->get_action_events_by_course($user, $course1, 6);$this->assertCount(0, $events);}/*** Test that get_action_events_by_course returns events before the* provided timesort value.*/public function test_get_action_events_by_course_before_time(): void {$user = $this->getDataGenerator()->create_user();$course1 = $this->getDataGenerator()->create_course();$course2 = $this->getDataGenerator()->create_course();$factory = new action_event_test_factory();$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);$this->resetAfterTest(true);$this->setAdminuser();$this->getDataGenerator()->enrol_user($user->id, $course1->id);$this->getDataGenerator()->enrol_user($user->id, $course2->id);for ($i = 1; $i < 6; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course1->id,]);}for ($i = 6; $i < 12; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course2->id,]);}$events = $vault->get_action_events_by_course($user, $course1, null, 3);$this->assertCount(3, $events);$this->assertEquals('Event 1', $events[0]->get_name());$this->assertEquals('Event 2', $events[1]->get_name());$this->assertEquals('Event 3', $events[2]->get_name());$events = $vault->get_action_events_by_course($user, $course1, null, 3, null, 1);$this->assertCount(1, $events);$this->assertEquals('Event 1', $events[0]->get_name());$events = $vault->get_action_events_by_course($user, $course1, 6);$this->assertCount(0, $events);}/*** Test that get_action_events_by_course returns events between the* provided timesort values.*/public function test_get_action_events_by_course_between_time(): void {$user = $this->getDataGenerator()->create_user();$course1 = $this->getDataGenerator()->create_course();$course2 = $this->getDataGenerator()->create_course();$factory = new action_event_test_factory();$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);$this->resetAfterTest(true);$this->setAdminuser();$this->getDataGenerator()->enrol_user($user->id, $course1->id);$this->getDataGenerator()->enrol_user($user->id, $course2->id);for ($i = 1; $i < 6; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course1->id,]);}for ($i = 6; $i < 12; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course2->id,]);}$events = $vault->get_action_events_by_course($user, $course1, 2, 4);$this->assertCount(3, $events);$this->assertEquals('Event 2', $events[0]->get_name());$this->assertEquals('Event 3', $events[1]->get_name());$this->assertEquals('Event 4', $events[2]->get_name());$events = $vault->get_action_events_by_course($user, $course1, 2, 4, null, 1);$this->assertCount(1, $events);$this->assertEquals('Event 2', $events[0]->get_name());}/*** Test that get_action_events_by_course returns events between the* provided timesort values and after the last seen event when one is* provided.*/public function test_get_action_events_by_course_between_time_after_event(): void {$user = $this->getDataGenerator()->create_user();$course1 = $this->getDataGenerator()->create_course();$course2 = $this->getDataGenerator()->create_course();$factory = new action_event_test_factory();$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);$records = [];$this->resetAfterTest(true);$this->setAdminuser();$this->getDataGenerator()->enrol_user($user->id, $course1->id);$this->getDataGenerator()->enrol_user($user->id, $course2->id);for ($i = 1; $i < 21; $i++) {$records[] = create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course1->id,]);}for ($i = 21; $i < 41; $i++) {$records[] = create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course2->id,]);}$aftereventid = $records[6]->id;$afterevent = $vault->get_event_by_id($aftereventid);$events = $vault->get_action_events_by_course($user, $course1, 3, 15, $afterevent);$this->assertCount(8, $events);$this->assertEquals('Event 8', $events[0]->get_name());$events = $vault->get_action_events_by_course($user, $course1, 3, 15, $afterevent, 3);$this->assertCount(3, $events);}/*** Test that get_action_events_by_course returns events between the* provided timesort values and the last seen event can be provided to* get paginated results.*/public function test_get_action_events_by_course_between_time_skip_even_records(): void {$user = $this->getDataGenerator()->create_user();$course1 = $this->getDataGenerator()->create_course();$course2 = $this->getDataGenerator()->create_course();// The factory will return every event that is divisible by 2.$factory = new action_event_test_factory(function($actionevent) {static $count = 0;$count++;return ($count % 2) ? true : false;});$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);$this->resetAfterTest(true);$this->setAdminuser();$this->getDataGenerator()->enrol_user($user->id, $course1->id);$this->getDataGenerator()->enrol_user($user->id, $course2->id);for ($i = 1; $i < 41; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course1->id,]);}for ($i = 41; $i < 81; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course2->id,]);}$events = $vault->get_action_events_by_course($user, $course1, 3, 35, null, 5);$this->assertCount(5, $events);$this->assertEquals('Event 3', $events[0]->get_name());$this->assertEquals('Event 5', $events[1]->get_name());$this->assertEquals('Event 7', $events[2]->get_name());$this->assertEquals('Event 9', $events[3]->get_name());$this->assertEquals('Event 11', $events[4]->get_name());$afterevent = $events[4];$events = $vault->get_action_events_by_course($user, $course1, 3, 35, $afterevent, 5);$this->assertCount(5, $events);$this->assertEquals('Event 13', $events[0]->get_name());$this->assertEquals('Event 15', $events[1]->get_name());$this->assertEquals('Event 17', $events[2]->get_name());$this->assertEquals('Event 19', $events[3]->get_name());$this->assertEquals('Event 21', $events[4]->get_name());}/*** Test that get_action_events_by_course returns events between the* provided timesort values. The database will continue to be read until the* number of events requested has been satisfied. In this case the first* five events are rejected so it should require two database requests.*/public function test_get_action_events_by_course_between_time_skip_first_records(): void {$user = $this->getDataGenerator()->create_user();$course1 = $this->getDataGenerator()->create_course();$course2 = $this->getDataGenerator()->create_course();$limit = 5;$seen = 0;// The factory will skip the first $limit events.$factory = new action_event_test_factory(function($actionevent) use (&$seen, $limit) {if ($seen < $limit) {$seen++;return false;} else {return true;}});$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);$this->resetAfterTest(true);$this->setAdminuser();$this->getDataGenerator()->enrol_user($user->id, $course1->id);$this->getDataGenerator()->enrol_user($user->id, $course2->id);for ($i = 1; $i < 21; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course1->id,]);}for ($i = 21; $i < 41; $i++) {create_event(['name' => sprintf('Event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course2->id,]);}$events = $vault->get_action_events_by_course($user, $course1, 1, 20, null, $limit);$this->assertCount($limit, $events);$this->assertEquals(sprintf('Event %d', $limit + 1), $events[0]->get_name());$this->assertEquals(sprintf('Event %d', $limit + 2), $events[1]->get_name());$this->assertEquals(sprintf('Event %d', $limit + 3), $events[2]->get_name());$this->assertEquals(sprintf('Event %d', $limit + 4), $events[3]->get_name());$this->assertEquals(sprintf('Event %d', $limit + 5), $events[4]->get_name());}/*** Test that get_action_events_by_course returns events between the* provided timesort values and after the last seen event when one is* provided. This should work even when the event ids aren't ordered the* same as the timesort order.*/public function test_get_action_events_by_course_non_consecutive_ids(): void {$this->resetAfterTest(true);$this->setAdminuser();$user = $this->getDataGenerator()->create_user();$course1 = $this->getDataGenerator()->create_course();$course2 = $this->getDataGenerator()->create_course();$factory = new action_event_test_factory();$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);$this->setAdminuser();$this->getDataGenerator()->enrol_user($user->id, $course1->id);$this->getDataGenerator()->enrol_user($user->id, $course2->id);/** The events should be ordered by timesort as follows:** 1 event 1* 2 event 1* 1 event 2* 2 event 2* 1 event 3* 2 event 3* 1 event 4* 2 event 4* 1 event 5* 2 event 5* 1 event 6* 2 event 6* 1 event 7* 2 event 7* 1 event 8* 2 event 8* 1 event 9* 2 event 9* 1 event 10* 2 event 10*/$records = [];for ($i = 1; $i < 11; $i++) {$records[] = create_event(['name' => sprintf('1 event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course1->id,]);}for ($i = 1; $i < 11; $i++) {$records[] = create_event(['name' => sprintf('2 event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course1->id,]);}// Create events for the other course.for ($i = 1; $i < 11; $i++) {$records[] = create_event(['name' => sprintf('3 event %d', $i),'eventtype' => 'user','userid' => $user->id,'timesort' => $i,'type' => CALENDAR_EVENT_TYPE_ACTION,'courseid' => $course2->id,]);}/** Expected result set:** 2 event 4* 1 event 5* 2 event 5* 1 event 6* 2 event 6* 1 event 7* 2 event 7* 1 event 8* 2 event 8*/$aftereventid = $records[3]->id;$afterevent = $vault->get_event_by_id($aftereventid);// Offset results by event with name "1 event 4" which has the same timesort// value as the lower boundary of this query (3). Confirm that the given// $afterevent is used to ignore events with the same timesortfrom values.$events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent);$this->assertCount(9, $events);$this->assertEquals('2 event 4', $events[0]->get_name());$this->assertEquals('2 event 8', $events[8]->get_name());/** Expected result set:** 2 event 4* 1 event 5*/$events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent, 2);$this->assertCount(2, $events);$this->assertEquals('2 event 4', $events[0]->get_name());$this->assertEquals('1 event 5', $events[1]->get_name());/** Expected result set:** 2 event 8*/$aftereventid = $records[7]->id;$afterevent = $vault->get_event_by_id($aftereventid);// Offset results by event with name "1 event 8" which has the same timesort// value as the upper boundary of this query (8). Confirm that the given// $afterevent is used to ignore events with the same timesortto values.$events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent);$this->assertCount(1, $events);$this->assertEquals('2 event 8', $events[0]->get_name());/** Expected empty result set.*/$aftereventid = $records[18]->id;$afterevent = $vault->get_event_by_id($aftereventid);// Offset results by event with name "2 event 9" which has a timesort// value larger than the upper boundary of this query (9 > 8). Confirm// that the given $afterevent is used for filtering events.$events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent);$this->assertEmpty($events);}/*** There are subtle cases where the priority of an event override may be identical to another.* For example, if you duplicate a group override, but make it apply to a different group. Now* there are two overrides with exactly the same overridden dates. In this case the priority of* both is 1.** In this situation:* - A user in group A should see only the A override* - A user in group B should see only the B override* - A user in both A and B should see both*/public function test_get_action_events_by_course_with_identical_group_override_priorities(): void {$this->resetAfterTest();$this->setAdminuser();$course = $this->getDataGenerator()->create_course();// Create an assign instance.$assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign');$assigninstance = $assigngenerator->create_instance(['course' => $course->id]);// Create users.$users = ['Only in group A' => $this->getDataGenerator()->create_user(),'Only in group B' => $this->getDataGenerator()->create_user(),'In group A and B' => $this->getDataGenerator()->create_user(),'In no groups' => $this->getDataGenerator()->create_user()];// Enrol users.foreach ($users as $user) {$this->getDataGenerator()->enrol_user($user->id, $course->id);}// Create groups.$groupa = $this->getDataGenerator()->create_group(['courseid' => $course->id]);$groupb = $this->getDataGenerator()->create_group(['courseid' => $course->id]);// Add members to groups.// Group A.$this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['Only in group A']->id]);$this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['In group A and B']->id]);// Group B.$this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['Only in group B']->id]);$this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['In group A and B']->id]);// Events with the same module name, instance and event type.$events = [['name' => 'Assignment 1 due date - Group A override','description' => '','format' => 1,'courseid' => $course->id,'groupid' => $groupa->id,'userid' => 2,'modulename' => 'assign','instance' => $assigninstance->id,'eventtype' => 'due','type' => CALENDAR_EVENT_TYPE_ACTION,'timestart' => 1,'timeduration' => 0,'visible' => 1,'priority' => 1],['name' => 'Assignment 1 due date - Group B override','description' => '','format' => 1,'courseid' => $course->id,'groupid' => $groupb->id,'userid' => 2,'modulename' => 'assign','instance' => $assigninstance->id,'eventtype' => 'due','type' => CALENDAR_EVENT_TYPE_ACTION,'timestart' => 1,'timeduration' => 0,'visible' => 1,'priority' => 1],['name' => 'Assignment 1 due date','description' => '','format' => 1,'courseid' => $course->id,'groupid' => 0,'userid' => 2,'modulename' => 'assign','instance' => $assigninstance->id,'eventtype' => 'due','type' => CALENDAR_EVENT_TYPE_ACTION,'timestart' => 1,'timeduration' => 0,'visible' => 1,'priority' => null,]];foreach ($events as $event) {\calendar_event::create($event, false);}$factory = new action_event_test_factory();$strategy = new raw_event_retrieval_strategy();$vault = new event_vault($factory, $strategy);$usersevents = array_reduce(array_keys($users), function($carry, $description) use ($users, $course, $vault) {// NB: This is currently needed to make get_action_events_by_timesort return the right thing.// It needs to be fixed, see MDL-58736.$this->setUser($users[$description]);return $carry + ['For user ' . lcfirst($description) => $vault->get_action_events_by_course($users[$description], $course)];}, []);foreach ($usersevents as $description => $userevents) {if ($description == 'For user in group A and B') {// User is in both A and B, so they should see the override for both// given that the priority is the same.$this->assertCount(2, $userevents);continue;}// Otherwise there should be only one assign event for each user.$this->assertCount(1, $userevents);}// User in only group A should see the group A override.$this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user only in group A'][0]->get_name());// User in only group B should see the group B override.$this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user only in group B'][0]->get_name());// User in group A and B should see see both overrides since the priorities are the same.$this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user in group A and B'][0]->get_name());$this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user in group A and B'][1]->get_name());// User in no groups should see the plain assignment event.$this->assertEquals('Assignment 1 due date', $usersevents['For user in no groups'][0]->get_name());}}