Proyectos de Subversion Moodle

Rev

Rev 1 | | Comparar con el anterior | Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
     */
11 efrain 41
    public function test_get_action_events_by_timesort_after_time(): void {
1 efrain 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
     */
11 efrain 82
    public function test_get_action_events_by_timesort_before_time(): void {
1 efrain 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
     */
11 efrain 123
    public function test_get_action_events_by_timesort_between_time(): void {
1 efrain 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
     */
11 efrain 161
    public function test_get_action_events_by_timesort_between_time_after_event(): void {
1 efrain 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
     */
11 efrain 199
    public function test_get_action_events_by_timesort_between_time_skip_even_records(): void {
1 efrain 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
     */
11 efrain 250
    public function test_get_action_events_by_timesort_between_time_skip_first_records(): void {
1 efrain 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
     */
11 efrain 296
    public function test_get_action_events_by_timesort_non_consecutive_ids(): void {
1 efrain 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
     */
11 efrain 426
    public function test_get_action_events_by_timesort_with_identical_group_override_priorities(): void {
1 efrain 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
     */
11 efrain 559
    public function test_get_action_events_by_timesort_with_suspended_user(): void {
1 efrain 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
     */
11 efrain 590
    public function test_get_action_events_by_course_after_time(): void {
1 efrain 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
     */
11 efrain 645
    public function test_get_action_events_by_course_before_time(): void {
1 efrain 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
     */
11 efrain 701
    public function test_get_action_events_by_course_between_time(): void {
1 efrain 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
     */
11 efrain 754
    public function test_get_action_events_by_course_between_time_after_event(): void {
1 efrain 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
     */
11 efrain 807
    public function test_get_action_events_by_course_between_time_skip_even_records(): void {
1 efrain 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
     */
11 efrain 873
    public function test_get_action_events_by_course_between_time_skip_first_records(): void {
1 efrain 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
     */
11 efrain 934
    public function test_get_action_events_by_course_non_consecutive_ids(): void {
1 efrain 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
     */
11 efrain 1083
    public function test_get_action_events_by_course_with_identical_group_override_priorities(): void {
1 efrain 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
}