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