See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401]
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 namespace core_calendar; 18 19 use action_event_test_factory; 20 use core_calendar\local\event\data_access\event_vault; 21 use core_calendar\local\event\strategies\raw_event_retrieval_strategy; 22 23 defined('MOODLE_INTERNAL') || die(); 24 25 global $CFG; 26 require_once($CFG->dirroot . '/calendar/tests/helpers.php'); 27 28 /** 29 * This file contains the class that handles testing of the calendar event vault. 30 * 31 * @package core_calendar 32 * @copyright 2017 Ryan Wyllie <ryan@moodle.com> 33 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 34 */ 35 class event_vault_test extends \advanced_testcase { 36 37 /** 38 * Test that get_action_events_by_timesort returns events after the 39 * provided timesort value. 40 */ 41 public function test_get_action_events_by_timesort_after_time() { 42 $this->resetAfterTest(true); 43 44 $user = $this->getDataGenerator()->create_user(); 45 $factory = new action_event_test_factory(); 46 $strategy = new raw_event_retrieval_strategy(); 47 $vault = new event_vault($factory, $strategy); 48 49 $this->setUser($user); 50 51 for ($i = 1; $i < 6; $i++) { 52 create_event([ 53 'name' => sprintf('Event %d', $i), 54 'eventtype' => 'user', 55 'userid' => $user->id, 56 'timesort' => $i, 57 'type' => CALENDAR_EVENT_TYPE_ACTION 58 ]); 59 } 60 61 $events = $vault->get_action_events_by_timesort($user, 3); 62 63 $this->assertCount(3, $events); 64 $this->assertEquals('Event 3', $events[0]->get_name()); 65 $this->assertEquals('Event 4', $events[1]->get_name()); 66 $this->assertEquals('Event 5', $events[2]->get_name()); 67 68 $events = $vault->get_action_events_by_timesort($user, 3, null, null, 1); 69 70 $this->assertCount(1, $events); 71 $this->assertEquals('Event 3', $events[0]->get_name()); 72 73 $events = $vault->get_action_events_by_timesort($user, 6); 74 75 $this->assertCount(0, $events); 76 } 77 78 /** 79 * Test that get_action_events_by_timesort returns events before the 80 * provided timesort value. 81 */ 82 public function test_get_action_events_by_timesort_before_time() { 83 $this->resetAfterTest(true); 84 $this->setAdminuser(); 85 86 $user = $this->getDataGenerator()->create_user(); 87 $factory = new action_event_test_factory(); 88 $strategy = new raw_event_retrieval_strategy(); 89 $vault = new event_vault($factory, $strategy); 90 91 for ($i = 1; $i < 6; $i++) { 92 create_event([ 93 'name' => sprintf('Event %d', $i), 94 'eventtype' => 'user', 95 'userid' => $user->id, 96 'timesort' => $i, 97 'type' => CALENDAR_EVENT_TYPE_ACTION, 98 'courseid' => 1 99 ]); 100 } 101 102 $events = $vault->get_action_events_by_timesort($user, null, 3); 103 104 $this->assertCount(3, $events); 105 $this->assertEquals('Event 1', $events[0]->get_name()); 106 $this->assertEquals('Event 2', $events[1]->get_name()); 107 $this->assertEquals('Event 3', $events[2]->get_name()); 108 109 $events = $vault->get_action_events_by_timesort($user, null, 3, null, 1); 110 111 $this->assertCount(1, $events); 112 $this->assertEquals('Event 1', $events[0]->get_name()); 113 114 $events = $vault->get_action_events_by_timesort($user, 6); 115 116 $this->assertCount(0, $events); 117 } 118 119 /** 120 * Test that get_action_events_by_timesort returns events between the 121 * provided timesort values. 122 */ 123 public function test_get_action_events_by_timesort_between_time() { 124 $this->resetAfterTest(true); 125 $this->setAdminuser(); 126 127 $user = $this->getDataGenerator()->create_user(); 128 $factory = new action_event_test_factory(); 129 $strategy = new raw_event_retrieval_strategy(); 130 $vault = new event_vault($factory, $strategy); 131 132 for ($i = 1; $i < 6; $i++) { 133 create_event([ 134 'name' => sprintf('Event %d', $i), 135 'eventtype' => 'user', 136 'userid' => $user->id, 137 'timesort' => $i, 138 'type' => CALENDAR_EVENT_TYPE_ACTION, 139 'courseid' => 1 140 ]); 141 } 142 143 $events = $vault->get_action_events_by_timesort($user, 2, 4); 144 145 $this->assertCount(3, $events); 146 $this->assertEquals('Event 2', $events[0]->get_name()); 147 $this->assertEquals('Event 3', $events[1]->get_name()); 148 $this->assertEquals('Event 4', $events[2]->get_name()); 149 150 $events = $vault->get_action_events_by_timesort($user, 2, 4, null, 1); 151 152 $this->assertCount(1, $events); 153 $this->assertEquals('Event 2', $events[0]->get_name()); 154 } 155 156 /** 157 * Test that get_action_events_by_timesort returns events between the 158 * provided timesort values and after the last seen event when one is 159 * provided. 160 */ 161 public function test_get_action_events_by_timesort_between_time_after_event() { 162 $this->resetAfterTest(true); 163 $this->setAdminuser(); 164 165 $user = $this->getDataGenerator()->create_user(); 166 $factory = new action_event_test_factory(); 167 $strategy = new raw_event_retrieval_strategy(); 168 $vault = new event_vault($factory, $strategy); 169 170 $records = []; 171 for ($i = 1; $i < 21; $i++) { 172 $records[] = create_event([ 173 'name' => sprintf('Event %d', $i), 174 'eventtype' => 'user', 175 'userid' => $user->id, 176 'timesort' => $i, 177 'type' => CALENDAR_EVENT_TYPE_ACTION, 178 'courseid' => 1 179 ]); 180 } 181 182 $aftereventid = $records[6]->id; 183 $afterevent = $vault->get_event_by_id($aftereventid); 184 $events = $vault->get_action_events_by_timesort($user, 3, 15, $afterevent); 185 186 $this->assertCount(8, $events); 187 $this->assertEquals('Event 8', $events[0]->get_name()); 188 189 $events = $vault->get_action_events_by_timesort($user, 3, 15, $afterevent, 3); 190 191 $this->assertCount(3, $events); 192 } 193 194 /** 195 * Test that get_action_events_by_timesort returns events between the 196 * provided timesort values and the last seen event can be provided to 197 * get paginated results. 198 */ 199 public function test_get_action_events_by_timesort_between_time_skip_even_records() { 200 $this->resetAfterTest(true); 201 $this->setAdminuser(); 202 203 $user = $this->getDataGenerator()->create_user(); 204 // The factory will return every event that is divisible by 2. 205 $factory = new action_event_test_factory(function($actionevent) { 206 static $count = 0; 207 $count++; 208 return ($count % 2) ? true : false; 209 }); 210 $strategy = new raw_event_retrieval_strategy(); 211 $vault = new event_vault($factory, $strategy); 212 213 for ($i = 1; $i < 41; $i++) { 214 create_event([ 215 'name' => sprintf('Event %d', $i), 216 'eventtype' => 'user', 217 'userid' => $user->id, 218 'timesort' => $i, 219 'type' => CALENDAR_EVENT_TYPE_ACTION, 220 'courseid' => 1 221 ]); 222 } 223 224 $events = $vault->get_action_events_by_timesort($user, 3, 35, null, 5); 225 226 $this->assertCount(5, $events); 227 $this->assertEquals('Event 3', $events[0]->get_name()); 228 $this->assertEquals('Event 5', $events[1]->get_name()); 229 $this->assertEquals('Event 7', $events[2]->get_name()); 230 $this->assertEquals('Event 9', $events[3]->get_name()); 231 $this->assertEquals('Event 11', $events[4]->get_name()); 232 233 $afterevent = $events[4]; 234 $events = $vault->get_action_events_by_timesort($user, 3, 35, $afterevent, 5); 235 236 $this->assertCount(5, $events); 237 $this->assertEquals('Event 13', $events[0]->get_name()); 238 $this->assertEquals('Event 15', $events[1]->get_name()); 239 $this->assertEquals('Event 17', $events[2]->get_name()); 240 $this->assertEquals('Event 19', $events[3]->get_name()); 241 $this->assertEquals('Event 21', $events[4]->get_name()); 242 } 243 244 /** 245 * Test that get_action_events_by_timesort returns events between the 246 * provided timesort values. The database will continue to be read until the 247 * number of events requested has been satisfied. In this case the first 248 * five events are rejected so it should require two database requests. 249 */ 250 public function test_get_action_events_by_timesort_between_time_skip_first_records() { 251 $this->resetAfterTest(true); 252 $this->setAdminuser(); 253 254 $user = $this->getDataGenerator()->create_user(); 255 $limit = 5; 256 $seen = 0; 257 // The factory will skip the first $limit events. 258 $factory = new action_event_test_factory(function($actionevent) use (&$seen, $limit) { 259 if ($seen < $limit) { 260 $seen++; 261 return false; 262 } else { 263 return true; 264 } 265 }); 266 $strategy = new raw_event_retrieval_strategy(); 267 $vault = new event_vault($factory, $strategy); 268 269 for ($i = 1; $i < 21; $i++) { 270 create_event([ 271 'name' => sprintf('Event %d', $i), 272 'eventtype' => 'user', 273 'userid' => $user->id, 274 'timesort' => $i, 275 'type' => CALENDAR_EVENT_TYPE_ACTION, 276 'courseid' => 1 277 ]); 278 } 279 280 $events = $vault->get_action_events_by_timesort($user, 1, 20, null, $limit); 281 282 $this->assertCount($limit, $events); 283 $this->assertEquals(sprintf('Event %d', $limit + 1), $events[0]->get_name()); 284 $this->assertEquals(sprintf('Event %d', $limit + 2), $events[1]->get_name()); 285 $this->assertEquals(sprintf('Event %d', $limit + 3), $events[2]->get_name()); 286 $this->assertEquals(sprintf('Event %d', $limit + 4), $events[3]->get_name()); 287 $this->assertEquals(sprintf('Event %d', $limit + 5), $events[4]->get_name()); 288 } 289 290 /** 291 * Test that get_action_events_by_timesort returns events between the 292 * provided timesort values and after the last seen event when one is 293 * provided. This should work even when the event ids aren't ordered the 294 * same as the timesort order. 295 */ 296 public function test_get_action_events_by_timesort_non_consecutive_ids() { 297 $this->resetAfterTest(true); 298 $this->setAdminuser(); 299 300 $user = $this->getDataGenerator()->create_user(); 301 $factory = new action_event_test_factory(); 302 $strategy = new raw_event_retrieval_strategy(); 303 $vault = new event_vault($factory, $strategy); 304 305 /* 306 * The events should be ordered by timesort as follows: 307 * 308 * 1 event 1 309 * 2 event 1 310 * 1 event 2 311 * 2 event 2 312 * 1 event 3 313 * 2 event 3 314 * 1 event 4 315 * 2 event 4 316 * 1 event 5 317 * 2 event 5 318 * 1 event 6 319 * 2 event 6 320 * 1 event 7 321 * 2 event 7 322 * 1 event 8 323 * 2 event 8 324 * 1 event 9 325 * 2 event 9 326 * 1 event 10 327 * 2 event 10 328 */ 329 $records = []; 330 for ($i = 1; $i < 11; $i++) { 331 $records[] = create_event([ 332 'name' => sprintf('1 event %d', $i), 333 'eventtype' => 'user', 334 'userid' => $user->id, 335 'timesort' => $i, 336 'type' => CALENDAR_EVENT_TYPE_ACTION, 337 'courseid' => 1 338 ]); 339 } 340 341 for ($i = 1; $i < 11; $i++) { 342 $records[] = create_event([ 343 'name' => sprintf('2 event %d', $i), 344 'eventtype' => 'user', 345 'userid' => $user->id, 346 'timesort' => $i, 347 'type' => CALENDAR_EVENT_TYPE_ACTION, 348 'courseid' => 1 349 ]); 350 } 351 352 /* 353 * Expected result set: 354 * 355 * 2 event 4 356 * 1 event 5 357 * 2 event 5 358 * 1 event 6 359 * 2 event 6 360 * 1 event 7 361 * 2 event 7 362 * 1 event 8 363 * 2 event 8 364 */ 365 $aftereventid = $records[3]->id; 366 $afterevent = $vault->get_event_by_id($aftereventid); 367 // Offset results by event with name "1 event 4" which has the same timesort 368 // value as the lower boundary of this query (3). Confirm that the given 369 // $afterevent is used to ignore events with the same timesortfrom values. 370 $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent); 371 372 $this->assertCount(9, $events); 373 $this->assertEquals('2 event 4', $events[0]->get_name()); 374 $this->assertEquals('2 event 8', $events[8]->get_name()); 375 376 /* 377 * Expected result set: 378 * 379 * 2 event 4 380 * 1 event 5 381 */ 382 $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent, 2); 383 384 $this->assertCount(2, $events); 385 $this->assertEquals('2 event 4', $events[0]->get_name()); 386 $this->assertEquals('1 event 5', $events[1]->get_name()); 387 388 /* 389 * Expected result set: 390 * 391 * 2 event 8 392 */ 393 $aftereventid = $records[7]->id; 394 $afterevent = $vault->get_event_by_id($aftereventid); 395 // Offset results by event with name "1 event 8" which has the same timesort 396 // value as the upper boundary of this query (8). Confirm that the given 397 // $afterevent is used to ignore events with the same timesortto values. 398 $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent); 399 400 $this->assertCount(1, $events); 401 $this->assertEquals('2 event 8', $events[0]->get_name()); 402 403 /* 404 * Expected empty result set. 405 */ 406 $aftereventid = $records[18]->id; 407 $afterevent = $vault->get_event_by_id($aftereventid); 408 // Offset results by event with name "2 event 9" which has a timesort 409 // value larger than the upper boundary of this query (9 > 8). Confirm 410 // that the given $afterevent is used for filtering events. 411 $events = $vault->get_action_events_by_timesort($user, 3, 8, $afterevent); 412 $this->assertEmpty($events); 413 } 414 415 /** 416 * There are subtle cases where the priority of an event override may be identical to another. 417 * For example, if you duplicate a group override, but make it apply to a different group. Now 418 * there are two overrides with exactly the same overridden dates. In this case the priority of 419 * both is 1. 420 * 421 * In this situation: 422 * - A user in group A should see only the A override 423 * - A user in group B should see only the B override 424 * - A user in both A and B should see both 425 */ 426 public function test_get_action_events_by_timesort_with_identical_group_override_priorities() { 427 $this->resetAfterTest(); 428 $this->setAdminuser(); 429 430 $course = $this->getDataGenerator()->create_course(); 431 432 // Create an assign instance. 433 $assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign'); 434 $assigninstance = $assigngenerator->create_instance(['course' => $course->id]); 435 436 // Create users. 437 $users = [ 438 'Only in group A' => $this->getDataGenerator()->create_user(), 439 'Only in group B' => $this->getDataGenerator()->create_user(), 440 'In group A and B' => $this->getDataGenerator()->create_user(), 441 'In no groups' => $this->getDataGenerator()->create_user() 442 ]; 443 444 // Enrol users. 445 foreach ($users as $user) { 446 $this->getDataGenerator()->enrol_user($user->id, $course->id); 447 } 448 449 // Create groups. 450 $groupa = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 451 $groupb = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 452 453 // Add members to groups. 454 // Group A. 455 $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['Only in group A']->id]); 456 $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['In group A and B']->id]); 457 458 // Group B. 459 $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['Only in group B']->id]); 460 $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['In group A and B']->id]); 461 462 // Events with the same module name, instance and event type. 463 $events = [ 464 [ 465 'name' => 'Assignment 1 due date - Group A override', 466 'description' => '', 467 'format' => 1, 468 'courseid' => $course->id, 469 'groupid' => $groupa->id, 470 'userid' => 2, 471 'modulename' => 'assign', 472 'instance' => $assigninstance->id, 473 'eventtype' => 'due', 474 'type' => CALENDAR_EVENT_TYPE_ACTION, 475 'timestart' => 1, 476 'timeduration' => 0, 477 'visible' => 1, 478 'priority' => 1 479 ], 480 [ 481 'name' => 'Assignment 1 due date - Group B override', 482 'description' => '', 483 'format' => 1, 484 'courseid' => $course->id, 485 'groupid' => $groupb->id, 486 'userid' => 2, 487 'modulename' => 'assign', 488 'instance' => $assigninstance->id, 489 'eventtype' => 'due', 490 'type' => CALENDAR_EVENT_TYPE_ACTION, 491 'timestart' => 1, 492 'timeduration' => 0, 493 'visible' => 1, 494 'priority' => 1 495 ], 496 [ 497 'name' => 'Assignment 1 due date', 498 'description' => '', 499 'format' => 1, 500 'courseid' => $course->id, 501 'groupid' => 0, 502 'userid' => 2, 503 'modulename' => 'assign', 504 'instance' => $assigninstance->id, 505 'eventtype' => 'due', 506 'type' => CALENDAR_EVENT_TYPE_ACTION, 507 'timestart' => 1, 508 'timeduration' => 0, 509 'visible' => 1, 510 'priority' => null, 511 ] 512 ]; 513 514 foreach ($events as $event) { 515 \calendar_event::create($event, false); 516 } 517 518 $factory = new action_event_test_factory(); 519 $strategy = new raw_event_retrieval_strategy(); 520 $vault = new event_vault($factory, $strategy); 521 522 $usersevents = array_reduce(array_keys($users), function($carry, $description) use ($users, $vault) { 523 // NB: This is currently needed to make get_action_events_by_timesort return the right thing. 524 // It needs to be fixed, see MDL-58736. 525 $this->setUser($users[$description]); 526 return $carry + ['For user ' . lcfirst($description) => $vault->get_action_events_by_timesort($users[$description])]; 527 }, []); 528 529 foreach ($usersevents as $description => $userevents) { 530 if ($description == 'For user in group A and B') { 531 // User is in both A and B, so they should see the override for both 532 // given that the priority is the same. 533 $this->assertCount(2, $userevents); 534 continue; 535 } 536 537 // Otherwise there should be only one assign event for each user. 538 $this->assertCount(1, $userevents); 539 } 540 541 // User in only group A should see the group A override. 542 $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user only in group A'][0]->get_name()); 543 544 // User in only group B should see the group B override. 545 $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user only in group B'][0]->get_name()); 546 547 // User in group A and B should see see both overrides since the priorities are the same. 548 $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user in group A and B'][0]->get_name()); 549 $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user in group A and B'][1]->get_name()); 550 551 // User in no groups should see the plain assignment event. 552 $this->assertEquals('Assignment 1 due date', $usersevents['For user in no groups'][0]->get_name()); 553 } 554 555 /** 556 * Test that if a user is suspended that events related to that course are not shown. 557 * User 1 is suspended. User 2 is active. 558 */ 559 public function test_get_action_events_by_timesort_with_suspended_user() { 560 $this->resetAfterTest(); 561 $user1 = $this->getDataGenerator()->create_user(); 562 $user2 = $this->getDataGenerator()->create_user(); 563 $course = $this->getDataGenerator()->create_course(); 564 $this->setAdminuser(); 565 $lesson = $this->getDataGenerator()->create_module('lesson', [ 566 'name' => 'Lesson 1', 567 'course' => $course->id, 568 'available' => time(), 569 'deadline' => (time() + (60 * 60 * 24 * 5)) 570 ] 571 ); 572 $this->getDataGenerator()->enrol_user($user1->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED); 573 $this->getDataGenerator()->enrol_user($user2->id, $course->id); 574 575 $factory = new action_event_test_factory(); 576 $strategy = new raw_event_retrieval_strategy(); 577 $vault = new event_vault($factory, $strategy); 578 579 $user1events = $vault->get_action_events_by_timesort($user1, null, null, null, 20, true); 580 $this->assertEmpty($user1events); 581 $user2events = $vault->get_action_events_by_timesort($user2, null, null, null, 20, true); 582 $this->assertCount(1, $user2events); 583 $this->assertEquals('Lesson 1 closes', $user2events[0]->get_name()); 584 } 585 586 /** 587 * Test that get_action_events_by_course returns events after the 588 * provided timesort value. 589 */ 590 public function test_get_action_events_by_course_after_time() { 591 $user = $this->getDataGenerator()->create_user(); 592 $course1 = $this->getDataGenerator()->create_course(); 593 $course2 = $this->getDataGenerator()->create_course(); 594 $factory = new action_event_test_factory(); 595 $strategy = new raw_event_retrieval_strategy(); 596 $vault = new event_vault($factory, $strategy); 597 598 $this->resetAfterTest(true); 599 $this->setAdminuser(); 600 $this->getDataGenerator()->enrol_user($user->id, $course1->id); 601 $this->getDataGenerator()->enrol_user($user->id, $course2->id); 602 603 for ($i = 1; $i < 6; $i++) { 604 create_event([ 605 'name' => sprintf('Event %d', $i), 606 'eventtype' => 'user', 607 'userid' => $user->id, 608 'timesort' => $i, 609 'type' => CALENDAR_EVENT_TYPE_ACTION, 610 'courseid' => $course1->id, 611 ]); 612 } 613 614 for ($i = 6; $i < 12; $i++) { 615 create_event([ 616 'name' => sprintf('Event %d', $i), 617 'eventtype' => 'user', 618 'userid' => $user->id, 619 'timesort' => $i, 620 'type' => CALENDAR_EVENT_TYPE_ACTION, 621 'courseid' => $course2->id, 622 ]); 623 } 624 625 $events = $vault->get_action_events_by_course($user, $course1, 3); 626 $this->assertCount(3, $events); 627 $this->assertEquals('Event 3', $events[0]->get_name()); 628 $this->assertEquals('Event 4', $events[1]->get_name()); 629 $this->assertEquals('Event 5', $events[2]->get_name()); 630 631 $events = $vault->get_action_events_by_course($user, $course1, 3, null, null, 1); 632 633 $this->assertCount(1, $events); 634 $this->assertEquals('Event 3', $events[0]->get_name()); 635 636 $events = $vault->get_action_events_by_course($user, $course1, 6); 637 638 $this->assertCount(0, $events); 639 } 640 641 /** 642 * Test that get_action_events_by_course returns events before the 643 * provided timesort value. 644 */ 645 public function test_get_action_events_by_course_before_time() { 646 $user = $this->getDataGenerator()->create_user(); 647 $course1 = $this->getDataGenerator()->create_course(); 648 $course2 = $this->getDataGenerator()->create_course(); 649 $factory = new action_event_test_factory(); 650 $strategy = new raw_event_retrieval_strategy(); 651 $vault = new event_vault($factory, $strategy); 652 653 $this->resetAfterTest(true); 654 $this->setAdminuser(); 655 $this->getDataGenerator()->enrol_user($user->id, $course1->id); 656 $this->getDataGenerator()->enrol_user($user->id, $course2->id); 657 658 for ($i = 1; $i < 6; $i++) { 659 create_event([ 660 'name' => sprintf('Event %d', $i), 661 'eventtype' => 'user', 662 'userid' => $user->id, 663 'timesort' => $i, 664 'type' => CALENDAR_EVENT_TYPE_ACTION, 665 'courseid' => $course1->id, 666 ]); 667 } 668 669 for ($i = 6; $i < 12; $i++) { 670 create_event([ 671 'name' => sprintf('Event %d', $i), 672 'eventtype' => 'user', 673 'userid' => $user->id, 674 'timesort' => $i, 675 'type' => CALENDAR_EVENT_TYPE_ACTION, 676 'courseid' => $course2->id, 677 ]); 678 } 679 680 $events = $vault->get_action_events_by_course($user, $course1, null, 3); 681 682 $this->assertCount(3, $events); 683 $this->assertEquals('Event 1', $events[0]->get_name()); 684 $this->assertEquals('Event 2', $events[1]->get_name()); 685 $this->assertEquals('Event 3', $events[2]->get_name()); 686 687 $events = $vault->get_action_events_by_course($user, $course1, null, 3, null, 1); 688 689 $this->assertCount(1, $events); 690 $this->assertEquals('Event 1', $events[0]->get_name()); 691 692 $events = $vault->get_action_events_by_course($user, $course1, 6); 693 694 $this->assertCount(0, $events); 695 } 696 697 /** 698 * Test that get_action_events_by_course returns events between the 699 * provided timesort values. 700 */ 701 public function test_get_action_events_by_course_between_time() { 702 $user = $this->getDataGenerator()->create_user(); 703 $course1 = $this->getDataGenerator()->create_course(); 704 $course2 = $this->getDataGenerator()->create_course(); 705 $factory = new action_event_test_factory(); 706 $strategy = new raw_event_retrieval_strategy(); 707 $vault = new event_vault($factory, $strategy); 708 709 $this->resetAfterTest(true); 710 $this->setAdminuser(); 711 $this->getDataGenerator()->enrol_user($user->id, $course1->id); 712 $this->getDataGenerator()->enrol_user($user->id, $course2->id); 713 714 for ($i = 1; $i < 6; $i++) { 715 create_event([ 716 'name' => sprintf('Event %d', $i), 717 'eventtype' => 'user', 718 'userid' => $user->id, 719 'timesort' => $i, 720 'type' => CALENDAR_EVENT_TYPE_ACTION, 721 'courseid' => $course1->id, 722 ]); 723 } 724 725 for ($i = 6; $i < 12; $i++) { 726 create_event([ 727 'name' => sprintf('Event %d', $i), 728 'eventtype' => 'user', 729 'userid' => $user->id, 730 'timesort' => $i, 731 'type' => CALENDAR_EVENT_TYPE_ACTION, 732 'courseid' => $course2->id, 733 ]); 734 } 735 736 $events = $vault->get_action_events_by_course($user, $course1, 2, 4); 737 738 $this->assertCount(3, $events); 739 $this->assertEquals('Event 2', $events[0]->get_name()); 740 $this->assertEquals('Event 3', $events[1]->get_name()); 741 $this->assertEquals('Event 4', $events[2]->get_name()); 742 743 $events = $vault->get_action_events_by_course($user, $course1, 2, 4, null, 1); 744 745 $this->assertCount(1, $events); 746 $this->assertEquals('Event 2', $events[0]->get_name()); 747 } 748 749 /** 750 * Test that get_action_events_by_course returns events between the 751 * provided timesort values and after the last seen event when one is 752 * provided. 753 */ 754 public function test_get_action_events_by_course_between_time_after_event() { 755 $user = $this->getDataGenerator()->create_user(); 756 $course1 = $this->getDataGenerator()->create_course(); 757 $course2 = $this->getDataGenerator()->create_course(); 758 $factory = new action_event_test_factory(); 759 $strategy = new raw_event_retrieval_strategy(); 760 $vault = new event_vault($factory, $strategy); 761 $records = []; 762 763 $this->resetAfterTest(true); 764 $this->setAdminuser(); 765 $this->getDataGenerator()->enrol_user($user->id, $course1->id); 766 $this->getDataGenerator()->enrol_user($user->id, $course2->id); 767 768 for ($i = 1; $i < 21; $i++) { 769 $records[] = create_event([ 770 'name' => sprintf('Event %d', $i), 771 'eventtype' => 'user', 772 'userid' => $user->id, 773 'timesort' => $i, 774 'type' => CALENDAR_EVENT_TYPE_ACTION, 775 'courseid' => $course1->id, 776 ]); 777 } 778 779 for ($i = 21; $i < 41; $i++) { 780 $records[] = create_event([ 781 'name' => sprintf('Event %d', $i), 782 'eventtype' => 'user', 783 'userid' => $user->id, 784 'timesort' => $i, 785 'type' => CALENDAR_EVENT_TYPE_ACTION, 786 'courseid' => $course2->id, 787 ]); 788 } 789 790 $aftereventid = $records[6]->id; 791 $afterevent = $vault->get_event_by_id($aftereventid); 792 $events = $vault->get_action_events_by_course($user, $course1, 3, 15, $afterevent); 793 794 $this->assertCount(8, $events); 795 $this->assertEquals('Event 8', $events[0]->get_name()); 796 797 $events = $vault->get_action_events_by_course($user, $course1, 3, 15, $afterevent, 3); 798 799 $this->assertCount(3, $events); 800 } 801 802 /** 803 * Test that get_action_events_by_course returns events between the 804 * provided timesort values and the last seen event can be provided to 805 * get paginated results. 806 */ 807 public function test_get_action_events_by_course_between_time_skip_even_records() { 808 $user = $this->getDataGenerator()->create_user(); 809 $course1 = $this->getDataGenerator()->create_course(); 810 $course2 = $this->getDataGenerator()->create_course(); 811 // The factory will return every event that is divisible by 2. 812 $factory = new action_event_test_factory(function($actionevent) { 813 static $count = 0; 814 $count++; 815 return ($count % 2) ? true : false; 816 }); 817 $strategy = new raw_event_retrieval_strategy(); 818 $vault = new event_vault($factory, $strategy); 819 820 $this->resetAfterTest(true); 821 $this->setAdminuser(); 822 $this->getDataGenerator()->enrol_user($user->id, $course1->id); 823 $this->getDataGenerator()->enrol_user($user->id, $course2->id); 824 825 for ($i = 1; $i < 41; $i++) { 826 create_event([ 827 'name' => sprintf('Event %d', $i), 828 'eventtype' => 'user', 829 'userid' => $user->id, 830 'timesort' => $i, 831 'type' => CALENDAR_EVENT_TYPE_ACTION, 832 'courseid' => $course1->id, 833 ]); 834 } 835 836 for ($i = 41; $i < 81; $i++) { 837 create_event([ 838 'name' => sprintf('Event %d', $i), 839 'eventtype' => 'user', 840 'userid' => $user->id, 841 'timesort' => $i, 842 'type' => CALENDAR_EVENT_TYPE_ACTION, 843 'courseid' => $course2->id, 844 ]); 845 } 846 847 $events = $vault->get_action_events_by_course($user, $course1, 3, 35, null, 5); 848 849 $this->assertCount(5, $events); 850 $this->assertEquals('Event 3', $events[0]->get_name()); 851 $this->assertEquals('Event 5', $events[1]->get_name()); 852 $this->assertEquals('Event 7', $events[2]->get_name()); 853 $this->assertEquals('Event 9', $events[3]->get_name()); 854 $this->assertEquals('Event 11', $events[4]->get_name()); 855 856 $afterevent = $events[4]; 857 $events = $vault->get_action_events_by_course($user, $course1, 3, 35, $afterevent, 5); 858 859 $this->assertCount(5, $events); 860 $this->assertEquals('Event 13', $events[0]->get_name()); 861 $this->assertEquals('Event 15', $events[1]->get_name()); 862 $this->assertEquals('Event 17', $events[2]->get_name()); 863 $this->assertEquals('Event 19', $events[3]->get_name()); 864 $this->assertEquals('Event 21', $events[4]->get_name()); 865 } 866 867 /** 868 * Test that get_action_events_by_course returns events between the 869 * provided timesort values. The database will continue to be read until the 870 * number of events requested has been satisfied. In this case the first 871 * five events are rejected so it should require two database requests. 872 */ 873 public function test_get_action_events_by_course_between_time_skip_first_records() { 874 $user = $this->getDataGenerator()->create_user(); 875 $course1 = $this->getDataGenerator()->create_course(); 876 $course2 = $this->getDataGenerator()->create_course(); 877 $limit = 5; 878 $seen = 0; 879 // The factory will skip the first $limit events. 880 $factory = new action_event_test_factory(function($actionevent) use (&$seen, $limit) { 881 if ($seen < $limit) { 882 $seen++; 883 return false; 884 } else { 885 return true; 886 } 887 }); 888 $strategy = new raw_event_retrieval_strategy(); 889 $vault = new event_vault($factory, $strategy); 890 891 $this->resetAfterTest(true); 892 $this->setAdminuser(); 893 $this->getDataGenerator()->enrol_user($user->id, $course1->id); 894 $this->getDataGenerator()->enrol_user($user->id, $course2->id); 895 896 for ($i = 1; $i < 21; $i++) { 897 create_event([ 898 'name' => sprintf('Event %d', $i), 899 'eventtype' => 'user', 900 'userid' => $user->id, 901 'timesort' => $i, 902 'type' => CALENDAR_EVENT_TYPE_ACTION, 903 'courseid' => $course1->id, 904 ]); 905 } 906 907 for ($i = 21; $i < 41; $i++) { 908 create_event([ 909 'name' => sprintf('Event %d', $i), 910 'eventtype' => 'user', 911 'userid' => $user->id, 912 'timesort' => $i, 913 'type' => CALENDAR_EVENT_TYPE_ACTION, 914 'courseid' => $course2->id, 915 ]); 916 } 917 918 $events = $vault->get_action_events_by_course($user, $course1, 1, 20, null, $limit); 919 920 $this->assertCount($limit, $events); 921 $this->assertEquals(sprintf('Event %d', $limit + 1), $events[0]->get_name()); 922 $this->assertEquals(sprintf('Event %d', $limit + 2), $events[1]->get_name()); 923 $this->assertEquals(sprintf('Event %d', $limit + 3), $events[2]->get_name()); 924 $this->assertEquals(sprintf('Event %d', $limit + 4), $events[3]->get_name()); 925 $this->assertEquals(sprintf('Event %d', $limit + 5), $events[4]->get_name()); 926 } 927 928 /** 929 * Test that get_action_events_by_course returns events between the 930 * provided timesort values and after the last seen event when one is 931 * provided. This should work even when the event ids aren't ordered the 932 * same as the timesort order. 933 */ 934 public function test_get_action_events_by_course_non_consecutive_ids() { 935 $this->resetAfterTest(true); 936 $this->setAdminuser(); 937 938 $user = $this->getDataGenerator()->create_user(); 939 $course1 = $this->getDataGenerator()->create_course(); 940 $course2 = $this->getDataGenerator()->create_course(); 941 $factory = new action_event_test_factory(); 942 $strategy = new raw_event_retrieval_strategy(); 943 $vault = new event_vault($factory, $strategy); 944 945 $this->setAdminuser(); 946 $this->getDataGenerator()->enrol_user($user->id, $course1->id); 947 $this->getDataGenerator()->enrol_user($user->id, $course2->id); 948 949 /* 950 * The events should be ordered by timesort as follows: 951 * 952 * 1 event 1 953 * 2 event 1 954 * 1 event 2 955 * 2 event 2 956 * 1 event 3 957 * 2 event 3 958 * 1 event 4 959 * 2 event 4 960 * 1 event 5 961 * 2 event 5 962 * 1 event 6 963 * 2 event 6 964 * 1 event 7 965 * 2 event 7 966 * 1 event 8 967 * 2 event 8 968 * 1 event 9 969 * 2 event 9 970 * 1 event 10 971 * 2 event 10 972 */ 973 $records = []; 974 for ($i = 1; $i < 11; $i++) { 975 $records[] = create_event([ 976 'name' => sprintf('1 event %d', $i), 977 'eventtype' => 'user', 978 'userid' => $user->id, 979 'timesort' => $i, 980 'type' => CALENDAR_EVENT_TYPE_ACTION, 981 'courseid' => $course1->id, 982 ]); 983 } 984 985 for ($i = 1; $i < 11; $i++) { 986 $records[] = create_event([ 987 'name' => sprintf('2 event %d', $i), 988 'eventtype' => 'user', 989 'userid' => $user->id, 990 'timesort' => $i, 991 'type' => CALENDAR_EVENT_TYPE_ACTION, 992 'courseid' => $course1->id, 993 ]); 994 } 995 996 // Create events for the other course. 997 for ($i = 1; $i < 11; $i++) { 998 $records[] = create_event([ 999 'name' => sprintf('3 event %d', $i), 1000 'eventtype' => 'user', 1001 'userid' => $user->id, 1002 'timesort' => $i, 1003 'type' => CALENDAR_EVENT_TYPE_ACTION, 1004 'courseid' => $course2->id, 1005 ]); 1006 } 1007 1008 /* 1009 * Expected result set: 1010 * 1011 * 2 event 4 1012 * 1 event 5 1013 * 2 event 5 1014 * 1 event 6 1015 * 2 event 6 1016 * 1 event 7 1017 * 2 event 7 1018 * 1 event 8 1019 * 2 event 8 1020 */ 1021 $aftereventid = $records[3]->id; 1022 $afterevent = $vault->get_event_by_id($aftereventid); 1023 // Offset results by event with name "1 event 4" which has the same timesort 1024 // value as the lower boundary of this query (3). Confirm that the given 1025 // $afterevent is used to ignore events with the same timesortfrom values. 1026 $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent); 1027 1028 $this->assertCount(9, $events); 1029 $this->assertEquals('2 event 4', $events[0]->get_name()); 1030 $this->assertEquals('2 event 8', $events[8]->get_name()); 1031 1032 /* 1033 * Expected result set: 1034 * 1035 * 2 event 4 1036 * 1 event 5 1037 */ 1038 $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent, 2); 1039 1040 $this->assertCount(2, $events); 1041 $this->assertEquals('2 event 4', $events[0]->get_name()); 1042 $this->assertEquals('1 event 5', $events[1]->get_name()); 1043 1044 /* 1045 * Expected result set: 1046 * 1047 * 2 event 8 1048 */ 1049 $aftereventid = $records[7]->id; 1050 $afterevent = $vault->get_event_by_id($aftereventid); 1051 // Offset results by event with name "1 event 8" which has the same timesort 1052 // value as the upper boundary of this query (8). Confirm that the given 1053 // $afterevent is used to ignore events with the same timesortto values. 1054 $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent); 1055 1056 $this->assertCount(1, $events); 1057 $this->assertEquals('2 event 8', $events[0]->get_name()); 1058 1059 /* 1060 * Expected empty result set. 1061 */ 1062 $aftereventid = $records[18]->id; 1063 $afterevent = $vault->get_event_by_id($aftereventid); 1064 // Offset results by event with name "2 event 9" which has a timesort 1065 // value larger than the upper boundary of this query (9 > 8). Confirm 1066 // that the given $afterevent is used for filtering events. 1067 $events = $vault->get_action_events_by_course($user, $course1, 3, 8, $afterevent); 1068 1069 $this->assertEmpty($events); 1070 } 1071 1072 /** 1073 * There are subtle cases where the priority of an event override may be identical to another. 1074 * For example, if you duplicate a group override, but make it apply to a different group. Now 1075 * there are two overrides with exactly the same overridden dates. In this case the priority of 1076 * both is 1. 1077 * 1078 * In this situation: 1079 * - A user in group A should see only the A override 1080 * - A user in group B should see only the B override 1081 * - A user in both A and B should see both 1082 */ 1083 public function test_get_action_events_by_course_with_identical_group_override_priorities() { 1084 $this->resetAfterTest(); 1085 $this->setAdminuser(); 1086 1087 $course = $this->getDataGenerator()->create_course(); 1088 1089 // Create an assign instance. 1090 $assigngenerator = $this->getDataGenerator()->get_plugin_generator('mod_assign'); 1091 $assigninstance = $assigngenerator->create_instance(['course' => $course->id]); 1092 1093 // Create users. 1094 $users = [ 1095 'Only in group A' => $this->getDataGenerator()->create_user(), 1096 'Only in group B' => $this->getDataGenerator()->create_user(), 1097 'In group A and B' => $this->getDataGenerator()->create_user(), 1098 'In no groups' => $this->getDataGenerator()->create_user() 1099 ]; 1100 1101 // Enrol users. 1102 foreach ($users as $user) { 1103 $this->getDataGenerator()->enrol_user($user->id, $course->id); 1104 } 1105 1106 // Create groups. 1107 $groupa = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 1108 $groupb = $this->getDataGenerator()->create_group(['courseid' => $course->id]); 1109 1110 // Add members to groups. 1111 // Group A. 1112 $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['Only in group A']->id]); 1113 $this->getDataGenerator()->create_group_member(['groupid' => $groupa->id, 'userid' => $users['In group A and B']->id]); 1114 1115 // Group B. 1116 $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['Only in group B']->id]); 1117 $this->getDataGenerator()->create_group_member(['groupid' => $groupb->id, 'userid' => $users['In group A and B']->id]); 1118 1119 // Events with the same module name, instance and event type. 1120 $events = [ 1121 [ 1122 'name' => 'Assignment 1 due date - Group A override', 1123 'description' => '', 1124 'format' => 1, 1125 'courseid' => $course->id, 1126 'groupid' => $groupa->id, 1127 'userid' => 2, 1128 'modulename' => 'assign', 1129 'instance' => $assigninstance->id, 1130 'eventtype' => 'due', 1131 'type' => CALENDAR_EVENT_TYPE_ACTION, 1132 'timestart' => 1, 1133 'timeduration' => 0, 1134 'visible' => 1, 1135 'priority' => 1 1136 ], 1137 [ 1138 'name' => 'Assignment 1 due date - Group B override', 1139 'description' => '', 1140 'format' => 1, 1141 'courseid' => $course->id, 1142 'groupid' => $groupb->id, 1143 'userid' => 2, 1144 'modulename' => 'assign', 1145 'instance' => $assigninstance->id, 1146 'eventtype' => 'due', 1147 'type' => CALENDAR_EVENT_TYPE_ACTION, 1148 'timestart' => 1, 1149 'timeduration' => 0, 1150 'visible' => 1, 1151 'priority' => 1 1152 ], 1153 [ 1154 'name' => 'Assignment 1 due date', 1155 'description' => '', 1156 'format' => 1, 1157 'courseid' => $course->id, 1158 'groupid' => 0, 1159 'userid' => 2, 1160 'modulename' => 'assign', 1161 'instance' => $assigninstance->id, 1162 'eventtype' => 'due', 1163 'type' => CALENDAR_EVENT_TYPE_ACTION, 1164 'timestart' => 1, 1165 'timeduration' => 0, 1166 'visible' => 1, 1167 'priority' => null, 1168 ] 1169 ]; 1170 1171 foreach ($events as $event) { 1172 \calendar_event::create($event, false); 1173 } 1174 1175 $factory = new action_event_test_factory(); 1176 $strategy = new raw_event_retrieval_strategy(); 1177 $vault = new event_vault($factory, $strategy); 1178 1179 $usersevents = array_reduce(array_keys($users), function($carry, $description) use ($users, $course, $vault) { 1180 // NB: This is currently needed to make get_action_events_by_timesort return the right thing. 1181 // It needs to be fixed, see MDL-58736. 1182 $this->setUser($users[$description]); 1183 return $carry + [ 1184 'For user ' . lcfirst($description) => $vault->get_action_events_by_course($users[$description], $course) 1185 ]; 1186 }, []); 1187 1188 foreach ($usersevents as $description => $userevents) { 1189 if ($description == 'For user in group A and B') { 1190 // User is in both A and B, so they should see the override for both 1191 // given that the priority is the same. 1192 $this->assertCount(2, $userevents); 1193 continue; 1194 } 1195 1196 // Otherwise there should be only one assign event for each user. 1197 $this->assertCount(1, $userevents); 1198 } 1199 1200 // User in only group A should see the group A override. 1201 $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user only in group A'][0]->get_name()); 1202 1203 // User in only group B should see the group B override. 1204 $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user only in group B'][0]->get_name()); 1205 1206 // User in group A and B should see see both overrides since the priorities are the same. 1207 $this->assertEquals('Assignment 1 due date - Group A override', $usersevents['For user in group A and B'][0]->get_name()); 1208 $this->assertEquals('Assignment 1 due date - Group B override', $usersevents['For user in group A and B'][1]->get_name()); 1209 1210 // User in no groups should see the plain assignment event. 1211 $this->assertEquals('Assignment 1 due date', $usersevents['For user in no groups'][0]->get_name()); 1212 } 1213 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body