Proyectos de Subversion Moodle

Rev

Rev 11 | | 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_backup;
18
 
19
use backup;
1441 ariadna 20
use core\di;
21
use copy_helper;
22
use core\hook\manager;
1 efrain 23
 
24
defined('MOODLE_INTERNAL') || die();
25
 
26
global $CFG;
27
require_once($CFG->dirroot . '/backup/util/includes/backup_includes.php');
28
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
29
require_once($CFG->libdir . '/completionlib.php');
30
 
31
/**
32
 * Course copy tests.
33
 *
34
 * @package    core_backup
35
 * @copyright  2020 onward The Moodle Users Association <https://moodleassociation.org/>
36
 * @author     Matt Porritt <mattp@catalyst-au.net>
37
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38
 * @coversDefaultClass \copy_helper
39
 */
1441 ariadna 40
final class copy_helper_test extends \advanced_testcase {
1 efrain 41
 
42
    /**
43
     *
44
     * @var \stdClass Course used for testing.
45
     */
46
    protected $course;
47
 
48
    /**
49
     *
50
     * @var int User used to perform backups.
51
     */
52
    protected $userid;
53
 
54
    /**
55
     *
56
     * @var array Ids of users in test course.
57
     */
58
    protected $courseusers;
59
 
60
    /**
61
     *
62
     * @var array Names of the created activities.
63
     */
64
    protected $activitynames;
65
 
66
    /**
67
     * Set up tasks for all tests.
68
     */
69
    protected function setUp(): void {
70
        global $DB, $CFG, $USER;
1441 ariadna 71
        parent::setUp();
1 efrain 72
 
73
        $this->resetAfterTest(true);
74
 
75
        $CFG->enableavailability = true;
76
        $CFG->enablecompletion = true;
77
 
78
        // Create a course with some availability data set.
79
        $generator = $this->getDataGenerator();
80
        $course = $generator->create_course(
81
            array('format' => 'topics', 'numsections' => 3,
82
                'enablecompletion' => COMPLETION_ENABLED),
83
            array('createsections' => true));
84
        $forum = $generator->create_module('forum', array(
85
            'course' => $course->id));
86
        $forum2 = $generator->create_module('forum', array(
87
            'course' => $course->id, 'completion' => COMPLETION_TRACKING_MANUAL));
88
 
89
        // We need a grade, easiest is to add an assignment.
90
        $assignrow = $generator->create_module('assign', array(
91
            'course' => $course->id));
92
        $assign = new \assign(\context_module::instance($assignrow->cmid), false, false);
93
        $item = $assign->get_grade_item();
94
 
95
        // Make a test grouping as well.
96
        $grouping = $generator->create_grouping(array('courseid' => $course->id,
97
            'name' => 'Grouping!'));
98
 
99
        // Create some users.
100
        $user1 = $generator->create_user();
101
        $user2 = $generator->create_user();
102
        $user3 = $generator->create_user();
103
        $user4 = $generator->create_user();
104
        $this->courseusers = array(
105
            $user1->id, $user2->id, $user3->id, $user4->id
106
        );
107
 
108
        // Enrol users into the course.
109
        $generator->enrol_user($user1->id, $course->id, 'student');
110
        $generator->enrol_user($user2->id, $course->id, 'editingteacher');
111
        $generator->enrol_user($user3->id, $course->id, 'manager');
112
        $generator->enrol_user($user4->id, $course->id, 'editingteacher');
113
        $generator->enrol_user($user4->id, $course->id, 'manager');
114
 
115
        $availability = '{"op":"|","show":false,"c":[' .
116
            '{"type":"completion","cm":' . $forum2->cmid .',"e":1},' .
117
            '{"type":"grade","id":' . $item->id . ',"min":4,"max":94},' .
118
            '{"type":"grouping","id":' . $grouping->id . '}' .
119
            ']}';
120
        $DB->set_field('course_modules', 'availability', $availability, array(
121
            'id' => $forum->cmid));
122
        $DB->set_field('course_sections', 'availability', $availability, array(
123
            'course' => $course->id, 'section' => 1));
124
 
125
        // Add some user data to the course.
126
        $discussion = $generator->get_plugin_generator('mod_forum')->create_discussion(['course' => $course->id,
127
            'forum' => $forum->id, 'userid' => $user1->id, 'timemodified' => time(),
128
            'name' => 'Frog']);
129
        $generator->get_plugin_generator('mod_forum')->create_post(['discussion' => $discussion->id, 'userid' => $user1->id]);
130
 
131
        $this->course  = $course;
132
        $this->userid = $USER->id; // Admin.
133
        $this->activitynames = array(
134
            $forum->name,
135
            $forum2->name,
136
            $assignrow->name
137
        );
138
 
139
        // Set the user doing the backup to be a manager in the course.
140
        // By default Managers can restore courses AND users, teachers can only do users.
141
        $this->setUser($user3);
142
 
143
        // Disable all loggers.
144
        $CFG->backup_error_log_logger_level = backup::LOG_NONE;
145
        $CFG->backup_output_indented_logger_level = backup::LOG_NONE;
146
        $CFG->backup_file_logger_level = backup::LOG_NONE;
147
        $CFG->backup_database_logger_level = backup::LOG_NONE;
148
        $CFG->backup_file_logger_level_extra = backup::LOG_NONE;
149
    }
150
 
151
    /**
152
     * Test process form data with invalid data.
153
     *
154
     * @covers ::process_formdata
155
     */
11 efrain 156
    public function test_process_formdata_missing_fields(): void {
1 efrain 157
        $this->expectException(\moodle_exception::class);
158
        \copy_helper::process_formdata(new \stdClass);
159
    }
160
 
161
    /**
162
     * Test processing form data.
163
     *
164
     * @covers ::process_formdata
165
     */
11 efrain 166
    public function test_process_formdata(): void {
1 efrain 167
        $validformdata = [
168
            'courseid' => 1729,
169
            'fullname' => 'Taxicab Numbers',
170
            'shortname' => 'Taxi101',
171
            'category' => 2,
172
            'visible' => 1,
173
            'startdate' => 87539319,
174
            'enddate' => 6963472309248,
175
            'idnumber' => 1730,
176
            'userdata' => 1
177
        ];
178
 
179
        $roles = [
180
            'role_one' => 1,
181
            'role_two' => 2,
182
            'role_three' => 0
183
        ];
184
 
185
        $expected = (object)array_merge($validformdata, ['keptroles' => []]);
186
        $expected->keptroles = [1, 2];
187
        $processed = \copy_helper::process_formdata(
188
            (object)array_merge(
189
                $validformdata,
190
                $roles,
191
                ['extra' => 'stuff', 'remove' => 'this'])
192
        );
193
 
194
        $this->assertEquals($expected, $processed);
195
    }
196
 
197
    /**
198
     * Test orphaned controller cleanup.
199
     *
200
     * @covers ::cleanup_orphaned_copy_controllers
201
     */
11 efrain 202
    public function test_cleanup_orphaned_copy_controllers(): void {
1 efrain 203
        global $DB;
204
 
205
        // Mock up the form data.
206
        $formdata = new \stdClass;
207
        $formdata->courseid = $this->course->id;
208
        $formdata->fullname = 'foo';
209
        $formdata->shortname = 'data1';
210
        $formdata->category = 1;
211
        $formdata->visible = 1;
212
        $formdata->startdate = 1582376400;
213
        $formdata->enddate = 0;
214
        $formdata->idnumber = 123;
215
        $formdata->userdata = 1;
216
        $formdata->role_1 = 1;
217
        $formdata->role_3 = 3;
218
        $formdata->role_5 = 5;
219
 
220
        $copies = [];
221
        for ($i = 0; $i < 5; $i++) {
222
            $formdata->shortname = 'data' . $i;
223
            $copies[] = \copy_helper::create_copy($formdata);
224
        }
225
 
226
        // Delete one of the restore controllers. Simulates a situation where copy creation
227
        // interrupted and the restore controller never gets created.
228
        $DB->delete_records('backup_controllers', ['backupid' => $copies[0]['restoreid']]);
229
 
230
        // Set a backup/restore controller pair to be in an intermediate state.
231
        \backup_controller::load_controller($copies[2]['backupid'])->set_status(backup::STATUS_FINISHED_OK);
232
 
233
        // Set a backup/restore controller pair to completed.
234
        \backup_controller::load_controller($copies[3]['backupid'])->set_status(backup::STATUS_FINISHED_OK);
235
        \restore_controller::load_controller($copies[3]['restoreid'])->set_status(backup::STATUS_FINISHED_OK);
236
 
237
        // Set a backup/restore controller pair to have a failed backup.
238
        \backup_controller::load_controller($copies[4]['backupid'])->set_status(backup::STATUS_FINISHED_ERR);
239
 
240
        // Create some backup/restore controllers that are unrelated to course copies.
241
        $bc = new \backup_controller(backup::TYPE_1COURSE, 1, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_ASYNC,
242
                2, backup::RELEASESESSION_YES);
243
        $rc = new \restore_controller('restore-test-1729', 1, backup::INTERACTIVE_NO, backup::MODE_ASYNC, 1, 2);
244
        $rc->save_controller();
245
        $unrelatedvanillacontrollers = ['backupid' => $bc->get_backupid(), 'restoreid' => $rc->get_restoreid()];
246
 
247
        $bc = new \backup_controller(backup::TYPE_1COURSE, 1, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_ASYNC,
248
                2, backup::RELEASESESSION_YES);
249
        $rc = new \restore_controller('restore-test-1729', 1, backup::INTERACTIVE_NO, backup::MODE_ASYNC, 1, 2);
250
        $bc->set_status(backup::STATUS_FINISHED_OK);
251
        $rc->set_status(backup::STATUS_FINISHED_OK);
252
        $unrelatedfinishedcontrollers = ['backupid' => $bc->get_backupid(), 'restoreid' => $rc->get_restoreid()];
253
 
254
        $bc = new \backup_controller(backup::TYPE_1COURSE, 1, backup::FORMAT_MOODLE, backup::INTERACTIVE_NO, backup::MODE_ASYNC,
255
                2, backup::RELEASESESSION_YES);
256
        $rc = new \restore_controller('restore-test-1729', 1, backup::INTERACTIVE_NO, backup::MODE_ASYNC, 1, 2);
257
        $bc->set_status(backup::STATUS_FINISHED_ERR);
258
        $rc->set_status(backup::STATUS_FINISHED_ERR);
259
        $unrelatedfailedcontrollers = ['backupid' => $bc->get_backupid(), 'restoreid' => $rc->get_restoreid()];
260
 
261
        // Clean up the backup_controllers table.
262
        $records = $DB->get_records('backup_controllers', null, '', 'id, backupid, status, operation, purpose, timecreated');
263
        \copy_helper::cleanup_orphaned_copy_controllers($records, 0);
264
 
265
        // Retrieve them again and check.
266
        $records = $DB->get_records('backup_controllers', null, '', 'backupid, status');
267
 
268
        // Verify the backup associated with the deleted restore is marked as failed.
269
        $this->assertEquals(backup::STATUS_FINISHED_ERR, $records[$copies[0]['backupid']]->status);
270
 
271
        // Verify other controllers remain untouched.
272
        $this->assertEquals(backup::STATUS_AWAITING, $records[$copies[1]['backupid']]->status);
273
        $this->assertEquals(backup::STATUS_REQUIRE_CONV, $records[$copies[1]['restoreid']]->status);
274
        $this->assertEquals(backup::STATUS_FINISHED_OK, $records[$copies[2]['backupid']]->status);
275
        $this->assertEquals(backup::STATUS_REQUIRE_CONV, $records[$copies[2]['restoreid']]->status);
276
        $this->assertEquals(backup::STATUS_FINISHED_OK, $records[$copies[3]['restoreid']]->status);
277
        $this->assertEquals(backup::STATUS_FINISHED_OK, $records[$copies[3]['backupid']]->status);
278
 
279
        // Verify that the restore associated with the failed backup is also marked as failed.
280
        $this->assertEquals(backup::STATUS_FINISHED_ERR, $records[$copies[4]['restoreid']]->status);
281
 
282
        // Verify that the unrelated controllers remain unchanged.
283
        $this->assertEquals(backup::STATUS_AWAITING, $records[$unrelatedvanillacontrollers['backupid']]->status);
284
        $this->assertEquals(backup::STATUS_REQUIRE_CONV, $records[$unrelatedvanillacontrollers['restoreid']]->status);
285
        $this->assertEquals(backup::STATUS_FINISHED_OK, $records[$unrelatedfinishedcontrollers['backupid']]->status);
286
        $this->assertEquals(backup::STATUS_FINISHED_OK, $records[$unrelatedfinishedcontrollers['restoreid']]->status);
287
        $this->assertEquals(backup::STATUS_FINISHED_ERR, $records[$unrelatedfailedcontrollers['backupid']]->status);
288
        $this->assertEquals(backup::STATUS_FINISHED_ERR, $records[$unrelatedfailedcontrollers['restoreid']]->status);
289
    }
290
 
291
    /**
292
     * Test creating a course copy.
293
     *
294
     * @covers ::create_copy
295
     */
11 efrain 296
    public function test_create_copy(): void {
1 efrain 297
 
298
        // Mock up the form data.
299
        $formdata = new \stdClass;
300
        $formdata->courseid = $this->course->id;
301
        $formdata->fullname = 'foo';
302
        $formdata->shortname = 'bar';
303
        $formdata->category = 1;
304
        $formdata->visible = 1;
305
        $formdata->startdate = 1582376400;
306
        $formdata->enddate = 0;
307
        $formdata->idnumber = 123;
308
        $formdata->userdata = 1;
309
        $formdata->role_1 = 1;
310
        $formdata->role_3 = 3;
311
        $formdata->role_5 = 5;
312
 
313
        $copydata = \copy_helper::process_formdata($formdata);
314
        $result = \copy_helper::create_copy($copydata);
315
 
316
        // Load the controllers, to extract the data we need.
317
        $bc = \backup_controller::load_controller($result['backupid']);
318
        $rc = \restore_controller::load_controller($result['restoreid']);
319
 
320
        // Check the backup controller.
321
        $this->assertEquals(backup::MODE_COPY, $bc->get_mode());
322
        $this->assertEquals($this->course->id, $bc->get_courseid());
323
        $this->assertEquals(backup::TYPE_1COURSE, $bc->get_type());
324
 
325
        // Check the restore controller.
326
        $newcourseid = $rc->get_courseid();
327
        $newcourse = get_course($newcourseid);
328
 
329
        $this->assertEquals(get_string('copyingcourse', 'backup'), $newcourse->fullname);
330
        $this->assertEquals(get_string('copyingcourseshortname', 'backup'), $newcourse->shortname);
331
        $this->assertEquals(backup::MODE_COPY, $rc->get_mode());
332
        $this->assertEquals($newcourseid, $rc->get_courseid());
333
 
334
        // Check the created ad-hoc task.
335
        $now = time();
336
        $task = \core\task\manager::get_next_adhoc_task($now);
337
 
338
        $this->assertInstanceOf('\\core\\task\\asynchronous_copy_task', $task);
339
        $this->assertEquals($result, (array)$task->get_custom_data());
340
 
341
        \core\task\manager::adhoc_task_complete($task);
342
    }
343
 
344
    /**
345
     * Test getting the current copies.
346
     *
347
     * @covers ::get_copies
348
     */
11 efrain 349
    public function test_get_copies(): void {
1 efrain 350
        global $USER;
351
 
352
        // Mock up the form data.
353
        $formdata = new \stdClass;
354
        $formdata->courseid = $this->course->id;
355
        $formdata->fullname = 'foo';
356
        $formdata->shortname = 'bar';
357
        $formdata->category = 1;
358
        $formdata->visible = 1;
359
        $formdata->startdate = 1582376400;
360
        $formdata->enddate = 0;
361
        $formdata->idnumber = '';
362
        $formdata->userdata = 1;
363
        $formdata->role_1 = 1;
364
        $formdata->role_3 = 3;
365
        $formdata->role_5 = 5;
366
 
367
        $formdata2 = clone($formdata);
368
        $formdata2->shortname = 'tree';
369
 
370
        // Create some copies.
371
        $copydata = \copy_helper::process_formdata($formdata);
372
        $result = \copy_helper::create_copy($copydata);
373
 
374
        // Backup, awaiting.
375
        $copies = \copy_helper::get_copies($USER->id);
376
        $this->assertEquals($result['backupid'], $copies[0]->backupid);
377
        $this->assertEquals($result['restoreid'], $copies[0]->restoreid);
378
        $this->assertEquals(\backup::STATUS_AWAITING, $copies[0]->status);
379
        $this->assertEquals(\backup::OPERATION_BACKUP, $copies[0]->operation);
380
 
381
        $bc = \backup_controller::load_controller($result['backupid']);
382
 
383
        // Backup, in progress.
384
        $bc->set_status(\backup::STATUS_EXECUTING);
385
        $copies = \copy_helper::get_copies($USER->id);
386
        $this->assertEquals($result['backupid'], $copies[0]->backupid);
387
        $this->assertEquals($result['restoreid'], $copies[0]->restoreid);
388
        $this->assertEquals(\backup::STATUS_EXECUTING, $copies[0]->status);
389
        $this->assertEquals(\backup::OPERATION_BACKUP, $copies[0]->operation);
390
 
391
        // Restore, ready to process.
392
        $bc->set_status(\backup::STATUS_FINISHED_OK);
393
        $copies = \copy_helper::get_copies($USER->id);
394
        $this->assertEquals(null, $copies[0]->backupid);
395
        $this->assertEquals($result['restoreid'], $copies[0]->restoreid);
396
        $this->assertEquals(\backup::STATUS_REQUIRE_CONV, $copies[0]->status);
397
        $this->assertEquals(\backup::OPERATION_RESTORE, $copies[0]->operation);
398
 
399
        // No records.
400
        $bc->set_status(\backup::STATUS_FINISHED_ERR);
401
        $copies = \copy_helper::get_copies($USER->id);
402
        $this->assertEmpty($copies);
403
 
404
        $copydata2 = \copy_helper::process_formdata($formdata2);
405
        $result2 = \copy_helper::create_copy($copydata2);
406
        // Set the second copy to be complete.
407
        $bc = \backup_controller::load_controller($result2['backupid']);
408
        $bc->set_status(\backup::STATUS_FINISHED_OK);
409
        // Set the restore to be finished.
410
        $rc = \backup_controller::load_controller($result2['restoreid']);
411
        $rc->set_status(\backup::STATUS_FINISHED_OK);
412
 
413
        // No records.
414
        $copies = \copy_helper::get_copies($USER->id);
415
        $this->assertEmpty($copies);
416
    }
417
 
418
    /**
419
     * Test getting the current copies when they are in an invalid state.
420
     *
421
     * @covers ::get_copies
422
     */
11 efrain 423
    public function test_get_copies_invalid_state(): void {
1 efrain 424
        global $DB, $USER;
425
 
426
        // Mock up the form data.
427
        $formdata = new \stdClass;
428
        $formdata->courseid = $this->course->id;
429
        $formdata->fullname = 'foo';
430
        $formdata->shortname = 'bar';
431
        $formdata->category = 1;
432
        $formdata->visible = 1;
433
        $formdata->startdate = 1582376400;
434
        $formdata->enddate = 0;
435
        $formdata->idnumber = '';
436
        $formdata->userdata = 1;
437
        $formdata->role_1 = 1;
438
        $formdata->role_3 = 3;
439
        $formdata->role_5 = 5;
440
 
441
        $formdata2 = clone ($formdata);
442
        $formdata2->shortname = 'tree';
443
 
444
        // Create some copies.
445
        $copydata = \copy_helper::process_formdata($formdata);
446
        $result = \copy_helper::create_copy($copydata);
447
        $copydata2 = \copy_helper::process_formdata($formdata2);
448
        $result2 = \copy_helper::create_copy($copydata2);
449
 
450
        $copies = \copy_helper::get_copies($USER->id);
451
 
452
        // Verify get_copies gives back both backup controllers.
453
        $this->assertEqualsCanonicalizing([$result['backupid'], $result2['backupid']], array_column($copies, 'backupid'));
454
 
455
        // Set one of the backup controllers to failed, this should cause it to not be present.
456
        \backup_controller::load_controller($result['backupid'])->set_status(backup::STATUS_FINISHED_ERR);
457
        $copies = \copy_helper::get_copies($USER->id);
458
 
459
        // Verify there is only one backup listed, and that it is not the failed one.
460
        $this->assertEqualsCanonicalizing([$result2['backupid']], array_column($copies, 'backupid'));
461
 
462
        // Set the controller back to awaiting.
463
        \backup_controller::load_controller($result['backupid'])->set_status(backup::STATUS_AWAITING);
464
        $copies = \copy_helper::get_copies($USER->id);
465
 
466
        // Verify both backup controllers are back.
467
        $this->assertEqualsCanonicalizing([$result['backupid'], $result2['backupid']], array_column($copies, 'backupid'));
468
 
469
        // Delete the restore controller for one of the copies, this should cause it to not be present.
470
        $DB->delete_records('backup_controllers', ['backupid' => $result['restoreid']]);
471
        $copies = \copy_helper::get_copies($USER->id);
472
 
473
        // Verify there is only one backup listed, and that it is not the failed one.
474
        $this->assertEqualsCanonicalizing([$result2['backupid']], array_column($copies, 'backupid'));
475
    }
476
 
477
    /**
478
     * Test getting the current copies for specific course.
479
     *
480
     * @covers ::get_copies
481
     */
11 efrain 482
    public function test_get_copies_course(): void {
1 efrain 483
        global $USER;
484
 
485
        // Mock up the form data.
486
        $formdata = new \stdClass;
487
        $formdata->courseid = $this->course->id;
488
        $formdata->fullname = 'foo';
489
        $formdata->shortname = 'bar';
490
        $formdata->category = 1;
491
        $formdata->visible = 1;
492
        $formdata->startdate = 1582376400;
493
        $formdata->enddate = 0;
494
        $formdata->idnumber = '';
495
        $formdata->userdata = 1;
496
        $formdata->role_1 = 1;
497
        $formdata->role_3 = 3;
498
        $formdata->role_5 = 5;
499
 
500
        // Create some copies.
501
        $copydata = \copy_helper::process_formdata($formdata);
502
        \copy_helper::create_copy($copydata);
503
 
504
        // No copies match this course id.
505
        $copies = \copy_helper::get_copies($USER->id, ($this->course->id + 1));
506
        $this->assertEmpty($copies);
507
    }
508
 
509
    /**
510
     * Test getting the current copies if course has been deleted.
511
     *
512
     * @covers ::get_copies
513
     */
11 efrain 514
    public function test_get_copies_course_deleted(): void {
1 efrain 515
        global $USER;
516
 
517
        // Mock up the form data.
518
        $formdata = new \stdClass;
519
        $formdata->courseid = $this->course->id;
520
        $formdata->fullname = 'foo';
521
        $formdata->shortname = 'bar';
522
        $formdata->category = 1;
523
        $formdata->visible = 1;
524
        $formdata->startdate = 1582376400;
525
        $formdata->enddate = 0;
526
        $formdata->idnumber = '';
527
        $formdata->userdata = 1;
528
        $formdata->role_1 = 1;
529
        $formdata->role_3 = 3;
530
        $formdata->role_5 = 5;
531
 
532
        // Create some copies.
533
        $copydata = \copy_helper::process_formdata($formdata);
534
        \copy_helper::create_copy($copydata);
535
 
536
        delete_course($this->course->id, false);
537
 
538
        // No copies match this course id as it has been deleted.
539
        $copies = \copy_helper::get_copies($USER->id, ($this->course->id));
540
        $this->assertEmpty($copies);
541
    }
542
 
543
    /**
544
     * Test course copy.
545
     */
11 efrain 546
    public function test_course_copy(): void {
1 efrain 547
        global $DB;
548
 
549
        // Mock up the form data.
550
        $formdata = new \stdClass;
551
        $formdata->courseid = $this->course->id;
552
        $formdata->fullname = 'copy course';
553
        $formdata->shortname = 'copy course short';
554
        $formdata->category = 1;
555
        $formdata->visible = 0;
556
        $formdata->startdate = 1582376400;
557
        $formdata->enddate = 1582386400;
558
        $formdata->idnumber = 123;
559
        $formdata->userdata = 1;
560
        $formdata->role_1 = 1;
561
        $formdata->role_3 = 3;
562
        $formdata->role_5 = 5;
563
 
564
        // Create the course copy records and associated ad-hoc task.
565
        $copydata = \copy_helper::process_formdata($formdata);
566
        $copyids = \copy_helper::create_copy($copydata);
567
 
568
        $courseid = $this->course->id;
569
 
570
        // We are expecting trace output during this test.
571
        $this->expectOutputRegex("/$courseid/");
572
 
573
        // Execute adhoc task.
574
        $now = time();
575
        $task = \core\task\manager::get_next_adhoc_task($now);
576
        $this->assertInstanceOf('\\core\\task\\asynchronous_copy_task', $task);
577
        $task->execute();
578
        \core\task\manager::adhoc_task_complete($task);
579
 
580
        $postbackuprec = $DB->get_record('backup_controllers', array('backupid' => $copyids['backupid']));
581
        $postrestorerec = $DB->get_record('backup_controllers', array('backupid' => $copyids['restoreid']));
582
 
583
        // Check backup was completed successfully.
584
        $this->assertEquals(backup::STATUS_FINISHED_OK, $postbackuprec->status);
585
        $this->assertEquals(1.0, $postbackuprec->progress);
586
 
587
        // Check restore was completed successfully.
588
        $this->assertEquals(backup::STATUS_FINISHED_OK, $postrestorerec->status);
589
        $this->assertEquals(1.0, $postrestorerec->progress);
590
 
591
        // Check the restored course itself.
592
        $coursecontext = \context_course::instance($postrestorerec->itemid);
593
        $users = get_enrolled_users($coursecontext);
594
 
595
        $modinfo = get_fast_modinfo($postrestorerec->itemid);
596
        $forums = $modinfo->get_instances_of('forum');
597
        $forum = reset($forums);
598
        $discussions = forum_get_discussions($forum);
599
        $course = $modinfo->get_course();
600
 
601
        $this->assertEquals($formdata->startdate, $course->startdate);
602
        $this->assertEquals($formdata->enddate, $course->enddate);
603
        $this->assertEquals('copy course', $course->fullname);
604
        $this->assertEquals('copy course short',  $course->shortname);
605
        $this->assertEquals(0,  $course->visible);
606
        $this->assertEquals(123,  $course->idnumber);
607
 
608
        foreach ($modinfo->get_cms() as $cm) {
609
            $this->assertContains($cm->get_formatted_name(), $this->activitynames);
610
        }
611
 
612
        foreach ($this->courseusers as $user) {
613
            $this->assertEquals($user, $users[$user]->id);
614
        }
615
 
616
        $this->assertEquals(count($this->courseusers), count($users));
617
        $this->assertEquals(2, count($discussions));
618
    }
619
 
620
    /**
621
     * Test course copy, not including any users (or data).
622
     */
11 efrain 623
    public function test_course_copy_no_users(): void {
1 efrain 624
        global $DB;
625
 
626
        // Mock up the form data.
627
        $formdata = new \stdClass;
628
        $formdata->courseid = $this->course->id;
629
        $formdata->fullname = 'copy course';
630
        $formdata->shortname = 'copy course short';
631
        $formdata->category = 1;
632
        $formdata->visible = 0;
633
        $formdata->startdate = 1582376400;
634
        $formdata->enddate = 1582386400;
635
        $formdata->idnumber = 123;
636
        $formdata->userdata = 1;
637
        $formdata->role_1 = 0;
638
        $formdata->role_3 = 0;
639
        $formdata->role_5 = 0;
640
 
641
        // Create the course copy records and associated ad-hoc task.
642
        $copydata = \copy_helper::process_formdata($formdata);
643
        $copyids = \copy_helper::create_copy($copydata);
644
 
645
        $courseid = $this->course->id;
646
 
647
        // We are expecting trace output during this test.
648
        $this->expectOutputRegex("/$courseid/");
649
 
650
        // Execute adhoc task.
651
        $now = time();
652
        $task = \core\task\manager::get_next_adhoc_task($now);
653
        $this->assertInstanceOf('\\core\\task\\asynchronous_copy_task', $task);
654
        $task->execute();
655
        \core\task\manager::adhoc_task_complete($task);
656
 
657
        $postrestorerec = $DB->get_record('backup_controllers', array('backupid' => $copyids['restoreid']));
658
 
659
        // Check the restored course itself.
660
        $coursecontext = \context_course::instance($postrestorerec->itemid);
661
        $users = get_enrolled_users($coursecontext);
662
 
663
        $modinfo = get_fast_modinfo($postrestorerec->itemid);
664
        $forums = $modinfo->get_instances_of('forum');
665
        $forum = reset($forums);
666
        $discussions = forum_get_discussions($forum);
667
        $course = $modinfo->get_course();
668
 
669
        $this->assertEquals($formdata->startdate, $course->startdate);
670
        $this->assertEquals($formdata->enddate, $course->enddate);
671
        $this->assertEquals('copy course', $course->fullname);
672
        $this->assertEquals('copy course short',  $course->shortname);
673
        $this->assertEquals(0,  $course->visible);
674
        $this->assertEquals(123,  $course->idnumber);
675
 
676
        foreach ($modinfo->get_cms() as $cm) {
677
            $this->assertContains($cm->get_formatted_name(), $this->activitynames);
678
        }
679
 
680
        // Should be no discussions as the user that made them wasn't included.
681
        $this->assertEquals(0, count($discussions));
682
 
683
        // There should only be one user in the new course, and that's the user who did the copy.
684
        $this->assertEquals(1, count($users));
685
        $this->assertEquals($this->courseusers[2], $users[$this->courseusers[2]]->id);
686
 
687
    }
688
 
689
    /**
690
     * Test course copy, including students and their data.
691
     */
11 efrain 692
    public function test_course_copy_students_data(): void {
1 efrain 693
        global $DB;
694
 
695
        // Mock up the form data.
696
        $formdata = new \stdClass;
697
        $formdata->courseid = $this->course->id;
698
        $formdata->fullname = 'copy course';
699
        $formdata->shortname = 'copy course short';
700
        $formdata->category = 1;
701
        $formdata->visible = 0;
702
        $formdata->startdate = 1582376400;
703
        $formdata->enddate = 1582386400;
704
        $formdata->idnumber = 123;
705
        $formdata->userdata = 1;
706
        $formdata->role_1 = 0;
707
        $formdata->role_3 = 0;
708
        $formdata->role_5 = 5;
709
 
710
        // Create the course copy records and associated ad-hoc task.
711
        $copydata = \copy_helper::process_formdata($formdata);
712
        $copyids = \copy_helper::create_copy($copydata);
713
 
714
        $courseid = $this->course->id;
715
 
716
        // We are expecting trace output during this test.
717
        $this->expectOutputRegex("/$courseid/");
718
 
719
        // Execute adhoc task.
720
        $now = time();
721
        $task = \core\task\manager::get_next_adhoc_task($now);
722
        $this->assertInstanceOf('\\core\\task\\asynchronous_copy_task', $task);
723
        $task->execute();
724
        \core\task\manager::adhoc_task_complete($task);
725
 
726
        $postrestorerec = $DB->get_record('backup_controllers', array('backupid' => $copyids['restoreid']));
727
 
728
        // Check the restored course itself.
729
        $coursecontext = \context_course::instance($postrestorerec->itemid);
730
        $users = get_enrolled_users($coursecontext);
731
 
732
        $modinfo = get_fast_modinfo($postrestorerec->itemid);
733
        $forums = $modinfo->get_instances_of('forum');
734
        $forum = reset($forums);
735
        $discussions = forum_get_discussions($forum);
736
        $course = $modinfo->get_course();
737
 
738
        $this->assertEquals($formdata->startdate, $course->startdate);
739
        $this->assertEquals($formdata->enddate, $course->enddate);
740
        $this->assertEquals('copy course', $course->fullname);
741
        $this->assertEquals('copy course short',  $course->shortname);
742
        $this->assertEquals(0,  $course->visible);
743
        $this->assertEquals(123,  $course->idnumber);
744
 
745
        foreach ($modinfo->get_cms() as $cm) {
746
            $this->assertContains($cm->get_formatted_name(), $this->activitynames);
747
        }
748
 
749
        // Should be no discussions as the user that made them wasn't included.
750
        $this->assertEquals(2, count($discussions));
751
 
752
        // There should only be two users in the new course. The copier and one student.
753
        $this->assertEquals(2, count($users));
754
        $this->assertEquals($this->courseusers[2], $users[$this->courseusers[2]]->id);
755
        $this->assertEquals($this->courseusers[0], $users[$this->courseusers[0]]->id);
756
    }
757
 
758
    /**
759
     * Test course copy, not including any users (or data).
760
     */
11 efrain 761
    public function test_course_copy_no_data(): void {
1 efrain 762
        global $DB;
763
 
764
        // Mock up the form data.
765
        $formdata = new \stdClass;
766
        $formdata->courseid = $this->course->id;
767
        $formdata->fullname = 'copy course';
768
        $formdata->shortname = 'copy course short';
769
        $formdata->category = 1;
770
        $formdata->visible = 0;
771
        $formdata->startdate = 1582376400;
772
        $formdata->enddate = 1582386400;
773
        $formdata->idnumber = 123;
774
        $formdata->userdata = 0;
775
        $formdata->role_1 = 1;
776
        $formdata->role_3 = 3;
777
        $formdata->role_5 = 5;
778
 
779
        // Create the course copy records and associated ad-hoc task.
780
        $copydata = \copy_helper::process_formdata($formdata);
781
        $copyids = \copy_helper::create_copy($copydata);
782
 
783
        $courseid = $this->course->id;
784
 
785
        // We are expecting trace output during this test.
786
        $this->expectOutputRegex("/$courseid/");
787
 
788
        // Execute adhoc task.
789
        $now = time();
790
        $task = \core\task\manager::get_next_adhoc_task($now);
791
        $this->assertInstanceOf('\\core\\task\\asynchronous_copy_task', $task);
792
        $task->execute();
793
        \core\task\manager::adhoc_task_complete($task);
794
 
795
        $postrestorerec = $DB->get_record('backup_controllers', array('backupid' => $copyids['restoreid']));
796
 
797
        // Check the restored course itself.
798
        $coursecontext = \context_course::instance($postrestorerec->itemid);
799
        $users = get_enrolled_users($coursecontext);
800
 
801
        get_fast_modinfo($postrestorerec->itemid, 0, true);
802
        $modinfo = get_fast_modinfo($postrestorerec->itemid);
803
        $forums = $modinfo->get_instances_of('forum');
804
        $forum = reset($forums);
805
        $discussions = forum_get_discussions($forum);
806
        $course = $modinfo->get_course();
807
 
808
        $this->assertEquals($formdata->startdate, $course->startdate);
809
        $this->assertEquals($formdata->enddate, $course->enddate);
810
        $this->assertEquals('copy course', $course->fullname);
811
        $this->assertEquals('copy course short',  $course->shortname);
812
        $this->assertEquals(0,  $course->visible);
813
        $this->assertEquals(123,  $course->idnumber);
814
 
815
        foreach ($modinfo->get_cms() as $cm) {
816
            $this->assertContains($cm->get_formatted_name(), $this->activitynames);
817
        }
818
 
819
        // Should be no discussions as the user data wasn't included.
820
        $this->assertEquals(0, count($discussions));
821
 
822
        // There should only be all users in the new course.
823
        $this->assertEquals(count($this->courseusers), count($users));
824
    }
825
 
826
    /**
827
     * Test instantiation with incomplete formdata.
828
     */
11 efrain 829
    public function test_malformed_instantiation(): void {
1 efrain 830
        // Mock up the form data, missing things so we get an exception.
831
        $formdata = new \stdClass;
832
        $formdata->courseid = $this->course->id;
833
        $formdata->fullname = 'copy course';
834
        $formdata->shortname = 'copy course short';
835
        $formdata->category = 1;
836
 
837
        // Expect and exception as form data is incomplete.
838
        $this->expectException(\moodle_exception::class);
839
        $copydata = \copy_helper::process_formdata($formdata);
840
        \copy_helper::create_copy($copydata);
841
    }
1441 ariadna 842
 
843
    /**
844
     * Test copy_helper_process_formdata hook.
845
     *
846
     * @covers \core_backup\hook\copy_helper_process_formdata
847
     */
848
    public function test_copy_helper_process_formdata_hook(): void {
849
        // Load the callback classes.
850
        require_once(__DIR__ . '/fixtures/helper_hooks.php');
851
 
852
        // Replace the version of the manager in the DI container with a phpunit one.
853
        di::set(
854
            manager::class,
855
            manager::phpunit_get_instance([
856
                // Load a list of hooks for `test_plugin1` from the fixture file.
857
                'test_plugin1' => __DIR__ .
858
                    '/fixtures/helper_hooks.php',
859
            ]),
860
        );
861
 
862
        $formdata = (object) [
863
            'courseid' => 4,
864
            'fullname' => 'Name',
865
            'shortname' => 'name',
866
            'category' => 12,
867
            'visible' => 1,
868
            'startdate' => '123456789',
869
            'enddate' => '123456789',
870
            'idnumber' => 'dnum',
871
            'userdata' => false,
872
            'extra' => 13,
873
        ];
874
 
875
        $processed = copy_helper::process_formdata($formdata);
876
 
877
        // Check that the extra fields are present.
878
        $this->assertTrue(isset($processed->extra));
879
        $this->assertEquals(13, $processed->extra);
880
    }
1 efrain 881
}