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
declare(strict_types=1);
18
 
19
namespace core_reportbuilder\local\helpers;
20
 
21
use advanced_testcase;
22
use invalid_parameter_exception;
1441 ariadna 23
use core\clock;
1 efrain 24
use core_cohort\reportbuilder\audience\cohortmember;
25
use core_reportbuilder_generator;
26
use core_reportbuilder\local\models\schedule as model;
27
use core_reportbuilder\reportbuilder\audience\manual;
28
use core_user\reportbuilder\datasource\users;
29
 
30
/**
31
 * Unit tests for the schedule helper class
32
 *
33
 * @package     core_reportbuilder
34
 * @covers      \core_reportbuilder\local\helpers\schedule
35
 * @copyright   2021 Paul Holden <paulh@moodle.com>
36
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
37
 */
1441 ariadna 38
final class schedule_test extends advanced_testcase {
1 efrain 39
 
1441 ariadna 40
    /** @var clock $clock */
41
    private readonly clock $clock;
42
 
1 efrain 43
    /**
1441 ariadna 44
     * Mock the clock
45
     */
46
    protected function setUp(): void {
47
        parent::setUp();
48
        $this->clock = $this->mock_clock_with_frozen(1622847600);
49
    }
50
 
51
    /**
1 efrain 52
     * Test create schedule
53
     */
54
    public function test_create_schedule(): void {
55
        $this->resetAfterTest();
56
        $this->setAdminUser();
57
 
58
        /** @var core_reportbuilder_generator $generator */
59
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
60
        $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
61
 
1441 ariadna 62
        // Create schedule for tomorrow.
63
        $timescheduled = $this->clock->time() + DAYSECS;
1 efrain 64
        $schedule = schedule::create_schedule((object) [
65
            'name' => 'My schedule',
66
            'reportid' => $report->get('id'),
67
            'format' => 'csv',
68
            'subject' => 'Hello',
69
            'message' => 'Hola',
70
            'timescheduled' => $timescheduled,
71
        ]);
72
 
73
        $this->assertEquals('My schedule', $schedule->get('name'));
74
        $this->assertEquals($report->get('id'), $schedule->get('reportid'));
75
        $this->assertEquals('csv', $schedule->get('format'));
76
        $this->assertEquals('Hello', $schedule->get('subject'));
77
        $this->assertEquals('Hola', $schedule->get('message'));
78
        $this->assertEquals($timescheduled, $schedule->get('timescheduled'));
79
        $this->assertEquals($timescheduled, $schedule->get('timenextsend'));
80
    }
81
 
82
    /**
83
     * Test update schedule
84
     */
85
    public function test_update_schedule(): void {
86
        $this->resetAfterTest();
87
        $this->setAdminUser();
88
 
89
        /** @var core_reportbuilder_generator $generator */
90
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
91
        $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
92
        $schedule = $generator->create_schedule(['reportid' => $report->get('id'), 'name' => 'My schedule']);
93
 
94
        // Update some record properties.
95
        $record = $schedule->to_record();
96
        $record->name = 'My updated schedule';
1441 ariadna 97
        $record->timescheduled += (12 * HOURSECS);
1 efrain 98
 
99
        $schedule = schedule::update_schedule($record);
100
        $this->assertEquals($record->name, $schedule->get('name'));
101
        $this->assertEquals($record->timescheduled, $schedule->get('timescheduled'));
1441 ariadna 102
        $this->assertEquals($record->timescheduled, $schedule->get('timenextsend'));
1 efrain 103
    }
104
 
105
    /**
106
     * Test update invalid schedule
107
     */
108
    public function test_update_schedule_invalid(): void {
109
        $this->resetAfterTest();
110
        $this->setAdminUser();
111
 
112
        /** @var core_reportbuilder_generator $generator */
113
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
114
        $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
115
 
116
        $this->expectException(invalid_parameter_exception::class);
117
        $this->expectExceptionMessage('Invalid schedule');
118
        schedule::update_schedule((object) ['id' => 42, 'reportid' => $report->get('id')]);
119
    }
120
 
121
    /**
122
     * Test toggle schedule
123
     */
124
    public function test_toggle_schedule(): void {
125
        $this->resetAfterTest();
126
        $this->setAdminUser();
127
 
128
        /** @var core_reportbuilder_generator $generator */
129
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
130
        $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
131
        $schedule = $generator->create_schedule(['reportid' => $report->get('id'), 'name' => 'My schedule']);
132
 
133
        // Disable the schedule.
134
        schedule::toggle_schedule($report->get('id'), $schedule->get('id'), false);
135
        $schedule = $schedule->read();
136
        $this->assertFalse($schedule->get('enabled'));
137
 
138
        // Enable the schedule.
139
        schedule::toggle_schedule($report->get('id'), $schedule->get('id'), true);
140
        $schedule = $schedule->read();
141
        $this->assertTrue($schedule->get('enabled'));
142
    }
143
 
144
    /**
145
     * Test toggle invalid schedule
146
     */
147
    public function test_toggle_schedule_invalid(): void {
148
        $this->resetAfterTest();
149
        $this->setAdminUser();
150
 
151
        /** @var core_reportbuilder_generator $generator */
152
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
153
        $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
154
 
155
        $this->expectException(invalid_parameter_exception::class);
156
        $this->expectExceptionMessage('Invalid schedule');
157
        schedule::toggle_schedule($report->get('id'), 42, true);
158
    }
159
 
160
    /**
161
     * Test delete schedule
162
     */
163
    public function test_delete_schedule(): void {
164
        $this->resetAfterTest();
165
        $this->setAdminUser();
166
 
167
        /** @var core_reportbuilder_generator $generator */
168
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
169
        $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
170
        $schedule = $generator->create_schedule(['reportid' => $report->get('id'), 'name' => 'My schedule']);
171
 
172
        $scheduleid = $schedule->get('id');
173
 
174
        schedule::delete_schedule($report->get('id'), $scheduleid);
175
        $this->assertFalse($schedule::record_exists($scheduleid));
176
    }
177
 
178
    /**
179
     * Test delete invalid schedule
180
     */
181
    public function test_delete_schedule_invalid(): void {
182
        $this->resetAfterTest();
183
        $this->setAdminUser();
184
 
185
        /** @var core_reportbuilder_generator $generator */
186
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
187
        $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
188
 
189
        $this->expectException(invalid_parameter_exception::class);
190
        $this->expectExceptionMessage('Invalid schedule');
191
        schedule::delete_schedule($report->get('id'), 42);
192
    }
193
 
194
    /**
195
     * Test getting schedule report users (those in matching audience)
196
     */
197
    public function test_get_schedule_report_users(): void {
198
        $this->resetAfterTest();
199
 
200
        /** @var core_reportbuilder_generator $generator */
201
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
202
        $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
203
 
204
        // Create cohort, with some members.
205
        $cohort = $this->getDataGenerator()->create_cohort();
206
        $cohortuserone = $this->getDataGenerator()->create_user(['firstname' => 'Zoe', 'lastname' => 'Zebra']);
207
        cohort_add_member($cohort->id, $cohortuserone->id);
208
        $cohortusertwo = $this->getDataGenerator()->create_user(['firstname' => 'Henrietta', 'lastname' => 'Hamster']);
209
        cohort_add_member($cohort->id, $cohortusertwo->id);
210
 
211
        // Create a third user, to be added manually.
212
        $manualuserone = $this->getDataGenerator()->create_user(['firstname' => 'Bob', 'lastname' => 'Badger']);
213
 
214
        $audiencecohort = $generator->create_audience([
215
            'reportid' => $report->get('id'),
216
            'classname' => cohortmember::class,
217
            'configdata' => ['cohorts' => [$cohort->id]],
218
        ]);
219
 
220
        $audiencemanual = $generator->create_audience([
221
            'reportid' => $report->get('id'),
222
            'classname' => manual::class,
223
            'configdata' => ['users' => [$manualuserone->id]],
224
        ]);
225
 
226
        // Now create our schedule.
227
        $schedule = $generator->create_schedule([
228
            'reportid' => $report->get('id'),
229
            'name' => 'My schedule',
230
            'audiences' => json_encode([
231
                $audiencecohort->get_persistent()->get('id'),
232
                $audiencemanual->get_persistent()->get('id'),
233
            ]),
234
        ]);
235
 
236
        $users = schedule::get_schedule_report_users($schedule);
237
        $this->assertEquals([
238
            'Bob',
239
            'Henrietta',
240
            'Zoe',
241
        ], array_column($users, 'firstname'));
242
 
243
        // Now delete one of our users, ensure they are no longer returned.
244
        delete_user($manualuserone);
245
 
246
        $users = schedule::get_schedule_report_users($schedule);
247
        $this->assertEquals([
248
            'Henrietta',
249
            'Zoe',
250
        ], array_column($users, 'firstname'));
251
    }
252
 
253
    /**
254
     * Test getting schedule report row count
255
     */
256
    public function test_get_schedule_report_count(): void {
257
        $this->resetAfterTest();
258
 
259
        /** @var core_reportbuilder_generator $generator */
260
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
261
        $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
262
        $schedule = $generator->create_schedule(['reportid' => $report->get('id'), 'name' => 'My schedule']);
263
 
264
        // There is only one row in the report (the only user on the site).
265
        $count = schedule::get_schedule_report_count($schedule);
1441 ariadna 266
        $this->assertDebuggingCalled();
1 efrain 267
        $this->assertEquals(1, $count);
268
    }
269
 
270
    /**
271
     * Data provider for {@see test_get_schedule_report_file}
272
     *
273
     * @return string[]
274
     */
1441 ariadna 275
    public static function get_schedule_report_file_format(): array {
1 efrain 276
        return [
277
            ['csv'],
278
            ['excel'],
279
            ['html'],
280
            ['json'],
281
            ['ods'],
282
            ['pdf'],
283
        ];
284
    }
285
 
286
    /**
287
     * Test getting schedule report exported file, in each supported format
288
     *
289
     * @param string $format
290
     *
291
     * @dataProvider get_schedule_report_file_format
292
     */
293
    public function test_get_schedule_report_file(string $format): void {
294
        $this->resetAfterTest();
295
        $this->setAdminUser();
296
 
297
        /** @var core_reportbuilder_generator $generator */
298
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
299
        $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
300
        $schedule = $generator->create_schedule(['reportid' => $report->get('id'), 'name' => 'My schedule', 'format' => $format]);
301
 
302
        // There is only one row in the report (the only user on the site).
303
        $file = schedule::get_schedule_report_file($schedule);
304
        $this->assertGreaterThan(64, $file->get_filesize());
305
    }
306
 
307
    /**
308
     * Data provider for {@see test_should_send_schedule}
309
     *
310
     * @return array[]
311
     */
1441 ariadna 312
    public static function should_send_schedule_provider(): array {
1 efrain 313
        return [
1441 ariadna 314
            'Time scheduled in the past' => [
315
                model::RECURRENCE_NONE, '-1 hour', null, null, true,
316
            ],
317
            'Time scheduled in the past, already sent prior to schedule' => [
318
                model::RECURRENCE_NONE, '-1 hour', '-2 hour', null, true,
319
            ],
320
            'Time scheduled in the past, already sent on schedule' => [
321
                model::RECURRENCE_NONE, '-1 hour', '-1 hour', null, false,
322
            ],
323
            'Time scheduled in the future' => [
324
                model::RECURRENCE_NONE, '+1 hour', null, null, false,
325
            ],
326
            'Time scheduled in the future, already sent prior to schedule' => [
327
                model::RECURRENCE_NONE, '+1 hour', '-1 hour', null, false,
328
            ],
329
            'Next send in the past' => [
330
                model::RECURRENCE_DAILY, '-1 hour', null, '-1 hour', true,
331
            ],
332
            'Next send in the future' => [
333
                model::RECURRENCE_DAILY, '-1 hour', null, '+1 hour', false,
334
            ],
1 efrain 335
        ];
336
    }
337
 
338
    /**
339
     * Test for whether a schedule should be sent
340
     *
1441 ariadna 341
     * @param int $recurrence
342
     * @param string $timescheduled Relative time suitable for passing to {@see strtotime}
343
     * @param string|null $timelastsent Relative time suitable for passing to {@see strtotime}, or null to ignore
344
     * @param string|null $timenextsend Relative time suitable for passing to {@see strtotime}, or null to ignore
1 efrain 345
     * @param bool $expected
346
     *
347
     * @dataProvider should_send_schedule_provider
348
     */
1441 ariadna 349
    public function test_should_send_schedule(
350
        int $recurrence,
351
        string $timescheduled,
352
        ?string $timelastsent,
353
        ?string $timenextsend,
354
        bool $expected,
355
    ): void {
1 efrain 356
        $this->resetAfterTest();
357
 
358
        /** @var core_reportbuilder_generator $generator */
359
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
360
        $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
361
 
1441 ariadna 362
        // Use relative time period if present, otherwise default to zero (never sent).
363
        $scheduletimelastsent = 0;
364
        if ($timelastsent !== null) {
365
            $scheduletimelastsent = strtotime($timelastsent, $this->clock->time());
366
        }
1 efrain 367
 
1441 ariadna 368
        $schedule = $generator->create_schedule([
369
            'reportid' => $report->get('id'),
370
            'name' => 'My schedule',
371
            'recurrence' => $recurrence,
372
            'timescheduled' => strtotime($timescheduled, $this->clock->time()),
373
            'timelastsent' => $scheduletimelastsent,
374
        ]);
375
 
1 efrain 376
        // If "Time next send" is specified, then override calculated value.
1441 ariadna 377
        if ($timenextsend !== null) {
378
            $schedule->set('timenextsend', strtotime($timenextsend, $this->clock->time()));
1 efrain 379
        }
380
 
381
        $this->assertEquals($expected, schedule::should_send_schedule($schedule));
382
    }
383
 
384
    /**
1441 ariadna 385
     * Test for whether a schedule should be sent that has been disabled
386
     */
387
    public function test_should_send_schedule_disabled(): void {
388
        $this->resetAfterTest();
389
 
390
        /** @var core_reportbuilder_generator $generator */
391
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
392
        $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
393
        $schedule = $generator->create_schedule([
394
            'reportid' => $report->get('id'),
395
            'name' => 'My schedule',
396
            'enabled' => 0,
397
        ]);
398
 
399
        $this->assertFalse(schedule::should_send_schedule($schedule));
400
    }
401
 
402
    /**
1 efrain 403
     * Data provider for {@see test_calculate_next_send_time}
404
     *
405
     * @return array[]
406
     */
1441 ariadna 407
    public static function calculate_next_send_time_provider(): array {
408
        // Times are based on the current clock time (Fri Jun 04 2021 23:00:00 UTC).
1 efrain 409
        return [
1441 ariadna 410
            'No recurrence' => [
411
                model::RECURRENCE_NONE, '2021-06-03 12:00', '2021-06-03 12:00',
412
            ],
413
            'Recurrence, time scheduled in future' => [
414
                model::RECURRENCE_DAILY, '2021-06-05 12:00', '2021-06-05 12:00',
415
            ],
416
            'Daily recurrence' => [
417
                model::RECURRENCE_DAILY, '2021-06-02 12:00', '2021-06-05 12:00',
418
            ],
419
            'Weekday recurrence' => [
420
                model::RECURRENCE_WEEKDAYS, '2021-06-02 12:00', '2021-06-07 12:00',
421
            ],
422
            'Weekly recurrence' => [
423
                model::RECURRENCE_WEEKLY, '2021-05-18 12:00', '2021-06-08 12:00',
424
            ],
425
            'Monthy recurrence' => [
426
                model::RECURRENCE_MONTHLY, '2021-03-19 12:00', '2021-06-19 12:00',
427
            ],
428
            'Annual recurrence' => [
429
                model::RECURRENCE_ANNUALLY, '2019-05-01 12:00', '2022-05-01 12:00',
430
            ],
1 efrain 431
         ];
432
    }
433
 
434
    /**
435
     * Test for calculating next schedule send time
436
     *
437
     * @param int $recurrence
1441 ariadna 438
     * @param string $timescheduled Absolute time suitable for passing to {@see strtotime}
439
     * @param string $expected Absolute time suitable for passing to {@see strtotime}
1 efrain 440
     *
441
     * @dataProvider calculate_next_send_time_provider
442
     */
1441 ariadna 443
    public function test_calculate_next_send_time(int $recurrence, string $timescheduled, string $expected): void {
1 efrain 444
        $this->resetAfterTest();
445
 
446
        /** @var core_reportbuilder_generator $generator */
447
        $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder');
448
        $report = $generator->create_report(['name' => 'My report', 'source' => users::class]);
449
 
1441 ariadna 450
        // Create model manually, as the generator automatically calculates next send itself.
451
        $schedule = new model(0, (object) [
1 efrain 452
            'reportid' => $report->get('id'),
453
            'name' => 'My schedule',
454
            'recurrence' => $recurrence,
1441 ariadna 455
            'timescheduled' => strtotime("{$timescheduled} UTC"),
1 efrain 456
        ]);
457
 
1441 ariadna 458
        $scheduleexpected = strtotime("{$expected} UTC");
459
        $this->assertEquals($scheduleexpected, schedule::calculate_next_send_time($schedule));
1 efrain 460
    }
461
}