2 // This file is part of Moodle - http://moodle.org/
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.
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
18 * Automated backup tests.
20 * @package core_backup
21 * @copyright 2019 John Yao <johnyao@catalyst-au.net>
22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
25 defined('MOODLE_INTERNAL') ||
die();
28 require_once($CFG->dirroot
. '/backup/util/helper/backup_cron_helper.class.php');
29 require_once($CFG->libdir
.'/cronlib.php');
30 require_once($CFG->libdir
. '/completionlib.php');
33 * Automated backup tests.
35 * @copyright 2019 John Yao <johnyao@catalyst-au.net>
36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 class core_backup_automated_backup_testcase
extends advanced_testcase
{
40 * @var \backup_cron_automated_helper
42 protected $backupcronautomatedhelper;
45 * @var stdClass $course
49 protected function setUp(): void
{
52 $this->resetAfterTest(true);
53 $this->setAdminUser();
54 $CFG->enableavailability
= true;
55 $CFG->enablecompletion
= true;
57 // Getting a testable backup_cron_automated_helper class.
58 $this->backupcronautomatedhelper
= new test_backup_cron_automated_helper();
60 $generator = $this->getDataGenerator();
61 $this->course
= $generator->create_course(
62 array('format' => 'topics', 'numsections' => 3,
63 'enablecompletion' => COMPLETION_ENABLED
),
64 array('createsections' => true));
65 $forum = $generator->create_module('forum', array(
66 'course' => $this->course
->id
));
67 $forum2 = $generator->create_module('forum', array(
68 'course' => $this->course
->id
, 'completion' => COMPLETION_TRACKING_MANUAL
));
70 // We need a grade, easiest is to add an assignment.
71 $assignrow = $generator->create_module('assign', array(
72 'course' => $this->course
->id
));
73 $assign = new assign(context_module
::instance($assignrow->cmid
), false, false);
74 $item = $assign->get_grade_item();
76 // Make a test grouping as well.
77 $grouping = $generator->create_grouping(array('courseid' => $this->course
->id
,
78 'name' => 'Grouping!'));
80 $availability = '{"op":"|","show":false,"c":[' .
81 '{"type":"completion","cm":' . $forum2->cmid
.',"e":1},' .
82 '{"type":"grade","id":' . $item->id
. ',"min":4,"max":94},' .
83 '{"type":"grouping","id":' . $grouping->id
. '}' .
85 $DB->set_field('course_modules', 'availability', $availability, array(
86 'id' => $forum->cmid
));
87 $DB->set_field('course_sections', 'availability', $availability, array(
88 'course' => $this->course
->id
, 'section' => 1));
92 * Tests the automated backup run when the there is course backup should be skipped.
94 public function test_automated_backup_skipped_run() {
97 // Enable automated back up.
98 set_config('backup_auto_active', true, 'backup');
99 set_config('backup_auto_weekdays', '1111111', 'backup');
101 // Start backup process.
102 $admin = get_admin();
104 // Backup entry should not exist.
105 $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course
->id
));
106 $this->assertFalse($backupcourse);
107 $this->assertInstanceOf(
108 backup_cron_automated_helper
::class,
109 $this->backupcronautomatedhelper
->return_this()
112 $classobject = $this->backupcronautomatedhelper
->return_this();
114 $method = new ReflectionMethod('\backup_cron_automated_helper', 'get_courses');
115 $method->setAccessible(true); // Allow accessing of private method.
116 $courses = $method->invoke($classobject);
118 $method = new ReflectionMethod('\backup_cron_automated_helper', 'check_and_push_automated_backups');
119 $method->setAccessible(true); // Allow accessing of private method.
120 $emailpending = $method->invokeArgs($classobject, [$courses, $admin]);
122 $coursename = $this->course
->fullname
;
123 $this->expectOutputRegex("/Skipping $coursename \(Not scheduled for backup until/");
124 $this->assertFalse($emailpending);
126 $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course
->id
));
127 $this->assertNotNull($backupcourse->laststatus
);
131 * Tests the automated backup run when the there is course backup can be pushed to adhoc task.
133 public function test_automated_backup_push_run() {
136 // Enable automated back up.
137 set_config('backup_auto_active', true, 'backup');
138 set_config('backup_auto_weekdays', '1111111', 'backup');
140 $admin = get_admin();
142 $classobject = $this->backupcronautomatedhelper
->return_this();
144 $method = new ReflectionMethod('\backup_cron_automated_helper', 'get_courses');
145 $method->setAccessible(true); // Allow accessing of private method.
146 $courses = $method->invoke($classobject);
148 // Create this backup course.
149 $backupcourse = new stdClass
;
150 $backupcourse->courseid
= $this->course
->id
;
151 $backupcourse->laststatus
= backup_cron_automated_helper
::BACKUP_STATUS_NOTYETRUN
;
152 $DB->insert_record('backup_courses', $backupcourse);
153 $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course
->id
));
155 // We now manually trigger a backup pushed to adhoc task.
156 // Make sure is in the past, which means should run now.
157 $backupcourse->nextstarttime
= time() - 10;
158 $DB->update_record('backup_courses', $backupcourse);
160 $method = new ReflectionMethod('\backup_cron_automated_helper', 'check_and_push_automated_backups');
161 $method->setAccessible(true); // Allow accessing of private method.
162 $emailpending = $method->invokeArgs($classobject, [$courses, $admin]);
163 $this->assertTrue($emailpending);
165 $coursename = $this->course
->fullname
;
166 $this->expectOutputRegex("/Putting backup of $coursename in adhoc task queue/");
168 $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course
->id
));
169 // Now this backup course status should be queued.
170 $this->assertEquals(backup_cron_automated_helper
::BACKUP_STATUS_QUEUED
, $backupcourse->laststatus
);
174 * Tests the automated backup inactive run.
176 public function test_inactive_run() {
177 backup_cron_automated_helper
::run_automated_backup();
178 $this->expectOutputString("Checking automated backup status...INACTIVE\n");
182 * Tests the invisible course being skipped.
184 public function test_should_skip_invisible_course() {
187 set_config('backup_auto_active', true, 'backup');
188 set_config('backup_auto_skip_hidden', true, 'backup');
189 set_config('backup_auto_weekdays', '1111111', 'backup');
190 // Create this backup course.
191 $backupcourse = new stdClass
;
192 $backupcourse->courseid
= $this->course
->id
;
193 // This is the status we believe last run was OK.
194 $backupcourse->laststatus
= backup_cron_automated_helper
::BACKUP_STATUS_SKIPPED
;
195 $DB->insert_record('backup_courses', $backupcourse);
196 $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course
->id
));
198 $this->assertTrue(course_change_visibility($this->course
->id
, false));
199 $course = $DB->get_record('course', array('id' => $this->course
->id
));
200 $this->assertEquals('0', $course->visible
);
201 $classobject = $this->backupcronautomatedhelper
->return_this();
202 $nextstarttime = backup_cron_automated_helper
::calculate_next_automated_backup(null, time());
204 $method = new ReflectionMethod('\backup_cron_automated_helper', 'should_skip_course_backup');
205 $method->setAccessible(true); // Allow accessing of private method.
206 $skipped = $method->invokeArgs($classobject, [$backupcourse, $course, $nextstarttime]);
208 $this->assertTrue($skipped);
209 $this->expectOutputRegex("/Skipping $course->fullname \(Not visible\)/");
213 * Tests the not modified course being skipped.
215 public function test_should_skip_not_modified_course_in_days() {
218 set_config('backup_auto_active', true, 'backup');
219 // Skip if not modified in two days.
220 set_config('backup_auto_skip_modif_days', 2, 'backup');
221 set_config('backup_auto_weekdays', '1111111', 'backup');
223 // Create this backup course.
224 $backupcourse = new stdClass
;
225 $backupcourse->courseid
= $this->course
->id
;
226 // This is the status we believe last run was OK.
227 $backupcourse->laststatus
= backup_cron_automated_helper
::BACKUP_STATUS_SKIPPED
;
228 $backupcourse->laststarttime
= time() - 2 * DAYSECS
;
229 $backupcourse->lastendtime
= time() - 1 * DAYSECS
;
230 $DB->insert_record('backup_courses', $backupcourse);
231 $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course
->id
));
232 $course = $DB->get_record('course', array('id' => $this->course
->id
));
234 $course->timemodified
= time() - 2 * DAYSECS
- 1;
236 $classobject = $this->backupcronautomatedhelper
->return_this();
237 $nextstarttime = backup_cron_automated_helper
::calculate_next_automated_backup(null, time());
239 $method = new ReflectionMethod('\backup_cron_automated_helper', 'should_skip_course_backup');
240 $method->setAccessible(true); // Allow accessing of private method.
241 $skipped = $method->invokeArgs($classobject, [$backupcourse, $course, $nextstarttime]);
243 $this->assertTrue($skipped);
244 $this->expectOutputRegex("/Skipping $course->fullname \(Not modified in the past 2 days\)/");
248 * Tests the backup not modified course being skipped.
250 public function test_should_skip_not_modified_course_since_prev() {
253 set_config('backup_auto_active', true, 'backup');
254 // Skip if not modified in two days.
255 set_config('backup_auto_skip_modif_prev', 2, 'backup');
256 set_config('backup_auto_weekdays', '1111111', 'backup');
258 // Create this backup course.
259 $backupcourse = new stdClass
;
260 $backupcourse->courseid
= $this->course
->id
;
261 // This is the status we believe last run was OK.
262 $backupcourse->laststatus
= backup_cron_automated_helper
::BACKUP_STATUS_SKIPPED
;
263 $backupcourse->laststarttime
= time() - 2 * DAYSECS
;
264 $backupcourse->lastendtime
= time() - 1 * DAYSECS
;
265 $DB->insert_record('backup_courses', $backupcourse);
266 $backupcourse = $DB->get_record('backup_courses', array('courseid' => $this->course
->id
));
267 $course = $DB->get_record('course', array('id' => $this->course
->id
));
269 $course->timemodified
= time() - 2 * DAYSECS
- 1;
271 $classobject = $this->backupcronautomatedhelper
->return_this();
272 $nextstarttime = backup_cron_automated_helper
::calculate_next_automated_backup(null, time());
274 $method = new ReflectionMethod('\backup_cron_automated_helper', 'should_skip_course_backup');
275 $method->setAccessible(true); // Allow accessing of private method.
276 $skipped = $method->invokeArgs($classobject, [$backupcourse, $course, $nextstarttime]);
278 $this->assertTrue($skipped);
279 $this->expectOutputRegex("/Skipping $course->fullname \(Not modified since previous backup\)/");
283 * Test the task completes when coureid is missing.
285 public function test_task_complete_when_courseid_is_missing() {
287 $admin = get_admin();
288 $classobject = $this->backupcronautomatedhelper
->return_this();
290 // Create this backup course.
291 $backupcourse = new stdClass
;
292 $backupcourse->courseid
= $this->course
->id
;
293 $backupcourse->laststatus
= backup_cron_automated_helper
::BACKUP_STATUS_NOTYETRUN
;
294 $DB->insert_record('backup_courses', $backupcourse);
295 $backupcourse = $DB->get_record('backup_courses', ['courseid' => $this->course
->id
]);
297 // Create a backup task.
298 $method = new ReflectionMethod('\backup_cron_automated_helper', 'push_course_backup_adhoc_task');
299 $method->setAccessible(true); // Allow accessing of private method.
300 $method->invokeArgs($classobject, [$backupcourse, $admin]);
302 // Delete course for this test.
303 delete_course($this->course
->id
, false);
305 $task = core\task\manager
::get_next_adhoc_task(time());
309 $output = ob_get_clean();
311 $this->assertStringContainsString('Invalid course id: ' . $this->course
->id
. ', task aborted.', $output);
312 core\task\manager
::adhoc_task_complete($task);
316 * Test the task completes when backup course is missing.
318 public function test_task_complete_when_backup_course_is_missing() {
320 $admin = get_admin();
321 $classobject = $this->backupcronautomatedhelper
->return_this();
323 // Create this backup course.
324 $backupcourse = new stdClass
;
325 $backupcourse->courseid
= $this->course
->id
;
326 $backupcourse->laststatus
= backup_cron_automated_helper
::BACKUP_STATUS_NOTYETRUN
;
327 $DB->insert_record('backup_courses', $backupcourse);
328 $backupcourse = $DB->get_record('backup_courses', ['courseid' => $this->course
->id
]);
330 // Create a backup task.
331 $method = new ReflectionMethod('\backup_cron_automated_helper', 'push_course_backup_adhoc_task');
332 $method->setAccessible(true); // Allow accessing of private method.
333 $method->invokeArgs($classobject, [$backupcourse, $admin]);
335 // Delete backup course for this test.
336 $DB->delete_records('backup_courses', ['courseid' => $this->course
->id
]);
338 $task = core\task\manager
::get_next_adhoc_task(time());
342 $output = ob_get_clean();
344 $this->assertStringContainsString('Automated backup for course: ' . $this->course
->fullname
. ' encounters an error.',
346 core\task\manager
::adhoc_task_complete($task);
351 * New backup_cron_automated_helper class for testing.
353 * This class extends the helper backup_cron_automated_helper class
354 * in order to utilise abstract class for testing.
357 * @copyright 2019 John Yao <johnyao@catalyst-au.net>
358 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
360 class test_backup_cron_automated_helper
extends backup_cron_automated_helper
{
362 * Returning this for testing.
364 public function return_this() {